feat(nimble): Migrate to nimble-1.10.0

This commit is contained in:
Rahul Tank
2026-09-09 15:42:05 +05:30
parent 2210471aba
commit 6a07858110
8 changed files with 190 additions and 91 deletions
+8 -3
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
*/
@@ -273,9 +273,14 @@ void esp_hid_cod_minor_print(uint8_t cod_min, FILE *fp);
/**
* @brief Convert BLE disconnect reason to string
* @param reason : The value of the reason
*
* @return: a pointer to the string or NULL
* Accepts Bluedroid `esp_gatt_conn_reason_t` values (raw HCI for most
* cases) and NimBLE `BLE_HS_HCI_ERR()` values (`0x200 + HCI status`).
*
* @param transport : HID transport (string map is BLE-only)
* @param reason : Disconnect reason from the host stack
*
* @return: a pointer to the string (never NULL)
*/
const char *esp_hid_disconnect_reason_str(esp_hid_transport_t transport, int reason);
+53 -15
View File
@@ -14,6 +14,9 @@
#if (CONFIG_GATTS_ENABLE || CONFIG_GATTC_ENABLE)
#include "esp_gatt_defs.h"
#endif
#if CONFIG_BT_NIMBLE_ENABLED
#include "host/ble_hs.h"
#endif
static const char *TAG = "hid_parser";
typedef struct {
@@ -524,22 +527,57 @@ void esp_hid_cod_minor_print(uint8_t cod_min, FILE *fp)
}
}
/* Core HCI disconnect statuses (Vol 1 Part F). Shared by Bluedroid GATT
* conn reasons and NimBLE BLE_HS_HCI_ERR(hci_status).
*/
static const char *ble_hci_disconn_reason_str(int hci_reason)
{
switch (hci_reason) {
case 0x05: return "AUTH_FAIL";
case 0x06: return "PINKEY_MISSING";
case 0x08: return "TIMEOUT";
case 0x13: return "TERMINATE_PEER_USER";
case 0x14: return "TERMINATE_PEER_RESOURCES";
case 0x15: return "TERMINATE_PEER_POWER_OFF";
case 0x16: return "TERMINATE_LOCAL_HOST";
case 0x1f: return "UNSPECIFIED";
case 0x22: return "LMP_TIMEOUT";
case 0x28: return "INSTANT_PASSED";
case 0x3b: return "UNACCEPTABLE_CONN_PARAMS";
case 0x3d: return "MIC_FAILURE";
case 0x3e: return "FAIL_ESTABLISH";
default: return NULL;
}
}
const char *esp_hid_disconnect_reason_str(esp_hid_transport_t transport, int reason)
{
if (transport == ESP_HID_TRANSPORT_BLE) {
#if (CONFIG_GATTS_ENABLE || CONFIG_GATTC_ENABLE)
switch ((esp_gatt_conn_reason_t)reason) {
case ESP_GATT_CONN_L2C_FAILURE: return "L2C_FAILURE";
case ESP_GATT_CONN_TIMEOUT: return "TIMEOUT";
case ESP_GATT_CONN_TERMINATE_PEER_USER: return "TERMINATE_PEER_USER";
case ESP_GATT_CONN_TERMINATE_LOCAL_HOST: return "TERMINATE_LOCAL_HOST";
case ESP_GATT_CONN_LMP_TIMEOUT: return "LMP_TIMEOUT";
case ESP_GATT_CONN_FAIL_ESTABLISH: return "FAIL_ESTABLISH";
case ESP_GATT_CONN_CONN_CANCEL: return "CONN_CANCEL";
case ESP_GATT_CONN_NONE: return "NONE";
default: break;
}
#endif /* CONFIG_GATTS_ENABLE || CONFIG_GATTC_ENABLE */
const char *str;
if (transport != ESP_HID_TRANSPORT_BLE) {
return s_unknown_str;
}
return s_unknown_str;
#if CONFIG_BT_NIMBLE_ENABLED
/* NimBLE GAP posts BLE_HS_HCI_ERR(hci) = 0x200 + hci. Do not fall through:
* HCI 0x01 would collide with Bluedroid ESP_GATT_CONN_L2C_FAILURE.
*/
if (reason > BLE_HS_ERR_HCI_BASE && reason < BLE_HS_ERR_L2C_BASE) {
str = ble_hci_disconn_reason_str(reason - BLE_HS_ERR_HCI_BASE);
return str ? str : s_unknown_str;
}
#endif
#if (CONFIG_GATTS_ENABLE || CONFIG_GATTC_ENABLE)
switch ((esp_gatt_conn_reason_t)reason) {
case ESP_GATT_CONN_L2C_FAILURE: return "L2C_FAILURE";
case ESP_GATT_CONN_CONN_CANCEL: return "CONN_CANCEL";
case ESP_GATT_CONN_NONE: return "NONE";
default:
break;
}
#endif /* CONFIG_GATTS_ENABLE || CONFIG_GATTC_ENABLE */
str = ble_hci_disconn_reason_str(reason);
return str ? str : s_unknown_str;
}
@@ -520,6 +520,13 @@ void app_main(void)
}
ESP_ERROR_CHECK( ret );
#if !SOC_MPI_SUPPORTED
/* Software 3072-bit modular exponentiation needs ~4.2 kB of contiguous
* internal heap. Start it before Wi-Fi and the BLE host claim their pools,
* otherwise the allocation fails with PSA_ERROR_INSUFFICIENT_MEMORY. */
blufi_dh_pregen_start();
#endif
initialise_wifi();
#if CONFIG_BT_CONTROLLER_ENABLED || !CONFIG_BT_NIMBLE_ENABLED
@@ -537,7 +544,6 @@ void app_main(void)
}
#if !SOC_MPI_SUPPORTED
blufi_dh_pregen_start();
blufi_dh_pregen_wait();
esp_blufi_adv_start();
#endif
+113 -71
View File
@@ -47,7 +47,7 @@
#define BLUFI_DEC_DOMAIN_STR "blufi_dec"
#if !SOC_MPI_SUPPORTED
#define BLUFI_DH_PREGEN_STACK_SIZE 4096
#define BLUFI_DH_PREGEN_STACK_SIZE 6144
#define BLUFI_DH_PREGEN_PRIO (tskIDLE_PRIORITY + 1)
#define BLUFI_DH_PREGEN_WAIT_MS 30000
#define BLUFI_DH_HEAVY_CRYPTO_WDT_MS 30000
@@ -125,11 +125,15 @@ static void blufi_cleanup_negotiation(bool abort_enc, bool abort_dec)
* with BLE advertising so only key agreement remains on the critical path
* when the phone sends its DH parameters.
*/
#define BLUFI_PREGEN_DONE_BIT BIT0
static psa_key_id_t s_pregen_private_key;
static uint8_t s_pregen_public_key[DH_SELF_PUB_KEY_LEN];
static size_t s_pregen_public_key_len;
static SemaphoreHandle_t s_pregen_done;
static bool s_pregen_ok;
static bool s_pregen_busy;
static EventGroupHandle_t s_pregen_evt;
static SemaphoreHandle_t s_pregen_req;
static TaskHandle_t s_pregen_task_hdl;
static void (*s_pregen_complete_cb)(void);
@@ -143,12 +147,8 @@ static void blufi_wdt_set_timeout(uint32_t timeout_ms)
esp_task_wdt_reconfigure(&cfg);
}
static void blufi_dh_pregen_task(void *arg)
static void blufi_dh_pregen_generate(void)
{
(void)arg;
blufi_wdt_set_timeout(BLUFI_DH_HEAVY_CRYPTO_WDT_MS);
psa_key_attributes_t attr = psa_key_attributes_init();
psa_set_key_type(&attr, PSA_KEY_TYPE_DH_KEY_PAIR(PSA_DH_FAMILY_RFC7919));
psa_set_key_bits(&attr, 3072);
@@ -170,44 +170,88 @@ static void blufi_dh_pregen_task(void *arg)
}
BLUFI_INFO("DH keypair pre-generation %s", s_pregen_ok ? "done" : "FAILED");
blufi_wdt_set_timeout(CONFIG_ESP_TASK_WDT_TIMEOUT_S * 1000);
xSemaphoreGive(s_pregen_done);
if (s_pregen_complete_cb) {
void (*cb)(void) = s_pregen_complete_cb;
s_pregen_complete_cb = NULL;
cb();
}
vTaskSuspend(NULL);
}
static void blufi_dh_pregen_start_impl(void (*done_cb)(void))
/*
* The worker outlives every connection. Creating it per keypair would mean
* asking for BLUFI_DH_PREGEN_STACK_SIZE of contiguous internal heap at the
* worst possible moment - right after a disconnect, with Wi-Fi and the BLE host
* fully up - which is where xTaskCreate() fails.
*/
static void blufi_dh_pregen_task(void *arg)
{
if (s_pregen_done != NULL) {
return;
(void)arg;
while (true) {
xSemaphoreTake(s_pregen_req, portMAX_DELAY);
blufi_wdt_set_timeout(BLUFI_DH_HEAVY_CRYPTO_WDT_MS);
blufi_dh_pregen_generate();
blufi_wdt_set_timeout(CONFIG_ESP_TASK_WDT_TIMEOUT_S * 1000);
s_pregen_busy = false;
xEventGroupSetBits(s_pregen_evt, BLUFI_PREGEN_DONE_BIT);
if (s_pregen_complete_cb) {
void (*cb)(void) = s_pregen_complete_cb;
s_pregen_complete_cb = NULL;
cb();
}
}
}
static bool blufi_dh_pregen_worker_ensure(void)
{
if (s_pregen_task_hdl != NULL) {
return true;
}
s_pregen_private_key = 0;
s_pregen_public_key_len = 0;
s_pregen_ok = false;
s_pregen_task_hdl = NULL;
s_pregen_complete_cb = done_cb;
s_pregen_done = xSemaphoreCreateBinary();
if (s_pregen_done == NULL) {
BLUFI_ERROR("Failed to create pre-gen semaphore");
return;
if (s_pregen_evt == NULL) {
s_pregen_evt = xEventGroupCreate();
}
if (s_pregen_req == NULL) {
s_pregen_req = xSemaphoreCreateBinary();
}
if (s_pregen_evt == NULL || s_pregen_req == NULL) {
BLUFI_ERROR("Failed to create pre-gen sync objects");
return false;
}
if (xTaskCreate(blufi_dh_pregen_task, "blufi_pregen",
BLUFI_DH_PREGEN_STACK_SIZE, NULL,
BLUFI_DH_PREGEN_PRIO, &s_pregen_task_hdl) != pdPASS) {
BLUFI_ERROR("Failed to create pre-gen task");
vSemaphoreDelete(s_pregen_done);
s_pregen_done = NULL;
s_pregen_task_hdl = NULL;
return false;
}
return true;
}
static void blufi_dh_pregen_start_impl(void (*done_cb)(void))
{
if (!blufi_dh_pregen_worker_ensure()) {
/* Advertise anyway; negotiation falls back to inline key generation. */
if (done_cb) {
done_cb();
}
return;
}
/* A keypair is already banked, or one is on its way. */
if (s_pregen_busy || (s_pregen_ok && s_pregen_private_key != 0)) {
if (done_cb) {
done_cb();
}
return;
}
s_pregen_public_key_len = 0;
s_pregen_ok = false;
s_pregen_complete_cb = done_cb;
s_pregen_busy = true;
xEventGroupClearBits(s_pregen_evt, BLUFI_PREGEN_DONE_BIT);
xSemaphoreGive(s_pregen_req);
}
void blufi_dh_pregen_start(void)
@@ -222,29 +266,30 @@ void blufi_dh_pregen_start_with_cb(void (*done_cb)(void))
void blufi_dh_pregen_wait(void)
{
if (s_pregen_done == NULL) {
if (s_pregen_evt == NULL) {
return;
}
xSemaphoreTake(s_pregen_done, portMAX_DELAY);
xSemaphoreGive(s_pregen_done);
xEventGroupWaitBits(s_pregen_evt, BLUFI_PREGEN_DONE_BIT,
pdFALSE, pdTRUE, portMAX_DELAY);
}
static void blufi_dh_pregen_cleanup(void)
/**
* @brief Hand the banked keypair to the caller, which takes ownership of it
*
* @return The private key id, or 0 when no usable keypair is banked
*/
static psa_key_id_t blufi_dh_pregen_take(void)
{
if (s_pregen_done != NULL) {
xSemaphoreTake(s_pregen_done, portMAX_DELAY);
vSemaphoreDelete(s_pregen_done);
s_pregen_done = NULL;
}
if (s_pregen_task_hdl != NULL) {
vTaskDelete(s_pregen_task_hdl);
s_pregen_task_hdl = NULL;
}
if (s_pregen_private_key != 0) {
psa_destroy_key(s_pregen_private_key);
s_pregen_private_key = 0;
if (!s_pregen_ok || s_pregen_private_key == 0) {
return 0;
}
psa_key_id_t key = s_pregen_private_key;
s_pregen_private_key = 0;
s_pregen_ok = false;
xEventGroupClearBits(s_pregen_evt, BLUFI_PREGEN_DONE_BIT);
return key;
}
#endif /* !SOC_MPI_SUPPORTED */
@@ -384,19 +429,27 @@ void blufi_dh_negotiate_data_handler(uint8_t *data, int len, uint8_t **output_da
* heap before key agreement, and widen the task WDT window. */
bool used_pregen = false;
if (s_pregen_done != NULL &&
xSemaphoreTake(s_pregen_done, pdMS_TO_TICKS(BLUFI_DH_PREGEN_WAIT_MS)) == pdTRUE) {
xSemaphoreGive(s_pregen_done);
if (s_pregen_ok && s_pregen_private_key != 0) {
private_key = s_pregen_private_key;
s_pregen_private_key = 0;
memcpy(blufi_sec->self_public_key, s_pregen_public_key, s_pregen_public_key_len);
public_key_len = s_pregen_public_key_len;
if (s_pregen_evt != NULL &&
(xEventGroupWaitBits(s_pregen_evt, BLUFI_PREGEN_DONE_BIT, pdFALSE, pdTRUE,
pdMS_TO_TICKS(BLUFI_DH_PREGEN_WAIT_MS)) & BLUFI_PREGEN_DONE_BIT)) {
size_t pregen_len = s_pregen_public_key_len;
private_key = blufi_dh_pregen_take();
if (private_key != 0) {
memcpy(blufi_sec->self_public_key, s_pregen_public_key, pregen_len);
public_key_len = pregen_len;
used_pregen = true;
BLUFI_INFO("Using pre-generated DH keypair");
}
}
/* Release the DH parameter buffer before any further modular
* exponentiation. Both the inline keypair generation and the key
* agreement below need a single ~4.2 kB contiguous internal
* allocation, so nothing reclaimable may be held across them. */
uint8_t peer_pub[DH_SELF_PUB_KEY_LEN];
memcpy(peer_pub, param, pub_len);
blufi_cleanup_dh_param();
if (!used_pregen) {
BLUFI_INFO("Pre-gen unavailable, generating DH keypair inline");
blufi_wdt_set_timeout(BLUFI_DH_HEAVY_CRYPTO_WDT_MS);
@@ -412,7 +465,6 @@ void blufi_dh_negotiate_data_handler(uint8_t *data, int len, uint8_t **output_da
BLUFI_ERROR("%s psa_generate_key failed %d\n", __func__, status);
blufi_wdt_set_timeout(CONFIG_ESP_TASK_WDT_TIMEOUT_S * 1000);
btc_blufi_report_error(ESP_BLUFI_DH_MALLOC_ERROR);
blufi_cleanup_dh_param();
return;
}
psa_reset_key_attributes(&keygen_attr);
@@ -424,20 +476,10 @@ void blufi_dh_negotiate_data_handler(uint8_t *data, int len, uint8_t **output_da
blufi_wdt_set_timeout(CONFIG_ESP_TASK_WDT_TIMEOUT_S * 1000);
psa_destroy_key(private_key);
btc_blufi_report_error(ESP_BLUFI_DH_MALLOC_ERROR);
blufi_cleanup_dh_param();
return;
}
}
uint8_t peer_pub[DH_SELF_PUB_KEY_LEN];
memcpy(peer_pub, param, pub_len);
blufi_cleanup_dh_param();
if (s_pregen_task_hdl != NULL) {
vTaskDelete(s_pregen_task_hdl);
s_pregen_task_hdl = NULL;
}
blufi_wdt_set_timeout(BLUFI_DH_HEAVY_CRYPTO_WDT_MS);
status = psa_raw_key_agreement(alg, private_key, peer_pub, pub_len,
blufi_sec->share_key, SHARE_KEY_LEN,
@@ -738,9 +780,9 @@ void blufi_security_deinit(void)
return;
}
#if !SOC_MPI_SUPPORTED
blufi_dh_pregen_cleanup();
#endif
/* The pre-generation worker and any keypair it has banked deliberately
* survive the disconnect: the next connection reuses them, and its task
* stack could not be reallocated once Wi-Fi and the BLE host are up. */
/* Clean up all resources */
blufi_cleanup_negotiation(true, true);
@@ -18,3 +18,7 @@ CONFIG_BT_NIMBLE_ROLE_OBSERVER=n
CONFIG_BT_NIMBLE_50_FEATURE_SUPPORT=n
CONFIG_BT_NIMBLE_SECURITY_ENABLE=n
CONFIG_BT_NIMBLE_CRYPTO_STACK_MBEDTLS=n
# ESP32-C2 has no hardware MPI, so the 3072-bit BLUFI Diffie-Hellman runs in
# software and needs ~4.2 kB of contiguous internal heap. BLUFI only exchanges
# small packets, so the 320-byte msys_2 pool is trimmed to leave that headroom.
CONFIG_BT_NIMBLE_MSYS_2_BLOCK_COUNT=8
@@ -22,7 +22,9 @@
#endif
static const char *tag = "NimBLE_CTS_CENT";
#if CONFIG_EXAMPLE_CI_ID && CONFIG_EXAMPLE_CI_PIPELINE_ID
static char remote_device_name[32];
#endif
static int ble_cts_cent_gap_event(struct ble_gap_event *event, void *arg);
#if CONFIG_EXAMPLE_CI_ID && CONFIG_EXAMPLE_CI_PIPELINE_ID
@@ -21,7 +21,9 @@
#endif
static const char *tag = "NimBLE_HTP_CENT";
#if CONFIG_EXAMPLE_CI_ID && CONFIG_EXAMPLE_CI_PIPELINE_ID
static char remote_device_name[32];
#endif
static int ble_htp_cent_gap_event(struct ble_gap_event *event, void *arg);
void ble_store_config_init(void);