Merge branch 'bugfix/ai_fixes_common_components_v5.4' into 'release/v5.4'

Bugfix/ai fixes common components (v5.4)

See merge request espressif/esp-idf!47568
This commit is contained in:
Rahul Tank
2026-04-24 10:54:19 +05:30
19 changed files with 1171 additions and 406 deletions
+8 -4
View File
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2015-2026 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
@@ -558,15 +558,19 @@ bt_status_t btc_init(void)
void btc_deinit(void) void btc_deinit(void)
{ {
osi_thread_t *thread = btc_thread;
if (!thread) {
return;
}
osi_thread_free(thread);
btc_thread = NULL;
#if BTC_GAP_BT_INCLUDED #if BTC_GAP_BT_INCLUDED
btc_gap_bt_deinit(); btc_gap_bt_deinit();
#endif #endif
#if BTC_DYNAMIC_MEMORY #if BTC_DYNAMIC_MEMORY
btc_deinit_mem(); btc_deinit_mem();
#endif #endif
osi_thread_free(btc_thread);
btc_thread = NULL;
#if (BLE_INCLUDED == TRUE) #if (BLE_INCLUDED == TRUE)
btc_gap_ble_deinit(); btc_gap_ble_deinit();
#endif ///BLE_INCLUDED == TRUE #endif ///BLE_INCLUDED == TRUE
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2015-2026 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
@@ -93,6 +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.
* The GAP layer rejects new connections while is_connected==true,
* so concurrent multi-connection access to this state never occurs. */
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)) {
@@ -129,12 +132,21 @@ void btc_blufi_recv_handler(uint8_t *data, int len)
blufi_env.recv_seq++; blufi_env.recv_seq++;
#define BLUFI_RESET_AGGR_BUF() do { \
if (blufi_env.aggr_buf) { \
osi_free(blufi_env.aggr_buf); \
blufi_env.aggr_buf = NULL; \
} \
blufi_env.offset = 0; \
} while (0)
// first step, decrypt // first step, decrypt
if (BLUFI_FC_IS_ENC(hdr->fc) if (BLUFI_FC_IS_ENC(hdr->fc)
&& (blufi_env.cbs && blufi_env.cbs->decrypt_func)) { && (blufi_env.cbs && blufi_env.cbs->decrypt_func)) {
ret = blufi_env.cbs->decrypt_func(hdr->seq, hdr->data, hdr->data_len); ret = blufi_env.cbs->decrypt_func(hdr->seq, hdr->data, hdr->data_len);
if (ret != hdr->data_len) { /* enc must be success and enc len must equal to plain len */ if (ret != hdr->data_len) { /* enc must be success and enc len must equal to plain len */
BTC_TRACE_ERROR("%s decrypt error %d\n", __func__, ret); BTC_TRACE_ERROR("%s decrypt error %d\n", __func__, ret);
BLUFI_RESET_AGGR_BUF();
btc_blufi_report_error(ESP_BLUFI_DECRYPT_ERROR); btc_blufi_report_error(ESP_BLUFI_DECRYPT_ERROR);
return; return;
} }
@@ -147,6 +159,7 @@ void btc_blufi_recv_handler(uint8_t *data, int len)
checksum_pkt = hdr->data[hdr->data_len] | (((uint16_t) hdr->data[hdr->data_len + 1]) << 8); checksum_pkt = hdr->data[hdr->data_len] | (((uint16_t) hdr->data[hdr->data_len + 1]) << 8);
if (checksum != checksum_pkt) { if (checksum != checksum_pkt) {
BTC_TRACE_ERROR("%s checksum error %04x, pkt %04x\n", __func__, checksum, checksum_pkt); BTC_TRACE_ERROR("%s checksum error %04x, pkt %04x\n", __func__, checksum, checksum_pkt);
BLUFI_RESET_AGGR_BUF();
btc_blufi_report_error(ESP_BLUFI_CHECKSUM_ERROR); btc_blufi_report_error(ESP_BLUFI_CHECKSUM_ERROR);
return; return;
} }
@@ -157,6 +170,11 @@ void btc_blufi_recv_handler(uint8_t *data, int len)
} }
if (BLUFI_FC_IS_FRAG(hdr->fc)) { if (BLUFI_FC_IS_FRAG(hdr->fc)) {
if(hdr->data_len<2) {
BTC_TRACE_ERROR("%s: Invalid fragment data length: %d", __func__, hdr->data_len);
btc_blufi_report_error(ESP_BLUFI_DATA_FORMAT_ERROR);
return;
}
if (blufi_env.offset == 0) { if (blufi_env.offset == 0) {
/* /*
blufi_env.aggr_buf should be NULL if blufi_env.offset is 0. blufi_env.aggr_buf should be NULL if blufi_env.offset is 0.
@@ -165,9 +183,16 @@ void btc_blufi_recv_handler(uint8_t *data, int len)
*/ */
if (blufi_env.aggr_buf) { if (blufi_env.aggr_buf) {
BTC_TRACE_ERROR("%s msg error, blufi_env.aggr_buf is not freed\n", __func__); BTC_TRACE_ERROR("%s msg error, blufi_env.aggr_buf is not freed\n", __func__);
osi_free(blufi_env.aggr_buf);
blufi_env.aggr_buf = NULL;
btc_blufi_report_error(ESP_BLUFI_MSG_STATE_ERROR); btc_blufi_report_error(ESP_BLUFI_MSG_STATE_ERROR);
return; return;
} }
if (hdr->data_len < 2) {
BTC_TRACE_ERROR("%s frag header too short: data_len=%d\n", __func__, hdr->data_len);
btc_blufi_report_error(ESP_BLUFI_DATA_FORMAT_ERROR);
return;
}
blufi_env.total_len = hdr->data[0] | (((uint16_t) hdr->data[1]) << 8); blufi_env.total_len = hdr->data[0] | (((uint16_t) hdr->data[1]) << 8);
blufi_env.aggr_buf = osi_malloc(blufi_env.total_len); blufi_env.aggr_buf = osi_malloc(blufi_env.total_len);
if (blufi_env.aggr_buf == NULL) { if (blufi_env.aggr_buf == NULL) {
@@ -181,6 +206,7 @@ void btc_blufi_recv_handler(uint8_t *data, int len)
blufi_env.offset += (hdr->data_len - 2); blufi_env.offset += (hdr->data_len - 2);
} else { } else {
BTC_TRACE_ERROR("%s payload is longer than packet length, len %d \n", __func__, blufi_env.total_len); BTC_TRACE_ERROR("%s payload is longer than packet length, len %d \n", __func__, blufi_env.total_len);
BLUFI_RESET_AGGR_BUF();
btc_blufi_report_error(ESP_BLUFI_DATA_FORMAT_ERROR); btc_blufi_report_error(ESP_BLUFI_DATA_FORMAT_ERROR);
return; return;
} }
@@ -190,12 +216,14 @@ void btc_blufi_recv_handler(uint8_t *data, int len)
/* blufi_env.aggr_buf should not be NULL */ /* blufi_env.aggr_buf should not be NULL */
if (blufi_env.aggr_buf == NULL) { if (blufi_env.aggr_buf == NULL) {
BTC_TRACE_ERROR("%s buffer is NULL\n", __func__); BTC_TRACE_ERROR("%s buffer is NULL\n", __func__);
blufi_env.offset = 0;
btc_blufi_report_error(ESP_BLUFI_DH_MALLOC_ERROR); btc_blufi_report_error(ESP_BLUFI_DH_MALLOC_ERROR);
return; return;
} }
/* payload length should be equal to total_len */ /* payload length should be equal to total_len */
if ((blufi_env.offset + hdr->data_len) != blufi_env.total_len) { if ((blufi_env.offset + hdr->data_len) != blufi_env.total_len) {
BTC_TRACE_ERROR("%s payload is longer than packet length, len %d \n", __func__, blufi_env.total_len); BTC_TRACE_ERROR("%s payload is longer than packet length, len %d \n", __func__, blufi_env.total_len);
BLUFI_RESET_AGGR_BUF();
btc_blufi_report_error(ESP_BLUFI_DATA_FORMAT_ERROR); btc_blufi_report_error(ESP_BLUFI_DATA_FORMAT_ERROR);
return; return;
} }
@@ -211,6 +239,9 @@ 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.
* portENTER_CRITICAL cannot protect here; */
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;
@@ -94,7 +94,7 @@ void btc_blufi_protocol_handler(uint8_t type, uint8_t *data, int len)
btc_transfer_context(&msg, NULL, 0, NULL, NULL); btc_transfer_context(&msg, NULL, 0, NULL, NULL);
break; break;
default: default:
BTC_TRACE_ERROR("%s Unkown Ctrl pkt %02x\n", __func__, type); BTC_TRACE_ERROR("%s Unknown Ctrl pkt %02x\n", __func__, type);
break; break;
} }
break; break;
@@ -111,6 +111,10 @@ void btc_blufi_protocol_handler(uint8_t type, uint8_t *data, int len)
} }
break; break;
case BLUFI_TYPE_DATA_SUBTYPE_STA_BSSID: case BLUFI_TYPE_DATA_SUBTYPE_STA_BSSID:
if (len < 6) {
BTC_TRACE_ERROR("%s STA_BSSID data too short: %d\n", __func__, len);
return;
}
msg.sig = BTC_SIG_API_CB; msg.sig = BTC_SIG_API_CB;
msg.pid = BTC_PID_BLUFI; msg.pid = BTC_PID_BLUFI;
msg.act = ESP_BLUFI_EVENT_RECV_STA_BSSID; msg.act = ESP_BLUFI_EVENT_RECV_STA_BSSID;
@@ -241,7 +245,7 @@ void btc_blufi_protocol_handler(uint8_t type, uint8_t *data, int len)
btc_transfer_context(&msg, &param, sizeof(esp_blufi_cb_param_t), btc_blufi_cb_deep_copy, btc_blufi_cb_deep_free); btc_transfer_context(&msg, &param, sizeof(esp_blufi_cb_param_t), btc_blufi_cb_deep_copy, btc_blufi_cb_deep_free);
break; break;
default: default:
BTC_TRACE_ERROR("%s Unkown Ctrl pkt %02x\n", __func__, type); BTC_TRACE_ERROR("%s Unknown Ctrl pkt %02x\n", __func__, type);
break; break;
} }
break; break;
@@ -44,6 +44,7 @@ typedef struct {
/* Control reference */ /* Control reference */
esp_blufi_callbacks_t *cbs; esp_blufi_callbacks_t *cbs;
BOOLEAN enabled; BOOLEAN enabled;
BOOLEAN notify_enabled;
uint8_t send_seq; uint8_t send_seq;
uint8_t recv_seq; uint8_t recv_seq;
uint8_t sec_mode; uint8_t sec_mode;
@@ -53,6 +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.
* BLUFI uses its own app-layer security (DH key exchange + AES + CRC).
* BLE link-layer pairing is impossible before provisioning credentials exist. */
/*** 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,
@@ -89,7 +92,7 @@ void esp_blufi_gatt_svr_register_cb(struct ble_gatt_register_ctxt *ctxt, void *a
case BLE_GATT_REGISTER_OP_CHR: case BLE_GATT_REGISTER_OP_CHR:
ESP_LOGI(TAG, "registering characteristic %s with " ESP_LOGI(TAG, "registering characteristic %s with "
"def_handle=%d val_handle=%d\n", "def_handle=%d val_handle=%d",
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);
@@ -174,26 +177,23 @@ static size_t read_value(uint16_t conn_handle, uint16_t attr_handle,
void *arg) void *arg)
{ {
const struct gatt_value *value = (const struct gatt_value *) arg; const struct gatt_value *value = (const struct gatt_value *) arg;
char str[BLE_UUID_STR_LEN];
int rc; int rc;
memset(str, '\0', sizeof(str));
if (ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR) { if (ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR) {
if (ctxt->chr->flags & BLE_GATT_CHR_F_READ_AUTHOR) { if (ctxt->chr->flags & BLE_GATT_CHR_F_READ_AUTHOR) {
return BLE_ATT_ERR_INSUFFICIENT_AUTHOR; return BLE_ATT_ERR_INSUFFICIENT_AUTHOR;
} }
ble_uuid_to_str(ctxt->chr->uuid, str);
} else { } else {
if (ctxt->dsc->att_flags & BLE_ATT_F_READ_AUTHOR) { if (ctxt->dsc->att_flags & BLE_ATT_F_READ_AUTHOR) {
return BLE_ATT_ERR_INSUFFICIENT_AUTHOR; return BLE_ATT_ERR_INSUFFICIENT_AUTHOR;
} }
ble_uuid_to_str(ctxt->dsc->uuid, str);
} }
rc = os_mbuf_append(ctxt->om, value->buf->om_data, value->buf->om_len); if (value->buf == NULL) {
return BLE_ATT_ERR_UNLIKELY;
}
rc = os_mbuf_appendfrom(ctxt->om, value->buf, 0, OS_MBUF_PKTLEN(value->buf));
return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
} }
@@ -259,14 +259,14 @@ static void deinit_gatt_values(void)
for (svc = gatt_svr_svcs; svc && svc->uuid; svc++) { for (svc = gatt_svr_svcs; svc && svc->uuid; svc++) {
for (chr = svc->characteristics; chr && chr->uuid; chr++) { for (chr = svc->characteristics; chr && chr->uuid; chr++) {
if (i < SERVER_MAX_VALUES && gatt_values[i].buf != NULL) { if (i < SERVER_MAX_VALUES && gatt_values[i].buf != NULL) {
os_mbuf_free(gatt_values[i].buf); /* Free the buffer */ os_mbuf_free_chain(gatt_values[i].buf); /* Free the buffer */
gatt_values[i].buf = NULL; /* Nullify the pointer to avoid dangling references */ gatt_values[i].buf = NULL; /* Nullify the pointer to avoid dangling references */
} }
++i; ++i;
for (dsc = chr->descriptors; dsc && dsc->uuid; dsc++) { for (dsc = chr->descriptors; dsc && dsc->uuid; dsc++) {
if (i < SERVER_MAX_VALUES && gatt_values[i].buf != NULL) { if (i < SERVER_MAX_VALUES && gatt_values[i].buf != NULL) {
os_mbuf_free(gatt_values[i].buf); /* Free the buffer */ os_mbuf_free_chain(gatt_values[i].buf); /* Free the buffer */
gatt_values[i].buf = NULL; /* Nullify the pointer to avoid dangling references */ gatt_values[i].buf = NULL; /* Nullify the pointer to avoid dangling references */
} }
++i; ++i;
@@ -349,6 +349,7 @@ esp_blufi_gap_event(struct ble_gap_event *event, void *arg)
ESP_LOGI(TAG, "disconnect; reason=%d", event->disconnect.reason); ESP_LOGI(TAG, "disconnect; reason=%d", event->disconnect.reason);
memcpy(blufi_env.remote_bda, event->disconnect.conn.peer_id_addr.val, ESP_BLUFI_BD_ADDR_LEN); memcpy(blufi_env.remote_bda, event->disconnect.conn.peer_id_addr.val, ESP_BLUFI_BD_ADDR_LEN);
blufi_env.is_connected = false; blufi_env.is_connected = false;
blufi_env.notify_enabled = false;
blufi_env.recv_seq = blufi_env.send_seq = 0; blufi_env.recv_seq = blufi_env.send_seq = 0;
blufi_env.sec_mode = 0x0; blufi_env.sec_mode = 0x0;
blufi_env.offset = 0; blufi_env.offset = 0;
@@ -377,14 +378,14 @@ esp_blufi_gap_event(struct ble_gap_event *event, void *arg)
case BLE_GAP_EVENT_ADV_COMPLETE: case BLE_GAP_EVENT_ADV_COMPLETE:
ESP_LOGI(TAG, "advertise complete; reason=%d", ESP_LOGI(TAG, "advertise complete; reason=%d",
event->adv_complete.reason); event->adv_complete.reason);
if (arg != NULL) { if (event->adv_complete.reason != 0 && arg != NULL) {
((void(*)(void))arg)(); ((void(*)(void))arg)();
} }
return 0; return 0;
case BLE_GAP_EVENT_SUBSCRIBE: case BLE_GAP_EVENT_SUBSCRIBE:
ESP_LOGI(TAG, "subscribe event; conn_handle=%d attr_handle=%d " ESP_LOGI(TAG, "subscribe event; conn_handle=%d attr_handle=%d "
"reason=%d prevn=%d curn=%d previ=%d curi=%d\n", "reason=%d prevn=%d curn=%d previ=%d curi=%d",
event->subscribe.conn_handle, event->subscribe.conn_handle,
event->subscribe.attr_handle, event->subscribe.attr_handle,
event->subscribe.reason, event->subscribe.reason,
@@ -392,6 +393,9 @@ esp_blufi_gap_event(struct ble_gap_event *event, void *arg)
event->subscribe.cur_notify, event->subscribe.cur_notify,
event->subscribe.prev_indicate, event->subscribe.prev_indicate,
event->subscribe.cur_indicate); event->subscribe.cur_indicate);
if (event->subscribe.attr_handle == gatt_values[1].val_handle) {
blufi_env.notify_enabled = (event->subscribe.cur_notify == 1);
}
return 0; return 0;
case BLE_GAP_EVENT_MTU: case BLE_GAP_EVENT_MTU:
@@ -454,9 +458,11 @@ void esp_blufi_adv_start(void)
fields.tx_pwr_lvl = BLE_HS_ADV_TX_PWR_LVL_AUTO; fields.tx_pwr_lvl = BLE_HS_ADV_TX_PWR_LVL_AUTO;
name = ble_svc_gap_device_name(); name = ble_svc_gap_device_name();
fields.name = (uint8_t *)name; if (name != NULL) {
fields.name_len = strlen(name); fields.name = (uint8_t *)name;
fields.name_is_complete = 1; fields.name_len = strlen(name);
fields.name_is_complete = 1;
}
fields.uuids16 = (ble_uuid16_t[]) { fields.uuids16 = (ble_uuid16_t[]) {
BLE_UUID16_INIT(BLUFI_APP_UUID) BLE_UUID16_INIT(BLUFI_APP_UUID)
@@ -496,12 +502,18 @@ uint8_t esp_blufi_init(void)
esp_blufi_cb_param_t param; esp_blufi_cb_param_t param;
param.init_finish.state = ESP_BLUFI_INIT_OK; param.init_finish.state = ESP_BLUFI_INIT_OK;
btc_blufi_cb_to_app(ESP_BLUFI_EVENT_INIT_FINISH, &param); btc_blufi_cb_to_app(ESP_BLUFI_EVENT_INIT_FINISH, &param);
return ESP_BLUFI_ERROR; return ESP_BLUFI_SUCCESS;
} }
void esp_blufi_deinit(void) void esp_blufi_deinit(void)
{ {
blufi_env.enabled = false; blufi_env.enabled = false;
if (blufi_env.aggr_buf != NULL) {
osi_free(blufi_env.aggr_buf);
blufi_env.aggr_buf = NULL;
}
esp_blufi_cb_param_t param; esp_blufi_cb_param_t param;
btc_msg_t msg; btc_msg_t msg;
memset (&msg, 0x0, sizeof (msg)); memset (&msg, 0x0, sizeof (msg));
@@ -516,6 +528,12 @@ void esp_blufi_send_notify(void *arg)
{ {
struct pkt_info *pkts = (struct pkt_info *) arg; struct pkt_info *pkts = (struct pkt_info *) arg;
struct os_mbuf *om; struct os_mbuf *om;
if (!blufi_env.notify_enabled) {
ESP_LOGW(TAG, "esp_blufi_send_notify: client has not subscribed, dropping notification");
return;
}
om = ble_hs_mbuf_from_flat(pkts->pkt, pkts->pkt_len); om = ble_hs_mbuf_from_flat(pkts->pkt, pkts->pkt_len);
if (om == NULL) { if (om == NULL) {
ESP_LOGE(TAG, "Error in allocating memory"); ESP_LOGE(TAG, "Error in allocating memory");
@@ -530,7 +548,9 @@ void esp_blufi_send_notify(void *arg)
void esp_blufi_disconnect(void) void esp_blufi_disconnect(void)
{ {
ble_gap_terminate(blufi_env.conn_id, BLE_ERR_REM_USER_CONN_TERM); if(blufi_env.is_connected) {
ble_gap_terminate(blufi_env.conn_id, BLE_ERR_REM_USER_CONN_TERM);
}
} }
void esp_blufi_adv_stop(void) void esp_blufi_adv_stop(void)
@@ -560,6 +580,9 @@ void esp_blufi_btc_init(void)
void esp_blufi_btc_deinit(void) void esp_blufi_btc_deinit(void)
{ {
/* btc_deinit() destroys the shared global BTC thread. In the NimBLE path
* BTC is only used by BLUFI, so this is safe. If another profile ever uses
* BTC alongside BLUFI, add reference counting to btc_init/btc_deinit. */
btc_deinit(); btc_deinit();
} }
@@ -596,6 +619,7 @@ int esp_blufi_handle_gap_events(struct ble_gap_event *event, void *arg)
esp_blufi_cb_param_t param; esp_blufi_cb_param_t param;
blufi_env.is_connected = false; blufi_env.is_connected = false;
blufi_env.notify_enabled = false;
blufi_env.recv_seq = blufi_env.send_seq = 0; blufi_env.recv_seq = blufi_env.send_seq = 0;
blufi_env.sec_mode = 0x0; blufi_env.sec_mode = 0x0;
blufi_env.offset = 0; blufi_env.offset = 0;
@@ -623,6 +647,12 @@ int esp_blufi_handle_gap_events(struct ble_gap_event *event, void *arg)
blufi_env.frag_size = (event->mtu.value < BLUFI_MAX_DATA_LEN ? event->mtu.value : blufi_env.frag_size = (event->mtu.value < BLUFI_MAX_DATA_LEN ? event->mtu.value :
BLUFI_MAX_DATA_LEN) - BLUFI_MTU_RESERVED_SIZE; BLUFI_MAX_DATA_LEN) - BLUFI_MTU_RESERVED_SIZE;
return 0; return 0;
case BLE_GAP_EVENT_SUBSCRIBE:
if (event->subscribe.attr_handle == gatt_values[1].val_handle) {
blufi_env.notify_enabled = (event->subscribe.cur_notify == 1);
}
return 0;
} }
} }
@@ -49,6 +49,7 @@ int tc_cbc_mode_encrypt(uint8_t *out, unsigned int outlen, const uint8_t *in,
/* input sanity check: */ /* input sanity check: */
if (out == (uint8_t *) 0 || if (out == (uint8_t *) 0 ||
in == (const uint8_t *) 0 || in == (const uint8_t *) 0 ||
iv == (const uint8_t *) 0 ||
sched == (TCAesKeySched_t) 0 || sched == (TCAesKeySched_t) 0 ||
inlen == 0 || inlen == 0 ||
outlen == 0 || outlen == 0 ||
@@ -90,6 +91,7 @@ int tc_cbc_mode_decrypt(uint8_t *out, unsigned int outlen, const uint8_t *in,
/* sanity check the inputs */ /* sanity check the inputs */
if (out == (uint8_t *) 0 || if (out == (uint8_t *) 0 ||
in == (const uint8_t *) 0 || in == (const uint8_t *) 0 ||
iv == (const uint8_t *) 0 ||
sched == (TCAesKeySched_t) 0 || sched == (TCAesKeySched_t) 0 ||
inlen == 0 || inlen == 0 ||
outlen == 0 || outlen == 0 ||
@@ -105,7 +107,7 @@ int tc_cbc_mode_decrypt(uint8_t *out, unsigned int outlen, const uint8_t *in,
* that would not otherwise be possible. * that would not otherwise be possible.
*/ */
p = iv; p = iv;
for (n = m = 0; n < inlen; ++n) { for (n = m = 0; n < outlen; ++n) {
if ((n % TC_AES_BLOCK_SIZE) == 0) { if ((n % TC_AES_BLOCK_SIZE) == 0) {
(void)tc_aes_decrypt(buffer, in, sched); (void)tc_aes_decrypt(buffer, in, sched);
in += TC_AES_BLOCK_SIZE; in += TC_AES_BLOCK_SIZE;
+26 -2
View File
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2025-2026 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
@@ -115,6 +115,9 @@ static void tc_ctr_prng_update(TCCtrPrng_t * const ctx, uint8_t const * const pr
/* 10.2.1.2 step 6 */ /* 10.2.1.2 step 6 */
memcpy(ctx->V, &(temp[TC_AES_KEY_SIZE]), TC_AES_BLOCK_SIZE); memcpy(ctx->V, &(temp[TC_AES_KEY_SIZE]), TC_AES_BLOCK_SIZE);
/* Clear transient key material from stack */
_set(temp, 0, sizeof(temp));
} }
} }
@@ -162,6 +165,11 @@ int tc_ctr_prng_init(TCCtrPrng_t * const ctx,
result = TC_CRYPTO_SUCCESS; result = TC_CRYPTO_SUCCESS;
} }
_set(personalization_buf, 0, sizeof(personalization_buf));
_set(seed_material, 0, sizeof(seed_material));
_set(zeroArr, 0, sizeof(zeroArr));
return result; return result;
} }
@@ -203,6 +211,10 @@ int tc_ctr_prng_reseed(TCCtrPrng_t * const ctx,
result = TC_CRYPTO_SUCCESS; result = TC_CRYPTO_SUCCESS;
} }
_set(additional_input_buf, 0, sizeof(additional_input_buf));
_set(seed_material, 0, sizeof(seed_material));
return result; return result;
} }
@@ -220,7 +232,19 @@ int tc_ctr_prng_generate(TCCtrPrng_t * const ctx,
unsigned int result = TC_CRYPTO_FAIL; unsigned int result = TC_CRYPTO_FAIL;
if ((0 != ctx) && (0 != out) && (outlen < MAX_BYTES_PER_REQ)) { if (0 == ctx) {
return TC_CRYPTO_FAIL;
}
if (0U == outlen) {
return TC_CRYPTO_SUCCESS;
}
if ((0 == out) || (outlen >= MAX_BYTES_PER_REQ)) {
return TC_CRYPTO_FAIL;
}
{
/* 10.2.1.5.1 step 1 */ /* 10.2.1.5.1 step 1 */
if (ctx->reseedCount > MAX_REQS_BEFORE_RESEED) { if (ctx->reseedCount > MAX_REQS_BEFORE_RESEED) {
result = TC_CTR_PRNG_RESEED_REQ; result = TC_CTR_PRNG_RESEED_REQ;
+62 -22
View File
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2025-2026 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
@@ -58,6 +58,7 @@
#include <tinycrypt/ecc.h> #include <tinycrypt/ecc.h>
#include <tinycrypt/ecc_platform_specific.h> #include <tinycrypt/ecc_platform_specific.h>
#include <assert.h>
#include <string.h> #include <string.h>
#include <stdio.h> #include <stdio.h>
@@ -302,11 +303,19 @@ void uECC_vli_modAdd(uECC_word_t *result, const uECC_word_t *left,
const uECC_word_t *right, const uECC_word_t *mod, const uECC_word_t *right, const uECC_word_t *mod,
wordcount_t num_words) wordcount_t num_words)
{ {
wordcount_t i;
uECC_word_t tmp[NUM_ECC_WORDS];
if (num_words <= 0 || num_words > NUM_ECC_WORDS) {
return;
}
uECC_word_t carry = uECC_vli_add(result, left, right, num_words); uECC_word_t carry = uECC_vli_add(result, left, right, num_words);
if (carry || uECC_vli_cmp_unsafe(mod, result, num_words) != 1) { uECC_word_t borrow = uECC_vli_sub(tmp, result, mod, num_words);
/* result > mod (result = mod + remainder), so subtract mod to get uECC_word_t do_reduce = (uECC_word_t)(carry || !borrow);
* remainder. */
uECC_vli_sub(result, result, mod, num_words); for (i = 0; i < num_words; ++i) {
result[i] = cond_set(tmp[i], result[i], do_reduce);
} }
} }
@@ -314,11 +323,17 @@ void uECC_vli_modSub(uECC_word_t *result, const uECC_word_t *left,
const uECC_word_t *right, const uECC_word_t *mod, const uECC_word_t *right, const uECC_word_t *mod,
wordcount_t num_words) wordcount_t num_words)
{ {
wordcount_t i;
uECC_word_t tmp[NUM_ECC_WORDS];
if (num_words <= 0 || num_words > NUM_ECC_WORDS) {
return;
}
uECC_word_t l_borrow = uECC_vli_sub(result, left, right, num_words); uECC_word_t l_borrow = uECC_vli_sub(result, left, right, num_words);
if (l_borrow) { uECC_vli_add(tmp, result, mod, num_words);
/* In this case, result == -diff == (max int) - diff. Since -x % d == d - x, for (i = 0; i < num_words; ++i) {
* we can get the correct result from result + mod (with overflow). */ result[i] = cond_set(tmp[i], result[i], l_borrow);
uECC_vli_add(result, result, mod, num_words);
} }
} }
@@ -332,6 +347,10 @@ void uECC_vli_mmod(uECC_word_t *result, uECC_word_t *product,
uECC_word_t *v[2] = {tmp, product}; uECC_word_t *v[2] = {tmp, product};
uECC_word_t index; uECC_word_t index;
if (num_words <= 0 || num_words > NUM_ECC_WORDS) {
return;
}
/* Shift mod so its highest set bit is at the maximum position. */ /* Shift mod so its highest set bit is at the maximum position. */
bitcount_t shift = (num_words * 2 * uECC_WORD_BITS) - bitcount_t shift = (num_words * 2 * uECC_WORD_BITS) -
uECC_vli_numBits(mod, num_words); uECC_vli_numBits(mod, num_words);
@@ -353,9 +372,8 @@ void uECC_vli_mmod(uECC_word_t *result, uECC_word_t *product,
wordcount_t i; wordcount_t i;
for (i = 0; i < num_words * 2; ++i) { for (i = 0; i < num_words * 2; ++i) {
uECC_word_t diff = v[index][i] - mod_multiple[i] - borrow; uECC_word_t diff = v[index][i] - mod_multiple[i] - borrow;
if (diff != v[index][i]) { uECC_word_t val = (diff > v[index][i]);
borrow = (diff > v[index][i]); borrow = cond_set(val, borrow, (diff != v[index][i]));
}
v[1 - index][i] = diff; v[1 - index][i] = diff;
} }
/* Swap the index if there was no borrow */ /* Swap the index if there was no borrow */
@@ -372,6 +390,10 @@ void uECC_vli_modMult(uECC_word_t *result, const uECC_word_t *left,
const uECC_word_t *right, const uECC_word_t *mod, const uECC_word_t *right, const uECC_word_t *mod,
wordcount_t num_words) wordcount_t num_words)
{ {
if (num_words <= 0 || num_words > NUM_ECC_WORDS) {
return;
}
uECC_word_t product[2 * NUM_ECC_WORDS]; uECC_word_t product[2 * NUM_ECC_WORDS];
uECC_vli_mult(product, left, right, num_words); uECC_vli_mult(product, left, right, num_words);
uECC_vli_mmod(result, product, mod, num_words); uECC_vli_mmod(result, product, mod, num_words);
@@ -642,7 +664,8 @@ void apply_z(uECC_word_t * X1, uECC_word_t * Y1, const uECC_word_t * const Z,
uECC_vli_modMult_fast(Y1, Y1, t1, curve); /* y1 * z^3 */ uECC_vli_modMult_fast(Y1, Y1, t1, curve); /* y1 * z^3 */
} }
#if !SOC_ECC_SUPPORTED #if !SOC_ECC_SUPPORTED || SOC_ESP_NIMBLE_CONTROLLER
/* Keep ESP32-C6 on the software micro-ecc path for BLE SC compatibility. */
/* P = (x1, y1) => 2P, (x2, y2) => P' */ /* P = (x1, y1) => 2P, (x2, y2) => P' */
static void XYcZ_initial_double(uECC_word_t * X1, uECC_word_t * Y1, static void XYcZ_initial_double(uECC_word_t * X1, uECC_word_t * Y1,
uECC_word_t * X2, uECC_word_t * Y2, uECC_word_t * X2, uECC_word_t * Y2,
@@ -676,7 +699,7 @@ static void XYcZ_addC(uECC_word_t * X1, uECC_word_t * Y1,
uECC_Curve curve) uECC_Curve curve)
{ {
/* t1 = X1, t2 = Y1, t3 = X2, t4 = Y2 */ /* t1 = X1, t2 = Y1, t3 = X2, t4 = Y2 */
uECC_word_t t5[NUM_ECC_WORDS]; uECC_word_t t5[NUM_ECC_WORDS] = {0};
uECC_word_t t6[NUM_ECC_WORDS]; uECC_word_t t6[NUM_ECC_WORDS];
uECC_word_t t7[NUM_ECC_WORDS]; uECC_word_t t7[NUM_ECC_WORDS];
wordcount_t num_words = curve->num_words; wordcount_t num_words = curve->num_words;
@@ -715,7 +738,7 @@ void XYcZ_add(uECC_word_t * X1, uECC_word_t * Y1,
uECC_Curve curve) uECC_Curve curve)
{ {
/* t1 = X1, t2 = Y1, t3 = X2, t4 = Y2 */ /* t1 = X1, t2 = Y1, t3 = X2, t4 = Y2 */
uECC_word_t t5[NUM_ECC_WORDS]; uECC_word_t t5[NUM_ECC_WORDS] = {0};
wordcount_t num_words = curve->num_words; wordcount_t num_words = curve->num_words;
uECC_vli_modSub(t5, X2, X1, curve->p, num_words); /* t5 = x2 - x1 */ uECC_vli_modSub(t5, X2, X1, curve->p, num_words); /* t5 = x2 - x1 */
@@ -741,7 +764,7 @@ void EccPoint_mult(uECC_word_t * result, const uECC_word_t * point,
const uECC_word_t * initial_Z, const uECC_word_t * initial_Z,
bitcount_t num_bits, uECC_Curve curve) bitcount_t num_bits, uECC_Curve curve)
{ {
#if SOC_ECC_SUPPORTED #if SOC_ECC_SUPPORTED && !SOC_ESP_NIMBLE_CONTROLLER
wordcount_t num_words = curve->num_words; wordcount_t num_words = curve->num_words;
/* Only p256r1 is supported currently. */ /* Only p256r1 is supported currently. */
@@ -799,10 +822,22 @@ uECC_word_t regularize_k(const uECC_word_t * const k, uECC_word_t *k0,
wordcount_t num_n_words = BITS_TO_WORDS(curve->num_n_bits); wordcount_t num_n_words = BITS_TO_WORDS(curve->num_n_bits);
bitcount_t num_n_bits = curve->num_n_bits; bitcount_t num_n_bits = curve->num_n_bits;
uECC_word_t carry;
uECC_word_t bit = 0;
bitcount_t max_n_bits;
uECC_word_t carry = uECC_vli_add(k0, k, curve->n, num_n_words) || if (num_n_words <= 0 || num_n_words > NUM_ECC_WORDS) {
(num_n_bits < ((bitcount_t)num_n_words * uECC_WORD_SIZE * 8) && return 0;
uECC_vli_testBit(k0, num_n_bits)); }
max_n_bits = (bitcount_t)num_n_words * uECC_WORD_SIZE * 8;
carry = uECC_vli_add(k0, k, curve->n, num_n_words);
if (num_n_bits < max_n_bits) {
bit = (uECC_vli_testBit(k0, num_n_bits) != 0);
}
carry |= bit;
uECC_vli_add(k1, k0, curve->n, num_n_words); uECC_vli_add(k1, k0, curve->n, num_n_words);
@@ -813,17 +848,22 @@ uECC_word_t EccPoint_compute_public_key(uECC_word_t *result,
uECC_word_t *private_key, uECC_word_t *private_key,
uECC_Curve curve) uECC_Curve curve)
{ {
#if !SOC_ECC_SUPPORTED || SOC_ESP_NIMBLE_CONTROLLER
uECC_word_t tmp1[NUM_ECC_WORDS]; uECC_word_t tmp1[NUM_ECC_WORDS];
uECC_word_t tmp2[NUM_ECC_WORDS]; uECC_word_t tmp2[NUM_ECC_WORDS];
uECC_word_t *p2[2] = {tmp1, tmp2}; uECC_word_t *p2[2] = {tmp1, tmp2};
uECC_word_t carry; uECC_word_t carry;
#endif
#if SOC_ECC_SUPPORTED && !SOC_ESP_NIMBLE_CONTROLLER
EccPoint_mult(result, curve->G, private_key, 0, curve->num_n_bits, curve);
#else
/* Regularize the bitcount for the private key so that attackers cannot /* Regularize the bitcount for the private key so that attackers cannot
* use a side channel attack to learn the number of leading zeros. */ * use a side channel attack to learn the number of leading zeros. */
carry = regularize_k(private_key, tmp1, tmp2, curve); carry = regularize_k(private_key, tmp1, tmp2, curve);
EccPoint_mult(result, curve->G, p2[!carry], 0, curve->num_n_bits + 1, curve); EccPoint_mult(result, curve->G, p2[!carry], 0, curve->num_n_bits + 1, curve);
#endif
if (EccPoint_isZero(result, curve)) { if (EccPoint_isZero(result, curve)) {
return 0; return 0;
@@ -862,7 +902,7 @@ int uECC_generate_random_int(uECC_word_t *random, const uECC_word_t *top,
uECC_word_t tries; uECC_word_t tries;
bitcount_t num_bits = uECC_vli_numBits(top, num_words); bitcount_t num_bits = uECC_vli_numBits(top, num_words);
if (!g_rng_function) { if (!g_rng_function || num_words <= 0 || num_words > NUM_ECC_WORDS) {
return 0; return 0;
} }
@@ -896,7 +936,7 @@ int uECC_valid_point(const uECC_word_t *point, uECC_Curve curve)
return -2; return -2;
} }
#if SOC_ECC_SUPPORTED #if SOC_ECC_SUPPORTED && !SOC_ESP_NIMBLE_CONTROLLER
/* Only p256r1 is supported currently. */ /* Only p256r1 is supported currently. */
if (curve != uECC_secp256r1()) { if (curve != uECC_secp256r1()) {
return -5; return -5;
+13 -8
View File
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2025-2026 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
@@ -63,12 +63,6 @@
#include <tinycrypt/ecc_dh.h> #include <tinycrypt/ecc_dh.h>
#include <string.h> #include <string.h>
#if default_RNG_defined
static uECC_RNG_Function g_rng_function = &default_CSPRNG;
#else
static uECC_RNG_Function g_rng_function = 0;
#endif
int uECC_make_key_with_d(uint8_t *public_key, uint8_t *private_key, int uECC_make_key_with_d(uint8_t *public_key, uint8_t *private_key,
unsigned int *d, uECC_Curve curve) unsigned int *d, uECC_Curve curve)
{ {
@@ -153,9 +147,11 @@ int uECC_shared_secret(const uint8_t *public_key, const uint8_t *private_key,
uECC_word_t _private[NUM_ECC_WORDS]; uECC_word_t _private[NUM_ECC_WORDS];
uECC_word_t tmp[NUM_ECC_WORDS]; uECC_word_t tmp[NUM_ECC_WORDS];
#if !SOC_ECC_SUPPORTED || SOC_ESP_NIMBLE_CONTROLLER
uECC_word_t *p2[2] = {_private, tmp}; uECC_word_t *p2[2] = {_private, tmp};
uECC_word_t *initial_Z = 0; uECC_word_t *initial_Z = 0;
uECC_word_t carry; uECC_word_t carry;
#endif
wordcount_t num_words = curve->num_words; wordcount_t num_words = curve->num_words;
wordcount_t num_bytes = curve->num_bytes; wordcount_t num_bytes = curve->num_bytes;
int r; int r;
@@ -171,13 +167,17 @@ int uECC_shared_secret(const uint8_t *public_key, const uint8_t *private_key,
public_key + num_bytes, public_key + num_bytes,
num_bytes); num_bytes);
#if SOC_ECC_SUPPORTED && !SOC_ESP_NIMBLE_CONTROLLER
EccPoint_mult(_public, _public, _private, 0, curve->num_n_bits, curve);
#else
/* Regularize the bitcount for the private key so that attackers cannot use a /* Regularize the bitcount for the private key so that attackers cannot use a
* side channel attack to learn the number of leading zeros. */ * side channel attack to learn the number of leading zeros. */
carry = regularize_k(_private, _private, tmp, curve); carry = regularize_k(_private, _private, tmp, curve);
/* If an RNG function was specified, try to get a random initial Z value to /* If an RNG function was specified, try to get a random initial Z value to
* improve protection against side-channel attacks. */ * improve protection against side-channel attacks. */
if (g_rng_function) { uECC_RNG_Function rng_function = uECC_get_rng();
if (rng_function) {
if (!uECC_generate_random_int(p2[carry], curve->p, num_words)) { if (!uECC_generate_random_int(p2[carry], curve->p, num_words)) {
r = 0; r = 0;
goto clear_and_out; goto clear_and_out;
@@ -187,14 +187,19 @@ int uECC_shared_secret(const uint8_t *public_key, const uint8_t *private_key,
EccPoint_mult(_public, _public, p2[!carry], initial_Z, curve->num_n_bits + 1, EccPoint_mult(_public, _public, p2[!carry], initial_Z, curve->num_n_bits + 1,
curve); curve);
#endif
uECC_vli_nativeToBytes(secret, num_bytes, _public); uECC_vli_nativeToBytes(secret, num_bytes, _public);
r = !EccPoint_isZero(_public, curve); r = !EccPoint_isZero(_public, curve);
clear_and_out: clear_and_out:
/* erasing temporary buffer used to store secret: */ /* erasing temporary buffer used to store secret: */
#if !SOC_ECC_SUPPORTED || SOC_ESP_NIMBLE_CONTROLLER
memset(p2, 0, sizeof(p2)); memset(p2, 0, sizeof(p2));
__asm__ __volatile__("" :: "g"(p2) : "memory"); __asm__ __volatile__("" :: "g"(p2) : "memory");
#endif
memset(_public, 0, sizeof(_public));
__asm__ __volatile__("" :: "g"(_public) : "memory");
memset(tmp, 0, sizeof(tmp)); memset(tmp, 0, sizeof(tmp));
__asm__ __volatile__("" :: "g"(tmp) : "memory"); __asm__ __volatile__("" :: "g"(tmp) : "memory");
memset(_private, 0, sizeof(_private)); memset(_private, 0, sizeof(_private));
+12 -10
View File
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2025-2026 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
@@ -61,12 +61,6 @@
#include <tinycrypt/ecc.h> #include <tinycrypt/ecc.h>
#include <tinycrypt/ecc_dsa.h> #include <tinycrypt/ecc_dsa.h>
#if default_RNG_defined
static uECC_RNG_Function g_rng_function = &default_CSPRNG;
#else
static uECC_RNG_Function g_rng_function = 0;
#endif
static void bits2int(uECC_word_t *native, const uint8_t *bits, static void bits2int(uECC_word_t *native, const uint8_t *bits,
unsigned bits_size, uECC_Curve curve) unsigned bits_size, uECC_Curve curve)
{ {
@@ -107,9 +101,11 @@ int uECC_sign_with_k(const uint8_t *private_key, const uint8_t *message_hash,
uECC_word_t tmp[NUM_ECC_WORDS]; uECC_word_t tmp[NUM_ECC_WORDS];
uECC_word_t s[NUM_ECC_WORDS]; uECC_word_t s[NUM_ECC_WORDS];
#if !SOC_ECC_SUPPORTED || SOC_ESP_NIMBLE_CONTROLLER
uECC_word_t *k2[2] = {tmp, s}; uECC_word_t *k2[2] = {tmp, s};
uECC_word_t p[NUM_ECC_WORDS * 2];
uECC_word_t carry; uECC_word_t carry;
#endif
uECC_word_t p[NUM_ECC_WORDS * 2];
wordcount_t num_words = curve->num_words; wordcount_t num_words = curve->num_words;
wordcount_t num_n_words = BITS_TO_WORDS(curve->num_n_bits); wordcount_t num_n_words = BITS_TO_WORDS(curve->num_n_bits);
bitcount_t num_n_bits = curve->num_n_bits; bitcount_t num_n_bits = curve->num_n_bits;
@@ -120,15 +116,20 @@ int uECC_sign_with_k(const uint8_t *private_key, const uint8_t *message_hash,
return 0; return 0;
} }
#if SOC_ECC_SUPPORTED && !SOC_ESP_NIMBLE_CONTROLLER
EccPoint_mult(p, curve->G, k, 0, num_n_bits, curve);
#else
carry = regularize_k(k, tmp, s, curve); carry = regularize_k(k, tmp, s, curve);
EccPoint_mult(p, curve->G, k2[!carry], 0, num_n_bits + 1, curve); EccPoint_mult(p, curve->G, k2[!carry], 0, num_n_bits + 1, curve);
#endif
if (uECC_vli_isZero(p, num_words)) { if (uECC_vli_isZero(p, num_words)) {
return 0; return 0;
} }
/* If an RNG function was specified, get a random number /* If an RNG function was specified, get a random number
to prevent side channel analysis of k. */ to prevent side channel analysis of k. */
if (!g_rng_function) { uECC_RNG_Function rng_function = uECC_get_rng();
if (!rng_function) {
uECC_vli_clear(tmp, num_n_words); uECC_vli_clear(tmp, num_n_words);
tmp[0] = 1; tmp[0] = 1;
} }
@@ -154,7 +155,8 @@ int uECC_sign_with_k(const uint8_t *private_key, const uint8_t *message_hash,
bits2int(tmp, message_hash, hash_size, curve); bits2int(tmp, message_hash, hash_size, curve);
uECC_vli_modAdd(s, tmp, s, curve->n, num_n_words); /* s = e + r*d */ uECC_vli_modAdd(s, tmp, s, curve->n, num_n_words); /* s = e + r*d */
uECC_vli_modMult(s, s, k, curve->n, num_n_words); /* s = (e + r*d) / k */ uECC_vli_modMult(s, s, k, curve->n, num_n_words); /* s = (e + r*d) / k */
if (uECC_vli_numBits(s, num_n_words) > (bitcount_t)curve->num_bytes * 8) { if (uECC_vli_isZero(s, num_n_words) ||
uECC_vli_numBits(s, num_n_words) > (bitcount_t)curve->num_bytes * 8) {
return 0; return 0;
} }
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2025-2026 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
@@ -86,6 +86,9 @@ static void update(TCHmacPrng_t prng, const uint8_t *e, unsigned int len)
const uint8_t separator0 = 0x00; const uint8_t separator0 = 0x00;
const uint8_t separator1 = 0x01; const uint8_t separator1 = 0x01;
/* tc_hmac_final wipes the HMAC context, so reload key before init */
(void)tc_hmac_set_key(&prng->h, prng->key, sizeof(prng->key));
/* use current state, e and separator 0 to compute a new prng key: */ /* use current state, e and separator 0 to compute a new prng key: */
(void)tc_hmac_init(&prng->h); (void)tc_hmac_init(&prng->h);
(void)tc_hmac_update(&prng->h, prng->v, sizeof(prng->v)); (void)tc_hmac_update(&prng->h, prng->v, sizeof(prng->v));
@@ -101,6 +104,7 @@ static void update(TCHmacPrng_t prng, const uint8_t *e, unsigned int len)
(void)tc_hmac_final(prng->v, sizeof(prng->v), &prng->h); (void)tc_hmac_final(prng->v, sizeof(prng->v), &prng->h);
/* use current state, e and separator 1 to compute a new prng key: */ /* use current state, e and separator 1 to compute a new prng key: */
(void)tc_hmac_set_key(&prng->h, prng->key, sizeof(prng->key));
(void)tc_hmac_init(&prng->h); (void)tc_hmac_init(&prng->h);
(void)tc_hmac_update(&prng->h, prng->v, sizeof(prng->v)); (void)tc_hmac_update(&prng->h, prng->v, sizeof(prng->v));
(void)tc_hmac_update(&prng->h, &separator1, sizeof(separator1)); (void)tc_hmac_update(&prng->h, &separator1, sizeof(separator1));
@@ -113,6 +117,9 @@ static void update(TCHmacPrng_t prng, const uint8_t *e, unsigned int len)
(void)tc_hmac_init(&prng->h); (void)tc_hmac_init(&prng->h);
(void)tc_hmac_update(&prng->h, prng->v, sizeof(prng->v)); (void)tc_hmac_update(&prng->h, prng->v, sizeof(prng->v));
(void)tc_hmac_final(prng->v, sizeof(prng->v), &prng->h); (void)tc_hmac_final(prng->v, sizeof(prng->v), &prng->h);
/* keep HMAC context prepared for callers that reuse prng->h */
(void)tc_hmac_set_key(&prng->h, prng->key, sizeof(prng->key));
} }
int tc_hmac_prng_init(TCHmacPrng_t prng, int tc_hmac_prng_init(TCHmacPrng_t prng,
@@ -198,6 +205,7 @@ int tc_hmac_prng_generate(uint8_t *out, unsigned int outlen, TCHmacPrng_t prng)
while (outlen != 0) { while (outlen != 0) {
/* operate HMAC in OFB mode to create "random" outputs */ /* operate HMAC in OFB mode to create "random" outputs */
(void)tc_hmac_set_key(&prng->h, prng->key, sizeof(prng->key));
(void)tc_hmac_init(&prng->h); (void)tc_hmac_init(&prng->h);
(void)tc_hmac_update(&prng->h, prng->v, sizeof(prng->v)); (void)tc_hmac_update(&prng->h, prng->v, sizeof(prng->v));
(void)tc_hmac_final(prng->v, sizeof(prng->v), &prng->h); (void)tc_hmac_final(prng->v, sizeof(prng->v), &prng->h);
+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;
+183 -42
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
*/ */
@@ -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,42 @@ 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();
/* Known timing issue: STOP_EVENT is posted here but ble_hid_free_config (called
* next) immediately deletes the event loop task, which may discard the queued
* event before it is dispatched. A proper fix requires deleting the event loop
* from within the STOP_EVENT handler itself (architectural refactor needed). */
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 +375,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 +396,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 +409,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 +435,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 +486,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 +549,88 @@ 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;
}
/* Known limitation: HOGP spec requires the BLE link to be encrypted before
* the host may access HID report characteristics. Encryption is not enforced
* here; it must be enforced either by adding BLE_GATT_CHR_F_READ_ENC /
* BLE_GATT_CHR_F_WRITE_ENC flags to characteristics in ble_svc_hid.c, or by
* configuring ble_hs_cfg.sm_* security parameters at the application level. */
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 +652,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 +669,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 +684,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 +693,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 +702,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 +723,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 +747,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 +779,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 +838,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
+6 -1
View File
@@ -197,6 +197,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.
* sm_mitm/sm_sc/sm_bonding are opt-in via Kconfig to let the example show
* multiple configurations; Just Works is acceptable for BLUFI provisioning. */
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;
@@ -242,11 +245,13 @@ esp_err_t esp_blufi_host_deinit(void)
{ {
esp_err_t ret = ESP_OK; esp_err_t ret = ESP_OK;
esp_blufi_gatt_svr_deinit();
ret = nimble_port_stop(); ret = nimble_port_stop();
if (ret != ESP_OK) { if (ret != ESP_OK) {
return ret; return ret;
} }
esp_blufi_gatt_svr_deinit();
if (ret == 0) { if (ret == 0) {
esp_nimble_deinit(); esp_nimble_deinit();
} }
@@ -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.
@@ -2,4 +2,4 @@
# Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap # Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap
nvs, data, nvs, , 0x6000, nvs, data, nvs, , 0x6000,
phy_init, data, phy, , 0x1000, phy_init, data, phy, , 0x1000,
factory, app, factory, , 0x142000, factory, app, factory, , 0x143000,
1 # Name, Type, SubType, Offset, Size, Flags
2 # Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap
3 nvs, data, nvs, , 0x6000,
4 phy_init, data, phy, , 0x1000,
5 factory, app, factory, , 0x142000, factory, app, factory, , 0x143000,