diff --git a/components/bt/host/nimble/nimble b/components/bt/host/nimble/nimble index 5045cb5eb88..61a88ab53bd 160000 --- a/components/bt/host/nimble/nimble +++ b/components/bt/host/nimble/nimble @@ -1 +1 @@ -Subproject commit 5045cb5eb885acb84d4ea86fbf9e5e0202cf0e84 +Subproject commit 61a88ab53bde2aef4b9022fbd43368eff50c5a1f diff --git a/components/esp_hid/src/ble_hidh.c b/components/esp_hid/src/ble_hidh.c index b10119ed9b3..90ae8cb14e8 100644 --- a/components/esp_hid/src/ble_hidh.c +++ b/components/esp_hid/src/ble_hidh.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2017-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2017-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -302,6 +302,21 @@ static void attach_report_listeners(esp_gatt_if_t gattc_if, esp_hidh_dev_t *dev) //subscribe to battery notifications if (dev->ble.battery_handle) { + uint8_t *rdata = NULL; + uint16_t rlen = 0; + + if (event_loop_handle && + read_char(gattc_if, dev->ble.conn_id, dev->ble.battery_handle, + ESP_GATT_AUTH_REQ_NO_MITM, &rdata, &rlen) == ESP_GATT_OK && + rlen >= 1 && rdata != NULL) { + esp_hidh_event_data_t p = {0}; + p.battery.dev = dev; + p.battery.level = rdata[0]; + esp_event_post_to(event_loop_handle, ESP_HIDH_EVENTS, ESP_HIDH_BATTERY_EVENT, + &p, sizeof(esp_hidh_event_data_t), portMAX_DELAY); + } + free(rdata); + register_for_notify(gattc_if, dev->addr.bda, dev->ble.battery_handle); if (dev->ble.battery_ccc_handle) { //Write CCC descr to enable notifications diff --git a/components/esp_hid/src/nimble_hidd.c b/components/esp_hid/src/nimble_hidd.c index 3f6b7a9c47c..747677ff460 100644 --- a/components/esp_hid/src/nimble_hidd.c +++ b/components/esp_hid/src/nimble_hidd.c @@ -43,6 +43,9 @@ static void (*s_prev_sync_cb)(void) = NULL; static struct ble_gap_event_listener nimble_gap_event_listener; static void nimble_host_synced(void); void nimble_host_reset(int reason); +static void nimble_report_write_cb(uint16_t attr_handle, uint8_t report_type, uint8_t report_id, + const uint8_t *data, uint16_t len); +static void nimble_char_write_cb(uint16_t attr_handle, uint16_t char_uuid16, uint8_t value); static inline void lock_hidd(void) { @@ -349,6 +352,8 @@ static int nimble_hidd_dev_deinit(void *devp) ble_hs_cfg.sync_cb = s_prev_sync_cb; } ble_hs_cfg.gatts_register_cb = NULL; + ble_svc_hid_register_report_write_cb(NULL); + ble_svc_hid_register_char_write_cb(NULL); unlock_hidd(); /* STOP_EVENT may be discarded because ble_hid_free_config() deletes the event @@ -423,6 +428,129 @@ static hidd_le_report_item_t* find_report_by_usage_and_type(uint8_t dev_index, u return NULL; } +static void nimble_report_write_cb(uint16_t attr_handle, uint8_t report_type, uint8_t report_id, + const uint8_t *data, uint16_t len) +{ + lock_hidd(); + if (s_dev == NULL || s_dev->event_loop_handle == NULL || data == NULL) { + unlock_hidd(); + return; + } + + hidd_le_report_item_t *match = NULL; + uint8_t map_index = 0; + for (uint8_t d = 0; d < s_dev->devices_len && match == NULL; d++) { + for (uint8_t r = 0; r < s_dev->devices[d].reports_len; r++) { + hidd_le_report_item_t *item = &s_dev->devices[d].reports[r]; + if (item->handle == attr_handle) { + match = item; + map_index = d; + break; + } + } + } + + if (match == NULL) { + unlock_hidd(); + return; + } + + if (report_type != ESP_HID_REPORT_TYPE_OUTPUT && + report_type != ESP_HID_REPORT_TYPE_FEATURE) { + ESP_LOGD(TAG, "Ignoring host write for unsupported report type=%u, id=%u, handle=%u", + report_type, report_id, attr_handle); + unlock_hidd(); + return; + } + + size_t event_data_size = sizeof(esp_hidd_event_data_t); + if (len > 0) { + event_data_size += len; + } + esp_hidd_event_data_t *p_cb_param = (esp_hidd_event_data_t *)calloc(1, event_data_size); + if (p_cb_param == NULL) { + ESP_LOGE(TAG, "%s malloc event data failed!", __func__); + unlock_hidd(); + return; + } + + if (len > 0) { + memcpy(((uint8_t *)p_cb_param) + sizeof(esp_hidd_event_data_t), data, len); + } + + if (report_type == ESP_HID_REPORT_TYPE_OUTPUT) { + p_cb_param->output.dev = s_dev->dev; + p_cb_param->output.usage = match->usage; + p_cb_param->output.report_id = report_id; + p_cb_param->output.length = len; + p_cb_param->output.data = (len > 0) ? (uint8_t *)data : NULL; /* fixed by esp_hidd_process_event_data_handler */ + p_cb_param->output.map_index = map_index; + esp_event_post_to(s_dev->event_loop_handle, ESP_HIDD_EVENTS, ESP_HIDD_OUTPUT_EVENT, + p_cb_param, event_data_size, portMAX_DELAY); + } else if (report_type == ESP_HID_REPORT_TYPE_FEATURE) { + p_cb_param->feature.dev = s_dev->dev; + p_cb_param->feature.usage = match->usage; + p_cb_param->feature.report_id = report_id; + p_cb_param->feature.length = len; + p_cb_param->feature.data = (len > 0) ? (uint8_t *)data : NULL; /* fixed by esp_hidd_process_event_data_handler */ + p_cb_param->feature.map_index = map_index; + esp_event_post_to(s_dev->event_loop_handle, ESP_HIDD_EVENTS, ESP_HIDD_FEATURE_EVENT, + p_cb_param, event_data_size, portMAX_DELAY); + } + free(p_cb_param); + unlock_hidd(); +} + +static void nimble_char_write_cb(uint16_t attr_handle, uint16_t char_uuid16, uint8_t value) +{ + lock_hidd(); + if (s_dev == NULL || s_dev->event_loop_handle == NULL) { + unlock_hidd(); + return; + } + + uint8_t map_index = 0; + bool found = false; + + for (uint8_t d = 0; d < s_dev->devices_len; d++) { + if (char_uuid16 == BLE_SVC_HID_CHR_UUID16_PROTOCOL_MODE && + s_dev->devices[d].hid_protocol_handle == attr_handle) { + found = true; + map_index = d; + break; + } + if (char_uuid16 == BLE_SVC_HID_CHR_UUID16_HID_CTRL_PT && + s_dev->devices[d].hid_control_handle == attr_handle) { + found = true; + map_index = d; + break; + } + } + + if (!found) { + unlock_hidd(); + return; + } + + esp_hidd_event_data_t cb_param = {0}; + if (char_uuid16 == BLE_SVC_HID_CHR_UUID16_PROTOCOL_MODE) { + s_dev->protocol = value; + cb_param.protocol_mode.dev = s_dev->dev; + cb_param.protocol_mode.protocol_mode = value; + cb_param.protocol_mode.map_index = map_index; + esp_event_post_to(s_dev->event_loop_handle, ESP_HIDD_EVENTS, ESP_HIDD_PROTOCOL_MODE_EVENT, + &cb_param, sizeof(esp_hidd_event_data_t), portMAX_DELAY); + } else if (char_uuid16 == BLE_SVC_HID_CHR_UUID16_HID_CTRL_PT) { + s_dev->control = value; + cb_param.control.dev = s_dev->dev; + cb_param.control.control = value; + cb_param.control.map_index = map_index; + esp_event_post_to(s_dev->event_loop_handle, ESP_HIDD_EVENTS, ESP_HIDD_CONTROL_EVENT, + &cb_param, sizeof(esp_hidd_event_data_t), portMAX_DELAY); + } + unlock_hidd(); +} + static int nimble_hidd_dev_input_set(void *devp, size_t index, size_t id, uint8_t *data, size_t length) { hidd_le_report_item_t *p_rpt; @@ -840,6 +968,8 @@ esp_err_t esp_ble_hidd_dev_init(esp_hidd_dev_t *dev_p, const esp_hid_device_conf ble_hs_cfg.reset_cb = nimble_host_reset; ble_hs_cfg.sync_cb = nimble_host_synced; ble_hs_cfg.gatts_register_cb = nimble_gatt_svr_register_cb; + ble_svc_hid_register_report_write_cb(nimble_report_write_cb); + ble_svc_hid_register_char_write_cb(nimble_char_write_cb); rc = nimble_hid_start_gatts(); if (rc != ESP_OK) { if (ble_hs_cfg.reset_cb == nimble_host_reset) { @@ -849,6 +979,8 @@ esp_err_t esp_ble_hidd_dev_init(esp_hidd_dev_t *dev_p, const esp_hid_device_conf ble_hs_cfg.sync_cb = s_prev_sync_cb; } ble_hs_cfg.gatts_register_cb = NULL; + ble_svc_hid_register_report_write_cb(NULL); + ble_svc_hid_register_char_write_cb(NULL); ble_hidd_dev_free(); return rc; } diff --git a/components/esp_hid/src/nimble_hidh.c b/components/esp_hid/src/nimble_hidh.c index 4c63de9dc3d..a062ebd0b2f 100644 --- a/components/esp_hid/src/nimble_hidh.c +++ b/components/esp_hid/src/nimble_hidh.c @@ -70,14 +70,14 @@ static inline void SEND_CB(void) static inline void LOCK_OPS(void) { if (s_ble_hidh_op_mutex) { - xSemaphoreTake(s_ble_hidh_op_mutex, portMAX_DELAY); + xSemaphoreTakeRecursive(s_ble_hidh_op_mutex, portMAX_DELAY); } } static inline void UNLOCK_OPS(void) { if (s_ble_hidh_op_mutex) { - xSemaphoreGive(s_ble_hidh_op_mutex); + xSemaphoreGiveRecursive(s_ble_hidh_op_mutex); } } @@ -789,6 +789,20 @@ static void attach_report_listeners(esp_hidh_dev_t *dev) report = dev->reports; if (dev->ble.battery_handle) { + uint8_t *rdata = NULL; + uint16_t rlen = 0; + + if (event_loop_handle && + read_char(dev->ble.conn_id, dev->ble.battery_handle, &rdata, &rlen) == 0 && + rlen >= 1 && rdata != NULL) { + esp_hidh_event_data_t p = {0}; + p.battery.dev = dev; + p.battery.level = rdata[0]; + esp_event_post_to(event_loop_handle, ESP_HIDH_EVENTS, ESP_HIDH_BATTERY_EVENT, + &p, sizeof(esp_hidh_event_data_t), portMAX_DELAY); + } + free(rdata); + register_for_notify(dev->ble.conn_id, dev->ble.battery_handle); if (dev->ble.battery_ccc_handle && dev->ble.conn_id >= 0 && dev->connected) { write_char_descr(dev, dev->ble.battery_ccc_handle, 2, (uint8_t *)&ccc_data); @@ -1174,7 +1188,7 @@ esp_err_t esp_ble_hidh_init(const esp_hidh_config_t *config) s_ble_hidh_cb_semaphore = xSemaphoreCreateBinary(); ESP_RETURN_ON_FALSE(s_ble_hidh_cb_semaphore, ESP_ERR_NO_MEM, TAG, "Allocation failed"); - s_ble_hidh_op_mutex = xSemaphoreCreateMutex(); + s_ble_hidh_op_mutex = xSemaphoreCreateRecursiveMutex(); if (s_ble_hidh_op_mutex == NULL) { vSemaphoreDelete(s_ble_hidh_cb_semaphore); s_ble_hidh_cb_semaphore = NULL; diff --git a/examples/bluetooth/esp_hid_device/main/esp_hid_device_main.c b/examples/bluetooth/esp_hid_device/main/esp_hid_device_main.c index d0081b843be..39e760f7e55 100644 --- a/examples/bluetooth/esp_hid_device/main/esp_hid_device_main.c +++ b/examples/bluetooth/esp_hid_device/main/esp_hid_device_main.c @@ -41,6 +41,7 @@ #include "esp_hid_gap.h" static const char *TAG = "HID_DEV_DEMO"; +#define HID_BATTERY_LEVEL 60 typedef struct { @@ -941,6 +942,7 @@ void app_main(void) ESP_LOGI(TAG, "setting ble device"); ESP_ERROR_CHECK( esp_hidd_dev_init(&ble_hid_config, ESP_HID_TRANSPORT_BLE, ble_hidd_event_callback, &s_ble_hid_param.hid_dev)); + ESP_ERROR_CHECK(esp_hidd_dev_battery_set(s_ble_hid_param.hid_dev, HID_BATTERY_LEVEL)); #endif #if CONFIG_BT_HID_DEVICE_ENABLED