Fix mouse coordinates, sequence BLE discovery/encryption, and prevent mouse from disabling on-screen keyboard
This commit is contained in:
@@ -71,6 +71,8 @@ struct HidHostCtx {
|
||||
bool securityInitiated = false;
|
||||
bool typeResolutionDone = false;
|
||||
bool readyBlockFired = false;
|
||||
bool encrypted = false;
|
||||
bool dscsDiscovered = false;
|
||||
lv_indev_t* kbIndev = nullptr;
|
||||
lv_indev_t* mouseIndev = nullptr;
|
||||
lv_obj_t* mouseCursor = nullptr;
|
||||
@@ -187,16 +189,16 @@ static void hidHostMouseReadCb(lv_indev_t* /*indev*/, lv_indev_data_t* data) {
|
||||
data->point.y = (lv_coord_t)cy;
|
||||
break;
|
||||
case LV_DISPLAY_ROTATION_90:
|
||||
data->point.x = (lv_coord_t)cy;
|
||||
data->point.y = (lv_coord_t)(oh - cx - 1);
|
||||
data->point.x = (lv_coord_t)(ow - cy - 1);
|
||||
data->point.y = (lv_coord_t)cx;
|
||||
break;
|
||||
case LV_DISPLAY_ROTATION_180:
|
||||
data->point.x = (lv_coord_t)(ow - cx - 1);
|
||||
data->point.y = (lv_coord_t)(oh - cy - 1);
|
||||
break;
|
||||
case LV_DISPLAY_ROTATION_270:
|
||||
data->point.x = (lv_coord_t)(ow - cy - 1);
|
||||
data->point.y = (lv_coord_t)cx;
|
||||
data->point.x = (lv_coord_t)cy;
|
||||
data->point.y = (lv_coord_t)(oh - cx - 1);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
@@ -214,10 +216,29 @@ static void hidHostMouseReadCb(lv_indev_t* /*indev*/, lv_indev_data_t* data) {
|
||||
}
|
||||
|
||||
static void hidHostHandleMouseReport(const uint8_t* data, uint16_t len) {
|
||||
if (len < 3) return;
|
||||
bool btn = (data[0] & 0x01) != 0;
|
||||
int8_t dx = (int8_t)data[1];
|
||||
int8_t dy = (int8_t)data[2];
|
||||
int32_t dx = 0;
|
||||
int32_t dy = 0;
|
||||
bool btn = false;
|
||||
|
||||
if (len >= 5) {
|
||||
// 12-bit packed coordinate format (Logitech/high-res HID mice)
|
||||
btn = (data[0] & 0x01) != 0;
|
||||
|
||||
int16_t raw_x = data[2] | ((data[3] & 0x0F) << 8);
|
||||
if (raw_x & 0x0800) raw_x |= 0xF000; // sign extend 12-bit to 16-bit
|
||||
dx = (int16_t)raw_x;
|
||||
|
||||
int16_t raw_y = (data[3] >> 4) | (data[4] << 4);
|
||||
if (raw_y & 0x0800) raw_y |= 0xF000; // sign extend 12-bit to 16-bit
|
||||
dy = (int16_t)raw_y;
|
||||
} else if (len >= 3) {
|
||||
// Standard 3-byte boot protocol
|
||||
btn = (data[0] & 0x01) != 0;
|
||||
dx = (int8_t)data[1];
|
||||
dy = (int8_t)data[2];
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
lv_display_t* disp = lv_display_get_default();
|
||||
int32_t w = disp ? lv_display_get_horizontal_resolution(disp) : 320;
|
||||
@@ -254,17 +275,33 @@ static void hidHostHandleMouseReport(const uint8_t* data, uint16_t len) {
|
||||
}
|
||||
}
|
||||
|
||||
// ---- Asynchronous coordination helpers ----
|
||||
|
||||
static void hidHostOnDscsDiscovered(HidHostCtx& ctx) {
|
||||
ctx.dscsDiscovered = true;
|
||||
if (ctx.encrypted) {
|
||||
ctx.rptRefReadIdx = 0;
|
||||
hidHostStartRptRefRead(ctx);
|
||||
} else {
|
||||
LOGGER.info("Descriptors discovered, waiting for encryption");
|
||||
}
|
||||
}
|
||||
|
||||
static void hidHostOnEncrypted(HidHostCtx& ctx) {
|
||||
ctx.encrypted = true;
|
||||
if (ctx.dscsDiscovered) {
|
||||
ctx.rptRefReadIdx = 0;
|
||||
hidHostStartRptRefRead(ctx);
|
||||
} else {
|
||||
LOGGER.info("Encryption established, waiting for descriptor discovery");
|
||||
}
|
||||
}
|
||||
|
||||
// ---- Timer callback for post-encryption CCCD retry ----
|
||||
|
||||
static void hidEncRetryTimerCb(void* /*arg*/) {
|
||||
if (hid_host_ctx) {
|
||||
if (!hid_host_ctx->typeResolutionDone) {
|
||||
LOGGER.warn("Post-encryption delay — type resolution timed out, proceeding");
|
||||
hid_host_ctx->typeResolutionDone = true;
|
||||
hid_host_ctx->subscribeIdx = 0;
|
||||
} else {
|
||||
LOGGER.info("Post-encryption delay complete — starting CCCD subscriptions");
|
||||
}
|
||||
LOGGER.info("CCCD delay complete — starting subscriptions");
|
||||
hidHostSubscribeNext(*hid_host_ctx);
|
||||
}
|
||||
}
|
||||
@@ -395,7 +432,12 @@ static void hidHostReadReportMap(HidHostCtx& ctx) {
|
||||
LOGGER.info("No Report Map char — skipping type resolution");
|
||||
ctx.typeResolutionDone = true;
|
||||
ctx.subscribeIdx = 0;
|
||||
hidHostSubscribeNext(ctx);
|
||||
if (hid_enc_retry_timer) {
|
||||
esp_timer_stop(hid_enc_retry_timer);
|
||||
esp_timer_start_once(hid_enc_retry_timer, 500 * 1000);
|
||||
} else {
|
||||
hidHostSubscribeNext(ctx);
|
||||
}
|
||||
return;
|
||||
}
|
||||
int rc = ble_gattc_read_long(ctx.connHandle, ctx.rptMapHandle, 0,
|
||||
@@ -419,14 +461,25 @@ static void hidHostReadReportMap(HidHostCtx& ctx) {
|
||||
}
|
||||
ctx.typeResolutionDone = true;
|
||||
ctx.subscribeIdx = 0;
|
||||
hidHostSubscribeNext(ctx);
|
||||
LOGGER.info("Type resolution complete — delaying CCCD subscriptions by 500ms");
|
||||
if (hid_enc_retry_timer) {
|
||||
esp_timer_stop(hid_enc_retry_timer);
|
||||
esp_timer_start_once(hid_enc_retry_timer, 500 * 1000);
|
||||
} else {
|
||||
hidHostSubscribeNext(ctx);
|
||||
}
|
||||
return 0;
|
||||
}, nullptr);
|
||||
if (rc != 0) {
|
||||
LOGGER.warn("Report map read_long failed rc={} — skipping", rc);
|
||||
ctx.typeResolutionDone = true;
|
||||
ctx.subscribeIdx = 0;
|
||||
hidHostSubscribeNext(ctx);
|
||||
if (hid_enc_retry_timer) {
|
||||
esp_timer_stop(hid_enc_retry_timer);
|
||||
esp_timer_start_once(hid_enc_retry_timer, 500 * 1000);
|
||||
} else {
|
||||
hidHostSubscribeNext(ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -479,6 +532,19 @@ static void hidHostSubscribeNext(HidHostCtx& ctx) {
|
||||
}
|
||||
getMainDispatcher().dispatch([] {
|
||||
if (!hid_host_ctx || hid_host_ctx->kbIndev != nullptr) return;
|
||||
|
||||
bool has_keyboard = false;
|
||||
for (const auto& rpt : hid_host_ctx->inputRpts) {
|
||||
if (rpt.type == HidReportType::Keyboard || rpt.type == HidReportType::Unknown) {
|
||||
has_keyboard = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!has_keyboard) {
|
||||
LOGGER.info("No keyboard reports found — skipping keyboard indev registration");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!tt::lvgl::lock(1000)) { LOGGER.warn("LVGL lock failed for kb indev"); return; }
|
||||
auto* kb = lv_indev_create();
|
||||
lv_indev_set_type(kb, LV_INDEV_TYPE_KEYPAD);
|
||||
@@ -563,12 +629,10 @@ static int hidHostDscDiscCb(uint16_t conn_handle, const struct ble_gatt_error* e
|
||||
hidHostDscDiscCb, nullptr);
|
||||
if (rc != 0) {
|
||||
LOGGER.warn("disc_all_dscs[{}] failed rc={}", next_idx, rc);
|
||||
ctx.rptRefReadIdx = 0;
|
||||
hidHostStartRptRefRead(ctx);
|
||||
hidHostOnDscsDiscovered(ctx);
|
||||
}
|
||||
} else {
|
||||
ctx.rptRefReadIdx = 0;
|
||||
hidHostStartRptRefRead(ctx);
|
||||
hidHostOnDscsDiscovered(ctx);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
@@ -614,8 +678,7 @@ static int hidHostChrDiscCb(uint16_t conn_handle, const struct ble_gatt_error* e
|
||||
hidHostDscDiscCb, nullptr);
|
||||
if (rc != 0) {
|
||||
LOGGER.warn("disc_all_dscs[0] failed rc={}", rc);
|
||||
ctx.rptRefReadIdx = 0;
|
||||
hidHostStartRptRefRead(ctx);
|
||||
hidHostOnDscsDiscovered(ctx);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
@@ -662,6 +725,11 @@ static int hidHostGapCb(struct ble_gap_event* event, void* /*arg*/) {
|
||||
if (event->connect.status == 0) {
|
||||
ctx.connHandle = event->connect.conn_handle;
|
||||
LOGGER.info("Connected (handle={})", ctx.connHandle);
|
||||
|
||||
// Initiate security immediately to encrypt the link for HOGP (HID over GATT)
|
||||
ble_gap_security_initiate(ctx.connHandle);
|
||||
ctx.securityInitiated = true;
|
||||
|
||||
int rc = ble_gattc_disc_all_svcs(ctx.connHandle, hidHostSvcDiscCb, nullptr);
|
||||
if (rc != 0) {
|
||||
LOGGER.warn("disc_all_svcs failed rc={}", rc);
|
||||
@@ -725,14 +793,8 @@ static int hidHostGapCb(struct ble_gap_event* event, void* /*arg*/) {
|
||||
case BLE_GAP_EVENT_ENC_CHANGE:
|
||||
if (event->enc_change.conn_handle == ctx.connHandle) {
|
||||
if (event->enc_change.status == 0) {
|
||||
LOGGER.info("Encryption established — retrying CCCD in 500ms");
|
||||
ctx.subscribeIdx = 0;
|
||||
if (hid_enc_retry_timer) {
|
||||
esp_timer_stop(hid_enc_retry_timer);
|
||||
esp_timer_start_once(hid_enc_retry_timer, 500 * 1000);
|
||||
} else {
|
||||
hidHostSubscribeNext(ctx);
|
||||
}
|
||||
LOGGER.info("Encryption established — notifying state machine");
|
||||
hidHostOnEncrypted(ctx);
|
||||
} else {
|
||||
LOGGER.warn("Encryption failed status={}", event->enc_change.status);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user