fix(nimble): Fix issues found in review for nimble examples

This commit is contained in:
Rahul Tank
2026-07-24 15:53:38 +05:30
parent 7ec47c264d
commit 8aa7006649
73 changed files with 1291 additions and 621 deletions
@@ -163,8 +163,10 @@ static void register_heap(void)
static int tasks_info(int argc, char **argv)
{
const size_t bytes_per_task = 40; /* see vTaskList description */
char *task_list_buffer = malloc(uxTaskGetNumberOfTasks() * bytes_per_task);
/* Use a larger per-task estimate (80 bytes) plus a safety margin of 10 extra
* slots to absorb TOCTOU races and SMP mode affinity fields. */
const size_t bytes_per_task = 80;
char *task_list_buffer = malloc((uxTaskGetNumberOfTasks() + 10) * bytes_per_task);
if (task_list_buffer == NULL) {
ESP_LOGE(TAG, "failed to allocate buffer for vTaskList output");
return 1;
@@ -211,14 +213,19 @@ static int deep_sleep(int argc, char **argv)
return 1;
}
if (deep_sleep_args.wakeup_time->count) {
uint64_t timeout = 1000ULL * deep_sleep_args.wakeup_time->ival[0];
int wakeup_ms = deep_sleep_args.wakeup_time->ival[0];
if (wakeup_ms <= 0) {
ESP_LOGE(TAG, "Invalid wakeup time: %d ms (must be > 0)", wakeup_ms);
return 1;
}
uint64_t timeout = 1000ULL * wakeup_ms;
ESP_LOGI(TAG, "Enabling timer wakeup, timeout=%lluus", timeout);
ESP_ERROR_CHECK( esp_sleep_enable_timer_wakeup(timeout) );
}
if (deep_sleep_args.wakeup_gpio_num->count) {
int io_num = deep_sleep_args.wakeup_gpio_num->ival[0];
if (!rtc_gpio_is_valid_gpio(io_num)) {
ESP_LOGE(TAG, "GPIO %d is not an RTC IO", io_num);
if (io_num < 0 || !rtc_gpio_is_valid_gpio(io_num)) {
ESP_LOGE(TAG, "GPIO %d is not a valid RTC IO", io_num);
return 1;
}
int level = 0;
@@ -229,11 +236,13 @@ static int deep_sleep(int argc, char **argv)
return 1;
}
}
#if SOC_PM_SUPPORT_EXT1_WAKEUP
ESP_LOGI(TAG, "Enabling wakeup on GPIO%d, wakeup on %s level",
io_num, level ? "HIGH" : "LOW");
#if SOC_PM_SUPPORT_EXT1_WAKEUP
ESP_ERROR_CHECK( esp_sleep_enable_ext1_wakeup_io(1ULL << io_num, level) );
#else
ESP_LOGE(TAG, "GPIO wakeup from deep sleep not supported on this target");
return 1;
#endif
}
@@ -301,21 +310,43 @@ static int light_sleep(int argc, char **argv)
ESP_LOGE(TAG, "Invalid wakeup level: %d", level);
return 1;
}
if (!GPIO_IS_VALID_GPIO(io_num)) {
ESP_LOGE(TAG, "Invalid GPIO number: %d", io_num);
return 1;
}
/* Configure the pin as input with a pull to avoid floating and false wakeups */
gpio_config_t io_conf = {
.pin_bit_mask = 1ULL << io_num,
.mode = GPIO_MODE_INPUT,
.pull_up_en = (level == 0) ? GPIO_PULLUP_ENABLE : GPIO_PULLUP_DISABLE,
.pull_down_en = (level == 1) ? GPIO_PULLDOWN_ENABLE : GPIO_PULLDOWN_DISABLE,
.intr_type = GPIO_INTR_DISABLE,
};
esp_err_t gpio_cfg_err = gpio_config(&io_conf);
if (gpio_cfg_err != ESP_OK) {
ESP_LOGE(TAG, "gpio_config failed for GPIO%d: %s", io_num, esp_err_to_name(gpio_cfg_err));
return 1;
}
ESP_LOGI(TAG, "Enabling wakeup on GPIO%d, wakeup on %s level",
io_num, level ? "HIGH" : "LOW");
ESP_ERROR_CHECK( gpio_wakeup_enable(io_num, level ? GPIO_INTR_HIGH_LEVEL : GPIO_INTR_LOW_LEVEL) );
esp_err_t wakeup_err = gpio_wakeup_enable(io_num, level ? GPIO_INTR_HIGH_LEVEL : GPIO_INTR_LOW_LEVEL);
if (wakeup_err != ESP_OK) {
ESP_LOGE(TAG, "gpio_wakeup_enable failed for GPIO%d: %s", io_num, esp_err_to_name(wakeup_err));
return 1;
}
}
if (io_count > 0) {
ESP_ERROR_CHECK( esp_sleep_enable_gpio_wakeup() );
}
if (CONFIG_ESP_CONSOLE_UART_NUM <= UART_NUM_1) {
if (CONFIG_ESP_CONSOLE_UART_NUM >= 0 && CONFIG_ESP_CONSOLE_UART_NUM <= UART_NUM_1) {
ESP_LOGI(TAG, "Enabling UART wakeup (press ENTER to exit light sleep)");
ESP_ERROR_CHECK( uart_set_wakeup_threshold(CONFIG_ESP_CONSOLE_UART_NUM, 3) );
ESP_ERROR_CHECK( esp_sleep_enable_uart_wakeup(CONFIG_ESP_CONSOLE_UART_NUM) );
}
fflush(stdout);
uart_wait_tx_idle_polling(CONFIG_ESP_CONSOLE_UART_NUM);
if (CONFIG_ESP_CONSOLE_UART_NUM >= 0) {
uart_wait_tx_idle_polling(CONFIG_ESP_CONSOLE_UART_NUM);
}
esp_light_sleep_start();
uint32_t causes = esp_sleep_get_wakeup_causes();
@@ -71,7 +71,7 @@ static uint16_t handle;
#define PHY_CODED_S8 3
#if CONFIG_EXAMPLE_EXTENDED_ADV
static int current_phy_updated;
static volatile int current_phy_updated;
#endif
/* State for callback-chained read throughput test */
@@ -194,8 +194,9 @@ blecent_notify(uint16_t conn_handle, uint16_t val_handle,
return 0;
err:
/* Terminate the connection. */
return ble_gap_terminate(peer->conn_handle, BLE_ERR_REM_USER_CONN_TERM);
/* Terminate the connection; return original error code so caller detects failure. */
ble_gap_terminate(peer->conn_handle, BLE_ERR_REM_USER_CONN_TERM);
return rc;
}
static int blecent_write(uint16_t conn_handle, uint16_t val_handle,
@@ -599,6 +600,11 @@ read_cleanup:
break;
case NOTIFY_THROUGHPUT:
if (test_data[1] <= 0) {
ESP_LOGE(tag, "Please enter non-zero value for test time in seconds!!");
break;
}
if (test_data[2] == PHY_CODED_S2) {
switch_conn_params(conn_handle, &conn_params_coded_s2);
} else if (test_data[2] == PHY_CODED_S8) {
@@ -641,7 +647,7 @@ read_cleanup:
" can be seen on peripheral terminal after %d seconds",
test_data[1]);
}
vTaskDelay(test_data[1]*1000 / portTICK_PERIOD_MS);
vTaskDelay((TickType_t)test_data[1] * 1000 / portTICK_PERIOD_MS);
/* Unsubscribe so the next notify test triggers a fresh
* BLE_GAP_EVENT_SUBSCRIBE on the peripheral (cur_notify 0→1) */
@@ -762,22 +768,17 @@ ext_blecent_should_connect(const struct ble_gap_ext_disc_desc *disc)
{
int offset = 0;
int ad_struct_len = 0;
uint8_t test_addr[6];
uint8_t parsed_addr[6];
uint8_t phy_uuid_found = 0;
if (disc->legacy_event_type != BLE_HCI_ADV_RPT_EVTYPE_ADV_IND &&
disc->legacy_event_type != BLE_HCI_ADV_RPT_EVTYPE_DIR_IND) {
if (!(disc->props & BLE_HCI_ADV_CONN_MASK)) {
return 0;
}
if (strlen(CONFIG_EXAMPLE_PEER_ADDR) && (strncmp(CONFIG_EXAMPLE_PEER_ADDR, "ADDR_ANY", strlen("ADDR_ANY")) != 0)) {
// ESP_LOGI(tag, "Peer address from menuconfig: %s", CONFIG_EXAMPLE_PEER_ADDR);
/* Convert string to address */
/* peer_addr_parse stores address in little-endian order matching disc->addr.val;
* no byte reversal needed. */
peer_addr_parse(CONFIG_EXAMPLE_PEER_ADDR, parsed_addr);
for (int i = 0; i < 6; i++) {
test_addr[5 - i] = parsed_addr[i];
}
if (memcmp(test_addr, disc->addr.val, sizeof(disc->addr.val)) != 0) {
if (memcmp(parsed_addr, disc->addr.val, sizeof(disc->addr.val)) != 0) {
return 0;
}
}
@@ -860,9 +861,13 @@ blecent_should_connect(const struct ble_gap_disc_desc *disc)
char serv_name[] = "nimble_prph";
if (fields.name != NULL) {
ESP_LOGI(tag, "Device Name = %s", (char *)fields.name);
/* fields.name is not null-terminated; use %.*s for safe printing */
ESP_LOGI(tag, "Device Name = %.*s", fields.name_len, (char *)fields.name);
if (memcmp(fields.name, serv_name, fields.name_len) == 0) {
/* Require exact length match to prevent prefix false-positives and
* avoid reading past the end of serv_name (stack over-read). */
if (fields.name_len == (uint8_t)strlen(serv_name) &&
memcmp(fields.name, serv_name, fields.name_len) == 0) {
ESP_LOGI(tag, "central connect to `nimble_prph` success");
return 1;
}
@@ -912,6 +917,9 @@ blecent_connect_if_interesting(void *disc)
rc = ble_hs_id_infer_auto(0, &own_addr_type);
if (rc != 0) {
ESP_LOGE(tag, "error determining address type; rc=%d", rc);
#if !(MYNEWT_VAL(BLE_HOST_ALLOW_CONNECT_WITH_SCAN))
blecent_scan();
#endif
return;
}
@@ -924,6 +932,9 @@ blecent_connect_if_interesting(void *disc)
ESP_LOGE(tag, "Error: Failed to connect to device; addr_type=%d "
"addr=%s; rc=%d\n",
conn_addr.type, addr_str(conn_addr.val), rc);
#if !(MYNEWT_VAL(BLE_HOST_ALLOW_CONNECT_WITH_SCAN))
blecent_scan();
#endif
return;
}
}
@@ -1038,7 +1049,6 @@ blecent_gap_event(struct ble_gap_event *event, void *arg)
/* Forget about peer. */
peer_delete(event->disconnect.conn.conn_handle);
vTaskDelay(200);
/* Resume scanning. */
blecent_scan();
@@ -1212,9 +1222,11 @@ app_main(void)
assert(rc == 0);
#endif
#if CONFIG_BT_NIMBLE_GAP_SERVICE
/* Set the default device name. */
rc = ble_svc_gap_device_name_set("gattc-throughput");
assert(rc == 0);
#endif
/* XXX Need to have template for store */
ble_store_config_init();
@@ -1241,32 +1253,9 @@ app_main(void)
printf(" | |\n");
printf("\n ===============================================================================================\n");
const char *prompt = LOG_COLOR_I "Throughput demo >> " LOG_RESET_COLOR;
while (true) {
/* Get a line using linenoise.
* The line is returned when ENTER is pressed.
*/
char *line = linenoise(prompt);
if (line == NULL) { /* Ignore empty lines */
continue;
}
/* Add the command to the history */
linenoiseHistoryAdd(line);
/* Try to run the command */
int ret;
esp_err_t err = esp_console_run(line, &ret);
if (err == ESP_ERR_NOT_FOUND) {
printf("Unrecognized command\n");
} else if (err == ESP_ERR_INVALID_ARG) {
// command was empty
} else if (err == ESP_OK && ret != ESP_OK) {
printf("Command returned non-zero error code: 0x%x (%s)\n", ret, esp_err_to_name(ret));
} else if (err != ESP_OK) {
printf("Internal error: %s\n", esp_err_to_name(err));
}
/* linenoise allocates line buffer on the heap, so need to free it */
linenoiseFree(line);
}
/* Start the REPL task that was created (but left blocked) by
* esp_console_new_repl_uart. Without this call the task would wait
* forever, leaking its 4 KB stack and TCB. */
ESP_ERROR_CHECK(esp_console_start_repl(repl));
/* app_main can now return; the REPL task handles console I/O from here. */
}
@@ -28,30 +28,44 @@ peer_addr_parse(const char *addr_str, uint8_t addr[PEER_ADDR_VAL_SIZE])
void
print_bytes(const uint8_t *bytes, int len)
{
/* Build the entire hex string into a buffer first and log it in one call.
* Calling MODLOG_DFLT per byte adds a full log header and newline to each
* byte, breaking the intended colon-separated format and blocking the CPU. */
char buf[256];
int pos = 0;
int i;
for (i = 0; i < len; i++) {
MODLOG_DFLT(DEBUG, "%s0x%02x", i != 0 ? ":" : "", bytes[i]);
for (i = 0; i < len && pos < (int)(sizeof(buf) - 5); i++) {
if (i != 0) {
buf[pos++] = ':';
}
pos += snprintf(buf + pos, sizeof(buf) - pos, "0x%02x", bytes[i]);
}
buf[pos] = '\0';
MODLOG_DFLT(DEBUG, "%s", buf);
}
void
print_mbuf(const struct os_mbuf *om)
{
int colon, i;
char buf[512];
int pos = 0;
int colon = 0;
int i;
colon = 0;
while (om != NULL) {
if (colon) {
MODLOG_DFLT(INFO, ":");
} else {
colon = 1;
}
for (i = 0; i < om->om_len; i++) {
MODLOG_DFLT(INFO, "%s0x%02x", i != 0 ? ":" : "", om->om_data[i]);
while (om != NULL && pos < (int)(sizeof(buf) - 6)) {
for (i = 0; i < om->om_len && pos < (int)(sizeof(buf) - 6); i++) {
if (colon) {
buf[pos++] = ':';
} else {
colon = 1;
}
pos += snprintf(buf + pos, sizeof(buf) - pos, "0x%02x", om->om_data[i]);
}
om = SLIST_NEXT(om, om_next);
}
buf[pos] = '\0';
MODLOG_DFLT(INFO, "%s", buf);
}
char *
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -40,7 +40,7 @@ chr_end_handle(const struct peer_svc *svc, const struct peer_chr *chr);
int
chr_is_empty(const struct peer_svc *svc, const struct peer_chr *chr);
static struct peer_chr *
peer_chr_find(const struct peer_svc *svc, uint16_t chr_def_handle,
peer_chr_find(const struct peer_svc *svc, uint16_t chr_val_handle,
struct peer_chr **out_prev);
static void
peer_disc_chrs(struct peer *peer);
@@ -168,7 +168,7 @@ peer_dsc_add(struct peer *peer, uint16_t chr_val_handle,
if (prev == NULL) {
SLIST_INSERT_HEAD(&chr->dscs, dsc, next);
} else {
SLIST_NEXT(prev, next) = dsc;
SLIST_INSERT_AFTER(prev, dsc, next);
}
return 0;
@@ -197,6 +197,7 @@ peer_disc_dscs(struct peer *peer)
peer_dsc_disced, peer);
if (rc != 0) {
peer_disc_complete(peer, rc);
return;
}
peer->disc_prev_chr_val = chr->chr.val_handle;
@@ -340,7 +341,7 @@ peer_chr_add(struct peer *peer, uint16_t svc_start_handle,
return BLE_HS_EUNKNOWN;
}
chr = peer_chr_find(svc, gatt_chr->def_handle, &prev);
chr = peer_chr_find(svc, gatt_chr->val_handle, &prev);
if (chr != NULL) {
/* Characteristic already discovered. */
return 0;
@@ -408,13 +409,20 @@ peer_disc_chrs(struct peer *peer)
struct peer_svc *svc;
int rc;
/* Search through the list of discovered service for the first service that
* contains undiscovered characteristics. Then, discover all
* characteristics belonging to that service.
*/
/* Advance cur_svc to the next service to discover characteristics for.
* Starting from NULL means start from the first service in the list.
* This sequential approach prevents re-processing services that had no
* characteristics (where SLIST_EMPTY would remain true and cause an
* infinite loop with the old SLIST_FOREACH-from-start approach). */
if (peer->cur_svc == NULL) {
peer->cur_svc = SLIST_FIRST(&peer->svcs);
} else {
peer->cur_svc = SLIST_NEXT(peer->cur_svc, next);
}
SLIST_FOREACH(svc, &peer->svcs, next) {
if (!peer_svc_is_empty(svc) && SLIST_EMPTY(&svc->chrs)) {
/* Scan forward past empty services to find the next one to process */
for (svc = peer->cur_svc; svc != NULL; svc = SLIST_NEXT(svc, next)) {
if (!peer_svc_is_empty(svc)) {
peer->cur_svc = svc;
rc = ble_gattc_disc_all_chrs(peer->conn_handle,
svc->svc.start_handle,
@@ -600,9 +608,10 @@ peer_disc_incs(struct peer *peer)
peer->cur_svc = SLIST_NEXT(peer->cur_svc, next);
if (peer->cur_svc == NULL) {
if (peer->disc_prev_chr_val > 0) {
/* cur_svc is already NULL: peer_disc_chrs starts from first svc */
peer_disc_chrs(peer);
return;
}
return; /* Always return; don't fall through into svc processing */
}
}
@@ -613,10 +622,18 @@ peer_disc_incs(struct peer *peer)
svc->svc.end_handle,
peer_inc_disced, peer);
if (rc != 0) {
/* Hard error: skip to characteristic discovery from beginning */
peer->cur_svc = NULL;
peer_disc_chrs(peer);
}
} else if (svc != NULL) {
/* svc is empty: advance to next service for include discovery */
peer_disc_incs(peer);
} else {
peer_disc_chrs(peer);
/* cur_svc is NULL on entry: no services at all */
if (peer->disc_prev_chr_val > 0) {
peer_disc_chrs(peer);
}
}
}
#endif
@@ -690,8 +707,12 @@ peer_svc_find_uuid(const struct peer *peer, const ble_uuid_t *uuid)
{
const struct peer_svc *svc;
if (peer == NULL || uuid == NULL) {
return NULL;
}
SLIST_FOREACH(svc, &peer->svcs, next) {
if ((uuid != NULL) && (ble_uuid_cmp(&(svc->svc.uuid.u), uuid) == 0)) {
if (ble_uuid_cmp(&(svc->svc.uuid.u), uuid) == 0) {
return svc;
}
}
@@ -727,6 +748,10 @@ peer_dsc_find_uuid(const struct peer *peer, const ble_uuid_t *svc_uuid,
const struct peer_chr *chr;
const struct peer_dsc *dsc;
if (peer == NULL) {
return NULL;
}
chr = peer_chr_find_uuid(peer, svc_uuid, chr_uuid);
if (chr == NULL) {
return NULL;
@@ -816,6 +841,7 @@ peer_svc_disced(uint16_t conn_handle, const struct ble_gatt_error *error,
#else
/* All services discovered; start discovering characteristics. */
if (peer->disc_prev_chr_val > 0) {
peer->cur_svc = NULL;
peer_disc_chrs(peer);
}
#endif
@@ -920,6 +946,8 @@ peer_add(uint16_t conn_handle)
static void
peer_free_mem(void)
{
SLIST_INIT(&peers);
free(peer_mem);
peer_mem = NULL;
@@ -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
*/
@@ -102,7 +102,7 @@ static int conn_mtu_handler(int argc, char *argv[])
static int throughput_demo_handler(int argc, char *argv[])
{
char pkey[8];
char pkey[8] = {0};
struct cli_msg msg = {
.type = CLI_MSG_TYPE_THROUGHPUT,
};
@@ -111,7 +111,15 @@ static int throughput_demo_handler(int argc, char *argv[])
return -1;
}
sscanf(argv[1], "%7s", pkey);
if (cli_handle == NULL) {
ESP_LOGE("CLI", "Queue not initialized");
return -1;
}
if (sscanf(argv[1], "%7s", pkey) != 1) {
ESP_LOGE("CLI", "Failed to parse throughput type");
return -1;
}
if (strcmp(pkey, "read") == 0) {
msg.data.key[0] = 1;
@@ -133,14 +141,17 @@ static int throughput_demo_handler(int argc, char *argv[])
static int yesno_handler(int argc, char *argv[])
{
char yesno[4];
char yesno[4] = {0};
bool yes;
if (argc != 2) {
return -1;
}
sscanf(argv[1], "%3s", yesno);
if (sscanf(argv[1], "%3s", yesno) != 1) {
ESP_LOGE("CLI", "Failed to parse yes/no argument");
return -1;
}
if (strcmp(yesno, "Yes") == 0 || strcmp(yesno, "YES") == 0 || strcmp(yesno, "yes") == 0) {
yes = 1;
@@ -151,23 +162,29 @@ static int yesno_handler(int argc, char *argv[])
}
ESP_LOGI("User entered", "%s %s", argv[0], yesno);
/* Send as 24-byte buffer to match queue item size */
uint8_t yesno_buf[24] = {0};
yesno_buf[0] = (uint8_t)yes;
/* Use struct cli_msg to exactly match the queue item size (sizeof(struct cli_msg))
* and avoid stack buffer over-read from sending undersized raw buffers. */
struct cli_msg msg = {
.type = CLI_MSG_TYPE_YESNO,
.data.yes = yes,
};
if (cli_handle) {
xQueueSend(cli_handle, yesno_buf, 500 / portTICK_PERIOD_MS);
xQueueSend(cli_handle, &msg, 500 / portTICK_PERIOD_MS);
}
return 0;
}
int scli_receive_yesno(bool *console_key)
{
/* Receive into temporary 24-byte buffer to match queue item size,
* then extract bool value to prevent buffer overflow */
uint8_t temp_buf[24];
int ret = xQueueReceive(cli_handle, temp_buf, YES_NO_PARAM);
struct cli_msg msg;
int ret;
if (cli_handle == NULL) {
return pdFALSE;
}
ret = xQueueReceive(cli_handle, &msg, YES_NO_PARAM);
if (ret == pdPASS) {
*console_key = (bool)temp_buf[0]; /* Extract first byte as bool */
*console_key = msg.data.yes;
}
return ret;
}
@@ -175,6 +192,9 @@ int scli_receive_yesno(bool *console_key)
int scli_receive_key(int console_key[6])
{
struct cli_msg msg;
if (cli_handle == NULL) {
return 0;
}
if (xQueueReceive(cli_handle, &msg, BLE_RX_PARAM) != pdTRUE) {
return 0;
}
@@ -198,6 +218,9 @@ int scli_receive_key(int console_key[6])
int cli_receive_key(int console_key[6])
{
struct cli_msg msg;
if (cli_handle == NULL) {
return 0;
}
if (xQueueReceive(cli_handle, &msg, BLE_RX_TIMEOUT) != pdTRUE) {
return 0;
}
@@ -67,10 +67,17 @@ ext_get_data(uint8_t ext_adv_pattern[], int size)
int rc;
data = os_msys_get_pkthdr(size, 0);
assert(data);
if (!data) {
ESP_LOGE(tag, "ext_get_data: mbuf alloc failed");
return NULL;
}
rc = os_mbuf_append(data, ext_adv_pattern, size);
assert(rc == 0);
if (rc != 0) {
ESP_LOGE(tag, "ext_get_data: mbuf_append failed; rc=%d", rc);
os_mbuf_free_chain(data);
return NULL;
}
return data;
}
@@ -146,13 +153,16 @@ ext_bleprph_advertise(void)
/*enable connectable advertising for all Phy*/
params.connectable = 1;
/* advertise using random addr */
params.own_addr_type = BLE_OWN_ADDR_PUBLIC;
/* Use dynamically inferred address type (set in gatts_on_sync via ble_hs_id_infer_auto) */
params.own_addr_type = gatts_addr_type;
/* Set current phy; get mbuf for scan rsp data; fill mbuf with scan rsp data */
params.primary_phy = BLE_HCI_LE_PHY_1M_PREF_MASK ;
params.secondary_phy = BLE_HCI_LE_PHY_2M_PREF_MASK ;
data = ext_get_data(ext_adv_pattern, sizeof(ext_adv_pattern));
if (!data) {
return;
}
params.sid = 0;
params.itvl_min = BLE_GAP_ADV_FAST_INTERVAL1_MIN;
@@ -281,7 +291,11 @@ notify_task(void *arg)
/* Memory not available for mbuf, yield briefly */
vTaskDelay(1);
}
} while (om == NULL);
} while (om == NULL && notify_state);
if (om == NULL) {
/* notify_state went false while waiting; stop test */
break;
}
rc = ble_gatts_notify_custom(conn_handle, notify_handle, om);
if (rc != 0) {
@@ -347,12 +361,14 @@ gatts_gap_event(struct ble_gap_event *event, void *arg)
}
if (event->connect.status != 0) {
/* Connection failed; resume advertising */
/* Connection failed; resume advertising. Skip HCI/conn_handle
* updates since the connection handle is invalid on failure. */
#if CONFIG_EXAMPLE_EXTENDED_ADV
ext_bleprph_advertise();
#else
gatts_advertise();
#endif
break;
}
rc = ble_hs_hci_util_set_data_len(event->connect.conn_handle,
@@ -418,10 +434,15 @@ gatts_gap_event(struct ble_gap_event *event, void *arg)
}
} else {
ESP_LOGI(tag, "Notifications disabled");
/* Wake up notify_task so it can exit the test loop cleanly;
* without this the task would block forever on ulTaskNotifyTake. */
if (notify_task_handle) {
xTaskNotifyGive(notify_task_handle);
}
}
} else if (event->subscribe.attr_handle != notify_handle) {
notify_state = event->subscribe.cur_notify;
}
/* Do NOT modify notify_state for other characteristics: that would
* corrupt the throughput test state while it is running. */
break;
case BLE_GAP_EVENT_NOTIFY_TX:
@@ -532,7 +553,8 @@ void app_main(void)
BaseType_t task_rc = xTaskCreate(notify_task, "notify_task", 4096, NULL, 10, &notify_task_handle);
if (task_rc != pdPASS) {
ESP_LOGE(tag, "Failed to create notify_task (rc=%d)", task_rc);
return ;
nimble_port_deinit();
return;
}
#if MYNEWT_VAL(BLE_GATTS)
rc = gatt_svr_init();
@@ -22,7 +22,7 @@
static const char *TAG = "l2cap_coc_cent";
#define L2CAP_COC_PSM 0x1002
#define L2CAP_COC_PSM 0x0080 /* valid dynamic LE L2CAP CoC PSM (0x0080-0x00FF) */
#define L2CAP_COC_MTU CONFIG_EXAMPLE_L2CAP_COC_MTU
#define COC_BUF_COUNT (6 * MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM))
/* Block size must include mbuf headers so each SDU fits in one pool entry. */
@@ -130,12 +130,9 @@ static void cent_l2cap_coc_connect(uint16_t conn_handle)
if (rc != 0) {
ESP_LOGE(TAG, "L2CAP COC connect failed; rc=%d", rc);
l2cap_connecting = false;
/* EINVAL: NimBLE returns before chan alloc, sdu_rx not consumed — free it.
* ENOTCONN: NimBLE frees sdu_rx on all ENOTCONN paths (early !conn check
* and late TX failure via ble_l2cap_coc_cleanup_chan). Do not free here. */
if (rc == BLE_HS_EINVAL) {
os_mbuf_free_chain(sdu_rx);
}
/* NimBLE takes ownership of sdu_rx on ALL error paths from ble_l2cap_connect
* (freed via ble_l2cap_chan_free → ble_l2cap_coc_cleanup_chan or directly in
* ble_l2cap_sig_coc_connect validation). Never free here to avoid double-free. */
}
}
@@ -713,6 +710,8 @@ void app_main(void)
ret = nimble_port_init();
if (ret != ESP_OK) {
ESP_LOGE(TAG, "nimble_port_init failed; rc=%d", ret);
vEventGroupDelete(coc_event_group);
coc_event_group = NULL;
return;
}
@@ -731,6 +730,9 @@ void app_main(void)
if (xTaskCreate(cent_send_task, "cent_send_task", 4096, NULL, 5, NULL) != pdPASS) {
ESP_LOGE(TAG, "Failed to create cent_send_task");
nimble_port_deinit();
vEventGroupDelete(coc_event_group);
coc_event_group = NULL;
return;
}
@@ -20,7 +20,7 @@
static const char *TAG = "l2cap_coc_prph";
#define L2CAP_COC_PSM 0x1002
#define L2CAP_COC_PSM 0x0080 /* valid dynamic LE L2CAP CoC PSM (0x0080-0x00FF) */
#define L2CAP_COC_MTU CONFIG_EXAMPLE_L2CAP_COC_MTU
#define COC_BUF_COUNT (6 * MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM))
/* Block size must include mbuf headers so each SDU fits in one pool entry. */
@@ -149,7 +149,11 @@ static void prph_advertise(void)
fields.name_len = strlen(name);
fields.name_is_complete = 1;
#endif
fields.uuids16 = (ble_uuid16_t[]){ BLE_UUID16_INIT(L2CAP_COC_UUID) };
/* Must be static: ble_gap_adv_set_fields copies the pointer, not the data.
* A stack compound literal becomes dangling after prph_advertise returns,
* causing corruption when BLE_NIMBLE_ENABLE_CONN_REATTEMPT re-uses the pointer. */
static const ble_uuid16_t adv_uuids16[] = { BLE_UUID16_INIT(L2CAP_COC_UUID) };
fields.uuids16 = adv_uuids16;
fields.num_uuids16 = 1;
fields.uuids16_is_complete = 1;
@@ -188,7 +192,11 @@ static int prph_l2cap_coc_accept(struct ble_l2cap_chan *chan)
return BLE_HS_ENOMEM;
}
int rc = ble_l2cap_recv_ready(chan, sdu_rx);
if (rc != 0) {
/* ble_l2cap_coc_recv_ready stores sdu_rx AFTER the BLE_HS_EBUSY check but
* BEFORE the BLE_HS_ENOENT check. On BLE_HS_EBUSY the buffer was never
* stored and must be freed here; on success or BLE_HS_ENOENT the buffer is
* owned by chan->coc_rx.sdus[] and freed by ble_l2cap_coc_cleanup_chan. */
if (rc != 0 && rc != BLE_HS_ENOENT) {
os_mbuf_free_chain(sdu_rx);
}
return rc;