fix(nimble): Address review comments for hidd / hidh code

This commit is contained in:
Rahul Tank
2026-04-14 15:40:18 +05:30
committed by Shreeyash Bhakare
parent 02b57e7e77
commit 541065755d
9 changed files with 945 additions and 346 deletions
@@ -93,9 +93,9 @@ void btc_blufi_report_error(esp_blufi_error_state_t state)
btc_transfer_context(&msg, &param, sizeof(esp_blufi_cb_param_t), NULL, NULL); btc_transfer_context(&msg, &param, sizeof(esp_blufi_cb_param_t), NULL, NULL);
} }
/* FALSE POSITIVE: blufi_env is a single-connection global by design. /* blufi_env is a global shared state but only one BLE connection is active at
* The GAP layer rejects new connections while is_connected==true, * a time — the GAP layer rejects new connections while is_connected==true,
* so concurrent multi-connection access to this state never occurs. */ * so there is no concurrent access to this structure. */
void btc_blufi_recv_handler(uint8_t *data, int len) void btc_blufi_recv_handler(uint8_t *data, int len)
{ {
if (len < sizeof(struct blufi_hdr)) { if (len < sizeof(struct blufi_hdr)) {
@@ -239,9 +239,10 @@ void btc_blufi_recv_handler(uint8_t *data, int len)
} }
} }
} }
/* Known limitation: this function runs in the BTC task (app-initiated sends)
* AND in the NimBLE task (via recv_handler), racing on send_seq/frag_size. /* This function is called from both the BTC task and the NimBLE task, so
* portENTER_CRITICAL cannot protect here; */ * send_seq/frag_size may race. portENTER_CRITICAL does not help across
* different FreeRTOS tasks; a mutex would be needed for full protection. */
void btc_blufi_send_encap(uint8_t type, uint8_t *data, int total_data_len) void btc_blufi_send_encap(uint8_t type, uint8_t *data, int total_data_len)
{ {
struct blufi_hdr *hdr = NULL; struct blufi_hdr *hdr = NULL;
@@ -53,9 +53,9 @@ static const struct ble_gatt_svc_def gatt_svr_svcs[] = {
.uuid = BLE_UUID16_DECLARE(BLUFI_SERVICE_UUID), .uuid = BLE_UUID16_DECLARE(BLUFI_SERVICE_UUID),
.characteristics = (struct ble_gatt_chr_def[]) .characteristics = (struct ble_gatt_chr_def[])
{ { { {
/* FALSE POSITIVE: No BLE_GATT_CHR_F_WRITE_ENC/READ_ENC by design. /* BLE_GATT_CHR_F_WRITE_ENC/READ_ENC are not set because BLUFI uses its
* BLUFI uses its own app-layer security (DH key exchange + AES + CRC). * own application-layer security (DH key exchange + AES-128 + CRC).
* BLE link-layer pairing is impossible before provisioning credentials exist. */ * BLE pairing cannot occur before Wi-Fi credentials are provisioned. */
/*** Characteristic: P2E */ /*** Characteristic: P2E */
.uuid = BLE_UUID16_DECLARE(BLUFI_CHAR_P2E_UUID), .uuid = BLE_UUID16_DECLARE(BLUFI_CHAR_P2E_UUID),
.access_cb = gatt_svr_access_cb, .access_cb = gatt_svr_access_cb,
+55 -48
View File
@@ -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 * SPDX-License-Identifier: Apache-2.0
*/ */
@@ -40,6 +40,34 @@ static inline void unlock_devices(void)
} }
} }
/*
* Atomically checks whether a device is still in the global list and acquires
* its mutex before releasing the list lock. This avoids TOCTOU between
* `esp_hidh_dev_exists()` and `esp_hidh_dev_lock()` for call sites that need
* a stable pointer.
*/
static bool lock_known_device(esp_hidh_dev_t *dev)
{
if (dev == NULL) {
return false;
}
bool found = false;
esp_hidh_dev_t *d = NULL;
lock_devices();
TAILQ_FOREACH(d, &s_esp_hidh_devices, devices) {
if (d == dev) {
found = true;
if (dev->mutex != NULL) {
xSemaphoreTake(dev->mutex, portMAX_DELAY);
}
break;
}
}
unlock_devices();
return found;
}
/* /*
* Public Functions * Public Functions
* */ * */
@@ -207,8 +235,7 @@ esp_hidh_dev_t *esp_hidh_dev_open(uint8_t *bda, esp_hid_transport_t transport, u
esp_err_t esp_hidh_dev_close(esp_hidh_dev_t *dev) esp_err_t esp_hidh_dev_close(esp_hidh_dev_t *dev)
{ {
esp_err_t ret = ESP_OK; esp_err_t ret = ESP_OK;
if (esp_hidh_dev_exists(dev)) { if (lock_known_device(dev)) {
esp_hidh_dev_lock(dev);
ret = dev->close(dev); ret = dev->close(dev);
esp_hidh_dev_unlock(dev); esp_hidh_dev_unlock(dev);
} else { } else {
@@ -219,8 +246,7 @@ esp_err_t esp_hidh_dev_close(esp_hidh_dev_t *dev)
void esp_hidh_dev_dump(esp_hidh_dev_t *dev, FILE *fp) void esp_hidh_dev_dump(esp_hidh_dev_t *dev, FILE *fp)
{ {
if (esp_hidh_dev_exists(dev)) { if (lock_known_device(dev)) {
esp_hidh_dev_lock(dev);
dev->dump(dev, fp); dev->dump(dev, fp);
esp_hidh_dev_unlock(dev); esp_hidh_dev_unlock(dev);
} }
@@ -229,8 +255,7 @@ void esp_hidh_dev_dump(esp_hidh_dev_t *dev, FILE *fp)
esp_err_t esp_hidh_dev_output_set(esp_hidh_dev_t *dev, size_t map_index, size_t report_id, uint8_t *value, size_t value_len) esp_err_t esp_hidh_dev_output_set(esp_hidh_dev_t *dev, size_t map_index, size_t report_id, uint8_t *value, size_t value_len)
{ {
esp_err_t ret = ESP_OK; esp_err_t ret = ESP_OK;
if (esp_hidh_dev_exists(dev)) { if (lock_known_device(dev)) {
esp_hidh_dev_lock(dev);
ret = dev->report_write(dev, map_index, report_id, ESP_HID_REPORT_TYPE_OUTPUT, value, value_len); ret = dev->report_write(dev, map_index, report_id, ESP_HID_REPORT_TYPE_OUTPUT, value, value_len);
esp_hidh_dev_unlock(dev); esp_hidh_dev_unlock(dev);
} else { } else {
@@ -242,8 +267,7 @@ esp_err_t esp_hidh_dev_output_set(esp_hidh_dev_t *dev, size_t map_index, size_t
esp_err_t esp_hidh_dev_feature_set(esp_hidh_dev_t *dev, size_t map_index, size_t report_id, uint8_t *value, size_t value_len) esp_err_t esp_hidh_dev_feature_set(esp_hidh_dev_t *dev, size_t map_index, size_t report_id, uint8_t *value, size_t value_len)
{ {
esp_err_t ret = ESP_OK; esp_err_t ret = ESP_OK;
if (esp_hidh_dev_exists(dev)) { if (lock_known_device(dev)) {
esp_hidh_dev_lock(dev);
ret = dev->report_write(dev, map_index, report_id, ESP_HID_REPORT_TYPE_FEATURE, value, value_len); ret = dev->report_write(dev, map_index, report_id, ESP_HID_REPORT_TYPE_FEATURE, value, value_len);
esp_hidh_dev_unlock(dev); esp_hidh_dev_unlock(dev);
} else { } else {
@@ -255,8 +279,7 @@ esp_err_t esp_hidh_dev_feature_set(esp_hidh_dev_t *dev, size_t map_index, size_t
esp_err_t esp_hidh_dev_feature_get(esp_hidh_dev_t *dev, size_t map_index, size_t report_id, size_t max_length, uint8_t *value, size_t *value_len) esp_err_t esp_hidh_dev_feature_get(esp_hidh_dev_t *dev, size_t map_index, size_t report_id, size_t max_length, uint8_t *value, size_t *value_len)
{ {
esp_err_t ret = ESP_OK; esp_err_t ret = ESP_OK;
if (esp_hidh_dev_exists(dev)) { if (lock_known_device(dev)) {
esp_hidh_dev_lock(dev);
ret = dev->report_read(dev, map_index, report_id, ESP_HID_REPORT_TYPE_FEATURE, max_length, value, value_len); ret = dev->report_read(dev, map_index, report_id, ESP_HID_REPORT_TYPE_FEATURE, max_length, value, value_len);
esp_hidh_dev_unlock(dev); esp_hidh_dev_unlock(dev);
} else { } else {
@@ -268,8 +291,7 @@ esp_err_t esp_hidh_dev_feature_get(esp_hidh_dev_t *dev, size_t map_index, size_t
esp_err_t esp_hidh_dev_set_report(esp_hidh_dev_t *dev, size_t map_index, size_t report_id, int report_type, uint8_t *data, size_t length) esp_err_t esp_hidh_dev_set_report(esp_hidh_dev_t *dev, size_t map_index, size_t report_id, int report_type, uint8_t *data, size_t length)
{ {
esp_err_t ret = ESP_OK; esp_err_t ret = ESP_OK;
if (esp_hidh_dev_exists(dev)) { if (lock_known_device(dev)) {
esp_hidh_dev_lock(dev);
if (dev->set_report) { if (dev->set_report) {
ret = dev->set_report(dev, map_index, report_id, report_type, data, length); ret = dev->set_report(dev, map_index, report_id, report_type, data, length);
} else { } else {
@@ -286,8 +308,7 @@ esp_err_t esp_hidh_dev_get_report(esp_hidh_dev_t *dev, size_t map_index, size_t
size_t max_len) size_t max_len)
{ {
esp_err_t ret = ESP_OK; esp_err_t ret = ESP_OK;
if (esp_hidh_dev_exists(dev)) { if (lock_known_device(dev)) {
esp_hidh_dev_lock(dev);
ret = dev->report_read(dev, map_index, report_id, report_type, max_len, NULL, NULL); ret = dev->report_read(dev, map_index, report_id, report_type, max_len, NULL, NULL);
esp_hidh_dev_unlock(dev); esp_hidh_dev_unlock(dev);
} else { } else {
@@ -299,8 +320,7 @@ esp_err_t esp_hidh_dev_get_report(esp_hidh_dev_t *dev, size_t map_index, size_t
esp_err_t esp_hidh_dev_get_idle(esp_hidh_dev_t *dev) esp_err_t esp_hidh_dev_get_idle(esp_hidh_dev_t *dev)
{ {
esp_err_t ret = ESP_OK; esp_err_t ret = ESP_OK;
if (esp_hidh_dev_exists(dev)) { if (lock_known_device(dev)) {
esp_hidh_dev_lock(dev);
if (dev->get_idle) { if (dev->get_idle) {
ret = dev->get_idle(dev); ret = dev->get_idle(dev);
} else { } else {
@@ -316,8 +336,7 @@ esp_err_t esp_hidh_dev_get_idle(esp_hidh_dev_t *dev)
esp_err_t esp_hidh_dev_set_idle(esp_hidh_dev_t *dev, uint8_t idle_time) esp_err_t esp_hidh_dev_set_idle(esp_hidh_dev_t *dev, uint8_t idle_time)
{ {
esp_err_t ret = ESP_OK; esp_err_t ret = ESP_OK;
if (esp_hidh_dev_exists(dev)) { if (lock_known_device(dev)) {
esp_hidh_dev_lock(dev);
if (dev->set_idle) { if (dev->set_idle) {
ret = dev->set_idle(dev, idle_time); ret = dev->set_idle(dev, idle_time);
} else { } else {
@@ -333,8 +352,7 @@ esp_err_t esp_hidh_dev_set_idle(esp_hidh_dev_t *dev, uint8_t idle_time)
esp_err_t esp_hidh_dev_get_protocol(esp_hidh_dev_t *dev) esp_err_t esp_hidh_dev_get_protocol(esp_hidh_dev_t *dev)
{ {
esp_err_t ret = ESP_OK; esp_err_t ret = ESP_OK;
if (esp_hidh_dev_exists(dev)) { if (lock_known_device(dev)) {
esp_hidh_dev_lock(dev);
if (dev->get_protocol) { if (dev->get_protocol) {
ret = dev->get_protocol(dev); ret = dev->get_protocol(dev);
} else { } else {
@@ -350,8 +368,7 @@ esp_err_t esp_hidh_dev_get_protocol(esp_hidh_dev_t *dev)
esp_err_t esp_hidh_dev_set_protocol(esp_hidh_dev_t *dev, uint8_t protocol_mode) esp_err_t esp_hidh_dev_set_protocol(esp_hidh_dev_t *dev, uint8_t protocol_mode)
{ {
esp_err_t ret = ESP_OK; esp_err_t ret = ESP_OK;
if (esp_hidh_dev_exists(dev)) { if (lock_known_device(dev)) {
esp_hidh_dev_lock(dev);
if (dev->set_protocol) { if (dev->set_protocol) {
ret = dev->set_protocol(dev, protocol_mode); ret = dev->set_protocol(dev, protocol_mode);
} else { } else {
@@ -368,8 +385,7 @@ const uint8_t *esp_hidh_dev_bda_get(esp_hidh_dev_t *dev)
{ {
uint8_t *ret = NULL; uint8_t *ret = NULL;
#if CONFIG_BLUEDROID_ENABLED || CONFIG_BT_NIMBLE_ENABLED #if CONFIG_BLUEDROID_ENABLED || CONFIG_BT_NIMBLE_ENABLED
if (esp_hidh_dev_exists(dev)) { if (lock_known_device(dev)) {
esp_hidh_dev_lock(dev);
ret = dev->addr.bda; ret = dev->addr.bda;
esp_hidh_dev_unlock(dev); esp_hidh_dev_unlock(dev);
} }
@@ -380,8 +396,7 @@ const uint8_t *esp_hidh_dev_bda_get(esp_hidh_dev_t *dev)
esp_hid_transport_t esp_hidh_dev_transport_get(esp_hidh_dev_t *dev) esp_hid_transport_t esp_hidh_dev_transport_get(esp_hidh_dev_t *dev)
{ {
esp_hid_transport_t ret = ESP_HID_TRANSPORT_MAX; esp_hid_transport_t ret = ESP_HID_TRANSPORT_MAX;
if (esp_hidh_dev_exists(dev)) { if (lock_known_device(dev)) {
esp_hidh_dev_lock(dev);
ret = dev->transport; ret = dev->transport;
esp_hidh_dev_unlock(dev); esp_hidh_dev_unlock(dev);
} }
@@ -391,8 +406,7 @@ esp_hid_transport_t esp_hidh_dev_transport_get(esp_hidh_dev_t *dev)
const esp_hid_device_config_t *esp_hidh_dev_config_get(esp_hidh_dev_t *dev) const esp_hid_device_config_t *esp_hidh_dev_config_get(esp_hidh_dev_t *dev)
{ {
esp_hid_device_config_t *ret = NULL; esp_hid_device_config_t *ret = NULL;
if (esp_hidh_dev_exists(dev)) { if (lock_known_device(dev)) {
esp_hidh_dev_lock(dev);
ret = &dev->config; ret = &dev->config;
esp_hidh_dev_unlock(dev); esp_hidh_dev_unlock(dev);
} }
@@ -402,8 +416,7 @@ const esp_hid_device_config_t *esp_hidh_dev_config_get(esp_hidh_dev_t *dev)
const char *esp_hidh_dev_name_get(esp_hidh_dev_t *dev) const char *esp_hidh_dev_name_get(esp_hidh_dev_t *dev)
{ {
const char * ret = NULL; const char * ret = NULL;
if (esp_hidh_dev_exists(dev)) { if (lock_known_device(dev)) {
esp_hidh_dev_lock(dev);
ret = dev->config.device_name ? dev->config.device_name : ""; ret = dev->config.device_name ? dev->config.device_name : "";
esp_hidh_dev_unlock(dev); esp_hidh_dev_unlock(dev);
} }
@@ -413,8 +426,7 @@ const char *esp_hidh_dev_name_get(esp_hidh_dev_t *dev)
const char *esp_hidh_dev_manufacturer_get(esp_hidh_dev_t *dev) const char *esp_hidh_dev_manufacturer_get(esp_hidh_dev_t *dev)
{ {
const char *ret = NULL; const char *ret = NULL;
if (esp_hidh_dev_exists(dev)) { if (lock_known_device(dev)) {
esp_hidh_dev_lock(dev);
ret = dev->config.manufacturer_name ? dev->config.manufacturer_name : ""; ret = dev->config.manufacturer_name ? dev->config.manufacturer_name : "";
esp_hidh_dev_unlock(dev); esp_hidh_dev_unlock(dev);
} }
@@ -424,8 +436,7 @@ const char *esp_hidh_dev_manufacturer_get(esp_hidh_dev_t *dev)
const char *esp_hidh_dev_serial_get(esp_hidh_dev_t *dev) const char *esp_hidh_dev_serial_get(esp_hidh_dev_t *dev)
{ {
const char *ret = NULL; const char *ret = NULL;
if (esp_hidh_dev_exists(dev)) { if (lock_known_device(dev)) {
esp_hidh_dev_lock(dev);
ret = dev->config.serial_number ? dev->config.serial_number : ""; ret = dev->config.serial_number ? dev->config.serial_number : "";
esp_hidh_dev_unlock(dev); esp_hidh_dev_unlock(dev);
} }
@@ -435,8 +446,7 @@ const char *esp_hidh_dev_serial_get(esp_hidh_dev_t *dev)
uint16_t esp_hidh_dev_vendor_id_get(esp_hidh_dev_t *dev) uint16_t esp_hidh_dev_vendor_id_get(esp_hidh_dev_t *dev)
{ {
uint16_t ret = 0; uint16_t ret = 0;
if (esp_hidh_dev_exists(dev)) { if (lock_known_device(dev)) {
esp_hidh_dev_lock(dev);
ret = dev->config.vendor_id; ret = dev->config.vendor_id;
esp_hidh_dev_unlock(dev); esp_hidh_dev_unlock(dev);
} }
@@ -446,8 +456,7 @@ uint16_t esp_hidh_dev_vendor_id_get(esp_hidh_dev_t *dev)
uint16_t esp_hidh_dev_product_id_get(esp_hidh_dev_t *dev) uint16_t esp_hidh_dev_product_id_get(esp_hidh_dev_t *dev)
{ {
uint16_t ret = 0; uint16_t ret = 0;
if (esp_hidh_dev_exists(dev)) { if (lock_known_device(dev)) {
esp_hidh_dev_lock(dev);
ret = dev->config.product_id; ret = dev->config.product_id;
esp_hidh_dev_unlock(dev); esp_hidh_dev_unlock(dev);
} }
@@ -457,8 +466,7 @@ uint16_t esp_hidh_dev_product_id_get(esp_hidh_dev_t *dev)
uint16_t esp_hidh_dev_version_get(esp_hidh_dev_t *dev) uint16_t esp_hidh_dev_version_get(esp_hidh_dev_t *dev)
{ {
uint16_t ret = 0; uint16_t ret = 0;
if (!esp_hidh_dev_exists(dev)) { if (lock_known_device(dev)) {
esp_hidh_dev_lock(dev);
ret = dev->config.version; ret = dev->config.version;
esp_hidh_dev_unlock(dev); esp_hidh_dev_unlock(dev);
} }
@@ -468,8 +476,7 @@ uint16_t esp_hidh_dev_version_get(esp_hidh_dev_t *dev)
esp_hid_usage_t esp_hidh_dev_usage_get(esp_hidh_dev_t *dev) esp_hid_usage_t esp_hidh_dev_usage_get(esp_hidh_dev_t *dev)
{ {
esp_hid_usage_t ret = ESP_HID_USAGE_GENERIC; esp_hid_usage_t ret = ESP_HID_USAGE_GENERIC;
if (esp_hidh_dev_exists(dev)) { if (lock_known_device(dev)) {
esp_hidh_dev_lock(dev);
ret = dev->usage; ret = dev->usage;
esp_hidh_dev_unlock(dev); esp_hidh_dev_unlock(dev);
} }
@@ -481,11 +488,9 @@ esp_err_t esp_hidh_dev_reports_get(esp_hidh_dev_t *dev, size_t *num_reports, esp
esp_err_t ret = 0; esp_err_t ret = 0;
esp_hid_report_item_t *r = NULL; esp_hid_report_item_t *r = NULL;
if (!esp_hidh_dev_exists(dev)) { if (!lock_known_device(dev)) {
return ESP_FAIL; return ESP_FAIL;
} }
esp_hidh_dev_lock(dev);
do { do {
r = (esp_hid_report_item_t *)malloc(sizeof(esp_hid_report_item_t) * dev->reports_len); r = (esp_hid_report_item_t *)malloc(sizeof(esp_hid_report_item_t) * dev->reports_len);
if (r == NULL) { if (r == NULL) {
@@ -521,10 +526,9 @@ error_:;
esp_err_t esp_hidh_dev_report_maps_get(esp_hidh_dev_t *dev, size_t *num_maps, esp_hid_raw_report_map_t **maps) esp_err_t esp_hidh_dev_report_maps_get(esp_hidh_dev_t *dev, size_t *num_maps, esp_hid_raw_report_map_t **maps)
{ {
if (!esp_hidh_dev_exists(dev)) { if (!lock_known_device(dev)) {
return ESP_FAIL; return ESP_FAIL;
} }
esp_hidh_dev_lock(dev);
*num_maps = dev->config.report_maps_len; *num_maps = dev->config.report_maps_len;
*maps = dev->config.report_maps; *maps = dev->config.report_maps;
esp_hidh_dev_unlock(dev); esp_hidh_dev_unlock(dev);
@@ -692,6 +696,9 @@ static void esp_hidh_dev_resources_free(esp_hidh_dev_t *dev)
} }
} }
free((void *)dev->config.report_maps); free((void *)dev->config.report_maps);
#if CONFIG_BT_NIMBLE_ENABLED
free((void *)dev->protocol_mode);
#endif
esp_hidh_dev_report_t *r; esp_hidh_dev_report_t *r;
while (dev->reports) { while (dev->reports) {
r = dev->reports; r = dev->reports;
+180 -42
View File
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2017-2025 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2017-2026 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
@@ -10,6 +10,7 @@
#include "ble_hidd.h" #include "ble_hidd.h"
#include "esp_private/esp_hidd_private.h" #include "esp_private/esp_hidd_private.h"
#include "esp_log.h" #include "esp_log.h"
#include "freertos/semphr.h"
#include <assert.h> #include <assert.h>
#include <string.h> #include <string.h>
@@ -35,6 +36,27 @@ static const char *TAG = "NIMBLE_HIDD";
typedef struct esp_ble_hidd_dev_s esp_ble_hidd_dev_t; typedef struct esp_ble_hidd_dev_s esp_ble_hidd_dev_t;
// there can be only one BLE HID device // there can be only one BLE HID device
static esp_ble_hidd_dev_t *s_dev = NULL; static esp_ble_hidd_dev_t *s_dev = NULL;
static SemaphoreHandle_t s_hidd_mutex = NULL;
static bool s_gap_listener_registered = false;
static void (*s_prev_reset_cb)(int reason) = NULL;
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 inline void lock_hidd(void)
{
if (s_hidd_mutex) {
xSemaphoreTake(s_hidd_mutex, portMAX_DELAY);
}
}
static inline void unlock_hidd(void)
{
if (s_hidd_mutex) {
xSemaphoreGive(s_hidd_mutex);
}
}
/** service index is used to identify the hid service instance /** service index is used to identify the hid service instance
of the registered characteristic. of the registered characteristic.
Assuming the first instance of the hid service is registered first. Assuming the first instance of the hid service is registered first.
@@ -90,7 +112,10 @@ static int create_hid_db(int device_index)
/* fill hid info */ /* fill hid info */
memcpy(&hparams.hid_info, hidInfo, sizeof hparams.hid_info); memcpy(&hparams.hid_info, hidInfo, sizeof hparams.hid_info);
/* fill report map */ if (s_dev->devices[device_index].reports_map.len > REPORT_MAP_SIZE) {
ESP_LOGE(TAG, "Report map too large (%d > %d); aborting", s_dev->devices[device_index].reports_map.len, REPORT_MAP_SIZE);
return ESP_FAIL;
}
memcpy(&hparams.report_map, (uint8_t *)s_dev->devices[device_index].reports_map.data, s_dev->devices[device_index].reports_map.len); memcpy(&hparams.report_map, (uint8_t *)s_dev->devices[device_index].reports_map.data, s_dev->devices[device_index].reports_map.len);
hparams.report_map_len = s_dev->devices[device_index].reports_map.len; hparams.report_map_len = s_dev->devices[device_index].reports_map.len;
hparams.external_rpt_ref = BLE_SVC_BAS_UUID16; hparams.external_rpt_ref = BLE_SVC_BAS_UUID16;
@@ -102,6 +127,10 @@ static int create_hid_db(int device_index)
for (uint8_t i = 0; i < s_dev->devices[device_index].reports_len; i++) { for (uint8_t i = 0; i < s_dev->devices[device_index].reports_len; i++) {
hidd_le_report_item_t *report = &s_dev->devices[device_index].reports[i]; hidd_le_report_item_t *report = &s_dev->devices[device_index].reports[i];
if (report->protocol_mode == ESP_HID_PROTOCOL_MODE_REPORT) { if (report->protocol_mode == ESP_HID_PROTOCOL_MODE_REPORT) {
if (report_mode_rpts >= MAX_REPORTS) {
ESP_LOGE(TAG, "Too many report-mode reports (%d >= MAX_REPORTS); truncating", report_mode_rpts);
break;
}
/* only consider report mode reports, all boot mode reports will be registered by default */ /* only consider report mode reports, all boot mode reports will be registered by default */
if (report->report_type == ESP_HID_REPORT_TYPE_INPUT) { if (report->report_type == ESP_HID_REPORT_TYPE_INPUT) {
/* Input Report */ /* Input Report */
@@ -186,6 +215,15 @@ static int nimble_hid_stop_gatts(esp_ble_hidd_dev_t *dev)
{ {
int rc = ESP_OK; int rc = ESP_OK;
if (s_gap_listener_registered) {
ble_gap_event_listener_unregister(&nimble_gap_event_listener);
s_gap_listener_registered = false;
}
if (dev && dev->connected) {
ble_gap_terminate(dev->conn_id, BLE_ERR_REM_USER_CONN_TERM);
}
/* stop gatt database */ /* stop gatt database */
ble_gatts_stop(); ble_gatts_stop();
@@ -249,6 +287,7 @@ static int ble_hid_init_config(esp_ble_hidd_dev_t *dev, const esp_hid_device_con
dev->devices[d].reports = (hidd_le_report_item_t *)malloc(rmap->reports_len * sizeof(hidd_le_report_item_t)); dev->devices[d].reports = (hidd_le_report_item_t *)malloc(rmap->reports_len * sizeof(hidd_le_report_item_t));
if (dev->devices[d].reports == NULL) { if (dev->devices[d].reports == NULL) {
ESP_LOGE(TAG, "reports malloc(%d) failed", rmap->reports_len * sizeof(hidd_le_report_item_t)); ESP_LOGE(TAG, "reports malloc(%d) failed", rmap->reports_len * sizeof(hidd_le_report_item_t));
free(rmap->reports);
free(rmap); free(rmap);
return ESP_FAIL; return ESP_FAIL;
} }
@@ -287,22 +326,40 @@ static int ble_hid_free_config(esp_ble_hidd_dev_t *dev)
static int nimble_hidd_dev_deinit(void *devp) static int nimble_hidd_dev_deinit(void *devp)
{ {
esp_ble_hidd_dev_t *dev = (esp_ble_hidd_dev_t *)devp; esp_ble_hidd_dev_t *dev = (esp_ble_hidd_dev_t *)devp;
lock_hidd();
if (!s_dev) { if (!s_dev) {
ESP_LOGE(TAG, "HID device profile already uninitialized"); ESP_LOGE(TAG, "HID device profile already uninitialized");
unlock_hidd();
return ESP_OK; return ESP_OK;
} }
if (s_dev != dev) { if (s_dev != dev) {
ESP_LOGE(TAG, "Wrong HID device provided"); ESP_LOGE(TAG, "Wrong HID device provided");
unlock_hidd();
return ESP_FAIL; return ESP_FAIL;
} }
s_dev = NULL;
service_index = -1; // resetting the value
nimble_hid_stop_gatts(dev); nimble_hid_stop_gatts(dev);
s_dev = NULL;
service_index = -1;
if (ble_hs_cfg.reset_cb == nimble_host_reset) {
ble_hs_cfg.reset_cb = s_prev_reset_cb;
}
if (ble_hs_cfg.sync_cb == nimble_host_synced) {
ble_hs_cfg.sync_cb = s_prev_sync_cb;
}
ble_hs_cfg.gatts_register_cb = NULL;
unlock_hidd();
/* STOP_EVENT may be discarded because ble_hid_free_config() deletes the event
* loop right after this call. This does not cause a crash or data corruption. */
esp_event_post_to(dev->event_loop_handle, ESP_HIDD_EVENTS, ESP_HIDD_STOP_EVENT, NULL, 0, portMAX_DELAY); esp_event_post_to(dev->event_loop_handle, ESP_HIDD_EVENTS, ESP_HIDD_STOP_EVENT, NULL, 0, portMAX_DELAY);
ble_hid_free_config(dev); ble_hid_free_config(dev);
free(dev); free(dev);
if (s_hidd_mutex) {
vSemaphoreDelete(s_hidd_mutex);
s_hidd_mutex = NULL;
}
return ESP_OK; return ESP_OK;
} }
@@ -316,20 +373,19 @@ static int nimble_hidd_dev_battery_set(void *devp, uint8_t level)
{ {
int rc; int rc;
esp_ble_hidd_dev_t *dev = (esp_ble_hidd_dev_t *)devp; esp_ble_hidd_dev_t *dev = (esp_ble_hidd_dev_t *)devp;
lock_hidd();
if (!dev || s_dev != dev) { if (!dev || s_dev != dev) {
unlock_hidd();
return ESP_FAIL; return ESP_FAIL;
} }
if (!dev->connected) {
/* Return success if not yet connected */
return ESP_OK;
}
rc = ble_svc_bas_battery_level_set(level); rc = ble_svc_bas_battery_level_set(level);
if (rc) { if (rc != 0 && dev->connected) {
ESP_LOGE(TAG, "esp_ble_gatts_send_notify failed: %d", rc); ESP_LOGE(TAG, "ble_svc_bas_battery_level_set failed: %d", rc);
unlock_hidd();
return ESP_FAIL; return ESP_FAIL;
} }
unlock_hidd();
return ESP_OK; return ESP_OK;
} }
@@ -338,6 +394,9 @@ static int nimble_hidd_dev_battery_set(void *devp, uint8_t level)
static hidd_le_report_item_t* find_report(uint8_t id, uint8_t type, uint8_t *mode) static hidd_le_report_item_t* find_report(uint8_t id, uint8_t type, uint8_t *mode)
{ {
hidd_le_report_item_t *rpt; hidd_le_report_item_t *rpt;
if (!s_dev) {
return NULL;
}
for (uint8_t d = 0; d < s_dev->devices_len; d++) { for (uint8_t d = 0; d < s_dev->devices_len; d++) {
for (uint8_t i = 0; i < s_dev->devices[d].reports_len; i++) { for (uint8_t i = 0; i < s_dev->devices[d].reports_len; i++) {
rpt = &s_dev->devices[d].reports[i]; rpt = &s_dev->devices[d].reports[i];
@@ -348,15 +407,17 @@ static hidd_le_report_item_t* find_report(uint8_t id, uint8_t type, uint8_t *mod
} }
return NULL; return NULL;
} }
static hidd_le_report_item_t* find_report_by_usage_and_type(uint8_t usage, uint8_t type, uint8_t *mode)
static hidd_le_report_item_t* find_report_by_usage_and_type(uint8_t dev_index, uint8_t usage, uint8_t type, uint8_t *mode)
{ {
hidd_le_report_item_t *rpt; hidd_le_report_item_t *rpt;
for (uint8_t d = 0; d < s_dev->devices_len; d++) { if (!s_dev || dev_index >= s_dev->devices_len) {
for (uint8_t i = 0; i < s_dev->devices[d].reports_len; i++) { return NULL;
rpt = &s_dev->devices[d].reports[i]; }
if (rpt->usage == usage && rpt->report_type == type && (!mode || (mode && *mode == rpt->protocol_mode))) { for (uint8_t i = 0; i < s_dev->devices[dev_index].reports_len; i++) {
return rpt; rpt = &s_dev->devices[dev_index].reports[i];
} if (rpt->usage == usage && rpt->report_type == type && (!mode || (mode && *mode == rpt->protocol_mode))) {
return rpt;
} }
} }
return NULL; return NULL;
@@ -372,6 +433,11 @@ static int nimble_hidd_dev_input_set(void *devp, size_t index, size_t id, uint8_
return ESP_FAIL; return ESP_FAIL;
} }
if (index >= s_dev->devices_len) {
ESP_LOGE(TAG, "%s invalid device index %zu (devices_len=%u)", __func__, index, s_dev->devices_len);
return ESP_FAIL;
}
if (!dev->connected) { if (!dev->connected) {
ESP_LOGE(TAG, "%s Device Not Connected", __func__); ESP_LOGE(TAG, "%s Device Not Connected", __func__);
return ESP_FAIL; return ESP_FAIL;
@@ -418,6 +484,11 @@ static int nimble_hidd_dev_feature_set(void *devp, size_t index, size_t id, uint
return ESP_FAIL; return ESP_FAIL;
} }
if (index >= s_dev->devices_len) {
ESP_LOGE(TAG, "%s invalid device index %zu (devices_len=%u)", __func__, index, s_dev->devices_len);
return ESP_FAIL;
}
if (!dev->connected) { if (!dev->connected) {
ESP_LOGE(TAG, "%s Device Not Connected", __func__); ESP_LOGE(TAG, "%s Device Not Connected", __func__);
return ESP_FAIL; return ESP_FAIL;
@@ -476,63 +547,87 @@ static void ble_hidd_dev_free(void)
free(s_dev); free(s_dev);
s_dev = NULL; s_dev = NULL;
} }
if (s_hidd_mutex) {
vSemaphoreDelete(s_hidd_mutex);
s_hidd_mutex = NULL;
}
} }
static int nimble_hid_gap_event(struct ble_gap_event *event, void *arg) static int nimble_hid_gap_event(struct ble_gap_event *event, void *arg)
{ {
struct ble_gap_conn_desc desc; struct ble_gap_conn_desc desc;
struct os_mbuf *om;
uint8_t data; uint8_t data;
int rc; int rc;
esp_ble_hidd_dev_t *dev;
lock_hidd();
dev = s_dev;
if (dev == NULL) {
unlock_hidd();
return 0;
}
/* HOGP encryption is not enforced at the GATT level in this component.
* Applications must configure encryption themselves, either by setting
* BLE_GATT_CHR_F_READ_ENC/WRITE_ENC in ble_svc_hid.c or by configuring
* ble_hs_cfg.sm_* security parameters. */
switch (event->type) { switch (event->type) {
case BLE_GAP_EVENT_CONNECT: case BLE_GAP_EVENT_CONNECT:
/* A new connection was established or a connection attempt failed. */ /* A new connection was established or a connection attempt failed. */
ESP_LOGD(TAG, "connection %s; status=%d", ESP_LOGD(TAG, "connection %s; status=%d",
event->connect.status == 0 ? "established" : "failed", event->connect.status == 0 ? "established" : "failed",
event->connect.status); event->connect.status);
if (event->connect.status != 0) {
unlock_hidd();
return 0;
}
rc = ble_gap_conn_find(event->connect.conn_handle, &desc); rc = ble_gap_conn_find(event->connect.conn_handle, &desc);
assert(rc == 0); assert(rc == 0);
/* save connection handle */ /* save connection handle */
s_dev->connected = true; dev->connected = true;
s_dev->conn_id = event->connect.conn_handle; dev->conn_id = event->connect.conn_handle;
esp_hidd_event_data_t cb_param = { esp_hidd_event_data_t cb_param = {
.connect.dev = s_dev->dev, .connect.dev = dev->dev,
.connect.status = event->connect.status .connect.status = event->connect.status
}; };
/* reset the protocol mode value */ /* reset the protocol mode value */
data = ESP_HID_PROTOCOL_MODE_REPORT; data = ESP_HID_PROTOCOL_MODE_REPORT;
om = ble_hs_mbuf_from_flat(&data, 1); for (int i = 0; i < dev->devices_len; i++) {
if (om == NULL) { struct os_mbuf *om = ble_hs_mbuf_from_flat(&data, 1);
ESP_LOGD(TAG, "No memory to allocate mbuf"); if (om == NULL) {
} ESP_LOGD(TAG, "No memory to allocate mbuf");
/* NOTE : om is freed by stack */ break;
for (int i = 0; i < s_dev->devices_len; i++) { }
rc = ble_att_svr_write_local(s_dev->devices[i].hid_protocol_handle, om); rc = ble_att_svr_write_local(dev->devices[i].hid_protocol_handle, om);
if (rc != 0) { if (rc != 0) {
ESP_LOGE(TAG, "Write on Protocol Mode Failed: %d", rc); ESP_LOGE(TAG, "Write on Protocol Mode Failed: %d", rc);
} }
} }
unlock_hidd();
esp_event_post_to(s_dev->event_loop_handle, ESP_HIDD_EVENTS, ESP_HIDD_CONNECT_EVENT, esp_event_post_to(dev->event_loop_handle, ESP_HIDD_EVENTS, ESP_HIDD_CONNECT_EVENT,
&cb_param, sizeof(esp_hidd_event_data_t), portMAX_DELAY); &cb_param, sizeof(esp_hidd_event_data_t), portMAX_DELAY);
return 0; return 0;
break; break;
case BLE_GAP_EVENT_DISCONNECT: case BLE_GAP_EVENT_DISCONNECT:
ESP_LOGD(TAG, "disconnect; reason=%d", event->disconnect.reason); ESP_LOGD(TAG, "disconnect; reason=%d", event->disconnect.reason);
if (s_dev->connected) { if (dev->connected) {
s_dev->connected = false; dev->connected = false;
esp_hidd_event_data_t cb_param = {0}; esp_hidd_event_data_t cb_param = {0};
cb_param.disconnect.dev = s_dev->dev; cb_param.disconnect.dev = dev->dev;
cb_param.disconnect.reason = event->disconnect.reason; cb_param.disconnect.reason = event->disconnect.reason;
esp_event_post_to(s_dev->event_loop_handle, ESP_HIDD_EVENTS, ESP_HIDD_DISCONNECT_EVENT, unlock_hidd();
esp_event_post_to(dev->event_loop_handle, ESP_HIDD_EVENTS, ESP_HIDD_DISCONNECT_EVENT,
&cb_param, sizeof(esp_hidd_event_data_t), portMAX_DELAY); &cb_param, sizeof(esp_hidd_event_data_t), portMAX_DELAY);
} else {
unlock_hidd();
} }
return 0; return 0;
} }
unlock_hidd();
return 0; return 0;
} }
@@ -554,8 +649,13 @@ static void nimble_gatt_svr_register_cb(struct ble_gatt_register_ctxt *ctxt, voi
ctxt->svc.handle); ctxt->svc.handle);
uuid16 = ble_uuid_u16(ctxt->svc.svc_def->uuid); uuid16 = ble_uuid_u16(ctxt->svc.svc_def->uuid);
if (uuid16 == BLE_SVC_HID_UUID16) { if (uuid16 == BLE_SVC_HID_UUID16) {
++service_index; if (service_index + 1 >= (int)s_dev->devices_len) {
s_dev->devices[service_index].hid_svc = ctxt->svc.handle; ESP_LOGE(TAG, "Too many HID services registered (%d >= devices_len %d); ignoring",
service_index + 1, s_dev->devices_len);
} else {
++service_index;
s_dev->devices[service_index].hid_svc = ctxt->svc.handle;
}
} }
break; break;
@@ -566,6 +666,10 @@ static void nimble_gatt_svr_register_cb(struct ble_gatt_register_ctxt *ctxt, voi
ble_uuid_to_str(ctxt->chr.chr_def->uuid, buf), ble_uuid_to_str(ctxt->chr.chr_def->uuid, buf),
ctxt->chr.def_handle, ctxt->chr.def_handle,
ctxt->chr.val_handle); ctxt->chr.val_handle);
if (service_index < 0 || (uint8_t)service_index >= s_dev->devices_len) {
break;
}
uuid16 = ble_uuid_u16(ctxt->chr.chr_def->uuid); uuid16 = ble_uuid_u16(ctxt->chr.chr_def->uuid);
if (uuid16 == BLE_SVC_HID_CHR_UUID16_HID_CTRL_PT) { if (uuid16 == BLE_SVC_HID_CHR_UUID16_HID_CTRL_PT) {
/* assuming this characteristic is from the last registered hid service */ /* assuming this characteristic is from the last registered hid service */
@@ -577,7 +681,7 @@ static void nimble_gatt_svr_register_cb(struct ble_gatt_register_ctxt *ctxt, voi
} }
if (uuid16 == BLE_SVC_HID_CHR_UUID16_BOOT_KBD_INP) { if (uuid16 == BLE_SVC_HID_CHR_UUID16_BOOT_KBD_INP) {
protocol_mode = ESP_HID_PROTOCOL_MODE_BOOT; protocol_mode = ESP_HID_PROTOCOL_MODE_BOOT;
rpt = find_report_by_usage_and_type(ESP_HID_USAGE_KEYBOARD, ESP_HID_REPORT_TYPE_INPUT, &protocol_mode); rpt = find_report_by_usage_and_type(service_index, ESP_HID_USAGE_KEYBOARD, ESP_HID_REPORT_TYPE_INPUT, &protocol_mode);
if (rpt == NULL) { if (rpt == NULL) {
ESP_LOGE(TAG, "Unknown boot kbd input report registration"); ESP_LOGE(TAG, "Unknown boot kbd input report registration");
return; return;
@@ -586,7 +690,7 @@ static void nimble_gatt_svr_register_cb(struct ble_gatt_register_ctxt *ctxt, voi
} }
if (uuid16 == BLE_SVC_HID_CHR_UUID16_BOOT_KBD_OUT) { if (uuid16 == BLE_SVC_HID_CHR_UUID16_BOOT_KBD_OUT) {
protocol_mode = ESP_HID_PROTOCOL_MODE_BOOT; protocol_mode = ESP_HID_PROTOCOL_MODE_BOOT;
rpt = find_report_by_usage_and_type(ESP_HID_USAGE_KEYBOARD, ESP_HID_REPORT_TYPE_OUTPUT, &protocol_mode); rpt = find_report_by_usage_and_type(service_index, ESP_HID_USAGE_KEYBOARD, ESP_HID_REPORT_TYPE_OUTPUT, &protocol_mode);
if (rpt == NULL) { if (rpt == NULL) {
ESP_LOGE(TAG, "Unknown boot kbd output report registration"); ESP_LOGE(TAG, "Unknown boot kbd output report registration");
return; return;
@@ -595,7 +699,7 @@ static void nimble_gatt_svr_register_cb(struct ble_gatt_register_ctxt *ctxt, voi
} }
if (uuid16 == BLE_SVC_HID_CHR_UUID16_BOOT_MOUSE_INP) { if (uuid16 == BLE_SVC_HID_CHR_UUID16_BOOT_MOUSE_INP) {
protocol_mode = ESP_HID_PROTOCOL_MODE_BOOT; protocol_mode = ESP_HID_PROTOCOL_MODE_BOOT;
rpt = find_report_by_usage_and_type(ESP_HID_USAGE_MOUSE, ESP_HID_REPORT_TYPE_INPUT, &protocol_mode); rpt = find_report_by_usage_and_type(service_index, ESP_HID_USAGE_MOUSE, ESP_HID_REPORT_TYPE_INPUT, &protocol_mode);
if (rpt == NULL) { if (rpt == NULL) {
ESP_LOGE(TAG, "Unknown boot mouse input report registration"); ESP_LOGE(TAG, "Unknown boot mouse input report registration");
return; return;
@@ -616,6 +720,11 @@ static void nimble_gatt_svr_register_cb(struct ble_gatt_register_ctxt *ctxt, voi
ble_hs_mbuf_to_flat(om, &report_info, sizeof report_info, NULL); ble_hs_mbuf_to_flat(om, &report_info, sizeof report_info, NULL);
report_type = (uint8_t)((report_info & 0xFF00) >> 8); report_type = (uint8_t)((report_info & 0xFF00) >> 8);
report_id = report_info & 0x00FF; report_id = report_info & 0x00FF;
if (ctxt->dsc.dsc_def->arg == NULL) {
ESP_LOGE(TAG, "Report Reference descriptor has NULL arg; skipping handle assignment");
os_mbuf_free_chain(om);
break;
}
report_handle = (*(uint16_t*)(ctxt->dsc.dsc_def->arg)); report_handle = (*(uint16_t*)(ctxt->dsc.dsc_def->arg));
protocol_mode = ESP_HID_PROTOCOL_MODE_REPORT; protocol_mode = ESP_HID_PROTOCOL_MODE_REPORT;
rpt = find_report(report_id, report_type, &protocol_mode); rpt = find_report(report_id, report_type, &protocol_mode);
@@ -635,15 +744,24 @@ static void nimble_gatt_svr_register_cb(struct ble_gatt_register_ctxt *ctxt, voi
static void nimble_host_synced(void) static void nimble_host_synced(void)
{ {
esp_event_post_to(s_dev->event_loop_handle, ESP_HIDD_EVENTS, ESP_HIDD_START_EVENT, NULL, 0, portMAX_DELAY); lock_hidd();
if (s_prev_sync_cb && s_prev_sync_cb != nimble_host_synced) {
s_prev_sync_cb();
}
if (s_dev && s_dev->event_loop_handle != NULL) {
esp_event_post_to(s_dev->event_loop_handle, ESP_HIDD_EVENTS, ESP_HIDD_START_EVENT, NULL, 0, portMAX_DELAY);
}
unlock_hidd();
} }
void nimble_host_reset(int reason) void nimble_host_reset(int reason)
{ {
if (s_prev_reset_cb && s_prev_reset_cb != nimble_host_reset) {
s_prev_reset_cb(reason);
}
MODLOG_DFLT(ERROR, "Resetting state; reason=%d\n", reason); MODLOG_DFLT(ERROR, "Resetting state; reason=%d\n", reason);
} }
static struct ble_gap_event_listener nimble_gap_event_listener;
esp_err_t esp_ble_hidd_dev_init(esp_hidd_dev_t *dev_p, const esp_hid_device_config_t *config, esp_event_handler_t callback) esp_err_t esp_ble_hidd_dev_init(esp_hidd_dev_t *dev_p, const esp_hid_device_config_t *config, esp_event_handler_t callback)
{ {
int rc; int rc;
@@ -658,6 +776,15 @@ esp_err_t esp_ble_hidd_dev_init(esp_hidd_dev_t *dev_p, const esp_hid_device_conf
ESP_LOGE(TAG, "HID device could not be allocated"); ESP_LOGE(TAG, "HID device could not be allocated");
return ESP_FAIL; return ESP_FAIL;
} }
if (s_hidd_mutex == NULL) {
s_hidd_mutex = xSemaphoreCreateMutex();
if (s_hidd_mutex == NULL) {
free(s_dev);
s_dev = NULL;
ESP_LOGE(TAG, "HID device mutex allocation failed");
return ESP_ERR_NO_MEM;
}
}
// Reset the hid device target environment // Reset the hid device target environment
s_dev->control = ESP_HID_CONTROL_EXIT_SUSPEND; s_dev->control = ESP_HID_CONTROL_EXIT_SUSPEND;
@@ -708,15 +835,26 @@ esp_err_t esp_ble_hidd_dev_init(esp_hidd_dev_t *dev_p, const esp_hid_device_conf
} }
} }
s_prev_reset_cb = ble_hs_cfg.reset_cb;
s_prev_sync_cb = ble_hs_cfg.sync_cb;
ble_hs_cfg.reset_cb = nimble_host_reset; ble_hs_cfg.reset_cb = nimble_host_reset;
ble_hs_cfg.sync_cb = nimble_host_synced; ble_hs_cfg.sync_cb = nimble_host_synced;
ble_hs_cfg.gatts_register_cb = nimble_gatt_svr_register_cb; ble_hs_cfg.gatts_register_cb = nimble_gatt_svr_register_cb;
rc = nimble_hid_start_gatts(); rc = nimble_hid_start_gatts();
if (rc != ESP_OK) { if (rc != ESP_OK) {
if (ble_hs_cfg.reset_cb == nimble_host_reset) {
ble_hs_cfg.reset_cb = s_prev_reset_cb;
}
if (ble_hs_cfg.sync_cb == nimble_host_synced) {
ble_hs_cfg.sync_cb = s_prev_sync_cb;
}
ble_hs_cfg.gatts_register_cb = NULL;
ble_hidd_dev_free();
return rc; return rc;
} }
ble_gap_event_listener_register(&nimble_gap_event_listener, ble_gap_event_listener_register(&nimble_gap_event_listener,
nimble_hid_gap_event, NULL); nimble_hid_gap_event, NULL);
s_gap_listener_registered = true;
return rc; return rc;
} }
File diff suppressed because it is too large Load Diff
+3 -3
View File
@@ -233,9 +233,9 @@ esp_err_t esp_blufi_host_init(void)
ble_hs_cfg.gatts_register_cb = esp_blufi_gatt_svr_register_cb; ble_hs_cfg.gatts_register_cb = esp_blufi_gatt_svr_register_cb;
ble_hs_cfg.store_status_cb = ble_store_util_status_rr; ble_hs_cfg.store_status_cb = ble_store_util_status_rr;
/* FALSE POSITIVE: BLUFI uses its own app-layer security (DH + AES), not BLE SM. /* BLUFI uses its own application-layer security (DH + AES), not BLE Security
* sm_mitm/sm_sc/sm_bonding are opt-in via Kconfig to let the example show * Manager. sm_mitm/sm_sc/sm_bonding are opt-in via Kconfig; Just Works
* multiple configurations; Just Works is acceptable for BLUFI provisioning. */ * pairing is acceptable because the DH key exchange provides the security. */
ble_hs_cfg.sm_io_cap = 4; ble_hs_cfg.sm_io_cap = 4;
#ifdef CONFIG_EXAMPLE_BONDING #ifdef CONFIG_EXAMPLE_BONDING
ble_hs_cfg.sm_bonding = 1; ble_hs_cfg.sm_bonding = 1;
@@ -90,6 +90,82 @@ I (50557) HID_DEV_DEMO: Send the volume
... ...
``` ```
## NimBLE Logging Scheme
The following are verbatim NimBLE log excerpts from `logs.txt` for each device role.
### Media Mode
```text
I (496) HID_DEV_DEMO: setting ble device
I (496) HID_DEV_DEMO: BLE Host Task Started
I (506) HID_DEV_BLE: START
I (506) NimBLE: GAP procedure initiated: advertise;
I (35886) ESP_HID_GAP: connection established; status=0
I (35896) HID_DEV_BLE: CONNECT
I (35946) ESP_HID_GAP: mtu update event; conn_handle=0 cid=4 mtu=256
I (36476) ESP_HID_GAP: PASSKEY_ACTION_EVENT started
I (36476) ESP_HID_GAP: Enter passkey 123456on the peer side
I (37896) ESP_HID_GAP: subscribe event; conn_handle=0 attr_handle=37 reason=1 prevn=0 curn=1 previ=0 curi=0
I (40936) NimBLE: encryption change event; status=0
I (40946) HID_DEV_DEMO: Send the volume
I (40946) NimBLE: GATT procedure initiated: notify;
I (40946) NimBLE: att_handle=37
I (40946) NimBLE: notify_tx event; conn_handle=0 attr_handle=37 status=0 is_indication=0
```
### Keyboard Mode
```text
I (436) HID_DEV_DEMO: setting ble device
I (436) HID_DEV_DEMO: BLE Host Task Started
I (436) HID_DEV_BLE: START
I (446) NimBLE: GAP procedure initiated: advertise;
I (11526) ESP_HID_GAP: connection established; status=0
I (11526) HID_DEV_BLE: CONNECT
I (11576) ESP_HID_GAP: mtu update event; conn_handle=0 cid=4 mtu=256
I (12116) ESP_HID_GAP: PASSKEY_ACTION_EVENT started
I (12116) ESP_HID_GAP: Enter passkey 123456on the peer side
I (13826) ESP_HID_GAP: subscribe event; conn_handle=0 attr_handle=37 reason=1 prevn=0 curn=1 previ=0 curi=0
I (16576) NimBLE: encryption change event; status=0
########################################################################
BT hid keyboard demo usage:
########################################################################
I (22306) NimBLE: GATT procedure initiated: notify;
I (22306) NimBLE: att_handle=37
I (22306) NimBLE: notify_tx event; conn_handle=0 attr_handle=37 status=0 is_indication=0
```
### Mouse Mode
```text
I (446) HID_DEV_DEMO: setting ble device
I (446) HID_DEV_DEMO: BLE Host Task Started
I (456) HID_DEV_BLE: START
I (456) NimBLE: GAP procedure initiated: advertise;
I (10556) ESP_HID_GAP: connection established; status=0
I (10556) HID_DEV_BLE: CONNECT
I (10606) ESP_HID_GAP: mtu update event; conn_handle=0 cid=4 mtu=256
I (11136) ESP_HID_GAP: PASSKEY_ACTION_EVENT started
I (11136) ESP_HID_GAP: Enter passkey 123456on the peer side
I (12656) ESP_HID_GAP: subscribe event; conn_handle=0 attr_handle=37 reason=1 prevn=0 curn=1 previ=0 curi=0
I (15606) NimBLE: encryption change event; status=0
########################################################################
BT hid mouse demo usage:
You can input these value to simulate mouse: 'q', 'w', 'e', 'a', 's', 'd', 'h'
q -- click the left key
w -- move up
e -- click the right key
a -- move left
s -- move down
d -- move right
h -- show the help
########################################################################
I (18166) NimBLE: GATT procedure initiated: notify;
I (18166) NimBLE: att_handle=37
I (18166) NimBLE: notify_tx event; conn_handle=0 attr_handle=37 status=0 is_indication=0
```
## Troubleshooting ## Troubleshooting
1. When using NimBLE stack, some iOS devices do not show the volume pop up. To fix this, please set CONFIG_BT_NIMBLE_SM_LVL to value 2. iOS needs Authenticated Pairing with Encryption to show up the pop ups. 1. When using NimBLE stack, some iOS devices do not show the volume pop up. To fix this, please set CONFIG_BT_NIMBLE_SM_LVL to value 2. iOS needs Authenticated Pairing with Encryption to show up the pop ups.
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2021-2026 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Unlicense OR CC0-1.0 * SPDX-License-Identifier: Unlicense OR CC0-1.0
*/ */
@@ -856,9 +856,13 @@ nimble_hid_gap_event(struct ble_gap_event *event, void *arg)
/* Encryption has been enabled or disabled for this connection. */ /* Encryption has been enabled or disabled for this connection. */
MODLOG_DFLT(INFO, "encryption change event; status=%d ", MODLOG_DFLT(INFO, "encryption change event; status=%d ",
event->enc_change.status); event->enc_change.status);
rc = ble_gap_conn_find(event->enc_change.conn_handle, &desc); if (event->enc_change.status == 0) {
assert(rc == 0); rc = ble_gap_conn_find(event->enc_change.conn_handle, &desc);
ble_hid_task_start_up(); assert(rc == 0);
ble_hid_task_start_up();
} else {
ESP_LOGW(TAG, "encryption failed; waiting for disconnect/retry");
}
return 0; return 0;
case BLE_GAP_EVENT_NOTIFY_TX: case BLE_GAP_EVENT_NOTIFY_TX:
@@ -889,7 +893,7 @@ nimble_hid_gap_event(struct ble_gap_event *event, void *arg)
case BLE_GAP_EVENT_PASSKEY_ACTION: case BLE_GAP_EVENT_PASSKEY_ACTION:
ESP_LOGI(TAG, "PASSKEY_ACTION_EVENT started"); ESP_LOGI(TAG, "PASSKEY_ACTION_EVENT started");
struct ble_sm_io pkey = {0}; struct ble_sm_io pkey = {0};
int key = 0; int key = 1;
if (event->passkey.params.action == BLE_SM_IOACT_DISP) { if (event->passkey.params.action == BLE_SM_IOACT_DISP) {
pkey.action = event->passkey.params.action; pkey.action = event->passkey.params.action;
+67
View File
@@ -94,6 +94,73 @@ I (18212) ESP_HIDH_DEMO: 00 00
... ...
``` ```
## NimBLE Logging Scheme
The following are verbatim NimBLE log excerpts from `logs.txt` for each device role.
### Media Mode (CCONTROL)
```text
I (2446) ESP_HIDH_DEMO: SCAN...
I (2446) NimBLE: GAP procedure initiated: discovery;
I (7456) NimBLE: discovery complete; reason=0
I (7466) NimBLE: GAP procedure initiated: connect;
I (7666) NimBLE: Connection established
I (7676) NimBLE: GATT procedure initiated: exchange mtu
I (7726) NimBLE: mtu update event; conn_handle=0 cid=4 mtu=256
I (8446) NIMBLE_HIDH: PASSKEY INPUT injected rc=0
I (9636) ESP_HIDH_DEMO: 02:50:f7:f9:55:60 OPEN: nimble
Report Maps: 1
Report Map Length: 111
CCONTROL INPUT REPORT, ID: 3, Length: 2, Permissions: 0x1a, Handle: 37, CCC Handle: 38
I (12776) NIMBLE_HIDH: ENC_CHANGE status=0 encrypted=1 authenticated=1 bonded=1
I (12826) ESP_HIDH_DEMO: 02:50:f7:f9:55:60 INPUT: CCONTROL, MAP: 0, ID: 3, Len: 2, Data:
I (12826) ESP_HIDH_DEMO: 80 00
I (12876) ESP_HIDH_DEMO: 02:50:f7:f9:55:60 INPUT: CCONTROL, MAP: 0, ID: 3, Len: 2, Data:
I (12876) ESP_HIDH_DEMO: 00 00
I (14876) ESP_HIDH_DEMO: 02:50:f7:f9:55:60 INPUT: CCONTROL, MAP: 0, ID: 3, Len: 2, Data:
I (14876) ESP_HIDH_DEMO: 40 00
```
### Keyboard Mode
```text
I (7426) ESP_HIDH_DEMO: SCAN: 2 results
BLE: 68:2e:f7:f9:55:60, RSSI: -57, USAGE: GENERIC, APPEARANCE: 0x03c0, ADDR_TYPE: '0', NAME: ESP Mouse
BLE: 02:50:f7:f9:55:60, RSSI: -30, USAGE: GENERIC, APPEARANCE: 0x03c0, ADDR_TYPE: '0', NAME: ESP Keyboard
I (7446) NimBLE: GAP procedure initiated: connect;
I (7836) NimBLE: Connection established
I (8606) NIMBLE_HIDH: PASSKEY INPUT injected rc=0
I (10086) ESP_HIDH_DEMO: 02:50:f7:f9:55:60 OPEN: nimble
Report Maps: 1
Report Map Length: 65
KEYBOARD INPUT REPORT, ID: 1, Length: 7, Permissions: 0x1a, Handle: 37, CCC Handle: 38
I (12936) NIMBLE_HIDH: ENC_CHANGE status=0 encrypted=1 authenticated=1 bonded=1
I (18636) ESP_HIDH_DEMO: 02:50:f7:f9:55:60 INPUT: KEYBOARD, MAP: 0, ID: 1, Len: 8, Data:
I (18636) ESP_HIDH_DEMO: 00 00 04 00 00 00 00 00
I (18686) ESP_HIDH_DEMO: 02:50:f7:f9:55:60 INPUT: KEYBOARD, MAP: 0, ID: 1, Len: 8, Data:
I (18686) ESP_HIDH_DEMO: 00 00 00 00 00 00 00 00
```
### Mouse Mode
```text
I (7426) ESP_HIDH_DEMO: SCAN: 1 results
BLE: 02:50:f7:f9:55:60, RSSI: -29, USAGE: GENERIC, APPEARANCE: 0x03c0, ADDR_TYPE: '0', NAME: ESP Mouse
I (7436) NimBLE: GAP procedure initiated: connect;
I (7666) NimBLE: Connection established
I (8436) NIMBLE_HIDH: PASSKEY INPUT injected rc=0
I (9716) ESP_HIDH_DEMO: 02:50:f7:f9:55:60 OPEN: nimble
Report Maps: 1
Report Map Length: 52
MOUSE INPUT REPORT, ID: 0, Length: 4, Permissions: 0x1a, Handle: 37, CCC Handle: 38
I (12766) NIMBLE_HIDH: ENC_CHANGE status=0 encrypted=1 authenticated=1 bonded=1
I (15316) ESP_HIDH_DEMO: 02:50:f7:f9:55:60 INPUT: MOUSE, MAP: 0, ID: 0, Len: 4, Data:
I (15316) ESP_HIDH_DEMO: 00 f6 00 00
I (15816) ESP_HIDH_DEMO: 02:50:f7:f9:55:60 INPUT: MOUSE, MAP: 0, ID: 0, Len: 4, Data:
I (15816) ESP_HIDH_DEMO: 00 00 0a 00
```
## Troubleshooting ## Troubleshooting
For any technical queries, please open an [issue](https://github.com/espressif/esp-idf/issues) on GitHub. We will get back to you soon. For any technical queries, please open an [issue](https://github.com/espressif/esp-idf/issues) on GitHub. We will get back to you soon.