diff --git a/examples/bluetooth/bluedroid/ble/ble_acl_latency/cent/main/gattc_latency.h b/examples/bluetooth/bluedroid/ble/ble_acl_latency/cent/main/gattc_latency.h index ddd9c5409a6..132a1129178 100644 --- a/examples/bluetooth/bluedroid/ble/ble_acl_latency/cent/main/gattc_latency.h +++ b/examples/bluetooth/bluedroid/ble/ble_acl_latency/cent/main/gattc_latency.h @@ -46,6 +46,7 @@ typedef struct { /* Latency test functions */ void latency_test_init(uint16_t conn_id, uint16_t char_handle); void latency_test_start(void); +void latency_test_stop(void); void latency_test_handle_notify(uint8_t *data, uint16_t len); void latency_test_print_results(void); diff --git a/examples/bluetooth/bluedroid/ble/ble_acl_latency/cent/main/gattc_latency_demo.c b/examples/bluetooth/bluedroid/ble/ble_acl_latency/cent/main/gattc_latency_demo.c index 09bd9ee5ee5..ac4a3800746 100644 --- a/examples/bluetooth/bluedroid/ble/ble_acl_latency/cent/main/gattc_latency_demo.c +++ b/examples/bluetooth/bluedroid/ble/ble_acl_latency/cent/main/gattc_latency_demo.c @@ -132,7 +132,7 @@ static void gattc_profile_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_ break; } ESP_LOGI(GATTC_TAG, "discover service complete conn_id %d", param->dis_srvc_cmpl.conn_id); - esp_ble_gattc_search_service(gattc_if, param->cfg_mtu.conn_id, &remote_filter_service_uuid); + esp_ble_gattc_search_service(gattc_if, param->dis_srvc_cmpl.conn_id, &remote_filter_service_uuid); break; case ESP_GATTC_CFG_MTU_EVT: if (param->cfg_mtu.status != ESP_GATT_OK){ @@ -311,6 +311,7 @@ static void gattc_profile_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_ case ESP_GATTC_DISCONNECT_EVT: connect = false; get_server = false; + latency_test_stop(); ESP_LOGI(GATTC_TAG, "ESP_GATTC_DISCONNECT_EVT, reason = %d", p_data->disconnect.reason); break; default: diff --git a/examples/bluetooth/bluedroid/ble/ble_acl_latency/cent/main/latency_test.c b/examples/bluetooth/bluedroid/ble/ble_acl_latency/cent/main/latency_test.c index 23743d9482e..87a14183afe 100644 --- a/examples/bluetooth/bluedroid/ble/ble_acl_latency/cent/main/latency_test.c +++ b/examples/bluetooth/bluedroid/ble/ble_acl_latency/cent/main/latency_test.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2025-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Unlicense OR CC0-1.0 */ @@ -22,9 +22,10 @@ static latency_record_t records[TEST_PACKET_COUNT]; static uint16_t test_conn_id = 0; static uint16_t char_handle = 0; static test_packet_t send_packets[TEST_PACKET_COUNT]; -static bool test_running = false; +/* Polled by latency_test_task and written by latency_test_stop() from another context. */ +static volatile bool test_running = false; static bool test_initialized = false; -static esp_gatt_if_t test_gattc_if = 0; +static esp_gatt_if_t test_gattc_if = ESP_GATT_IF_NONE; /** * Fill random data @@ -45,7 +46,6 @@ latency_test_init(uint16_t conn_id, uint16_t handle) { test_conn_id = conn_id; char_handle = handle; - test_running = false; test_initialized = true; memset(records, 0, sizeof(records)); @@ -69,7 +69,7 @@ latency_test_set_gattc_if(esp_gatt_if_t gattc_if) static int send_test_packet(uint16_t seq) { - if (char_handle == 0 || test_gattc_if == 0) { + if (char_handle == 0 || test_gattc_if == ESP_GATT_IF_NONE) { ESP_LOGE(TAG, "Test not initialized"); return -1; } @@ -113,21 +113,19 @@ latency_test_task(void *arg) test_running = true; - /* Send all test packets */ - for (int i = 0; i < TEST_PACKET_COUNT; i++) { + /* Send all test packets, abort early if latency_test_stop() is called */ + for (int i = 0; i < TEST_PACKET_COUNT && test_running; i++) { int rc = send_test_packet(i); if (rc != 0) { ESP_LOGW(TAG, "Send failed for seq=%d", i); } - - /* Wait interval */ vTaskDelay(pdMS_TO_TICKS(TEST_PACKET_INTERVAL_MS)); } - ESP_LOGI(TAG, "All packets sent, waiting for responses..."); - - /* Wait for all responses */ - vTaskDelay(pdMS_TO_TICKS(2000)); + if (test_running) { + ESP_LOGI(TAG, "All packets sent, waiting for responses..."); + vTaskDelay(pdMS_TO_TICKS(2000)); + } /* Print results */ latency_test_print_results(); @@ -152,9 +150,26 @@ latency_test_start(void) return; } + if (test_gattc_if == ESP_GATT_IF_NONE) { + ESP_LOGE(TAG, "GATT client interface not set"); + return; + } + xTaskCreate(latency_test_task, "latency_test", 4096, NULL, 5, NULL); } +/** + * Stop latency test + * + * Only raises the stop flag; the task observes it at the next loop iteration + * (or vTaskDelay boundary) and self-terminates via vTaskDelete(NULL). + */ +void +latency_test_stop(void) +{ + test_running = false; +} + /** * Handle notification */ @@ -189,6 +204,11 @@ latency_test_handle_notify(uint8_t *data, uint16_t len) return; } + if (records[seq].received) { + ESP_LOGD(TAG, "Duplicate notify for seq=%d, ignored", seq); + return; + } + /* Record receive time */ records[seq].recv_time_us = recv_time; records[seq].received = true; diff --git a/examples/bluetooth/bluedroid/ble/ble_acl_latency/periph/main/gatts_latency_demo.c b/examples/bluetooth/bluedroid/ble/ble_acl_latency/periph/main/gatts_latency_demo.c index c0e9cab82c5..3de9a386980 100644 --- a/examples/bluetooth/bluedroid/ble/ble_acl_latency/periph/main/gatts_latency_demo.c +++ b/examples/bluetooth/bluedroid/ble/ble_acl_latency/periph/main/gatts_latency_demo.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2025-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Unlicense OR CC0-1.0 */ @@ -7,6 +7,7 @@ #include #include #include +#include #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "esp_system.h" @@ -18,6 +19,7 @@ #include "esp_gatts_api.h" #include "esp_bt_main.h" #include "esp_gatt_common_api.h" +#include "esp_gatt_defs.h" #include "gatts_latency.h" #define GATTS_TAG "BLE_ACL_LATENCY_PERIPH" @@ -191,16 +193,18 @@ static void gatts_profile_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_ } esp_err_t ret = esp_ble_gap_config_adv_data(&adv_data); - if (ret){ + if (ret != ESP_OK) { ESP_LOGE(GATTS_TAG, "Config adv data failed, error code = %x", ret); + } else { + adv_config_done |= adv_config_flag; } - adv_config_done |= adv_config_flag; ret = esp_ble_gap_config_adv_data(&scan_rsp_data); - if (ret){ + if (ret != ESP_OK) { ESP_LOGE(GATTS_TAG, "Config scan response data failed, error code = %x", ret); + } else { + adv_config_done |= scan_rsp_config_flag; } - adv_config_done |= scan_rsp_config_flag; esp_err_t create_attr_ret = esp_ble_gatts_create_attr_tab(gatt_db, gatts_if, LATENCY_IDX_NB, 0); if (create_attr_ret){ @@ -209,7 +213,41 @@ static void gatts_profile_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_ } break; case ESP_GATTS_READ_EVT: - ESP_LOGD(GATTS_TAG, "ESP_GATTS_READ_EVT"); + ESP_LOGD(GATTS_TAG, "ESP_GATTS_READ_EVT, handle=%d, need_rsp=%d", param->read.handle, param->read.need_rsp); + if (!param->read.need_rsp) { + break; + } + { + esp_gatt_rsp_t rsp; + memset(&rsp, 0, sizeof(rsp)); + rsp.attr_value.handle = param->read.handle; + rsp.attr_value.offset = param->read.offset; + + uint16_t vlen = 0; + const uint8_t *v = NULL; + esp_gatt_status_t gst = esp_ble_gatts_get_attr_value(param->read.handle, &vlen, &v); + + if (gst != ESP_GATT_OK || v == NULL) { + rsp.attr_value.len = 0; + esp_ble_gatts_send_response(gatts_if, param->read.conn_id, param->read.trans_id, + ESP_GATT_ERROR, &rsp); + break; + } + if (param->read.offset > vlen) { + rsp.attr_value.len = 0; + esp_ble_gatts_send_response(gatts_if, param->read.conn_id, param->read.trans_id, + ESP_GATT_INVALID_OFFSET, &rsp); + break; + } + + uint16_t remain = vlen - param->read.offset; + uint16_t copy_len = remain > ESP_GATT_MAX_ATTR_LEN ? ESP_GATT_MAX_ATTR_LEN : remain; + if (copy_len > 0) { + memcpy(rsp.attr_value.value, v + param->read.offset, copy_len); + } + rsp.attr_value.len = copy_len; + esp_ble_gatts_send_response(gatts_if, param->read.conn_id, param->read.trans_id, ESP_GATT_OK, &rsp); + } break; case ESP_GATTS_WRITE_EVT: if (!param->write.is_prep){ @@ -220,9 +258,35 @@ static void gatts_profile_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_ esp_ble_gatts_send_indicate(gatts_if, param->write.conn_id, handle_table[LATENCY_IDX_CHAR_VAL], param->write.len, param->write.value, false); } + if (param->write.need_rsp) { + esp_err_t sr = esp_ble_gatts_send_response(gatts_if, param->write.conn_id, param->write.trans_id, + ESP_GATT_OK, NULL); + if (sr != ESP_OK) { + ESP_LOGE(GATTS_TAG, "send_response failed: %s", esp_err_to_name(sr)); + } + } + } else { + /* This latency demo does not support Prepare/Long/Reliable Write. + * Reply with ATT Error 0x06 (Request Not Supported) instead of leaving + * the request unanswered, otherwise the peer would hit an ATT timeout. + */ + ESP_LOGW(GATTS_TAG, "Prepare write not supported, handle=%d, offset=%d, len=%d", + param->write.handle, param->write.offset, param->write.len); + if (param->write.need_rsp) { + esp_err_t sr = esp_ble_gatts_send_response(gatts_if, param->write.conn_id, param->write.trans_id, + ESP_GATT_REQ_NOT_SUPPORTED, NULL); + if (sr != ESP_OK) { + ESP_LOGE(GATTS_TAG, "send_response (prep reject) failed: %s", esp_err_to_name(sr)); + } + } } break; case ESP_GATTS_EXEC_WRITE_EVT: + ESP_LOGD(GATTS_TAG, "ESP_GATTS_EXEC_WRITE_EVT, conn_id=%d, trans_id=%" PRIu32 ", flag=0x%02x", + param->exec_write.conn_id, param->exec_write.trans_id, param->exec_write.exec_write_flag); + esp_ble_gatts_send_response(gatts_if, param->exec_write.conn_id, param->exec_write.trans_id, + ESP_GATT_OK, NULL); + break; case ESP_GATTS_MTU_EVT: ESP_LOGI(GATTS_TAG, "MTU exchange, MTU=%d", param->mtu.mtu); break; diff --git a/examples/bluetooth/bluedroid/ble/ble_ancs/main/ble_ancs.c b/examples/bluetooth/bluedroid/ble/ble_ancs/main/ble_ancs.c index 9fe2ae71a78..f9e80f3c2c6 100644 --- a/examples/bluetooth/bluedroid/ble/ble_ancs/main/ble_ancs.c +++ b/examples/bluetooth/bluedroid/ble/ble_ancs/main/ble_ancs.c @@ -29,9 +29,9 @@ A GATT notification delivered through the Notification Source characteristic con Control Point characteristic to interact with the iOS notification. */ -char *EventID_to_String(uint8_t EventID) +const char *EventID_to_String(uint8_t EventID) { - char *str = NULL; + const char *str = NULL; switch (EventID) { case EventIDNotificationAdded: @@ -50,9 +50,9 @@ char *EventID_to_String(uint8_t EventID) return str; } -char *CategoryID_to_String(uint8_t CategoryID) +const char *CategoryID_to_String(uint8_t CategoryID) { - char *Cidstr = NULL; + const char *Cidstr = NULL; switch(CategoryID) { case CategoryIDOther: Cidstr = "Other"; @@ -103,17 +103,20 @@ char *CategoryID_to_String(uint8_t CategoryID) void esp_receive_apple_notification_source(uint8_t *message, uint16_t message_len) { - if (!message || message_len < 5) { + if (!message || message_len < 8) { return; } uint8_t EventID = message[0]; - char *EventIDS = EventID_to_String(EventID); + const char *EventIDS = EventID_to_String(EventID); uint8_t EventFlags = message[1]; uint8_t CategoryID = message[2]; - char *Cidstr = CategoryID_to_String(CategoryID); + const char *Cidstr = CategoryID_to_String(CategoryID); uint8_t CategoryCount = message[3]; - uint32_t NotificationUID = (message[4]) | (message[5]<< 8) | (message[6]<< 16) | (message[7] << 24); + uint32_t NotificationUID = (uint32_t)message[4] + | ((uint32_t)message[5] << 8) + | ((uint32_t)message[6] << 16) + | ((uint32_t)message[7] << 24); ESP_LOGI(BLE_ANCS_TAG, "EventID:%s EventFlags:0x%x CategoryID:%s CategoryCount:%d NotificationUID:%" PRIu32, EventIDS, EventFlags, Cidstr, CategoryCount, NotificationUID); } @@ -132,7 +135,10 @@ void esp_receive_apple_data_source(uint8_t *message, uint16_t message_len) ESP_LOGE(BLE_ANCS_TAG, "Message too short for NotificationAttributes"); break; } - uint32_t NotificationUID = (message[1]) | (message[2]<< 8) | (message[3]<< 16) | (message[4] << 24); + uint32_t NotificationUID = (uint32_t)message[1] + | ((uint32_t)message[2] << 8) + | ((uint32_t)message[3] << 16) + | ((uint32_t)message[4] << 24); uint32_t remian_attr_len = message_len - 5; uint8_t *attrs = &message[5]; ESP_LOGI(BLE_ANCS_TAG, "recevice Notification Attributes response Command_id %d NotificationUID %" PRIu32, Command_id, NotificationUID); @@ -143,7 +149,7 @@ void esp_receive_apple_data_source(uint8_t *message, uint16_t message_len) break; } uint8_t AttributeID = attrs[0]; - uint16_t len = attrs[1] | (attrs[2] << 8); + uint16_t len = (uint16_t)attrs[1] | ((uint16_t)attrs[2] << 8); if(len > (remian_attr_len - 3)) { ESP_LOGE(BLE_ANCS_TAG, "data error"); break; @@ -198,9 +204,9 @@ void esp_receive_apple_data_source(uint8_t *message, uint16_t message_len) } } -char *Errcode_to_String(uint16_t status) +const char *Errcode_to_String(uint16_t status) { - char *Errstr = NULL; + const char *Errstr = NULL; switch (status) { case Unknown_command: Errstr = "Unknown_command"; @@ -214,6 +220,9 @@ char *Errcode_to_String(uint16_t status) case Action_failed: Errstr = "Action_failed"; break; + case Internal_error: + Errstr = "Internal_error"; + break; default: Errstr = "unknown_failed"; break; diff --git a/examples/bluetooth/bluedroid/ble/ble_ancs/main/ble_ancs.h b/examples/bluetooth/bluedroid/ble/ble_ancs/main/ble_ancs.h index 6d310e21907..0509145a85d 100644 --- a/examples/bluetooth/bluedroid/ble/ble_ancs/main/ble_ancs.h +++ b/examples/bluetooth/bluedroid/ble/ble_ancs/main/ble_ancs.h @@ -95,6 +95,7 @@ typedef enum { Invalid_command = (0xA1), //The command was improperly formatted. Invalid_parameter = (0xA2), // One of the parameters (for example, the NotificationUID) does not refer to an existing object on the NP. Action_failed = (0xA3), //The action was not performed + Internal_error = (0xA4), //An internal error occurred on the NP (Apple ANCS specification). } esp_error_code; typedef enum { @@ -111,8 +112,8 @@ typedef enum { #define ESP_NOTIFICATIONUID_LEN 4 -char *EventID_to_String(uint8_t EventID); -char *CategoryID_to_String(uint8_t CategoryID); +const char *EventID_to_String(uint8_t EventID); +const char *CategoryID_to_String(uint8_t CategoryID); void esp_receive_apple_notification_source(uint8_t *message, uint16_t message_len); void esp_receive_apple_data_source(uint8_t *message, uint16_t message_len); -char *Errcode_to_String(uint16_t status); +const char *Errcode_to_String(uint16_t status); diff --git a/examples/bluetooth/bluedroid/ble/ble_ancs/main/ble_ancs_demo.c b/examples/bluetooth/bluedroid/ble/ble_ancs/main/ble_ancs_demo.c index b47edcd30b5..907c7e59bd9 100644 --- a/examples/bluetooth/bluedroid/ble/ble_ancs/main/ble_ancs_demo.c +++ b/examples/bluetooth/bluedroid/ble/ble_ancs/main/ble_ancs_demo.c @@ -8,6 +8,7 @@ #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "freertos/event_groups.h" +#include "freertos/semphr.h" #include "esp_system.h" #include "esp_log.h" #include "nvs_flash.h" @@ -51,6 +52,7 @@ struct data_source_buffer { }; static struct data_source_buffer data_buffer = {0}; +static SemaphoreHandle_t data_buffer_mux; //In its basic form, the ANCS exposes three characteristics: // service UUID: 7905F431-B5CE-4E99-A40F-4B1E122D00D0 @@ -189,8 +191,8 @@ void esp_get_notification_attributes(uint8_t *notificationUID, uint8_t num_attr, ESP_LOGE(BLE_ANCS_TAG, "Command buffer overflow in get_notification_attributes"); return; } - cmd[index ++] = p_attr->attribute_len; - cmd[index ++] = (p_attr->attribute_len << 8); + cmd[index ++] = p_attr->attribute_len & 0xFF; + cmd[index ++] = (p_attr->attribute_len >> 8) & 0xFF; } p_attr ++; num_attr --; @@ -253,12 +255,23 @@ void esp_perform_notification_action(uint8_t *notificationUID, uint8_t ActionID) static void periodic_timer_callback(void* arg) { - esp_timer_stop(periodic_timer); + if (data_buffer_mux == NULL) { + return; + } + if (xSemaphoreTake(data_buffer_mux, 0) != pdTRUE) { + /* Mutex held by GATT handler; retry soon without blocking esp_timer task */ + esp_err_t tr = esp_timer_start_once(periodic_timer, 50000); + if (tr != ESP_OK) { + ESP_LOGE(BLE_ANCS_TAG, "Data source idle timer retry failed: %s", esp_err_to_name(tr)); + } + return; + } if (data_buffer.len > 0) { esp_receive_apple_data_source(data_buffer.buffer, data_buffer.len); memset(data_buffer.buffer, 0, data_buffer.len); data_buffer.len = 0; } + xSemaphoreGive(data_buffer_mux); } static void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) @@ -525,6 +538,10 @@ static void gattc_profile_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_ case ESP_GATTC_NOTIFY_EVT: //esp_log_buffer_hex(BLE_ANCS_TAG, param->notify.value, param->notify.value_len); if (param->notify.handle == gl_profile_tab[PROFILE_A_APP_ID].notification_source_handle) { + if (!param->notify.value || param->notify.value_len < 8) { + ESP_LOGW(BLE_ANCS_TAG, "Notification source too short (%u), need 8 bytes", param->notify.value_len); + break; + } esp_receive_apple_notification_source(param->notify.value, param->notify.value_len); uint8_t *notificationUID = ¶m->notify.value[4]; if (param->notify.value[0] == EventIDNotificationAdded && param->notify.value[2] == CategoryIDIncomingCall) { @@ -537,23 +554,35 @@ static void gattc_profile_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_ esp_get_notification_attributes(notificationUID, sizeof(p_attr)/sizeof(esp_noti_attr_list_t), p_attr); } } else if (param->notify.handle == gl_profile_tab[PROFILE_A_APP_ID].data_source_handle) { + if (data_buffer_mux == NULL) { + break; + } + if (xSemaphoreTake(data_buffer_mux, portMAX_DELAY) != pdTRUE) { + break; + } if ((data_buffer.len + param->notify.value_len) > sizeof(data_buffer.buffer)) { ESP_LOGE(BLE_ANCS_TAG, "Data source buffer overflow detected, discarding data"); memset(data_buffer.buffer, 0, sizeof(data_buffer.buffer)); data_buffer.len = 0; + xSemaphoreGive(data_buffer_mux); break; } memcpy(&data_buffer.buffer[data_buffer.len], param->notify.value, param->notify.value_len); data_buffer.len += param->notify.value_len; if (param->notify.value_len == (gl_profile_tab[PROFILE_A_APP_ID].MTU_size - 3)) { - // cpoy and wait next packet, start timer 500ms - esp_timer_start_periodic(periodic_timer, 500000); + /* Retriggerable idle timeout: stop then one-shot so each full fragment resets 500 ms */ + esp_timer_stop(periodic_timer); + esp_err_t tr = esp_timer_start_once(periodic_timer, 500000); + if (tr != ESP_OK) { + ESP_LOGE(BLE_ANCS_TAG, "Data source idle timer start failed: %s", esp_err_to_name(tr)); + } } else { esp_timer_stop(periodic_timer); esp_receive_apple_data_source(data_buffer.buffer, data_buffer.len); memset(data_buffer.buffer, 0, data_buffer.len); data_buffer.len = 0; } + xSemaphoreGive(data_buffer_mux); } else { ESP_LOGI(BLE_ANCS_TAG, "unknown handle, receive notify value:"); } @@ -572,7 +601,7 @@ static void gattc_profile_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_ } case ESP_GATTC_WRITE_CHAR_EVT: if (param->write.status != ESP_GATT_OK) { - char *Errstr = Errcode_to_String(param->write.status); + const char *Errstr = Errcode_to_String(param->write.status); if (Errstr) { ESP_LOGE(BLE_ANCS_TAG, "write control point error %s", Errstr); } @@ -582,6 +611,18 @@ static void gattc_profile_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_ break; case ESP_GATTC_DISCONNECT_EVT: ESP_LOGI(BLE_ANCS_TAG, "ESP_GATTC_DISCONNECT_EVT, reason = 0x%x", param->disconnect.reason); + if (data_buffer_mux != NULL && xSemaphoreTake(data_buffer_mux, portMAX_DELAY) == pdTRUE) { + esp_timer_stop(periodic_timer); + memset(data_buffer.buffer, 0, sizeof(data_buffer.buffer)); + data_buffer.len = 0; + xSemaphoreGive(data_buffer_mux); + } else { + /* Mutex unavailable: only stop the timer (esp_timer_stop is thread-safe). + * Skip buffer reset to avoid an unsynchronized write that could race with + * NOTIFY_EVT / periodic_timer_callback if the mutex were ever held elsewhere. */ + ESP_LOGE(BLE_ANCS_TAG, "data_buffer_mux unavailable on disconnect, skip buffer reset"); + esp_timer_stop(periodic_timer); + } get_service = false; esp_ble_gap_start_advertising(&adv_params); break; @@ -640,6 +681,8 @@ static void esp_gattc_cb(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp void init_timer(void) { ESP_ERROR_CHECK(esp_timer_create(&periodic_timer_args, &periodic_timer)); + data_buffer_mux = xSemaphoreCreateMutex(); + ESP_ERROR_CHECK(data_buffer_mux != NULL ? ESP_OK : ESP_ERR_NO_MEM); } void app_main(void) diff --git a/examples/bluetooth/bluedroid/ble/ble_compatibility_test/main/ble_compatibility_test.c b/examples/bluetooth/bluedroid/ble/ble_compatibility_test/main/ble_compatibility_test.c index 855e8e0c54c..321a02f32a7 100644 --- a/examples/bluetooth/bluedroid/ble/ble_compatibility_test/main/ble_compatibility_test.c +++ b/examples/bluetooth/bluedroid/ble/ble_compatibility_test/main/ble_compatibility_test.c @@ -7,6 +7,7 @@ /******************************************************************************** * * This file is for gatt server. It can send adv data, and get connected by client. +* Only one BLE ACL connection is supported (see sdkconfig.defaults: CONFIG_BT_ACL_CONNECTIONS=1). * *********************************************************************************/ @@ -64,6 +65,7 @@ typedef struct { int prepare_len; } prepare_type_env_t; +/* This demo targets one connected client; a single prepare-write buffer is enough. */ static prepare_type_env_t prepare_write_env; //#define CONFIG_SET_RAW_ADV_DATA @@ -440,7 +442,11 @@ void example_prepare_write_event_env(esp_gatt_if_t gatts_if, prepare_type_env_t memcpy(prepare_write_env->prepare_buf + param->write.offset, param->write.value, param->write.len); - prepare_write_env->prepare_len += param->write.len; + /* Extent of prepared value: max(offset+len), not sum(len) — overlaps/retries must not inflate length. */ + uint32_t span_end = (uint32_t)param->write.offset + (uint32_t)param->write.len; + if (span_end > (uint32_t)prepare_write_env->prepare_len) { + prepare_write_env->prepare_len = (int)span_end; + } } uint8_t long_write[16] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF}; @@ -561,7 +567,7 @@ static void gatts_profile_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_ break; case ESP_GATTS_EXEC_WRITE_EVT: // the length of gattc prepare write data must be less than GATTS_EXAMPLE_CHAR_VAL_LEN_MAX. - ESP_LOGI(EXAMPLE_TAG, "ESP_GATTS_EXEC_WRITE_EVT, Length=%d", prepare_write_env.prepare_len); + ESP_LOGI(EXAMPLE_TAG, "ESP_GATTS_EXEC_WRITE_EVT, Length=%d", prepare_write_env.prepare_len); example_exec_write_event_env(&prepare_write_env, param); break; case ESP_GATTS_MTU_EVT: @@ -580,6 +586,11 @@ static void gatts_profile_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_ break; case ESP_GATTS_DISCONNECT_EVT: ESP_LOGI(EXAMPLE_TAG, "ESP_GATTS_DISCONNECT_EVT, reason = %d", param->disconnect.reason); + if (prepare_write_env.prepare_buf) { + free(prepare_write_env.prepare_buf); + prepare_write_env.prepare_buf = NULL; + } + prepare_write_env.prepare_len = 0; esp_ble_gap_start_advertising(&adv_params); break; case ESP_GATTS_CREAT_ATTR_TAB_EVT:{ diff --git a/examples/bluetooth/bluedroid/ble/ble_eddystone_sender/main/esp_eddystone_demo.c b/examples/bluetooth/bluedroid/ble/ble_eddystone_sender/main/esp_eddystone_demo.c index 9ca35596820..27a772d0251 100644 --- a/examples/bluetooth/bluedroid/ble/ble_eddystone_sender/main/esp_eddystone_demo.c +++ b/examples/bluetooth/bluedroid/ble/ble_eddystone_sender/main/esp_eddystone_demo.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2021-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Unlicense OR CC0-1.0 */ @@ -77,22 +77,24 @@ static void eddystone_send_raw(const esp_eddystone_result_t *res) } case EDDYSTONE_FRAME_TYPE_URL: { - size_t url_len = strlen((char*)res->inform.url.encoded_url); //encoded url length - if(url_len > EDDYSTONE_URL_MAX_LEN){ - url_len = EDDYSTONE_URL_MAX_LEN; + size_t url_len = strlen((char*)res->inform.url.encoded_url); // encoded url length + /* Eddystone: max 17 bytes encoded URL after scheme byte; also raw_adv_data[31] leaves + * only 20 bytes at index 11 (3 header + url_len), so url_len <= 17. */ + if (url_len > EDDYSTONE_URL_ENCODED_MAX_LEN) { + url_len = EDDYSTONE_URL_ENCODED_MAX_LEN; } - raw_adv_data[index++] = url_len+6; //length + raw_adv_data[index++] = url_len + 6; // length raw_adv_data[index++] = ESP_BLE_AD_TYPE_SERVICE_DATA; raw_adv_data[index++] = 0xAA; raw_adv_data[index++] = 0xFE; - uint8_t service_data[EDDYSTONE_URL_MAX_LEN+2] = {0}; + uint8_t service_data[EDDYSTONE_URL_ENCODED_MAX_LEN + 3] = {0}; service_data[0] = EDDYSTONE_FRAME_TYPE_URL; service_data[1] = res->inform.url.tx_power; service_data[2] = res->inform.url.url_scheme; memcpy(&service_data[3], res->inform.url.encoded_url, url_len); - memcpy(&raw_adv_data[index], service_data, url_len+3); - index += url_len+3; + memcpy(&raw_adv_data[index], service_data, url_len + 3); + index += url_len + 3; break; } @@ -107,7 +109,7 @@ static void eddystone_send_raw(const esp_eddystone_result_t *res) service_data[2] = (res->inform.tlm.battery_voltage >> 8) & 0xFF; service_data[3] = res->inform.tlm.battery_voltage & 0xFF; service_data[4] = (res->inform.tlm.temperature >> 8) & 0xFF; - service_data[4] = res->inform.tlm.temperature & 0xFF; + service_data[5] = res->inform.tlm.temperature & 0xFF; service_data[6] = (res->inform.tlm.adv_count >> 24) & 0xFF; service_data[7] = (res->inform.tlm.adv_count >> 16) & 0xFF; service_data[8] = (res->inform.tlm.adv_count >> 8) & 0xFF; @@ -166,8 +168,8 @@ void esp_eddystone_appRegister(void) void esp_eddystone_init(void) { - esp_bluedroid_init(); - esp_bluedroid_enable(); + ESP_ERROR_CHECK(esp_bluedroid_init()); + ESP_ERROR_CHECK(esp_bluedroid_enable()); esp_eddystone_appRegister(); } @@ -176,8 +178,8 @@ void app_main(void) ESP_ERROR_CHECK(nvs_flash_init()); ESP_ERROR_CHECK(esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT)); esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT(); - esp_bt_controller_init(&bt_cfg); - esp_bt_controller_enable(ESP_BT_MODE_BLE); + ESP_ERROR_CHECK(esp_bt_controller_init(&bt_cfg)); + ESP_ERROR_CHECK(esp_bt_controller_enable(ESP_BT_MODE_BLE)); esp_eddystone_init(); esp_eddystone_result_t eddystone_result; diff --git a/examples/bluetooth/bluedroid/ble/ble_enc_adv_data/enc_adv_data_cent/main/ble_ead.c b/examples/bluetooth/bluedroid/ble/ble_enc_adv_data/enc_adv_data_cent/main/ble_ead.c index 92420dd4060..f2f7a13eb55 100644 --- a/examples/bluetooth/bluedroid/ble/ble_enc_adv_data/enc_adv_data_cent/main/ble_ead.c +++ b/examples/bluetooth/bluedroid/ble/ble_enc_adv_data/enc_adv_data_cent/main/ble_ead.c @@ -174,7 +174,8 @@ static int ble_aes_ccm_encrypt(const uint8_t *key, const uint8_t *nonce, static int ble_aes_ccm_decrypt(const uint8_t *key, const uint8_t *nonce, const uint8_t *ciphertext, size_t ciphertext_len, const uint8_t *aad, size_t aad_len, - uint8_t *plaintext, size_t tag_len) + uint8_t *plaintext, size_t tag_len, + size_t plaintext_capacity) { #if defined(CONFIG_BT_SMP_CRYPTO_STACK_TINYCRYPT) struct tc_aes_key_sched_struct sched; @@ -197,6 +198,11 @@ static int ble_aes_ccm_decrypt(const uint8_t *key, const uint8_t *nonce, plaintext_len = ciphertext_len - tag_len; + if (plaintext_len > plaintext_capacity) { + ESP_LOGE(TAG, "plaintext_len (%zu) > plaintext_capacity (%zu)", plaintext_len, plaintext_capacity); + return -1; + } + /* Set AES encryption key */ ret = tc_aes128_set_encrypt_key(&sched, key); if (ret != TC_CRYPTO_SUCCESS) { @@ -337,13 +343,14 @@ int ble_ead_encrypt(const uint8_t session_key[BLE_EAD_KEY_SIZE], int ble_ead_decrypt(const uint8_t session_key[BLE_EAD_KEY_SIZE], const uint8_t iv[BLE_EAD_IV_SIZE], const uint8_t *encrypted_payload, size_t encrypted_payload_size, - uint8_t *payload) + uint8_t *payload, size_t payload_capacity) { int ret; uint8_t nonce[BLE_EAD_NONCE_SIZE]; const uint8_t *randomizer; const uint8_t *ciphertext; size_t ciphertext_len; + size_t expected_plaintext_len; if (session_key == NULL) { ESP_LOGE(TAG, "session_key is NULL"); @@ -370,6 +377,13 @@ int ble_ead_decrypt(const uint8_t session_key[BLE_EAD_KEY_SIZE], return -1; } + expected_plaintext_len = BLE_EAD_DECRYPTED_PAYLOAD_SIZE(encrypted_payload_size); + if (expected_plaintext_len > payload_capacity) { + ESP_LOGE(TAG, "EAD plaintext length %zu exceeds payload buffer %zu", + expected_plaintext_len, payload_capacity); + return -1; + } + /* Extract randomizer from the start of encrypted payload */ randomizer = encrypted_payload; @@ -388,7 +402,8 @@ int ble_ead_decrypt(const uint8_t session_key[BLE_EAD_KEY_SIZE], ret = ble_aes_ccm_decrypt(session_key, nonce, ciphertext, ciphertext_len, ble_ead_aad, BLE_EAD_AAD_SIZE, - payload, BLE_EAD_MIC_SIZE); + payload, BLE_EAD_MIC_SIZE, + payload_capacity); return ret; } diff --git a/examples/bluetooth/bluedroid/ble/ble_enc_adv_data/enc_adv_data_cent/main/ble_ead.h b/examples/bluetooth/bluedroid/ble/ble_enc_adv_data/enc_adv_data_cent/main/ble_ead.h index a9bf8954ebf..4994f6b994f 100644 --- a/examples/bluetooth/bluedroid/ble/ble_enc_adv_data/enc_adv_data_cent/main/ble_ead.h +++ b/examples/bluetooth/bluedroid/ble/ble_enc_adv_data/enc_adv_data_cent/main/ble_ead.h @@ -79,14 +79,15 @@ int ble_ead_encrypt(const uint8_t session_key[BLE_EAD_KEY_SIZE], * @param encrypted_payload Encrypted advertising data (includes randomizer and MIC) * @param encrypted_payload_size Size of encrypted data * @param payload Output buffer for decrypted data - * Size must be at least BLE_EAD_DECRYPTED_PAYLOAD_SIZE(encrypted_payload_size) + * @param payload_capacity Size of @a payload in bytes; must be >= + * BLE_EAD_DECRYPTED_PAYLOAD_SIZE(encrypted_payload_size) * * @return 0 on success, negative error code on failure */ int ble_ead_decrypt(const uint8_t session_key[BLE_EAD_KEY_SIZE], const uint8_t iv[BLE_EAD_IV_SIZE], const uint8_t *encrypted_payload, size_t encrypted_payload_size, - uint8_t *payload); + uint8_t *payload, size_t payload_capacity); #ifdef __cplusplus } diff --git a/examples/bluetooth/bluedroid/ble/ble_enc_adv_data/enc_adv_data_cent/main/enc_adv_data_cent.c b/examples/bluetooth/bluedroid/ble/ble_enc_adv_data/enc_adv_data_cent/main/enc_adv_data_cent.c index e03c3b86ad2..009346703db 100644 --- a/examples/bluetooth/bluedroid/ble/ble_enc_adv_data/enc_adv_data_cent/main/enc_adv_data_cent.c +++ b/examples/bluetooth/bluedroid/ble/ble_enc_adv_data/enc_adv_data_cent/main/enc_adv_data_cent.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2025-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Unlicense OR CC0-1.0 */ @@ -19,6 +19,7 @@ #include #include #include +#include #include "nvs.h" #include "nvs_flash.h" #include "esp_bt.h" @@ -57,12 +58,14 @@ static peer_info_t peers[MAX_PEERS] = {0}; /* GATT client state */ static bool is_connected = false; +static bool connect_pending = false; /* enh_open issued; CONNECT_EVT not yet received */ static bool get_server = false; static uint16_t conn_id_stored = 0; static uint16_t service_start_handle = 0; static uint16_t service_end_handle = 0; static uint16_t key_material_char_handle = INVALID_HANDLE; -static esp_bd_addr_t current_peer_addr = {0}; +/* BDA for the active GATT connection; set in CONNECT_EVT, cleared on disconnect */ +static esp_bd_addr_t gattc_remote_bda = {0}; /* GATT interface */ static esp_gatt_if_t gattc_if_stored = ESP_GATT_IF_NONE; @@ -146,27 +149,54 @@ static void decrypt_enc_adv_data(const uint8_t *adv_data, uint8_t adv_len, const uint8_t dec_data[32]; /* Buffer for decrypted data */ size_t dec_len = BLE_EAD_DECRYPTED_PAYLOAD_SIZE(enc_data_len); + if (dec_len > sizeof(dec_data)) { + ESP_LOGW(TAG, "Encrypted AD would yield %zu plaintext bytes; example buffer is %zu — skip", + dec_len, sizeof(dec_data)); + break; + } int rc = ble_ead_decrypt( peers[peer_idx].key_material.session_key, peers[peer_idx].key_material.iv, enc_data, enc_data_len, - dec_data); + dec_data, sizeof(dec_data)); if (rc == 0) { + size_t safe_dec_len = dec_len; + if (safe_dec_len > sizeof(dec_data)) { + ESP_LOGW(TAG, "dec_len %zu > buffer %zu, clamping for log/parse", + dec_len, sizeof(dec_data)); + safe_dec_len = sizeof(dec_data); + } ESP_LOGI(TAG, "Decryption successful!"); ESP_LOGI(TAG, "Decrypted data:"); - ESP_LOG_BUFFER_HEX(TAG, dec_data, dec_len); + ESP_LOG_BUFFER_HEX(TAG, dec_data, safe_dec_len); - /* Parse decrypted advertising structure */ - if (dec_len >= 2) { - uint8_t dec_type = dec_data[1]; - if (dec_type == ESP_BLE_AD_TYPE_NAME_CMPL || dec_type == ESP_BLE_AD_TYPE_NAME_SHORT) { - char name[32] = {0}; - size_t name_len = dec_data[0] - 1; - if (name_len < sizeof(name)) { - memcpy(name, &dec_data[2], name_len); - ESP_LOGI(TAG, "Decrypted device name: %s", name); + /* Parse decrypted advertising structure (do not trust length octet past plaintext) */ + if (safe_dec_len >= 2) { + if (dec_data[0] == 0) { + ESP_LOGW(TAG, "Malformed decrypted AD: zero inner length"); + } else { + /* BLE: octet 0 is L = len(type+data); element occupies 1+L octets */ + const size_t inner_total = 1U + (size_t)dec_data[0]; + if (inner_total > safe_dec_len) { + ESP_LOGW(TAG, "Malformed decrypted AD: inner len claims %zu octets, have %zu", + inner_total, safe_dec_len); + } else { + uint8_t dec_type = dec_data[1]; + if (dec_type == ESP_BLE_AD_TYPE_NAME_CMPL || + dec_type == ESP_BLE_AD_TYPE_NAME_SHORT) { + char name[32] = {0}; + size_t name_len = (size_t)dec_data[0] - 1U; + /* Name in dec_data[2 .. name_copy_end); last index is name_copy_end - 1 */ + const size_t name_copy_end = 2U + name_len; + if (name_len < sizeof(name) && + name_copy_end <= sizeof(dec_data) && + name_copy_end <= safe_dec_len) { + memcpy(name, &dec_data[2], name_len); + ESP_LOGI(TAG, "Decrypted device name: %s", name); + } + } } } } @@ -193,11 +223,14 @@ static bool should_connect(const uint8_t *adv_data, uint8_t adv_len) uint8_t type = adv_data[offset + 1]; if (type == ESP_BLE_AD_TYPE_16SRV_CMPL || type == ESP_BLE_AD_TYPE_16SRV_PART) { - /* Check for GAP service UUID */ - for (int i = 0; i < len - 1; i += 2) { - uint16_t uuid = adv_data[offset + 2 + i] | (adv_data[offset + 3 + i] << 8); - if (uuid == GAP_SERVICE_UUID) { - return true; + /* Octets after AD type = len - 1; each 16-bit UUID needs 2 payload bytes */ + int payload_len = (int)len - 1; + if (payload_len >= 2) { + for (int i = 0; i + 1 < payload_len; i += 2) { + uint16_t uuid = adv_data[offset + 2 + i] | (adv_data[offset + 3 + i] << 8); + if (uuid == GAP_SERVICE_UUID) { + return true; + } } } } @@ -250,26 +283,33 @@ static void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param decrypt_enc_adv_data(adv_data, adv_len, scan_result->scan_rst.bda); } else { /* Need to connect and get key */ - if (!is_connected) { - ESP_LOGI(TAG, "Connecting to get key material..."); - add_peer(scan_result->scan_rst.bda); - memcpy(current_peer_addr, scan_result->scan_rst.bda, sizeof(esp_bd_addr_t)); + if (!is_connected && !connect_pending) { + int peer_slot = add_peer(scan_result->scan_rst.bda); + if (peer_slot < 0) { + ESP_LOGE(TAG, "Peer table full (max %d); cannot track key for " + ESP_BD_ADDR_STR " — skip connection (increase " + "MAX_PEERS or free a slot)", + MAX_PEERS, ESP_BD_ADDR_HEX(scan_result->scan_rst.bda)); + } else { + ESP_LOGI(TAG, "Connecting to get key material..."); + esp_ble_gap_stop_scanning(); - esp_ble_gap_stop_scanning(); - - esp_ble_gatt_creat_conn_params_t conn_params = {0}; - memcpy(conn_params.remote_bda, scan_result->scan_rst.bda, ESP_BD_ADDR_LEN); - conn_params.remote_addr_type = scan_result->scan_rst.ble_addr_type; - conn_params.own_addr_type = BLE_ADDR_TYPE_PUBLIC; - conn_params.is_direct = true; - conn_params.is_aux = false; - esp_ble_gattc_enh_open(gattc_if_stored, &conn_params); + esp_ble_gatt_creat_conn_params_t conn_params = {0}; + memcpy(conn_params.remote_bda, scan_result->scan_rst.bda, + ESP_BD_ADDR_LEN); + conn_params.remote_addr_type = scan_result->scan_rst.ble_addr_type; + conn_params.own_addr_type = BLE_ADDR_TYPE_PUBLIC; + conn_params.is_direct = true; + conn_params.is_aux = false; + connect_pending = true; + esp_ble_gattc_enh_open(gattc_if_stored, &conn_params); + } } } } } else if (scan_result->scan_rst.search_evt == ESP_GAP_SEARCH_INQ_CMPL_EVT) { ESP_LOGI(TAG, "Scan complete"); - if (!is_connected) { + if (!is_connected && !connect_pending) { start_scan(); /* Restart scanning */ } } @@ -314,6 +354,8 @@ static void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_ case ESP_GATTC_CONNECT_EVT: ESP_LOGI(TAG, "Connected, conn_id %d", param->connect.conn_id); conn_id_stored = param->connect.conn_id; + connect_pending = false; + memcpy(gattc_remote_bda, param->connect.remote_bda, sizeof(esp_bd_addr_t)); is_connected = true; /* Request MTU exchange */ @@ -324,6 +366,8 @@ static void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_ if (param->open.status != ESP_GATT_OK) { ESP_LOGE(TAG, "Open failed: %d", param->open.status); is_connected = false; + connect_pending = false; + memset(gattc_remote_bda, 0, sizeof(gattc_remote_bda)); start_scan(); } break; @@ -360,25 +404,29 @@ static void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_ if (get_server) { /* Get characteristics */ uint16_t count = 0; - esp_ble_gattc_get_attr_count(gattc_if, conn_id_stored, - ESP_GATT_DB_CHARACTERISTIC, - service_start_handle, - service_end_handle, - INVALID_HANDLE, &count); + esp_gatt_status_t gc_st = esp_ble_gattc_get_attr_count(gattc_if, conn_id_stored, + ESP_GATT_DB_CHARACTERISTIC, + service_start_handle, + service_end_handle, + INVALID_HANDLE, &count); - if (count > 0) { - esp_gattc_char_elem_t *char_elem = malloc(sizeof(esp_gattc_char_elem_t) * count); + if (gc_st != ESP_GATT_OK) { + ESP_LOGE(TAG, "get_attr_count failed: %d", gc_st); + } else if (count > 0) { + esp_gattc_char_elem_t *char_elem = calloc(count, sizeof(esp_gattc_char_elem_t)); if (char_elem) { esp_bt_uuid_t km_uuid = { .len = ESP_UUID_LEN_16, .uuid = {.uuid16 = KEY_MATERIAL_CHAR_UUID}, }; - esp_ble_gattc_get_char_by_uuid(gattc_if, conn_id_stored, - service_start_handle, - service_end_handle, - km_uuid, char_elem, &count); + gc_st = esp_ble_gattc_get_char_by_uuid(gattc_if, conn_id_stored, + service_start_handle, + service_end_handle, + km_uuid, char_elem, &count); - if (count > 0) { + if (gc_st != ESP_GATT_OK) { + ESP_LOGE(TAG, "get_char_by_uuid failed: %d", gc_st); + } else if (count > 0) { key_material_char_handle = char_elem[0].char_handle; ESP_LOGI(TAG, "Key Material characteristic found, handle %d", key_material_char_handle); @@ -402,7 +450,7 @@ static void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_ if (param->read.handle == key_material_char_handle && param->read.value_len == sizeof(ble_ead_key_material_t)) { /* Store key material */ - int peer_idx = find_peer(current_peer_addr); + int peer_idx = find_peer(gattc_remote_bda); if (peer_idx >= 0) { memcpy(&peers[peer_idx].key_material, param->read.value, sizeof(ble_ead_key_material_t)); @@ -424,6 +472,8 @@ static void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_ case ESP_GATTC_DISCONNECT_EVT: ESP_LOGI(TAG, "Disconnected, reason 0x%02x", param->disconnect.reason); is_connected = false; + connect_pending = false; + memset(gattc_remote_bda, 0, sizeof(gattc_remote_bda)); get_server = false; key_material_char_handle = INVALID_HANDLE; start_scan(); diff --git a/examples/bluetooth/bluedroid/ble/ble_enc_adv_data/enc_adv_data_cent/main/enc_adv_data_cent_no_connect.c b/examples/bluetooth/bluedroid/ble/ble_enc_adv_data/enc_adv_data_cent/main/enc_adv_data_cent_no_connect.c index 62416b1ecc1..7fd45def779 100644 --- a/examples/bluetooth/bluedroid/ble/ble_enc_adv_data/enc_adv_data_cent/main/enc_adv_data_cent_no_connect.c +++ b/examples/bluetooth/bluedroid/ble/ble_enc_adv_data/enc_adv_data_cent/main/enc_adv_data_cent_no_connect.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2025-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Unlicense OR CC0-1.0 */ @@ -67,10 +67,14 @@ static bool is_target_device(const uint8_t *adv_data, uint8_t adv_len) uint8_t type = adv_data[offset + 1]; if (type == ESP_BLE_AD_TYPE_16SRV_CMPL || type == ESP_BLE_AD_TYPE_16SRV_PART) { - for (int i = 0; i < len - 1; i += 2) { - uint16_t uuid = adv_data[offset + 2 + i] | (adv_data[offset + 3 + i] << 8); - if (uuid == CUSTOM_SERVICE_UUID) { - return true; + /* Octets after AD type = len - 1; each 16-bit UUID needs 2 payload bytes */ + int payload_len = (int)len - 1; + if (payload_len >= 2) { + for (int i = 0; i + 1 < payload_len; i += 2) { + uint16_t uuid = adv_data[offset + 2 + i] | (adv_data[offset + 3 + i] << 8); + if (uuid == CUSTOM_SERVICE_UUID) { + return true; + } } } } @@ -116,30 +120,53 @@ static void decrypt_adv_data_no_connect(const uint8_t *adv_data, uint8_t adv_len /* Decrypt using pre-shared key */ uint8_t dec_data[32]; size_t dec_len = BLE_EAD_DECRYPTED_PAYLOAD_SIZE(enc_data_len); + if (dec_len > sizeof(dec_data)) { + ESP_LOGW(TAG, "Encrypted AD would yield %zu plaintext bytes; example buffer is %zu — skip", + dec_len, sizeof(dec_data)); + return; + } int rc = ble_ead_decrypt( pre_shared_key.session_key, pre_shared_key.iv, enc_data, enc_data_len, - dec_data); + dec_data, sizeof(dec_data)); if (rc == 0) { + size_t safe_dec_len = dec_len; + if (safe_dec_len > sizeof(dec_data)) { + ESP_LOGW(TAG, "dec_len %zu > buffer %zu, clamping for log/parse", + dec_len, sizeof(dec_data)); + safe_dec_len = sizeof(dec_data); + } ESP_LOGI(TAG, "✅ Decryption successful (no connection needed!)"); - ESP_LOGI(TAG, "Decrypted data (%d bytes):", dec_len); - ESP_LOG_BUFFER_HEX(TAG, dec_data, dec_len); + ESP_LOGI(TAG, "Decrypted data (%zu bytes):", safe_dec_len); + ESP_LOG_BUFFER_HEX(TAG, dec_data, safe_dec_len); - /* Parse the decrypted advertising structure */ - if (dec_len >= 2) { - uint8_t inner_len = dec_data[0]; - uint8_t inner_type = dec_data[1]; - - if (inner_type == ESP_BLE_AD_TYPE_NAME_CMPL || - inner_type == ESP_BLE_AD_TYPE_NAME_SHORT) { - char name[32] = {0}; - size_t name_len = inner_len - 1; - if (name_len < sizeof(name) && name_len <= dec_len - 2) { - memcpy(name, &dec_data[2], name_len); - ESP_LOGI(TAG, "📛 Decrypted device name: \"%s\"", name); + /* Parse decrypted AD (do not trust length octet past plaintext) */ + if (safe_dec_len >= 2) { + if (dec_data[0] == 0) { + ESP_LOGW(TAG, "Malformed decrypted AD: zero inner length"); + } else { + const size_t inner_total = 1U + (size_t)dec_data[0]; + if (inner_total > safe_dec_len) { + ESP_LOGW(TAG, + "Malformed decrypted AD: inner len claims %zu octets, have %zu", + inner_total, safe_dec_len); + } else { + uint8_t inner_type = dec_data[1]; + if (inner_type == ESP_BLE_AD_TYPE_NAME_CMPL || + inner_type == ESP_BLE_AD_TYPE_NAME_SHORT) { + char name[32] = {0}; + size_t name_len = (size_t)dec_data[0] - 1U; + const size_t name_copy_end = 2U + name_len; + if (name_len < sizeof(name) && + name_copy_end <= sizeof(dec_data) && + name_copy_end <= safe_dec_len) { + memcpy(name, &dec_data[2], name_len); + ESP_LOGI(TAG, "📛 Decrypted device name: \"%s\"", name); + } + } } } } diff --git a/examples/bluetooth/bluedroid/ble/ble_enc_adv_data/enc_adv_data_prph/main/ble_ead.c b/examples/bluetooth/bluedroid/ble/ble_enc_adv_data/enc_adv_data_prph/main/ble_ead.c index cc0abeca1dc..c3c0ac6ce4f 100644 --- a/examples/bluetooth/bluedroid/ble/ble_enc_adv_data/enc_adv_data_prph/main/ble_ead.c +++ b/examples/bluetooth/bluedroid/ble/ble_enc_adv_data/enc_adv_data_prph/main/ble_ead.c @@ -168,7 +168,8 @@ static int ble_aes_ccm_encrypt(const uint8_t *key, const uint8_t *nonce, static int ble_aes_ccm_decrypt(const uint8_t *key, const uint8_t *nonce, const uint8_t *ciphertext, size_t ciphertext_len, const uint8_t *aad, size_t aad_len, - uint8_t *plaintext, size_t tag_len) + uint8_t *plaintext, size_t tag_len, + size_t plaintext_capacity) { #if defined(CONFIG_BT_SMP_CRYPTO_STACK_TINYCRYPT) struct tc_aes_key_sched_struct sched; @@ -191,6 +192,11 @@ static int ble_aes_ccm_decrypt(const uint8_t *key, const uint8_t *nonce, plaintext_len = ciphertext_len - tag_len; + if (plaintext_len > plaintext_capacity) { + ESP_LOGE(TAG, "plaintext_len (%zu) > plaintext_capacity (%zu)", plaintext_len, plaintext_capacity); + return -1; + } + /* Set AES encryption key */ ret = tc_aes128_set_encrypt_key(&sched, key); if (ret != TC_CRYPTO_SUCCESS) { @@ -330,13 +336,14 @@ int ble_ead_encrypt(const uint8_t session_key[BLE_EAD_KEY_SIZE], int ble_ead_decrypt(const uint8_t session_key[BLE_EAD_KEY_SIZE], const uint8_t iv[BLE_EAD_IV_SIZE], const uint8_t *encrypted_payload, size_t encrypted_payload_size, - uint8_t *payload) + uint8_t *payload, size_t payload_capacity) { int ret; uint8_t nonce[BLE_EAD_NONCE_SIZE]; const uint8_t *randomizer; const uint8_t *ciphertext; size_t ciphertext_len; + size_t expected_plaintext_len; if (session_key == NULL) { ESP_LOGE(TAG, "session_key is NULL"); @@ -363,6 +370,13 @@ int ble_ead_decrypt(const uint8_t session_key[BLE_EAD_KEY_SIZE], return -1; } + expected_plaintext_len = BLE_EAD_DECRYPTED_PAYLOAD_SIZE(encrypted_payload_size); + if (expected_plaintext_len > payload_capacity) { + ESP_LOGE(TAG, "EAD plaintext length %zu exceeds payload buffer %zu", + expected_plaintext_len, payload_capacity); + return -1; + } + /* Extract randomizer from the start of encrypted payload */ randomizer = encrypted_payload; @@ -381,7 +395,8 @@ int ble_ead_decrypt(const uint8_t session_key[BLE_EAD_KEY_SIZE], ret = ble_aes_ccm_decrypt(session_key, nonce, ciphertext, ciphertext_len, ble_ead_aad, BLE_EAD_AAD_SIZE, - payload, BLE_EAD_MIC_SIZE); + payload, BLE_EAD_MIC_SIZE, + payload_capacity); return ret; } diff --git a/examples/bluetooth/bluedroid/ble/ble_enc_adv_data/enc_adv_data_prph/main/ble_ead.h b/examples/bluetooth/bluedroid/ble/ble_enc_adv_data/enc_adv_data_prph/main/ble_ead.h index a9bf8954ebf..4994f6b994f 100644 --- a/examples/bluetooth/bluedroid/ble/ble_enc_adv_data/enc_adv_data_prph/main/ble_ead.h +++ b/examples/bluetooth/bluedroid/ble/ble_enc_adv_data/enc_adv_data_prph/main/ble_ead.h @@ -79,14 +79,15 @@ int ble_ead_encrypt(const uint8_t session_key[BLE_EAD_KEY_SIZE], * @param encrypted_payload Encrypted advertising data (includes randomizer and MIC) * @param encrypted_payload_size Size of encrypted data * @param payload Output buffer for decrypted data - * Size must be at least BLE_EAD_DECRYPTED_PAYLOAD_SIZE(encrypted_payload_size) + * @param payload_capacity Size of @a payload in bytes; must be >= + * BLE_EAD_DECRYPTED_PAYLOAD_SIZE(encrypted_payload_size) * * @return 0 on success, negative error code on failure */ int ble_ead_decrypt(const uint8_t session_key[BLE_EAD_KEY_SIZE], const uint8_t iv[BLE_EAD_IV_SIZE], const uint8_t *encrypted_payload, size_t encrypted_payload_size, - uint8_t *payload); + uint8_t *payload, size_t payload_capacity); #ifdef __cplusplus } diff --git a/examples/bluetooth/bluedroid/ble/ble_enc_adv_data/enc_adv_data_prph/main/enc_adv_data_prph.c b/examples/bluetooth/bluedroid/ble/ble_enc_adv_data/enc_adv_data_prph/main/enc_adv_data_prph.c index 7520bbc9d85..f6d910a1157 100644 --- a/examples/bluetooth/bluedroid/ble/ble_enc_adv_data/enc_adv_data_prph/main/enc_adv_data_prph.c +++ b/examples/bluetooth/bluedroid/ble/ble_enc_adv_data/enc_adv_data_prph/main/enc_adv_data_prph.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2025-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Unlicense OR CC0-1.0 */ @@ -59,7 +59,6 @@ static ble_ead_key_material_t key_material = { /* GATT state */ static esp_gatt_if_t gatts_if_stored = ESP_GATT_IF_NONE; static uint16_t conn_id_stored = 0; -static bool is_connected = false; /* Advertising parameters */ static esp_ble_adv_params_t adv_params = { @@ -158,8 +157,13 @@ static void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param { switch (event) { case ESP_GAP_BLE_ADV_DATA_RAW_SET_COMPLETE_EVT: - ESP_LOGI(TAG, "Raw advertising data set complete"); - start_advertising(); + if (param->adv_data_raw_cmpl.status != ESP_BT_STATUS_SUCCESS) { + ESP_LOGE(TAG, "Raw advertising data set failed: %d", + param->adv_data_raw_cmpl.status); + } else { + ESP_LOGI(TAG, "Raw advertising data set complete"); + start_advertising(); + } break; case ESP_GAP_BLE_ADV_START_COMPLETE_EVT: @@ -236,7 +240,6 @@ static void gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_ ESP_LOGI(TAG, "Connected, conn_id %d, remote "ESP_BD_ADDR_STR"", param->connect.conn_id, ESP_BD_ADDR_HEX(param->connect.remote_bda)); conn_id_stored = param->connect.conn_id; - is_connected = true; /* Update connection parameters */ esp_ble_conn_update_params_t conn_params = {0}; @@ -251,7 +254,6 @@ static void gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_ case ESP_GATTS_DISCONNECT_EVT: ESP_LOGI(TAG, "Disconnected, remote "ESP_BD_ADDR_STR", reason 0x%02x", ESP_BD_ADDR_HEX(param->disconnect.remote_bda), param->disconnect.reason); - is_connected = false; /* Re-encrypt and restart advertising with new randomizer */ set_encrypted_adv_data(); diff --git a/examples/bluetooth/bluedroid/ble/ble_hid_device_demo/main/esp_hidd_prf_api.c b/examples/bluetooth/bluedroid/ble/ble_hid_device_demo/main/esp_hidd_prf_api.c index b7d1fbd68e5..36f4e56dcc5 100644 --- a/examples/bluetooth/bluedroid/ble/ble_hid_device_demo/main/esp_hidd_prf_api.c +++ b/examples/bluetooth/bluedroid/ble/ble_hid_device_demo/main/esp_hidd_prf_api.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2021-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Unlicense OR CC0-1.0 */ @@ -37,9 +37,11 @@ esp_err_t esp_hidd_register_callbacks(esp_hidd_event_cb_t callbacks) return hidd_status; } - esp_ble_gatts_app_register(BATTRAY_APP_ID); + if ((hidd_status = esp_ble_gatts_app_register(BATTRAY_APP_ID)) != ESP_OK) { + return hidd_status; + } - if((hidd_status = esp_ble_gatts_app_register(HIDD_APP_ID)) != ESP_OK) { + if ((hidd_status = esp_ble_gatts_app_register(HIDD_APP_ID)) != ESP_OK) { return hidd_status; } @@ -52,8 +54,10 @@ esp_err_t esp_hidd_profile_init(void) ESP_LOGE(HID_LE_PRF_TAG, "HID device profile already initialized"); return ESP_FAIL; } - // Reset the hid device target environment + /* Reset the hid device target environment */ memset(&hidd_le_env, 0, sizeof(hidd_le_env_t)); + hidd_le_env.gatt_if = ESP_GATT_IF_NONE; + hidd_le_env.bat_gatt_if = ESP_GATT_IF_NONE; hidd_le_env.enabled = true; return ESP_OK; } @@ -62,20 +66,32 @@ esp_err_t esp_hidd_profile_deinit(void) { uint16_t hidd_svc_hdl = hidd_le_env.hidd_inst.att_tbl[HIDD_LE_IDX_SVC]; if (!hidd_le_env.enabled) { - ESP_LOGE(HID_LE_PRF_TAG, "HID device profile already initialized"); + ESP_LOGW(HID_LE_PRF_TAG, "HID device profile not initialized, deinit skipped"); return ESP_OK; } - if(hidd_svc_hdl != 0) { - esp_ble_gatts_stop_service(hidd_svc_hdl); - esp_ble_gatts_delete_service(hidd_svc_hdl); + if (hidd_svc_hdl != 0) { + esp_ble_gatts_stop_service(hidd_svc_hdl); + esp_ble_gatts_delete_service(hidd_svc_hdl); } else { - return ESP_FAIL; - } + ESP_LOGW(HID_LE_PRF_TAG, "HID service handle unset, skip stop/delete"); + } - /* register the HID device profile to the BTA_GATTS module*/ - esp_ble_gatts_app_unregister(hidd_le_env.gatt_if); + /* Release both GATTS apps (battery registered first, then HID) */ + if (hidd_le_env.bat_gatt_if != ESP_GATT_IF_NONE) { + esp_ble_gatts_app_unregister(hidd_le_env.bat_gatt_if); + hidd_le_env.bat_gatt_if = ESP_GATT_IF_NONE; + } else { + ESP_LOGW(HID_LE_PRF_TAG, "Battery gatt_if invalid, app_unregister skipped (possible stack slot leak)"); + } + if (hidd_le_env.gatt_if != ESP_GATT_IF_NONE) { + esp_ble_gatts_app_unregister(hidd_le_env.gatt_if); + hidd_le_env.gatt_if = ESP_GATT_IF_NONE; + } else { + ESP_LOGW(HID_LE_PRF_TAG, "HID gatt_if invalid, app_unregister skipped (possible stack slot leak)"); + } + hidd_le_env.enabled = false; return ESP_OK; } @@ -112,7 +128,7 @@ void esp_hidd_send_keyboard_value(uint16_t conn_id, key_mask_t special_key_mask, buffer[i+2] = keyboard_cmd[i]; } - ESP_LOGD(HID_LE_PRF_TAG, "the key vaule = %d,%d,%d, %d, %d, %d,%d, %d", buffer[0], buffer[1], buffer[2], buffer[3], buffer[4], buffer[5], buffer[6], buffer[7]); + ESP_LOGD(HID_LE_PRF_TAG, "the key value = %d,%d,%d, %d, %d, %d,%d, %d", buffer[0], buffer[1], buffer[2], buffer[3], buffer[4], buffer[5], buffer[6], buffer[7]); hid_dev_send_report(hidd_le_env.gatt_if, conn_id, HID_RPT_ID_KEY_IN, HID_REPORT_TYPE_INPUT, HID_KEYBOARD_IN_RPT_LEN, buffer); return; diff --git a/examples/bluetooth/bluedroid/ble/ble_hid_device_demo/main/hid_device_le_prf.c b/examples/bluetooth/bluedroid/ble/ble_hid_device_demo/main/hid_device_le_prf.c index fc86ee30dc3..c99e20693fe 100644 --- a/examples/bluetooth/bluedroid/ble/ble_hid_device_demo/main/hid_device_le_prf.c +++ b/examples/bluetooth/bluedroid/ble/ble_hid_device_demo/main/hid_device_le_prf.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2021-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Unlicense OR CC0-1.0 */ @@ -170,7 +170,7 @@ static const uint8_t hidReportMap[] = { 0x09, 0xA6, // Usage(Vendor defined) 0x09, 0xA9, // Usage(Vendor defined) 0x75, 0x08, // Report Size - 0x95, 0x7F, // Report Count = 127 Btyes + 0x95, 0x7F, // Report Count = 127 Bytes 0x91, 0x02, // Output(Data, Variable, Absolute) 0xC0, // End Collection #endif @@ -285,7 +285,7 @@ static const uint8_t char_prop_read_notify = ESP_GATT_CHAR_PROP_BIT_READ|ESP_GAT static const uint8_t char_prop_read_write_notify = ESP_GATT_CHAR_PROP_BIT_READ|ESP_GATT_CHAR_PROP_BIT_WRITE|ESP_GATT_CHAR_PROP_BIT_NOTIFY; static const uint8_t char_prop_read_write_write_nr = ESP_GATT_CHAR_PROP_BIT_READ|ESP_GATT_CHAR_PROP_BIT_WRITE|ESP_GATT_CHAR_PROP_BIT_WRITE_NR; -/// battary Service +/// battery Service static const uint16_t battary_svc = ESP_GATT_UUID_BATTERY_SERVICE_SVC; static const uint16_t bat_lev_uuid = ESP_GATT_UUID_BATTERY_LEVEL; @@ -296,23 +296,23 @@ static uint8_t battary_lev = 50; /// Full HRS Database Description - Used to add attributes into the database static const esp_gatts_attr_db_t bas_att_db[BAS_IDX_NB] = { - // Battary Service Declaration + // Battery Service Declaration [BAS_IDX_SVC] = {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&primary_service_uuid, ESP_GATT_PERM_READ, sizeof(uint16_t), sizeof(battary_svc), (uint8_t *)&battary_svc}}, - // Battary level Characteristic Declaration + // Battery level Characteristic Declaration [BAS_IDX_BATT_LVL_CHAR] = {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&character_declaration_uuid, ESP_GATT_PERM_READ, CHAR_DECLARATION_SIZE,CHAR_DECLARATION_SIZE, (uint8_t *)&char_prop_read_notify}}, - // Battary level Characteristic Value + // Battery level Characteristic Value [BAS_IDX_BATT_LVL_VAL] = {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&bat_lev_uuid, ESP_GATT_PERM_READ, sizeof(uint8_t),sizeof(uint8_t), &battary_lev}}, - // Battary level Characteristic - Client Characteristic Configuration Descriptor + // Battery level Characteristic - Client Characteristic Configuration Descriptor [BAS_IDX_BATT_LVL_NTF_CFG] = {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&character_client_config_uuid, ESP_GATT_PERM_READ|ESP_GATT_PERM_WRITE, sizeof(uint16_t),sizeof(bat_lev_ccc), (uint8_t *)bat_lev_ccc}}, - // Battary level report Characteristic Declaration + // Battery level report Characteristic Declaration [BAS_IDX_BATT_LVL_PRES_FMT] = {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&char_format_uuid, ESP_GATT_PERM_READ, sizeof(struct prf_char_pres_fmt), 0, NULL}}, }; @@ -550,6 +550,7 @@ void esp_hidd_prf_cb_hdl(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, } } if(param->reg.app_id == BATTRAY_APP_ID) { + hidd_le_env.bat_gatt_if = gatts_if; hidd_param.init_finish.gatts_if = gatts_if; if(hidd_le_env.hidd_cb != NULL) { (hidd_le_env.hidd_cb)(ESP_BAT_EVENT_REG, &hidd_param); @@ -587,7 +588,8 @@ void esp_hidd_prf_cb_hdl(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, break; case ESP_GATTS_WRITE_EVT: { esp_hidd_cb_param_t cb_param = {0}; - if (param->write.handle == hidd_le_env.hidd_inst.att_tbl[HIDD_LE_IDX_REPORT_LED_OUT_VAL]) { + if (hidd_le_env.hidd_cb != NULL && + param->write.handle == hidd_le_env.hidd_inst.att_tbl[HIDD_LE_IDX_REPORT_LED_OUT_VAL]) { cb_param.led_write.conn_id = param->write.conn_id; cb_param.led_write.report_id = HID_RPT_ID_LED_OUT; cb_param.led_write.length = param->write.len; @@ -595,8 +597,8 @@ void esp_hidd_prf_cb_hdl(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, (hidd_le_env.hidd_cb)(ESP_HIDD_EVENT_BLE_LED_REPORT_WRITE_EVT, &cb_param); } #if (SUPPORT_REPORT_VENDOR == true) - if (param->write.handle == hidd_le_env.hidd_inst.att_tbl[HIDD_LE_IDX_REPORT_VENDOR_OUT_VAL] && - hidd_le_env.hidd_cb != NULL) { + if (hidd_le_env.hidd_cb != NULL && + param->write.handle == hidd_le_env.hidd_inst.att_tbl[HIDD_LE_IDX_REPORT_VENDOR_OUT_VAL]) { cb_param.vendor_write.conn_id = param->write.conn_id; cb_param.vendor_write.report_id = HID_RPT_ID_VENDOR_OUT; cb_param.vendor_write.length = param->write.len; @@ -607,24 +609,26 @@ void esp_hidd_prf_cb_hdl(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, break; } case ESP_GATTS_CREAT_ATTR_TAB_EVT: { - if (param->add_attr_tab.num_handle == BAS_IDX_NB && - param->add_attr_tab.svc_uuid.uuid.uuid16 == ESP_GATT_UUID_BATTERY_SERVICE_SVC && - param->add_attr_tab.status == ESP_GATT_OK) { + if (param->add_attr_tab.status != ESP_GATT_OK) { + ESP_LOGE(HID_LE_PRF_TAG, "ATTR_TAB_EVT failed: status=%d num_handle=%d", + param->add_attr_tab.status, param->add_attr_tab.num_handle); + } else if (param->add_attr_tab.num_handle == BAS_IDX_NB && + param->add_attr_tab.svc_uuid.uuid.uuid16 == ESP_GATT_UUID_BATTERY_SERVICE_SVC) { incl_svc.start_hdl = param->add_attr_tab.handles[BAS_IDX_SVC]; - incl_svc.end_hdl = incl_svc.start_hdl + BAS_IDX_NB -1; + incl_svc.end_hdl = incl_svc.start_hdl + BAS_IDX_NB - 1; ESP_LOGI(HID_LE_PRF_TAG, "%s(), start added the hid service to the stack database. incl_handle = %d", __func__, incl_svc.start_hdl); + esp_ble_gatts_start_service(param->add_attr_tab.handles[BAS_IDX_SVC]); esp_ble_gatts_create_attr_tab(hidd_le_gatt_db, gatts_if, HIDD_LE_IDX_NB, 0); - } - if (param->add_attr_tab.num_handle == HIDD_LE_IDX_NB && - param->add_attr_tab.status == ESP_GATT_OK) { + } else if (param->add_attr_tab.num_handle == HIDD_LE_IDX_NB) { memcpy(hidd_le_env.hidd_inst.att_tbl, param->add_attr_tab.handles, - HIDD_LE_IDX_NB*sizeof(uint16_t)); - ESP_LOGI(HID_LE_PRF_TAG, "hid svc handle = %x",hidd_le_env.hidd_inst.att_tbl[HIDD_LE_IDX_SVC]); + HIDD_LE_IDX_NB * sizeof(uint16_t)); + ESP_LOGI(HID_LE_PRF_TAG, "hid svc handle = %x", hidd_le_env.hidd_inst.att_tbl[HIDD_LE_IDX_SVC]); hid_add_id_tbl(); - esp_ble_gatts_start_service(hidd_le_env.hidd_inst.att_tbl[HIDD_LE_IDX_SVC]); + esp_ble_gatts_start_service(hidd_le_env.hidd_inst.att_tbl[HIDD_LE_IDX_SVC]); } else { - esp_ble_gatts_start_service(param->add_attr_tab.handles[0]); + ESP_LOGW(HID_LE_PRF_TAG, "ATTR_TAB_EVT unexpected num_handle=%d", + param->add_attr_tab.num_handle); } break; } @@ -645,8 +649,10 @@ void hidd_le_create_service(esp_gatt_if_t gatts_if) void hidd_le_init(void) { - // Reset the hid device target environment + /* Reset the hid device target environment */ memset(&hidd_le_env, 0, sizeof(hidd_le_env_t)); + hidd_le_env.gatt_if = ESP_GATT_IF_NONE; + hidd_le_env.bat_gatt_if = ESP_GATT_IF_NONE; } void hidd_clcb_alloc (uint16_t conn_id, esp_bd_addr_t bda) @@ -666,16 +672,15 @@ void hidd_clcb_alloc (uint16_t conn_id, esp_bd_addr_t bda) return; } -bool hidd_clcb_dealloc (uint16_t conn_id) +bool hidd_clcb_dealloc(uint16_t conn_id) { - uint8_t i_clcb = 0; - hidd_clcb_t *p_clcb = NULL; - - for (i_clcb = 0, p_clcb= hidd_le_env.hidd_clcb; i_clcb < HID_MAX_APPS; i_clcb++, p_clcb++) { + for (uint8_t i_clcb = 0; i_clcb < HID_MAX_APPS; i_clcb++) { + hidd_clcb_t *p_clcb = &hidd_le_env.hidd_clcb[i_clcb]; + if (p_clcb->in_use && p_clcb->conn_id == conn_id) { memset(p_clcb, 0, sizeof(hidd_clcb_t)); return true; + } } - return false; } @@ -735,12 +740,12 @@ void hidd_set_attr_value(uint16_t handle, uint16_t val_len, const uint8_t *value return; } -void hidd_get_attr_value(uint16_t handle, uint16_t *length, uint8_t **value) +void hidd_get_attr_value(uint16_t handle, uint16_t *length, const uint8_t **value) { hidd_inst_t *hidd_inst = &hidd_le_env.hidd_inst; if(hidd_inst->att_tbl[HIDD_LE_IDX_HID_INFO_VAL] <= handle && hidd_inst->att_tbl[HIDD_LE_IDX_REPORT_REP_REF] >= handle){ - esp_ble_gatts_get_attr_value(handle, length, (const uint8_t **)value); + esp_ble_gatts_get_attr_value(handle, length, value); } else { ESP_LOGE(HID_LE_PRF_TAG, "%s error:Invalid handle value.", __func__); } diff --git a/examples/bluetooth/bluedroid/ble/ble_hid_device_demo/main/hidd_le_prf_int.h b/examples/bluetooth/bluedroid/ble/ble_hid_device_demo/main/hidd_le_prf_int.h index 6b48ac43426..7abac8dcdea 100644 --- a/examples/bluetooth/bluedroid/ble/ble_hid_device_demo/main/hidd_le_prf_int.h +++ b/examples/bluetooth/bluedroid/ble/ble_hid_device_demo/main/hidd_le_prf_int.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2021-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Unlicense OR CC0-1.0 */ @@ -183,7 +183,7 @@ enum { HIDD_LE_CHAR_MAX //= HIDD_LE_REPORT_CHAR + HIDD_LE_NB_REPORT_INST_MAX, }; -///att read event table Indexs +///att read event table Indexes enum { HIDD_LE_READ_INFO_EVT, HIDD_LE_READ_CTNL_PT_EVT, @@ -306,7 +306,8 @@ typedef struct /* service engine control block */ typedef struct { hidd_clcb_t hidd_clcb[HID_MAX_APPS]; /* connection link*/ - esp_gatt_if_t gatt_if; + esp_gatt_if_t gatt_if; /* HIDD_APP_ID */ + esp_gatt_if_t bat_gatt_if; /* BATTRAY_APP_ID */ bool enabled; bool is_take; bool is_primery; @@ -327,7 +328,7 @@ void hidd_le_create_service(esp_gatt_if_t gatts_if); void hidd_set_attr_value(uint16_t handle, uint16_t val_len, const uint8_t *value); -void hidd_get_attr_value(uint16_t handle, uint16_t *length, uint8_t **value); +void hidd_get_attr_value(uint16_t handle, uint16_t *length, const uint8_t **value); esp_err_t hidd_register_cb(void); diff --git a/examples/bluetooth/bluedroid/ble/ble_multi_conn/ble_multi_conn_cent/main/ble_multiconn_cent_demo.c b/examples/bluetooth/bluedroid/ble/ble_multi_conn/ble_multi_conn_cent/main/ble_multiconn_cent_demo.c index 992ce309ba0..6c6d0dfe85a 100644 --- a/examples/bluetooth/bluedroid/ble/ble_multi_conn/ble_multi_conn_cent/main/ble_multiconn_cent_demo.c +++ b/examples/bluetooth/bluedroid/ble/ble_multi_conn/ble_multi_conn_cent/main/ble_multiconn_cent_demo.c @@ -219,6 +219,22 @@ static const esp_gatts_attr_db_t gatt_db[HRS_IDX_NB] = }; static uint16_t profile_handle_table[HRS_IDX_NB]; +/** After a prepared write is executed, read committed value from GATT DB and relay to peers. */ +static void relay_demo_char_value_to_peers(void) +{ + uint16_t len = 0; + const uint8_t *value = NULL; + uint16_t h = profile_handle_table[IDX_CHAR_VAL_A]; + + if (h == 0) { + return; + } + if (esp_ble_gatts_get_attr_value(h, &len, &value) != ESP_GATT_OK || value == NULL || len == 0) { + return; + } + traverse_send_peer(len, (uint8_t *)value); +} + #if (BLE50_SUPPORTED == 1) static esp_ble_gap_ext_adv_t ext_adv[1] = { [0] = {ADV_HANDLE_INST, ADV_DURATION, ADV_MAX_EVTS}, @@ -238,8 +254,9 @@ static void gattc_profile_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_ break; case ESP_GATTC_CONNECT_EVT: if (param->connect.link_role == 0) { + ++multi_conn_num; ESP_LOGI(DEMO_TAG, "Connected, conn_id %d, remote "ESP_BD_ADDR_STR", total %u", param->connect.conn_id, - ESP_BD_ADDR_HEX(param->connect.remote_bda), ++multi_conn_num); + ESP_BD_ADDR_HEX(param->connect.remote_bda), (unsigned)multi_conn_num); Peer new_peer; new_peer.conn_id = param->connect.conn_id; new_peer.conn_handle = param->connect.conn_handle; @@ -261,20 +278,33 @@ static void gattc_profile_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_ } } break; - case ESP_GATTC_DISCONNECT_EVT: - ESP_LOGI(DEMO_TAG, "Disconnected, remote "ESP_BD_ADDR_STR", reason 0x%02x, total %u", - ESP_BD_ADDR_HEX(param->disconnect.remote_bda), param->disconnect.reason, - (multi_conn_num ? --multi_conn_num : multi_conn_num)); - if (param->disconnect.conn_id != prph_conn_id) { - Peer *peer = find_peer(param->disconnect.conn_id); - if (peer) { - peer_remove(peer->conn_id); + case ESP_GATTC_DISCONNECT_EVT: { + /* Only central-role connections are added to the peer list (see ESP_GATTC_CONNECT_EVT); + * peripheral-role connections are tracked separately via prph_conn_id and cleaned up + * entirely in ESP_GATTS_DISCONNECT_EVT. Use peer_lookup() as the role oracle here: + * a non-NULL result is the unambiguous proof that this disconnect belongs to a + * central-role peer, regardless of GATTC/GATTS event ordering. + * + * Note: Bluedroid posts ESP_GATTS_DISCONNECT_EVT to the BTC queue before + * ESP_GATTC_DISCONNECT_EVT (the GATTS path is delivered directly, while the GATTC + * path is relayed through the BTA queue), so by the time we get here for a + * peripheral disconnect, prph_conn_id has already been reset to 0xFFFF. Relying on + * (conn_id != prph_conn_id) would therefore mis-classify the peripheral disconnect + * as a central one and incorrectly decrement multi_conn_num / give restart_scan_sem. + * peer_lookup() is used instead of find_peer() so peripheral disconnects don't + * trigger a spurious "peer not found" ERROR log. */ + Peer *peer = peer_lookup(param->disconnect.conn_id); + if (peer) { + peer_remove(peer->conn_id); + if (multi_conn_num) { + --multi_conn_num; } + ESP_LOGI(DEMO_TAG, "Disconnected, remote "ESP_BD_ADDR_STR", reason 0x%02x, total %u", + ESP_BD_ADDR_HEX(param->disconnect.remote_bda), param->disconnect.reason, (unsigned)multi_conn_num); xSemaphoreGive(restart_scan_sem); - } else { - prph_conn_id = 0xFFFF; } break; + } case ESP_GATTC_OPEN_EVT: ESP_LOGI(DEMO_TAG, "Open, conn_id %d, status %d", param->open.conn_id, param->open.status); break; @@ -340,14 +370,27 @@ static void gatts_profile_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_ esp_ble_gatts_create_attr_tab(gatt_db, gatts_if, HRS_IDX_NB, SVC_INST_ID); break; case ESP_GATTS_WRITE_EVT: - ESP_LOGI(DEMO_TAG, "Characteristic write received, conn_id %u, value", param->write.conn_id); + ESP_LOGI(DEMO_TAG, "Characteristic write received, conn_id %u, prep=%d", param->write.conn_id, + (int)param->write.is_prep); ESP_LOG_BUFFER_HEX(DEMO_TAG, param->write.value, param->write.len); - traverse_send_peer(param->write.len, param->write.value); + /* Prepared writes must be relayed only after ESP_GATTS_EXEC_WRITE_EVT (EXEC). */ + if (param->write.is_prep) { + break; + } + if (param->write.handle == profile_handle_table[IDX_CHAR_VAL_A]) { + traverse_send_peer(param->write.len, param->write.value); + } + break; + case ESP_GATTS_EXEC_WRITE_EVT: + if (param->exec_write.exec_write_flag == ESP_GATT_PREP_WRITE_EXEC) { + relay_demo_char_value_to_peers(); + } break; case ESP_GATTS_CONNECT_EVT: if (param->connect.link_role == 1) { + ++multi_conn_num; ESP_LOGI(DEMO_TAG, "Connected, conn_id %u, remote "ESP_BD_ADDR_STR", total %u", - param->connect.conn_id, ESP_BD_ADDR_HEX(param->connect.remote_bda), ++multi_conn_num); + param->connect.conn_id, ESP_BD_ADDR_HEX(param->connect.remote_bda), (unsigned)multi_conn_num); prph_conn_id = param->connect.conn_id; advertising_state = DISABLED; #if (BLE50_SUPPORTED == 1) @@ -361,9 +404,17 @@ static void gatts_profile_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_ break; case ESP_GATTS_DISCONNECT_EVT: if (param->disconnect.conn_id == prph_conn_id) { + unsigned total_after; + if (multi_conn_num) { + --multi_conn_num; + total_after = (unsigned)multi_conn_num; + } else { + total_after = 0; + } ESP_LOGI(DEMO_TAG, "Disconnected, remote "ESP_BD_ADDR_STR", reason 0x%x, total %u", - ESP_BD_ADDR_HEX(param->disconnect.remote_bda), param->disconnect.reason, - (multi_conn_num ? --multi_conn_num : multi_conn_num)); + ESP_BD_ADDR_HEX(param->disconnect.remote_bda), param->disconnect.reason, total_after); + /* Clear before SET_STATIC_RAND_ADDR_EVT: esp_gap_cb requires 0xFFFF to restart advertising */ + prph_conn_id = 0xFFFF; advertising_state = PENDING; #if (BLE50_SUPPORTED == 1) esp_ble_gap_addr_create_static(adv_rand_addr); @@ -376,7 +427,7 @@ static void gatts_profile_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_ break; case ESP_GATTS_CREAT_ATTR_TAB_EVT: ESP_LOGI(DEMO_TAG, "The number handle = %x", param->add_attr_tab.num_handle); - if (param->create.status == ESP_GATT_OK) { + if (param->add_attr_tab.status == ESP_GATT_OK) { if (param->add_attr_tab.num_handle == HRS_IDX_NB) { memcpy(profile_handle_table, param->add_attr_tab.handles, sizeof(profile_handle_table)); @@ -386,7 +437,7 @@ static void gatts_profile_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_ param->add_attr_tab.num_handle, HRS_IDX_NB); } } else { - ESP_LOGE(DEMO_TAG, " Create attribute table failed, status %x", param->create.status); + ESP_LOGE(DEMO_TAG, "Create attribute table failed, status %x", param->add_attr_tab.status); } break; default: @@ -484,7 +535,7 @@ static void esp_gap_cb(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *par ESP_BLE_AD_TYPE_NAME_CMPL, &adv_name_len); // ESP_LOGI(DEMO_TAG, "Scan result, device "ESP_BD_ADDR_STR", name len %u", ESP_BD_ADDR_HEX(param->ext_adv_report.params.addr), adv_name_len); // ESP_LOG_BUFFER_CHAR(DEMO_TAG, adv_name, adv_name_len); - if (strlen(remote_target_name) == adv_name_len && strncmp((char *)adv_name, remote_target_name, adv_name_len) == 0) + if (adv_name != NULL && strlen(remote_target_name) == adv_name_len && strncmp((char *)adv_name, remote_target_name, adv_name_len) == 0) { esp_ble_gap_stop_ext_scan(); scan_state = DISABLED; @@ -532,13 +583,17 @@ static void esp_gap_cb(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *par if (scan_result->scan_rst.search_evt == ESP_GAP_SEARCH_INQ_RES_EVT) { uint8_t *adv_name = NULL; uint8_t adv_name_len = 0; + const unsigned buf_cap = sizeof(scan_result->scan_rst.ble_adv); + unsigned comb = (unsigned)scan_result->scan_rst.adv_data_len + + (unsigned)scan_result->scan_rst.scan_rsp_len; + uint16_t safe_adv_len = (comb > buf_cap) ? (uint16_t)buf_cap : (uint16_t)comb; adv_name = esp_ble_resolve_adv_data_by_type(scan_result->scan_rst.ble_adv, - scan_result->scan_rst.adv_data_len + scan_result->scan_rst.scan_rsp_len, + safe_adv_len, ESP_BLE_AD_TYPE_NAME_CMPL, &adv_name_len); // ESP_LOGI(DEMO_TAG, "Scan result, device "ESP_BD_ADDR_STR", name len %u", ESP_BD_ADDR_HEX(scan_result->scan_rst.bda), adv_name_len); // ESP_LOG_BUFFER_CHAR(DEMO_TAG, adv_name, adv_name_len); - if (strlen(remote_target_name) == adv_name_len && strncmp((char *)adv_name, remote_target_name, adv_name_len) == 0) { + if (adv_name != NULL && strlen(remote_target_name) == adv_name_len && strncmp((char *)adv_name, remote_target_name, adv_name_len) == 0) { esp_ble_gap_stop_scanning(); scan_state = DISABLED; @@ -711,10 +766,12 @@ void app_main(void) if (multi_conn_num < BLE_PEER_MAX_NUM && scan_state == DISABLED) { scan_state = PENDING; #if (BLE50_SUPPORTED == 0) + /* Legacy: cannot set scan random addr until adv stops; completion continues in + * ESP_GAP_BLE_ADV_STOP_COMPLETE_EVT. Do not break out of while(1) or app_main exits. */ if (advertising_state != DISABLED) { advertising_state = DISABLED; esp_ble_gap_stop_advertising(); - break; + continue; } #endif esp_ble_gap_addr_create_static(new_rand_addr); diff --git a/examples/bluetooth/bluedroid/ble/ble_multi_conn/ble_multi_conn_cent/main/ble_multiconn_cent_demo.h b/examples/bluetooth/bluedroid/ble/ble_multi_conn/ble_multi_conn_cent/main/ble_multiconn_cent_demo.h index 3394dd33754..df5a2baaada 100644 --- a/examples/bluetooth/bluedroid/ble/ble_multi_conn/ble_multi_conn_cent/main/ble_multiconn_cent_demo.h +++ b/examples/bluetooth/bluedroid/ble/ble_multi_conn/ble_multi_conn_cent/main/ble_multiconn_cent_demo.h @@ -66,4 +66,6 @@ esp_err_t peer_remove(uint16_t conn_id); Peer *find_peer(uint16_t conn_id); +Peer *peer_lookup(uint16_t conn_id); + #endif diff --git a/examples/bluetooth/bluedroid/ble/ble_multi_conn/ble_multi_conn_cent/main/peer_manager.c b/examples/bluetooth/bluedroid/ble/ble_multi_conn/ble_multi_conn_cent/main/peer_manager.c index 6b22e1144f2..f20ffc4b2b6 100644 --- a/examples/bluetooth/bluedroid/ble/ble_multi_conn/ble_multi_conn_cent/main/peer_manager.c +++ b/examples/bluetooth/bluedroid/ble/ble_multi_conn/ble_multi_conn_cent/main/peer_manager.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2025-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Unlicense OR CC0-1.0 */ @@ -8,6 +8,19 @@ static Peer remote_peer_lst[MAX_CONN_NUM]; +/* Silent lookup: returns NULL without logging if conn_id is not in the peer list. + * Use this when the caller legitimately treats a miss as "not a central peer" + * (e.g. disconnect path where peripheral-role conn_ids are intentionally absent). */ +Peer *peer_lookup(uint16_t conn_id) +{ + for (int i = 0; i < MAX_CONN_NUM; i++) { + if (remote_peer_lst[i].conn_id == conn_id) { + return &remote_peer_lst[i]; + } + } + return NULL; +} + void peer_manager_init(void) { for (int i = 0; i < MAX_CONN_NUM; i++) { @@ -20,6 +33,18 @@ void peer_manager_init(void) esp_err_t peer_add(Peer *peer) { + if (peer == NULL) { + return ESP_ERR_INVALID_ARG; + } + Peer *existing = peer_lookup(peer->conn_id); + if (existing != NULL) { + /* Same conn_id already tracked — refresh metadata, do not consume a second slot */ + existing->conn_handle = peer->conn_handle; + existing->gattc_if = peer->gattc_if; + memcpy(&existing->peer_addr, &peer->peer_addr, sizeof(esp_bd_addr_t)); + ESP_LOGW(PEER_MANAGER_TAG, "peer_add: conn_id %u already in list, updated", peer->conn_id); + return ESP_OK; + } for (int i = 0; i < MAX_CONN_NUM; i++) { if (remote_peer_lst[i].conn_id == 0xFFFF) { remote_peer_lst[i].char_handle = 0xFFFF; @@ -51,14 +76,11 @@ esp_err_t peer_remove(uint16_t conn_id) Peer *find_peer(uint16_t conn_id) { - for (int i = 0; i < MAX_CONN_NUM; i++) { - if (remote_peer_lst[i].conn_id == conn_id) { - return &remote_peer_lst[i]; - } + Peer *p = peer_lookup(conn_id); + if (p == NULL) { + ESP_LOGE(PEER_MANAGER_TAG, "peer not found in list, conn_id %d", conn_id); } - - ESP_LOGE(PEER_MANAGER_TAG, "peer not found in list, conn_id %d", conn_id); - return NULL; + return p; } void traverse_send_peer(uint16_t len, uint8_t *value) diff --git a/examples/bluetooth/bluedroid/ble/ble_multi_conn/ble_multi_conn_prph/main/ble_multiconn_prph_demo.c b/examples/bluetooth/bluedroid/ble/ble_multi_conn/ble_multi_conn_prph/main/ble_multiconn_prph_demo.c index de2564d9d7a..90a233ff898 100644 --- a/examples/bluetooth/bluedroid/ble/ble_multi_conn/ble_multi_conn_prph/main/ble_multiconn_prph_demo.c +++ b/examples/bluetooth/bluedroid/ble/ble_multi_conn/ble_multi_conn_prph/main/ble_multiconn_prph_demo.c @@ -139,10 +139,14 @@ static void ble_prph_set_new_adv(void) is_advertising = true; esp_ble_gap_addr_create_static(new_rand_addr); #if (BLE50_SUPPORTED == 1) - esp_ble_gap_ext_adv_set_rand_addr(EXT_ADV_HANDLE, new_rand_addr); + esp_err_t err = esp_ble_gap_ext_adv_set_rand_addr(EXT_ADV_HANDLE, new_rand_addr); #else - esp_ble_gap_set_rand_addr(new_rand_addr); + esp_err_t err = esp_ble_gap_set_rand_addr(new_rand_addr); #endif + if (err != ESP_OK) { + is_advertising = false; + ESP_LOGE(DEMO_TAG, "Set random addr failed: %s", esp_err_to_name(err)); + } } } @@ -178,41 +182,67 @@ static void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param case ESP_GAP_BLE_EXT_ADV_SET_RAND_ADDR_COMPLETE_EVT: ESP_LOGI(DEMO_TAG, "Extended adv random address set, status %d, "ESP_BD_ADDR_STR"", param->ext_adv_set_rand_addr.status, ESP_BD_ADDR_HEX(new_rand_addr)); + if (param->ext_adv_set_rand_addr.status != ESP_BT_STATUS_SUCCESS) { + is_advertising = false; + break; + } esp_ble_gap_ext_adv_start(NUM_EXT_ADV_SET, &ext_adv[0]); break; case ESP_GAP_BLE_EXT_ADV_SET_PARAMS_COMPLETE_EVT: ESP_LOGI(DEMO_TAG, "Extended advertising params set, status %d", param->ext_adv_set_params.status); + if (param->ext_adv_set_params.status != ESP_BT_STATUS_SUCCESS) { + break; + } esp_ble_gap_config_ext_adv_data_raw(EXT_ADV_HANDLE, sizeof(adv_data_raw), &adv_data_raw[0]); break; case ESP_GAP_BLE_EXT_ADV_DATA_SET_COMPLETE_EVT: ESP_LOGI(DEMO_TAG, "Extended advertising data set, status %d", param->ext_adv_data_set.status); + if (param->ext_adv_data_set.status != ESP_BT_STATUS_SUCCESS) { + break; + } ble_prph_restart_adv(); break; case ESP_GAP_BLE_EXT_ADV_START_COMPLETE_EVT: ESP_LOGI(DEMO_TAG, "Extended advertising start, status %d", param->ext_adv_start.status); - is_advertising = true; + if (param->ext_adv_start.status == ESP_BT_STATUS_SUCCESS) { + is_advertising = true; + } else { + is_advertising = false; + } break; case ESP_GAP_BLE_ADV_TERMINATED_EVT: ESP_LOGI(DEMO_TAG, "Extended advertising terminated, status = %d", param->adv_terminate.status); if (param->adv_terminate.status == 0x00) { ESP_LOGI(DEMO_TAG, "Advertising successfully ended with a connection being created"); - is_advertising = false; } + /* Any terminate reason means advertising has stopped; clear flag so restart can run */ + is_advertising = false; break; #else case ESP_GAP_BLE_ADV_DATA_RAW_SET_COMPLETE_EVT: ESP_LOGI(DEMO_TAG, "Advertising data set, status %d", param->adv_data_raw_cmpl.status); + if (param->adv_data_raw_cmpl.status != ESP_BT_STATUS_SUCCESS) { + break; + } esp_ble_gap_addr_create_static(new_rand_addr); esp_ble_gap_set_rand_addr(new_rand_addr); break; case ESP_GAP_BLE_SET_STATIC_RAND_ADDR_EVT: ESP_LOGI(DEMO_TAG, "Random address set, status %d, addr "ESP_BD_ADDR_STR"", param->set_rand_addr_cmpl.status, ESP_BD_ADDR_HEX(new_rand_addr)); + if (param->set_rand_addr_cmpl.status != ESP_BT_STATUS_SUCCESS) { + is_advertising = false; + break; + } esp_ble_gap_start_advertising(&legacy_adv_params); break; case ESP_GAP_BLE_ADV_START_COMPLETE_EVT: ESP_LOGI(DEMO_TAG, "Advertising start, status %d", param->adv_start_cmpl.status); - is_advertising = true; + if (param->adv_start_cmpl.status == ESP_BT_STATUS_SUCCESS) { + is_advertising = true; + } else { + is_advertising = false; + } break; case ESP_GAP_BLE_ADV_STOP_COMPLETE_EVT: ESP_LOGI(DEMO_TAG, "Advertising stop, status %d", param->adv_stop_cmpl.status); @@ -248,16 +278,20 @@ static void gatts_profile_event_handler(esp_gatts_cb_event_t event, ESP_LOG_BUFFER_HEX(DEMO_TAG, param->write.value, param->write.len); break; case ESP_GATTS_CONNECT_EVT: + prph_conn_num++; ESP_LOGI(DEMO_TAG, "Connected, conn_id %u, remote "ESP_BD_ADDR_STR", total %u", - param->connect.conn_id, ESP_BD_ADDR_HEX(param->connect.remote_bda), ++prph_conn_num); + param->connect.conn_id, ESP_BD_ADDR_HEX(param->connect.remote_bda), prph_conn_num); is_advertising = false; #if CONFIG_EXAMPLE_RESTART_ADV_AFTER_CONNECTED ble_prph_restart_adv(); #endif break; case ESP_GATTS_DISCONNECT_EVT: + if (prph_conn_num > 0) { + prph_conn_num--; + } ESP_LOGI(DEMO_TAG, "Disconnected, remote "ESP_BD_ADDR_STR", reason 0x%x, total %d", - ESP_BD_ADDR_HEX(param->disconnect.remote_bda), param->disconnect.reason, (prph_conn_num ? --prph_conn_num : prph_conn_num)); + ESP_BD_ADDR_HEX(param->disconnect.remote_bda), param->disconnect.reason, prph_conn_num); /* start advertising again when disconnected */ ble_prph_restart_adv(); break; @@ -274,7 +308,7 @@ static void gatts_profile_event_handler(esp_gatts_cb_event_t event, case ESP_GATTS_CREAT_ATTR_TAB_EVT: { ESP_LOGI(DEMO_TAG, "The number handle = %x", param->add_attr_tab.num_handle); - if (param->create.status == ESP_GATT_OK) { + if (param->add_attr_tab.status == ESP_GATT_OK) { if (param->add_attr_tab.num_handle == HRS_IDX_NB) { memcpy(profile_handle_table, param->add_attr_tab.handles, sizeof(profile_handle_table)); @@ -289,7 +323,7 @@ static void gatts_profile_event_handler(esp_gatts_cb_event_t event, param->add_attr_tab.num_handle, HRS_IDX_NB); } } else { - ESP_LOGE(DEMO_TAG, " Create attribute table failed, error code = %x", param->create.status); + ESP_LOGE(DEMO_TAG, " Create attribute table failed, error code = %x", param->add_attr_tab.status); } break; } diff --git a/examples/bluetooth/bluedroid/ble/ble_spp_client/main/spp_client_demo.c b/examples/bluetooth/bluedroid/ble/ble_spp_client/main/spp_client_demo.c index 42c482374b7..7bb0b9db019 100644 --- a/examples/bluetooth/bluedroid/ble/ble_spp_client/main/spp_client_demo.c +++ b/examples/bluetooth/bluedroid/ble/ble_spp_client/main/spp_client_demo.c @@ -17,6 +17,7 @@ #include "driver/uart.h" #include "esp_bt.h" +#include "esp_err.h" #include "nvs_flash.h" #include "esp_bt_device.h" #include "esp_gap_ble_api.h" @@ -110,6 +111,7 @@ static bool connect = false; static char * notify_value_p = NULL; static int notify_value_offset = 0; static int notify_value_count = 0; +static size_t notify_value_alloc_size = 0; static bool start = false; static uint64_t notify_len = 0; static uint64_t start_time = 0; @@ -125,60 +127,71 @@ static esp_bt_uuid_t spp_service_uuid = { .uuid = {.uuid16 = ESP_GATT_SPP_SERVICE_UUID,}, }; +/* Drop any in-flight notify reassembly buffer/counters. Used both on errors and on disconnect. */ +static void reset_notify_reasm(void) +{ + if (notify_value_p != NULL) { + free(notify_value_p); + notify_value_p = NULL; + } + notify_value_offset = 0; + notify_value_count = 0; + notify_value_alloc_size = 0; +} + static void notify_event_handler(esp_ble_gattc_cb_param_t * p_data) { - uint8_t handle = 0; + uint16_t handle = p_data->notify.handle; - handle = p_data->notify.handle; if (db == NULL) { ESP_LOGE(GATTC_TAG, " %s db is NULL", __func__); return; } if (handle == db[SPP_IDX_SPP_DATA_NTY_VAL].attribute_handle) { - if ((p_data->notify.value[0] == '#') && (p_data->notify.value[1] == '#')) { - if ((++notify_value_count) != p_data->notify.value[3]) { - if(notify_value_p != NULL){ - free(notify_value_p); - } - notify_value_count = 0; - notify_value_p = NULL; - notify_value_offset = 0; - ESP_LOGE(GATTC_TAG,"notify value count is not continuous, %s", __func__); + /* Fragment header is 4 bytes: "##", total_frags, seq. */ + if (p_data->notify.value_len >= 4 && + p_data->notify.value[0] == '#' && p_data->notify.value[1] == '#') { + uint8_t total_frags = p_data->notify.value[2]; + uint8_t seq = p_data->notify.value[3]; + uint16_t payload_len = p_data->notify.value_len - 4; + + if ((++notify_value_count) != seq) { + ESP_LOGE(GATTC_TAG, "notify value count is not continuous, %s", __func__); + reset_notify_reasm(); return; } - if (p_data->notify.value[3] == 1) { - notify_value_p = (char *)malloc(((spp_mtu_size-7)*(p_data->notify.value[2]))*sizeof(char)); + if (seq == 1) { + notify_value_alloc_size = (size_t)(spp_mtu_size - 7) * total_frags; + notify_value_p = (char *)malloc(notify_value_alloc_size); if (notify_value_p == NULL) { ESP_LOGE(GATTC_TAG, "malloc failed, %s L#%d", __func__, __LINE__); - notify_value_count = 0; + reset_notify_reasm(); return; } - memcpy((notify_value_p + notify_value_offset), (p_data->notify.value + 4), (p_data->notify.value_len - 4)); - if (p_data->notify.value[2] == p_data->notify.value[3]) { - uart_write_bytes(UART_NUM_0, (char *)(notify_value_p), (p_data->notify.value_len - 4 + notify_value_offset)); - free(notify_value_p); - notify_value_p = NULL; - notify_value_offset = 0; - return; - } - notify_value_offset += (p_data->notify.value_len - 4); - } else if (p_data->notify.value[3] <= p_data->notify.value[2]) { - memcpy((notify_value_p + notify_value_offset), (p_data->notify.value + 4), (p_data->notify.value_len - 4)); - if (p_data->notify.value[3] == p_data->notify.value[2]) { - uart_write_bytes(UART_NUM_0, (char *)(notify_value_p), (p_data->notify.value_len - 4 + notify_value_offset)); - free(notify_value_p); - notify_value_count = 0; - notify_value_p = NULL; - notify_value_offset = 0; - return; - } - notify_value_offset += (p_data->notify.value_len - 4); + } else if (notify_value_p == NULL) { + ESP_LOGE(GATTC_TAG, "fragment %u without start, %s", seq, __func__); + reset_notify_reasm(); + return; } + /* Bound the write against the actually allocated reasm buffer to defeat + * malicious peers that send total_frags=0 or grow total_frags mid-stream. */ + if ((size_t)notify_value_offset + payload_len > notify_value_alloc_size) { + ESP_LOGE(GATTC_TAG, "fragment payload would overflow reasm buffer, %s", __func__); + reset_notify_reasm(); + return; + } + memcpy(notify_value_p + notify_value_offset, p_data->notify.value + 4, payload_len); + if (seq == total_frags) { + uart_write_bytes(UART_NUM_0, notify_value_p, payload_len + notify_value_offset); + reset_notify_reasm(); + return; + } + notify_value_offset += payload_len; } else { uart_write_bytes(UART_NUM_0, (char *)(p_data->notify.value), p_data->notify.value_len); } - } else if (handle == ((db+SPP_IDX_SPP_STATUS_VAL)->attribute_handle)) { + } else if (handle == db[SPP_IDX_SPP_STATUS_VAL].attribute_handle) { ESP_LOG_BUFFER_CHAR(GATTC_TAG, (char *)p_data->notify.value, p_data->notify.value_len); //TODO:server notify status characteristic } else { @@ -196,13 +209,42 @@ static void free_gattc_srv_db(void) cmd = 0; spp_srv_start_handle = 0; spp_srv_end_handle = 0; - notify_value_p = NULL; - notify_value_offset = 0; - notify_value_count = 0; + reset_notify_reasm(); if (db) { free(db); db = NULL; } + count = SPP_IDX_NB; +} + +/** + * Resolve the CCCD handle for a known notify-capable characteristic value index by looking up the + * adjacent ESP_GATT_UUID_CHAR_CLIENT_CONFIG descriptor. Returns 0 on mismatch. + * + * This is safer than `(db+cmd+1)->attribute_handle` because the descriptor slot is verified. + */ +static uint16_t spp_get_cccd_handle(uint16_t char_val_idx) +{ + size_t cfg_idx; + + if (db == NULL) { + return 0; + } + switch (char_val_idx) { + case SPP_IDX_SPP_DATA_NTY_VAL: cfg_idx = SPP_IDX_SPP_DATA_NTF_CFG; break; + case SPP_IDX_SPP_STATUS_VAL: cfg_idx = SPP_IDX_SPP_STATUS_CFG; break; +#ifdef SUPPORT_HEARTBEAT + case SPP_IDX_SPP_HEARTBEAT_VAL: cfg_idx = SPP_IDX_SPP_HEARTBEAT_CFG; break; +#endif + default: return 0; + } + const esp_gattc_db_elem_t *d = &db[cfg_idx]; + if (d->type != ESP_GATT_DB_DESCRIPTOR || + d->uuid.len != ESP_UUID_LEN_16 || + d->uuid.uuid.uuid16 != ESP_GATT_UUID_CHAR_CLIENT_CONFIG) { + return 0; + } + return d->attribute_handle; } static void esp_gap_cb(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) @@ -246,7 +288,9 @@ static void esp_gap_cb(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *par ESP_BLE_AD_TYPE_NAME_CMPL, &adv_name_len); ESP_LOGI(GATTC_TAG, "Scan result, device "ESP_BD_ADDR_STR", name len %u", ESP_BD_ADDR_HEX(scan_result->scan_rst.bda), adv_name_len); ESP_LOG_BUFFER_CHAR(GATTC_TAG, adv_name, adv_name_len); - if (adv_name != NULL && strncmp((char *)adv_name, device_name, adv_name_len) == 0) { + /* Full-name match (strncmp(adv_name, device_name, adv_name_len) would also accept prefixes). */ + if (adv_name != NULL && adv_name_len == (sizeof(device_name) - 1) && + memcmp(adv_name, device_name, adv_name_len) == 0) { if (connect == false) { connect = true; esp_ble_gap_stop_scanning(); @@ -373,6 +417,11 @@ static void gattc_profile_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_ if (p_data->reg_for_notify.status != ESP_GATT_OK) { break; } + uint16_t descr_handle = spp_get_cccd_handle(cmd); + if (descr_handle == 0) { + ESP_LOGW(GATTC_TAG, "CCCD handle not resolved for char index %u", (unsigned)cmd); + break; + } uint16_t notify_en = 0x01; #ifdef CONFIG_EXAMPLE_SPP_RELIABLE if (cmd == SPP_IDX_SPP_DATA_NTY_VAL) { @@ -382,7 +431,7 @@ static void gattc_profile_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_ esp_ble_gattc_write_char_descr( spp_gattc_if, spp_conn_id, - (db+cmd+1)->attribute_handle, + descr_handle, sizeof(notify_en), (uint8_t *)¬ify_en, ESP_GATT_WRITE_TYPE_RSP, @@ -438,26 +487,35 @@ static void gattc_profile_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_ break; case ESP_GATTC_CFG_MTU_EVT: ESP_LOGI(GATTC_TAG, "MTU exchange, status %d, MTU %d", param->cfg_mtu.status, param->cfg_mtu.mtu); - if(p_data->cfg_mtu.status != ESP_OK){ + if (param->cfg_mtu.status != ESP_GATT_OK) { break; } - spp_mtu_size = p_data->cfg_mtu.mtu; + spp_mtu_size = param->cfg_mtu.mtu; - db = (esp_gattc_db_elem_t *)malloc(count*sizeof(esp_gattc_db_elem_t)); - if(db == NULL){ + if (db != NULL) { + free(db); + db = NULL; + } + count = SPP_IDX_NB; + db = (esp_gattc_db_elem_t *)malloc(count * sizeof(esp_gattc_db_elem_t)); + if (db == NULL) { ESP_LOGE(GATTC_TAG, "Malloc db failed"); break; } - if(esp_ble_gattc_get_db(spp_gattc_if, spp_conn_id, spp_srv_start_handle, spp_srv_end_handle, db, &count) != ESP_GATT_OK){ + if (esp_ble_gattc_get_db(spp_gattc_if, spp_conn_id, spp_srv_start_handle, spp_srv_end_handle, db, &count) != ESP_GATT_OK) { ESP_LOGE(GATTC_TAG, "Get db failed"); + free(db); + db = NULL; break; } - if(count != SPP_IDX_NB){ + if (count != SPP_IDX_NB) { ESP_LOGE(GATTC_TAG, "Get db count != SPP_IDX_NB, count = %d, SPP_IDX_NB = %d", count, SPP_IDX_NB); + free(db); + db = NULL; break; } - for(int i = 0;i < SPP_IDX_NB;i++){ - switch((db+i)->type){ + for (int i = 0; i < SPP_IDX_NB; i++) { + switch ((db + i)->type) { case ESP_GATT_DB_PRIMARY_SERVICE: ESP_LOGI(GATTC_TAG, "PRIMARY_SERVICE, attribute_handle %d, start_handle %d, end_handle %d, properties 0x%x, uuid 0x%04x", (db+i)->attribute_handle, (db+i)->start_handle, (db+i)->end_handle, (db+i)->properties, (db+i)->uuid.uuid.uuid16); @@ -572,11 +630,11 @@ void ble_client_appRegister(void) ESP_LOGE(GATTC_TAG, "set local MTU failed: %s", esp_err_to_name_r(local_mtu_ret, err_msg, sizeof(err_msg))); } - cmd_reg_queue = xQueueCreate(10, sizeof(uint32_t)); + cmd_reg_queue = xQueueCreate(10, sizeof(uint16_t)); xTaskCreate(spp_client_reg_task, "spp_client_reg_task", 2048, NULL, 10, NULL); #ifdef SUPPORT_HEARTBEAT - cmd_heartbeat_queue = xQueueCreate(10, sizeof(uint32_t)); + cmd_heartbeat_queue = xQueueCreate(10, sizeof(uint16_t)); xTaskCreate(spp_heart_beat_task, "spp_heart_beat_task", 2048, NULL, 10, NULL); #endif esp_ble_gattc_app_register(PROFILE_APP_ID); diff --git a/examples/bluetooth/bluedroid/ble/ble_spp_server/main/ble_spp_server_demo.c b/examples/bluetooth/bluedroid/ble/ble_spp_server/main/ble_spp_server_demo.c index 73dcf1d92b1..f2be2cf3317 100644 --- a/examples/bluetooth/bluedroid/ble/ble_spp_server/main/ble_spp_server_demo.c +++ b/examples/bluetooth/bluedroid/ble/ble_spp_server/main/ble_spp_server_demo.c @@ -58,7 +58,7 @@ static const uint8_t spp_adv_data[23] = { 0x0F, ESP_BLE_AD_TYPE_NAME_CMPL, 'E', 'S', 'P', '_', 'S', 'P', 'P', '_', 'S', 'E', 'R','V', 'E', 'R' }; -static uint16_t spp_mtu_size = SPP_GATT_MTU_SIZE; +static uint16_t spp_mtu_size = 23; static uint16_t spp_conn_id = 0xffff; static esp_gatt_if_t spp_gatts_if = 0xff; QueueHandle_t spp_uart_queue = NULL; @@ -107,19 +107,25 @@ typedef struct spp_receive_data_node{ struct spp_receive_data_node * next_node; }spp_receive_data_node_t; -static spp_receive_data_node_t * temp_spp_recv_data_node_p1 = NULL; -static spp_receive_data_node_t * temp_spp_recv_data_node_p2 = NULL; - typedef struct spp_receive_data_buff{ int32_t node_num; int32_t buff_size; - spp_receive_data_node_t * first_node; + spp_receive_data_node_t *first_node; + spp_receive_data_node_t *last_node; }spp_receive_data_buff_t; -static spp_receive_data_buff_t SppRecvDataBuff = { +/* Command queue carries (data, len); we cannot use strlen() on the payload because + * BLE writes are not NUL-terminated. */ +typedef struct { + uint16_t len; + uint8_t *data; +} spp_cmd_queue_msg_t; + +static spp_receive_data_buff_t spp_prep_wr_buff = { .node_num = 0, .buff_size = 0, - .first_node = NULL + .first_node = NULL, + .last_node = NULL, }; static void gatts_profile_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param); @@ -270,67 +276,74 @@ static uint8_t find_char_and_desr_index(uint16_t handle) static bool store_wr_buffer(esp_ble_gatts_cb_param_t *p_data) { - temp_spp_recv_data_node_p1 = (spp_receive_data_node_t *)malloc(sizeof(spp_receive_data_node_t)); - - if(temp_spp_recv_data_node_p1 == NULL){ - ESP_LOGI(GATTS_TABLE_TAG, "malloc error %s %d", __func__, __LINE__); + if (p_data == NULL) { return false; } - temp_spp_recv_data_node_p1->len = p_data->write.len; - temp_spp_recv_data_node_p1->next_node = NULL; - temp_spp_recv_data_node_p1->node_buff = (uint8_t *)malloc(p_data->write.len); - if (temp_spp_recv_data_node_p1->node_buff == NULL) { - ESP_LOGI(GATTS_TABLE_TAG, "malloc error %s %d\n", __func__, __LINE__); - // Security fix: Free the node and return false to prevent memory leak - free(temp_spp_recv_data_node_p1); - temp_spp_recv_data_node_p1 = NULL; + /* Per Bluetooth Core Spec (Vol 3, Part F, 3.4.6.1) the Part Attribute Value + * in ATT_PREPARE_WRITE_REQ may be 0..(ATT_MTU-5) bytes, so a zero-length + * fragment is a valid request that carries no payload. Skip allocating an + * empty node here: malloc(0) is implementation-defined in C and on + * ESP-IDF's multi_heap returns NULL, which would otherwise be reported as + * a (false) allocation failure. */ + if (p_data->write.len == 0) { + return true; + } + + spp_receive_data_node_t *node = (spp_receive_data_node_t *)malloc(sizeof(spp_receive_data_node_t)); + if (node == NULL) { + ESP_LOGE(GATTS_TABLE_TAG, "malloc error %s %d", __func__, __LINE__); return false; } - memcpy(temp_spp_recv_data_node_p1->node_buff, p_data->write.value, p_data->write.len); - // Security fix: Link to list only after successful allocation - if(temp_spp_recv_data_node_p2 != NULL){ - temp_spp_recv_data_node_p2->next_node = temp_spp_recv_data_node_p1; + node->len = p_data->write.len; + node->next_node = NULL; + node->node_buff = (uint8_t *)malloc(p_data->write.len); + if (node->node_buff == NULL) { + ESP_LOGE(GATTS_TABLE_TAG, "malloc error %s %d", __func__, __LINE__); + free(node); + return false; } - temp_spp_recv_data_node_p2 = temp_spp_recv_data_node_p1; - SppRecvDataBuff.buff_size += p_data->write.len; + memcpy(node->node_buff, p_data->write.value, p_data->write.len); - if(SppRecvDataBuff.node_num == 0){ - SppRecvDataBuff.first_node = temp_spp_recv_data_node_p1; - SppRecvDataBuff.node_num++; - }else{ - SppRecvDataBuff.node_num++; + if (spp_prep_wr_buff.last_node != NULL) { + spp_prep_wr_buff.last_node->next_node = node; + } else { + spp_prep_wr_buff.first_node = node; } + spp_prep_wr_buff.last_node = node; + spp_prep_wr_buff.buff_size += p_data->write.len; + spp_prep_wr_buff.node_num++; return true; } -static void free_write_buffer(void) +static void free_prep_wr_buffer(void) { - temp_spp_recv_data_node_p1 = SppRecvDataBuff.first_node; + spp_receive_data_node_t *cur = spp_prep_wr_buff.first_node; - while(temp_spp_recv_data_node_p1 != NULL){ - temp_spp_recv_data_node_p2 = temp_spp_recv_data_node_p1->next_node; - if (temp_spp_recv_data_node_p1->node_buff) { - free(temp_spp_recv_data_node_p1->node_buff); + while (cur != NULL) { + spp_receive_data_node_t *next = cur->next_node; + if (cur->node_buff) { + free(cur->node_buff); } - free(temp_spp_recv_data_node_p1); - temp_spp_recv_data_node_p1 = temp_spp_recv_data_node_p2; + free(cur); + cur = next; } - SppRecvDataBuff.node_num = 0; - SppRecvDataBuff.buff_size = 0; - SppRecvDataBuff.first_node = NULL; + spp_prep_wr_buff.node_num = 0; + spp_prep_wr_buff.buff_size = 0; + spp_prep_wr_buff.first_node = NULL; + spp_prep_wr_buff.last_node = NULL; } -static void print_write_buffer(void) +static void print_prep_wr_buffer(void) { - temp_spp_recv_data_node_p1 = SppRecvDataBuff.first_node; + spp_receive_data_node_t *cur = spp_prep_wr_buff.first_node; - while (temp_spp_recv_data_node_p1 != NULL) { - uart_write_bytes(UART_NUM_0, (char *)(temp_spp_recv_data_node_p1->node_buff), temp_spp_recv_data_node_p1->len); - temp_spp_recv_data_node_p1 = temp_spp_recv_data_node_p1->next_node; + while (cur != NULL) { + uart_write_bytes(UART_NUM_0, (char *)(cur->node_buff), cur->len); + cur = cur->next_node; } } @@ -448,22 +461,26 @@ static void spp_uart_init(void) #ifdef SUPPORT_HEARTBEAT void spp_heartbeat_task(void * arg) { - uint16_t cmd_id; + uint32_t cmd_id; for(;;) { vTaskDelay(50 / portTICK_PERIOD_MS); if(xQueueReceive(cmd_heartbeat_queue, &cmd_id, portMAX_DELAY)) { - while(1){ - heartbeat_count_num++; - vTaskDelay(5000/ portTICK_PERIOD_MS); - if((heartbeat_count_num >3)&&(is_connected)){ - esp_ble_gap_disconnect(spp_remote_bda); - } - if(is_connected && enable_heart_ntf){ - esp_ble_gatts_send_indicate(spp_gatts_if, spp_conn_id, spp_handle_table[SPP_IDX_SPP_HEARTBEAT_VAL],sizeof(heartbeat_s), heartbeat_s, false); - }else if(!is_connected){ + heartbeat_count_num = 0; + while (1) { + if (!is_connected) { break; } + vTaskDelay(5000 / portTICK_PERIOD_MS); + heartbeat_count_num++; + if ((heartbeat_count_num > 3) && is_connected) { + esp_ble_gap_disconnect(spp_remote_bda); + break; + } + if (is_connected && enable_heart_ntf) { + esp_ble_gatts_send_indicate(spp_gatts_if, spp_conn_id, spp_handle_table[SPP_IDX_SPP_HEARTBEAT_VAL], + sizeof(heartbeat_s), heartbeat_s, false); + } } } } @@ -473,13 +490,15 @@ void spp_heartbeat_task(void * arg) void spp_cmd_task(void * arg) { - uint8_t * cmd_id; + spp_cmd_queue_msg_t msg; for (;;) { vTaskDelay(50 / portTICK_PERIOD_MS); - if(xQueueReceive(cmd_cmd_queue, &cmd_id, portMAX_DELAY)) { - ESP_LOG_BUFFER_CHAR(GATTS_TABLE_TAG, (char *)(cmd_id), strlen((char *)cmd_id)); - free(cmd_id); + if (xQueueReceive(cmd_cmd_queue, &msg, portMAX_DELAY)) { + if (msg.data != NULL) { + ESP_LOG_BUFFER_CHAR(GATTS_TABLE_TAG, (char *)msg.data, msg.len); + free(msg.data); + } } } vTaskDelete(NULL); @@ -498,7 +517,7 @@ static void spp_task_init(void) xTaskCreate(spp_heartbeat_task, "spp_heartbeat_task", 2048, NULL, 10, NULL); #endif - cmd_cmd_queue = xQueueCreate(10, sizeof(uint32_t)); + cmd_cmd_queue = xQueueCreate(10, sizeof(spp_cmd_queue_msg_t)); xTaskCreate(spp_cmd_task, "spp_cmd_task", 4096, NULL, 10, NULL); } @@ -517,7 +536,7 @@ static void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param ESP_LOGI(GATTS_TABLE_TAG, "Advertising start successfully"); break; case ESP_GAP_BLE_ADV_STOP_COMPLETE_EVT: - if (param->adv_start_cmpl.status != ESP_BT_STATUS_SUCCESS) { + if (param->adv_stop_cmpl.status != ESP_BT_STATUS_SUCCESS) { ESP_LOGE(GATTS_TABLE_TAG, "Advertising stop failed, status %d", param->adv_stop_cmpl.status); break; } @@ -555,15 +574,17 @@ static void gatts_profile_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_ res = find_char_and_desr_index(p_data->write.handle); if (p_data->write.is_prep == false) { if (res == SPP_IDX_SPP_COMMAND_VAL) { - uint8_t * spp_cmd_buff = NULL; - spp_cmd_buff = (uint8_t *)malloc((spp_mtu_size - 3) * sizeof(uint8_t)); - if(spp_cmd_buff == NULL){ + uint8_t *spp_cmd_buff = (uint8_t *)malloc(p_data->write.len); + if (spp_cmd_buff == NULL) { ESP_LOGE(GATTS_TABLE_TAG, "%s malloc failed", __func__); break; } - memset(spp_cmd_buff, 0x0, (spp_mtu_size - 3)); memcpy(spp_cmd_buff, p_data->write.value, p_data->write.len); - xQueueSend(cmd_cmd_queue, &spp_cmd_buff, 10/portTICK_PERIOD_MS); + spp_cmd_queue_msg_t msg = { .len = p_data->write.len, .data = spp_cmd_buff }; + if (xQueueSend(cmd_cmd_queue, &msg, 10 / portTICK_PERIOD_MS) != pdTRUE) { + ESP_LOGE(GATTS_TABLE_TAG, "%s cmd_cmd_queue send failed", __func__); + free(spp_cmd_buff); + } } else if (res == SPP_IDX_SPP_DATA_NTF_CFG) { if ((p_data->write.len == 2) && (p_data->write.value[0] == 0x01) && (p_data->write.value[1] == 0x00)) { ESP_LOGI(GATTS_TABLE_TAG, "SPP data notification enable"); @@ -587,9 +608,11 @@ static void gatts_profile_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_ if ((p_data->write.len == 2) && (p_data->write.value[0] == 0x01) && (p_data->write.value[1] == 0x00)) { ESP_LOGI(GATTS_TABLE_TAG, "SPP heartbeat notification enable"); enable_heart_ntf = true; + heartbeat_count_num = 0; } else if ((p_data->write.len == 2) && (p_data->write.value[0] == 0x00) && (p_data->write.value[1] == 0x00)) { ESP_LOGI(GATTS_TABLE_TAG, "SPP heartbeat notification disable"); enable_heart_ntf = false; + heartbeat_count_num = 0; } } else if (res == SPP_IDX_SPP_HEARTBEAT_VAL) { if ((p_data->write.len == sizeof(heartbeat_s)) && (memcmp(heartbeat_s, p_data->write.value, sizeof(heartbeat_s)) == 0)) { @@ -611,14 +634,15 @@ static void gatts_profile_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_ } break; } - case ESP_GATTS_EXEC_WRITE_EVT: { - ESP_LOGI(GATTS_TABLE_TAG, "Execute write"); - if (p_data->exec_write.exec_write_flag) { - print_write_buffer(); - free_write_buffer(); - } - break; - } + case ESP_GATTS_EXEC_WRITE_EVT: + /* End of prepared-write transaction: print on EXEC, then always release queued chunks + * (master only freed on EXEC, which leaked on CANCEL). */ + ESP_LOGI(GATTS_TABLE_TAG, "Execute write flag 0x%02x", p_data->exec_write.exec_write_flag); + if (p_data->exec_write.exec_write_flag == ESP_GATT_PREP_WRITE_EXEC) { + print_prep_wr_buffer(); + } + free_prep_wr_buffer(); + break; case ESP_GATTS_RESPONSE_EVT: break; case ESP_GATTS_MTU_EVT: @@ -643,18 +667,20 @@ static void gatts_profile_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_ case ESP_GATTS_CONNECT_EVT: ESP_LOGI(GATTS_TABLE_TAG, "Connected, conn_id %u, remote "ESP_BD_ADDR_STR"", param->connect.conn_id, ESP_BD_ADDR_HEX(param->connect.remote_bda)); + free_prep_wr_buffer(); spp_conn_id = p_data->connect.conn_id; spp_gatts_if = gatts_if; is_connected = true; memcpy(&spp_remote_bda,&p_data->connect.remote_bda,sizeof(esp_bd_addr_t)); #ifdef SUPPORT_HEARTBEAT - uint16_t cmd = 0; - xQueueSend(cmd_heartbeat_queue,&cmd,10/portTICK_PERIOD_MS); + uint32_t cmd = 0; + xQueueSend(cmd_heartbeat_queue, &cmd, 10 / portTICK_PERIOD_MS); #endif break; case ESP_GATTS_DISCONNECT_EVT: ESP_LOGI(GATTS_TABLE_TAG, "Disconnected, remote "ESP_BD_ADDR_STR", reason 0x%02x", ESP_BD_ADDR_HEX(param->disconnect.remote_bda), param->disconnect.reason); + free_prep_wr_buffer(); spp_mtu_size = 23; is_connected = false; enable_data_ntf = false; @@ -669,6 +695,7 @@ static void gatts_profile_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_ case ESP_GATTS_CANCEL_OPEN_EVT: break; case ESP_GATTS_CLOSE_EVT: + free_prep_wr_buffer(); break; case ESP_GATTS_LISTEN_EVT: break; diff --git a/examples/bluetooth/bluedroid/ble/ble_throughput/throughput_client/main/example_ble_client_throughput.c b/examples/bluetooth/bluedroid/ble/ble_throughput/throughput_client/main/example_ble_client_throughput.c index 88707b1f650..4d07abb113b 100644 --- a/examples/bluetooth/bluedroid/ble/ble_throughput/throughput_client/main/example_ble_client_throughput.c +++ b/examples/bluetooth/bluedroid/ble/ble_throughput/throughput_client/main/example_ble_client_throughput.c @@ -125,6 +125,9 @@ static uint8_t check_sum(uint8_t *addr, uint16_t count) if (addr == NULL || count == 0) { return 0; } + if (count > (ESP_GATT_MAX_MTU_SIZE - 3U)) { + return 0; + } for(int i = 0; i < count; i++) { sum = sum + addr[i]; @@ -287,6 +290,7 @@ static void gattc_profile_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_ /* free descr_elem_result */ free(descr_elem_result); + descr_elem_result = NULL; } } else{ @@ -298,10 +302,23 @@ static void gattc_profile_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_ } case ESP_GATTC_NOTIFY_EVT: { #if (CONFIG_EXAMPLE_GATTS_NOTIFY_THROUGHPUT) - if (p_data->notify.is_notify && - (p_data->notify.value[p_data->notify.value_len - 1] == - check_sum(p_data->notify.value, p_data->notify.value_len - 1))){ - notify_len += p_data->notify.value_len; + if (p_data->notify.is_notify) { + uint16_t vlen = p_data->notify.value_len; + uint8_t *val = p_data->notify.value; + const uint16_t max_notify_len = (uint16_t)(ESP_GATT_MAX_MTU_SIZE - 3U); + if (val == NULL) { + ESP_LOGW(GATTC_TAG, "notify ignored: null value"); + } else if (vlen == 0) { + ESP_LOGW(GATTC_TAG, "notify ignored: zero length"); + } else if (vlen < 2) { + ESP_LOGW(GATTC_TAG, "notify ignored: length too short for payload+checksum"); + } else if (vlen > max_notify_len) { + ESP_LOGW(GATTC_TAG, "notify ignored: length exceeds bound"); + } else if (val[vlen - 1] == check_sum(val, (uint16_t)(vlen - 1U))) { + notify_len += vlen; + } else { + ESP_LOGE(GATTC_TAG, "notify checksum mismatch"); + } } else { ESP_LOGE(GATTC_TAG, "Indication received, value:"); } @@ -346,6 +363,11 @@ static void gattc_profile_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_ current_time = 0; notify_len = 0; #endif /* #if (CONFIG_EXAMPLE_GATTS_NOTIFY_THROUGHPUT) */ +#if (CONFIG_EXAMPLE_GATTC_WRITE_THROUGHPUT) + /* Unblock throughput_client_task if it is waiting on gattc_semaphore while congested. */ + can_send_write = true; + xSemaphoreGive(gattc_semaphore); +#endif /* #if (CONFIG_EXAMPLE_GATTC_WRITE_THROUGHPUT) */ ESP_LOGI(GATTC_TAG, "Disconnected, remote "ESP_BD_ADDR_STR", reason 0x%02x", ESP_BD_ADDR_HEX(p_data->disconnect.remote_bda), p_data->disconnect.reason); break; @@ -404,7 +426,7 @@ static void esp_gap_cb(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *par esp_ble_conn_params_t phy_1m_conn_params = {0}; #if(CONFIG_EXAMPLE_GATTC_WRITE_THROUGHPUT && CONFIG_EXAMPLE_GATTS_NOTIFY_THROUGHPUT) - phy_1m_conn_params.interval_max = 34; + phy_1m_conn_params.interval_min = 34; phy_1m_conn_params.interval_max = 34; #else phy_1m_conn_params.interval_max = 32; @@ -539,9 +561,14 @@ static void throughput_cal_task(void *param) uint32_t bit_rate = 0; if (start_time) { current_time = esp_timer_get_time(); - bit_rate = notify_len * SECOND_TO_USECOND / (current_time - start_time); - ESP_LOGI(GATTC_TAG, "Notify Bit rate = %" PRIu32 " Byte/s, = %" PRIu32 " bit/s, time = %ds", - bit_rate, bit_rate<<3, (int)((current_time - start_time) / SECOND_TO_USECOND)); + uint64_t elapsed_us = current_time - start_time; + if (elapsed_us > 0) { + bit_rate = (uint32_t)(notify_len * SECOND_TO_USECOND / elapsed_us); + ESP_LOGI(GATTC_TAG, "Notify Bit rate = %" PRIu32 " Byte/s, = %" PRIu32 " bit/s, time = %ds", + bit_rate, bit_rate << 3, (int)(elapsed_us / SECOND_TO_USECOND)); + } else { + ESP_LOGI(GATTC_TAG, "Notify Bit rate = 0 Byte/s, = 0 bit/s (elapsed 0 us)"); + } } else { ESP_LOGI(GATTC_TAG, "Notify Bit rate = 0 Byte/s, = 0 bit/s"); } @@ -599,6 +626,17 @@ void app_main(void) return; } +#if (CONFIG_EXAMPLE_GATTC_WRITE_THROUGHPUT) + /* Create the semaphore before registering the GATTC callback so that any + * xSemaphoreGive() invoked from the callback is guaranteed to see a valid handle. + */ + gattc_semaphore = xSemaphoreCreateBinary(); + if (!gattc_semaphore) { + ESP_LOGE(GATTC_TAG, "%s, init fail, the gattc semaphore create fail.", __func__); + return; + } +#endif /* #if (CONFIG_EXAMPLE_GATTC_WRITE_THROUGHPUT) */ + //register the callback function to the gattc module ret = esp_ble_gattc_register_callback(esp_gattc_cb); if(ret){ @@ -624,12 +662,4 @@ void app_main(void) #if (CONFIG_EXAMPLE_GATTS_NOTIFY_THROUGHPUT) xTaskCreatePinnedToCore(&throughput_cal_task, "throughput_cal_task", 4096, NULL, 9, NULL, BLUETOOTH_TASK_PINNED_TO_CORE); #endif - -#if (CONFIG_EXAMPLE_GATTC_WRITE_THROUGHPUT) - gattc_semaphore = xSemaphoreCreateBinary(); - if (!gattc_semaphore) { - ESP_LOGE(GATTC_TAG, "%s, init fail, the gattc semaphore create fail.", __func__); - return; - } -#endif /* #if (CONFIG_EXAMPLE_GATTC_WRITE_THROUGHPUT) */ } diff --git a/examples/bluetooth/bluedroid/ble/ble_throughput/throughput_server/main/example_ble_server_throughput.c b/examples/bluetooth/bluedroid/ble/ble_throughput/throughput_server/main/example_ble_server_throughput.c index 74720b7ebe5..d678acd345a 100644 --- a/examples/bluetooth/bluedroid/ble/ble_throughput/throughput_server/main/example_ble_server_throughput.c +++ b/examples/bluetooth/bluedroid/ble/ble_throughput/throughput_server/main/example_ble_server_throughput.c @@ -58,6 +58,7 @@ static bool start = false; static uint64_t write_len = 0; static uint64_t start_time = 0; static uint64_t current_time = 0; +static portMUX_TYPE s_write_throughput_stats_mux = portMUX_INITIALIZER_UNLOCKED; #endif /* #if (CONFIG_EXAMPLE_GATTC_WRITE_THROUGHPUT) */ static bool is_connect = false; @@ -203,6 +204,18 @@ static prepare_type_env_t a_prepare_write_env; void example_write_event_env(esp_gatt_if_t gatts_if, prepare_type_env_t *prepare_write_env, esp_ble_gatts_cb_param_t *param); void example_exec_write_event_env(prepare_type_env_t *prepare_write_env, esp_ble_gatts_cb_param_t *param); +static void prepare_write_env_clear(prepare_type_env_t *env) +{ + if (env == NULL) { + return; + } + if (env->prepare_buf != NULL) { + free(env->prepare_buf); + env->prepare_buf = NULL; + } + env->prepare_len = 0; +} + static uint8_t check_sum(uint8_t *addr, uint16_t count) { uint32_t sum = 0; @@ -210,6 +223,9 @@ static uint8_t check_sum(uint8_t *addr, uint16_t count) if (addr == NULL || count == 0) { return 0; } + if (count > (ESP_GATT_MAX_MTU_SIZE - 3U)) { + return 0; + } for(int i = 0; i < count; i++) { sum = sum + addr[i]; @@ -333,14 +349,14 @@ void example_write_event_env(esp_gatt_if_t gatts_if, prepare_type_env_t *prepare } void example_exec_write_event_env(prepare_type_env_t *prepare_write_env, esp_ble_gatts_cb_param_t *param){ + if (prepare_write_env == NULL) { + ESP_LOGE(GATTS_TAG, "exec_write: prepare_write_env is NULL"); + return; + } if (param->exec_write.exec_write_flag != ESP_GATT_PREP_WRITE_EXEC){ ESP_LOGI(GATTS_TAG,"Prepare write cancel"); } - if (prepare_write_env->prepare_buf) { - free(prepare_write_env->prepare_buf); - prepare_write_env->prepare_buf = NULL; - } - prepare_write_env->prepare_len = 0; + prepare_write_env_clear(prepare_write_env); } static void gatts_profile_a_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param) { @@ -441,17 +457,32 @@ static void gatts_profile_a_event_handler(esp_gatts_cb_event_t event, esp_gatt_i example_write_event_env(gatts_if, &a_prepare_write_env, param); #if (CONFIG_EXAMPLE_GATTC_WRITE_THROUGHPUT) if (param->write.handle == gl_profile_tab[PROFILE_A_APP_ID].char_handle) { - // The last value byte is the checksum data, should used to check the data is received corrected or not. - if (param->write.value[param->write.len - 1] == - check_sum(param->write.value, param->write.len - 1)) { - write_len += param->write.len; + uint16_t wlen = param->write.len; + uint8_t *wval = param->write.value; + /* len==0 makes (wlen-1) wrap; cap len before indexing or passing to check_sum. */ + const uint16_t max_write_len = (uint16_t)(ESP_GATT_MAX_MTU_SIZE - 3U); + if (wval == NULL) { + ESP_LOGW(GATTS_TAG, "write ignored: null value"); + } else if (wlen == 0) { + ESP_LOGW(GATTS_TAG, "write ignored: zero length"); + } else if (wlen < 2) { + ESP_LOGW(GATTS_TAG, "write ignored: length too short for payload+checksum"); + } else if (wlen > max_write_len) { + ESP_LOGW(GATTS_TAG, "write ignored: length exceeds bound"); + } else if (wval[wlen - 1] == check_sum(wval, (uint16_t)(wlen - 1U))) { + portENTER_CRITICAL(&s_write_throughput_stats_mux); + write_len += wlen; + portEXIT_CRITICAL(&s_write_throughput_stats_mux); + } else { + ESP_LOGE(GATTS_TAG, "write checksum mismatch"); } + portENTER_CRITICAL(&s_write_throughput_stats_mux); if (start == false) { start_time = esp_timer_get_time(); start = true; - break; } + portEXIT_CRITICAL(&s_write_throughput_stats_mux); } #endif /* #if (CONFIG_EXAMPLE_GATTC_WRITE_THROUGHPUT) */ @@ -461,11 +492,13 @@ static void gatts_profile_a_event_handler(esp_gatts_cb_event_t event, esp_gatt_i ESP_LOGI(GATTS_TAG,"Execute write"); #if (CONFIG_EXAMPLE_GATTC_WRITE_THROUGHPUT) if (param->exec_write.exec_write_flag == ESP_GATT_PREP_WRITE_CANCEL) { + portENTER_CRITICAL(&s_write_throughput_stats_mux); if (write_len > a_prepare_write_env.prepare_len) { write_len -= a_prepare_write_env.prepare_len; } else { write_len = 0; } + portEXIT_CRITICAL(&s_write_throughput_stats_mux); } #endif /* #if (CONFIG_EXAMPLE_GATTC_WRITE_THROUGHPUT) */ esp_ble_gatts_send_response(gatts_if, param->write.conn_id, param->write.trans_id, ESP_GATT_OK, NULL); @@ -537,10 +570,34 @@ static void gatts_profile_a_event_handler(esp_gatts_cb_event_t event, esp_gatt_i ESP_LOGI(GATTS_TAG, "Connected, conn_id %d, remote "ESP_BD_ADDR_STR"", param->connect.conn_id, ESP_BD_ADDR_HEX(param->connect.remote_bda)); gl_profile_tab[PROFILE_A_APP_ID].conn_id = param->connect.conn_id; + prepare_write_env_clear(&a_prepare_write_env); +#if (CONFIG_EXAMPLE_GATTC_WRITE_THROUGHPUT) + portENTER_CRITICAL(&s_write_throughput_stats_mux); + write_len = 0; + start_time = 0; + start = false; + current_time = 0; + portEXIT_CRITICAL(&s_write_throughput_stats_mux); +#endif +#if (CONFIG_EXAMPLE_GATTS_NOTIFY_THROUGHPUT) + can_send_notify = false; +#endif break; } case ESP_GATTS_DISCONNECT_EVT: is_connect = false; + prepare_write_env_clear(&a_prepare_write_env); +#if (CONFIG_EXAMPLE_GATTC_WRITE_THROUGHPUT) + portENTER_CRITICAL(&s_write_throughput_stats_mux); + write_len = 0; + start_time = 0; + start = false; + current_time = 0; + portEXIT_CRITICAL(&s_write_throughput_stats_mux); +#endif +#if (CONFIG_EXAMPLE_GATTS_NOTIFY_THROUGHPUT) + can_send_notify = false; +#endif ESP_LOGI(GATTS_TAG, "Disconnected, remote "ESP_BD_ADDR_STR", reason 0x%02x", ESP_BD_ADDR_HEX(param->disconnect.remote_bda), param->disconnect.reason); esp_ble_gap_start_advertising(&adv_params); @@ -636,11 +693,22 @@ void throughput_cal_task(void *param) { uint32_t bit_rate = 0; vTaskDelay(2000 / portTICK_PERIOD_MS); - if (is_connect && start_time) { - current_time = esp_timer_get_time(); - bit_rate = write_len * SECOND_TO_USECOND / (current_time - start_time); - ESP_LOGI(GATTS_TAG, "GATTC write Bit rate = %" PRIu32 " Byte/s, = %" PRIu32 " bit/s, time %d", - bit_rate, bit_rate<<3, (int)((current_time - start_time) / SECOND_TO_USECOND)); + if (is_connect) { + uint64_t snap_write_len; + uint64_t snap_start_time; + portENTER_CRITICAL(&s_write_throughput_stats_mux); + snap_start_time = start_time; + snap_write_len = write_len; + portEXIT_CRITICAL(&s_write_throughput_stats_mux); + if (snap_start_time != 0) { + current_time = esp_timer_get_time(); + uint64_t elapsed_us = current_time - snap_start_time; + if (elapsed_us > 0) { + bit_rate = (uint32_t)(snap_write_len * SECOND_TO_USECOND / elapsed_us); + ESP_LOGI(GATTS_TAG, "GATTC write Bit rate = %" PRIu32 " Byte/s, = %" PRIu32 " bit/s, time %d", + bit_rate, bit_rate << 3, (int)(elapsed_us / SECOND_TO_USECOND)); + } + } } } diff --git a/examples/bluetooth/bluedroid/ble/gatt_client/main/gattc_demo.c b/examples/bluetooth/bluedroid/ble/gatt_client/main/gattc_demo.c index db8d271e315..529c0ecc6a8 100644 --- a/examples/bluetooth/bluedroid/ble/gatt_client/main/gattc_demo.c +++ b/examples/bluetooth/bluedroid/ble/gatt_client/main/gattc_demo.c @@ -201,6 +201,7 @@ static void gattc_profile_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_ } /* free char_elem_result */ free(char_elem_result); + char_elem_result = NULL; }else{ ESP_LOGE(GATTC_TAG, "no char found"); } @@ -259,6 +260,7 @@ static void gattc_profile_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_ /* free descr_elem_result */ free(descr_elem_result); + descr_elem_result = NULL; } } else{ diff --git a/examples/bluetooth/bluedroid/ble/gatt_security_client/main/example_ble_sec_gattc_demo.c b/examples/bluetooth/bluedroid/ble/gatt_security_client/main/example_ble_sec_gattc_demo.c index 38fe1f50ce1..b359ef221b9 100644 --- a/examples/bluetooth/bluedroid/ble/gatt_security_client/main/example_ble_sec_gattc_demo.c +++ b/examples/bluetooth/bluedroid/ble/gatt_security_client/main/example_ble_sec_gattc_demo.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2021-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Unlicense OR CC0-1.0 */ @@ -15,6 +15,7 @@ ****************************************************************************/ #include +#include #include #include #include @@ -177,6 +178,7 @@ static void gattc_profile_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_ case ESP_GATTC_OPEN_EVT: if (param->open.status != ESP_GATT_OK){ ESP_LOGE(GATTC_TAG, "Open failed, status %x", p_data->open.status); + connect = false; break; } ESP_LOGI(GATTC_TAG, "Open successfully, MTU %d", p_data->open.mtu); diff --git a/examples/bluetooth/bluedroid/ble/gatt_security_server/main/example_ble_sec_gatts_demo.c b/examples/bluetooth/bluedroid/ble/gatt_security_server/main/example_ble_sec_gatts_demo.c index 05468b56b79..aa7de73c5d2 100644 --- a/examples/bluetooth/bluedroid/ble/gatt_security_server/main/example_ble_sec_gatts_demo.c +++ b/examples/bluetooth/bluedroid/ble/gatt_security_server/main/example_ble_sec_gatts_demo.c @@ -177,9 +177,9 @@ static const esp_gatts_attr_db_t heart_rate_gatt_db[HRS_IDX_NB] = sizeof(uint8_t), sizeof(heart_ctrl_point), (uint8_t *)heart_ctrl_point}}, }; -static char *esp_key_type_to_str(esp_ble_key_type_t key_type) +static const char *esp_key_type_to_str(esp_ble_key_type_t key_type) { - char *key_str = NULL; + const char *key_str = NULL; switch(key_type) { case ESP_LE_KEY_NONE: key_str = "ESP_LE_KEY_NONE"; @@ -256,12 +256,16 @@ static char *esp_auth_req_to_str(esp_ble_auth_req_t auth_req) static void show_bonded_devices(void) { int dev_num = esp_ble_get_bond_device_num(); + if (dev_num < 0) { + ESP_LOGE(GATTS_TABLE_TAG, "Get bond device num failed (stack may be disabled), ret %d", dev_num); + return; + } if (dev_num == 0) { ESP_LOGI(GATTS_TABLE_TAG, "Bonded devices number zero\n"); return; } - esp_ble_bond_dev_t *dev_list = (esp_ble_bond_dev_t *)malloc(sizeof(esp_ble_bond_dev_t) * dev_num); + esp_ble_bond_dev_t *dev_list = (esp_ble_bond_dev_t *)malloc(sizeof(esp_ble_bond_dev_t) * (size_t)dev_num); if (!dev_list) { ESP_LOGI(GATTS_TABLE_TAG, "malloc failed, return\n"); return; @@ -279,12 +283,16 @@ static void show_bonded_devices(void) static void __attribute__((unused)) remove_all_bonded_devices(void) { int dev_num = esp_ble_get_bond_device_num(); + if (dev_num < 0) { + ESP_LOGE(GATTS_TABLE_TAG, "Get bond device num failed (stack may be disabled), ret %d", dev_num); + return; + } if (dev_num == 0) { ESP_LOGI(GATTS_TABLE_TAG, "Bonded devices number zero\n"); return; } - esp_ble_bond_dev_t *dev_list = (esp_ble_bond_dev_t *)malloc(sizeof(esp_ble_bond_dev_t) * dev_num); + esp_ble_bond_dev_t *dev_list = (esp_ble_bond_dev_t *)malloc(sizeof(esp_ble_bond_dev_t) * (size_t)dev_num); if (!dev_list) { ESP_LOGI(GATTS_TABLE_TAG, "malloc failed, return\n"); return; @@ -460,7 +468,7 @@ static void gatts_profile_event_handler(esp_gatts_cb_event_t event, case ESP_GATTS_CONGEST_EVT: break; case ESP_GATTS_CREAT_ATTR_TAB_EVT: { - if (param->create.status == ESP_GATT_OK){ + if (param->add_attr_tab.status == ESP_GATT_OK){ if(param->add_attr_tab.num_handle == HRS_IDX_NB) { ESP_LOGI(GATTS_TABLE_TAG, "Attribute table create successfully, num_handle %x", param->add_attr_tab.num_handle); memcpy(heart_rate_handle_table, param->add_attr_tab.handles, @@ -471,7 +479,7 @@ static void gatts_profile_event_handler(esp_gatts_cb_event_t event, param->add_attr_tab.num_handle, HRS_IDX_NB); } }else{ - ESP_LOGE(GATTS_TABLE_TAG, "Attribute table create failed, error code = %x", param->create.status); + ESP_LOGE(GATTS_TABLE_TAG, "Attribute table create failed, error code = %x", param->add_attr_tab.status); } break; } diff --git a/examples/bluetooth/bluedroid/ble/gatt_server/main/gatts_demo.c b/examples/bluetooth/bluedroid/ble/gatt_server/main/gatts_demo.c index c9061a8a66f..059b1fedea5 100644 --- a/examples/bluetooth/bluedroid/ble/gatt_server/main/gatts_demo.c +++ b/examples/bluetooth/bluedroid/ble/gatt_server/main/gatts_demo.c @@ -197,6 +197,18 @@ typedef struct { static prepare_type_env_t a_prepare_write_env; static prepare_type_env_t b_prepare_write_env; +static void prepare_write_env_clear(prepare_type_env_t *env) +{ + if (env == NULL) { + return; + } + if (env->prepare_buf != NULL) { + free(env->prepare_buf); + env->prepare_buf = NULL; + } + env->prepare_len = 0; +} + void example_write_event_env(esp_gatt_if_t gatts_if, prepare_type_env_t *prepare_write_env, esp_ble_gatts_cb_param_t *param); void example_exec_write_event_env(prepare_type_env_t *prepare_write_env, esp_ble_gatts_cb_param_t *param); @@ -205,27 +217,43 @@ static void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param switch (event) { #ifdef CONFIG_EXAMPLE_SET_RAW_ADV_DATA case ESP_GAP_BLE_ADV_DATA_RAW_SET_COMPLETE_EVT: + if (param->adv_data_raw_cmpl.status != ESP_BT_STATUS_SUCCESS) { + ESP_LOGE(GATTS_TAG, "Raw adv data set failed, status %d", param->adv_data_raw_cmpl.status); + break; + } adv_config_done &= (~adv_config_flag); - if (adv_config_done==0){ + if (adv_config_done == 0) { esp_ble_gap_start_advertising(&adv_params); } break; case ESP_GAP_BLE_SCAN_RSP_DATA_RAW_SET_COMPLETE_EVT: + if (param->scan_rsp_data_raw_cmpl.status != ESP_BT_STATUS_SUCCESS) { + ESP_LOGE(GATTS_TAG, "Raw scan rsp data set failed, status %d", param->scan_rsp_data_raw_cmpl.status); + break; + } adv_config_done &= (~scan_rsp_config_flag); - if (adv_config_done==0){ + if (adv_config_done == 0) { esp_ble_gap_start_advertising(&adv_params); } break; #else case ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT: + if (param->adv_data_cmpl.status != ESP_BT_STATUS_SUCCESS) { + ESP_LOGE(GATTS_TAG, "Adv data set failed, status %d", param->adv_data_cmpl.status); + break; + } adv_config_done &= (~adv_config_flag); - if (adv_config_done == 0){ + if (adv_config_done == 0) { esp_ble_gap_start_advertising(&adv_params); } break; case ESP_GAP_BLE_SCAN_RSP_DATA_SET_COMPLETE_EVT: + if (param->scan_rsp_data_cmpl.status != ESP_BT_STATUS_SUCCESS) { + ESP_LOGE(GATTS_TAG, "Scan rsp data set failed, status %d", param->scan_rsp_data_cmpl.status); + break; + } adv_config_done &= (~scan_rsp_config_flag); - if (adv_config_done == 0){ + if (adv_config_done == 0) { esp_ble_gap_start_advertising(&adv_params); } break; @@ -239,7 +267,7 @@ static void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param ESP_LOGI(GATTS_TAG, "Advertising start successfully"); break; case ESP_GAP_BLE_ADV_STOP_COMPLETE_EVT: - if (param->adv_start_cmpl.status != ESP_BT_STATUS_SUCCESS) { + if (param->adv_stop_cmpl.status != ESP_BT_STATUS_SUCCESS) { ESP_LOGE(GATTS_TAG, "Advertising stop failed, status %d", param->adv_stop_cmpl.status); break; } @@ -273,7 +301,7 @@ void example_write_event_env(esp_gatt_if_t gatts_if, prepare_type_env_t *prepare status = ESP_GATT_INVALID_ATTR_LEN; } if (status == ESP_GATT_OK && prepare_write_env->prepare_buf == NULL) { - prepare_write_env->prepare_buf = (uint8_t *)malloc(PREPARE_BUF_MAX_SIZE*sizeof(uint8_t)); + prepare_write_env->prepare_buf = (uint8_t *)calloc(PREPARE_BUF_MAX_SIZE, sizeof(uint8_t)); prepare_write_env->prepare_len = 0; if (prepare_write_env->prepare_buf == NULL) { ESP_LOGE(GATTS_TAG, "Gatt_server prep no mem"); @@ -304,7 +332,14 @@ void example_write_event_env(esp_gatt_if_t gatts_if, prepare_type_env_t *prepare memcpy(prepare_write_env->prepare_buf + param->write.offset, param->write.value, param->write.len); - prepare_write_env->prepare_len += param->write.len; + /* Extent is max(end of this fragment), not sum(len): same offset overwrites, not appends. */ + int frag_end = (int)param->write.offset + (int)param->write.len; + if (frag_end > prepare_write_env->prepare_len) { + prepare_write_env->prepare_len = frag_end; + } + if (prepare_write_env->prepare_len > PREPARE_BUF_MAX_SIZE) { + prepare_write_env->prepare_len = PREPARE_BUF_MAX_SIZE; + } }else{ esp_ble_gatts_send_response(gatts_if, param->write.conn_id, param->write.trans_id, status, NULL); @@ -314,15 +349,19 @@ void example_write_event_env(esp_gatt_if_t gatts_if, prepare_type_env_t *prepare void example_exec_write_event_env(prepare_type_env_t *prepare_write_env, esp_ble_gatts_cb_param_t *param){ if (param->exec_write.exec_write_flag == ESP_GATT_PREP_WRITE_EXEC){ - esp_log_buffer_hex(GATTS_TAG, prepare_write_env->prepare_buf, prepare_write_env->prepare_len); + int log_len = prepare_write_env->prepare_len; + if (log_len < 0) { + log_len = 0; + } else if (log_len > PREPARE_BUF_MAX_SIZE) { + log_len = PREPARE_BUF_MAX_SIZE; + } + if (prepare_write_env->prepare_buf != NULL && log_len > 0) { + ESP_LOG_BUFFER_HEX(GATTS_TAG, prepare_write_env->prepare_buf, (size_t)log_len); + } }else{ ESP_LOGI(GATTS_TAG,"Prepare write cancel"); } - if (prepare_write_env->prepare_buf) { - free(prepare_write_env->prepare_buf); - prepare_write_env->prepare_buf = NULL; - } - prepare_write_env->prepare_len = 0; + prepare_write_env_clear(prepare_write_env); } static void gatts_profile_a_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param) { @@ -545,13 +584,14 @@ static void gatts_profile_a_event_handler(esp_gatts_cb_event_t event, esp_gatt_i case ESP_GATTS_DISCONNECT_EVT: ESP_LOGI(GATTS_TAG, "Disconnected, remote "ESP_BD_ADDR_STR", reason 0x%02x", ESP_BD_ADDR_HEX(param->disconnect.remote_bda), param->disconnect.reason); + prepare_write_env_clear(&a_prepare_write_env); esp_ble_gap_start_advertising(&adv_params); local_mtu = 23; // Reset MTU for a single connection break; case ESP_GATTS_CONF_EVT: ESP_LOGI(GATTS_TAG, "Confirm receive, status %d, attr_handle %d", param->conf.status, param->conf.handle); - if (param->conf.status != ESP_GATT_OK){ - esp_log_buffer_hex(GATTS_TAG, param->conf.value, param->conf.len); + if (param->conf.status == ESP_GATT_OK && param->conf.value != NULL && param->conf.len > 0) { + ESP_LOG_BUFFER_HEX(GATTS_TAG, param->conf.value, param->conf.len); } break; case ESP_GATTS_OPEN_EVT: @@ -691,11 +731,13 @@ static void gatts_profile_b_event_handler(esp_gatts_cb_event_t event, esp_gatt_i break; case ESP_GATTS_CONF_EVT: ESP_LOGI(GATTS_TAG, "Confirm receive, status %d, attr_handle %d", param->conf.status, param->conf.handle); - if (param->conf.status != ESP_GATT_OK){ - esp_log_buffer_hex(GATTS_TAG, param->conf.value, param->conf.len); + if (param->conf.status == ESP_GATT_OK && param->conf.value != NULL && param->conf.len > 0) { + ESP_LOG_BUFFER_HEX(GATTS_TAG, param->conf.value, param->conf.len); } - break; + break; case ESP_GATTS_DISCONNECT_EVT: + prepare_write_env_clear(&b_prepare_write_env); + break; case ESP_GATTS_OPEN_EVT: case ESP_GATTS_CANCEL_OPEN_EVT: case ESP_GATTS_CLOSE_EVT: diff --git a/examples/bluetooth/bluedroid/ble/gatt_server_service_table/main/gatts_table_creat_demo.c b/examples/bluetooth/bluedroid/ble/gatt_server_service_table/main/gatts_table_creat_demo.c index f55a351153f..80b9cea3c13 100644 --- a/examples/bluetooth/bluedroid/ble/gatt_server_service_table/main/gatts_table_creat_demo.c +++ b/examples/bluetooth/bluedroid/ble/gatt_server_service_table/main/gatts_table_creat_demo.c @@ -14,6 +14,7 @@ * ****************************************************************************/ +#include #include "freertos/FreeRTOS.h" #include "freertos/task.h" @@ -56,8 +57,21 @@ typedef struct { int prepare_len; } prepare_type_env_t; +/* Single-connection demo: one prepare-write buffer for the lone GATT link. */ static prepare_type_env_t prepare_write_env; +static void prepare_write_env_clear(prepare_type_env_t *env) +{ + if (env == NULL) { + return; + } + if (env->prepare_buf != NULL) { + free(env->prepare_buf); + env->prepare_buf = NULL; + } + env->prepare_len = 0; +} + #define CONFIG_SET_RAW_ADV_DATA #ifdef CONFIG_SET_RAW_ADV_DATA static uint8_t raw_adv_data[] = { @@ -287,7 +301,7 @@ void example_prepare_write_event_env(esp_gatt_if_t gatts_if, prepare_type_env_t status = ESP_GATT_INVALID_ATTR_LEN; } if (status == ESP_GATT_OK && prepare_write_env->prepare_buf == NULL) { - prepare_write_env->prepare_buf = (uint8_t *)malloc(PREPARE_BUF_MAX_SIZE * sizeof(uint8_t)); + prepare_write_env->prepare_buf = (uint8_t *)calloc(PREPARE_BUF_MAX_SIZE, sizeof(uint8_t)); prepare_write_env->prepare_len = 0; if (prepare_write_env->prepare_buf == NULL) { ESP_LOGE(GATTS_TABLE_TAG, "%s, Gatt_server prep no mem", __func__); @@ -320,21 +334,32 @@ void example_prepare_write_event_env(esp_gatt_if_t gatts_if, prepare_type_env_t memcpy(prepare_write_env->prepare_buf + param->write.offset, param->write.value, param->write.len); - prepare_write_env->prepare_len += param->write.len; + /* Extent is max(end of fragment), not sum(len); cap to allocated size. */ + int frag_end = (int)param->write.offset + (int)param->write.len; + if (frag_end > prepare_write_env->prepare_len) { + prepare_write_env->prepare_len = frag_end; + } + if (prepare_write_env->prepare_len > PREPARE_BUF_MAX_SIZE) { + prepare_write_env->prepare_len = PREPARE_BUF_MAX_SIZE; + } } void example_exec_write_event_env(prepare_type_env_t *prepare_write_env, esp_ble_gatts_cb_param_t *param){ if (param->exec_write.exec_write_flag == ESP_GATT_PREP_WRITE_EXEC && prepare_write_env->prepare_buf){ - esp_log_buffer_hex(GATTS_TABLE_TAG, prepare_write_env->prepare_buf, prepare_write_env->prepare_len); + int log_len = prepare_write_env->prepare_len; + if (log_len < 0) { + log_len = 0; + } else if (log_len > PREPARE_BUF_MAX_SIZE) { + log_len = PREPARE_BUF_MAX_SIZE; + } + if (log_len > 0) { + ESP_LOG_BUFFER_HEX(GATTS_TABLE_TAG, prepare_write_env->prepare_buf, (size_t)log_len); + } }else{ ESP_LOGI(GATTS_TABLE_TAG,"ESP_GATT_PREP_WRITE_CANCEL"); } - if (prepare_write_env->prepare_buf) { - free(prepare_write_env->prepare_buf); - prepare_write_env->prepare_buf = NULL; - } - prepare_write_env->prepare_len = 0; + prepare_write_env_clear(prepare_write_env); } static void gatts_profile_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param) @@ -458,6 +483,7 @@ static void gatts_profile_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_ break; case ESP_GATTS_DISCONNECT_EVT: ESP_LOGI(GATTS_TABLE_TAG, "ESP_GATTS_DISCONNECT_EVT, reason = 0x%x", param->disconnect.reason); + prepare_write_env_clear(&prepare_write_env); esp_ble_gap_start_advertising(&adv_params); break; case ESP_GATTS_CREAT_ATTR_TAB_EVT:{ diff --git a/examples/bluetooth/bluedroid/ble/gattc_multi_connect/main/gattc_multi_connect.c b/examples/bluetooth/bluedroid/ble/gattc_multi_connect/main/gattc_multi_connect.c index f777a2382b4..9070bda1e57 100644 --- a/examples/bluetooth/bluedroid/ble/gattc_multi_connect/main/gattc_multi_connect.c +++ b/examples/bluetooth/bluedroid/ble/gattc_multi_connect/main/gattc_multi_connect.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2021-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Unlicense OR CC0-1.0 */ @@ -125,10 +125,11 @@ static struct gattc_profile_inst gl_profile_tab[PROFILE_NUM] = { }; +/* Restarts scanning; does not clear Isconnecting — that flag is tied to enh_open/OPEN_EVT only + * so unrelated events (e.g. another profile disconnect) cannot drop the guard mid–connect attempt. */ static void start_scan(void) { stop_scan_done = false; - Isconnecting = false; uint32_t duration = 30; esp_ble_gap_start_scanning(duration); } @@ -154,7 +155,8 @@ static void gattc_profile_a_event_handler(esp_gattc_cb_event_t event, esp_gatt_i //open failed, ignore the first device, connect the second device ESP_LOGE(GATTC_TAG, "connect device failed, status %d", p_data->open.status); conn_device_a = false; - //start_scan(); + Isconnecting = false; + start_scan(); break; } memcpy(gl_profile_tab[PROFILE_A_APP_ID].remote_bda, p_data->open.remote_bda, 6); @@ -166,6 +168,7 @@ static void gattc_profile_a_event_handler(esp_gattc_cb_event_t event, esp_gatt_i if (mtu_ret){ ESP_LOGE(GATTC_TAG, "config MTU error, error code = %x", mtu_ret); } + Isconnecting = false; break; case ESP_GATTC_CFG_MTU_EVT: if (param->cfg_mtu.status != ESP_GATT_OK){ @@ -267,6 +270,9 @@ static void gattc_profile_a_event_handler(esp_gattc_cb_event_t event, esp_gatt_i &count); if (ret_status != ESP_GATT_OK){ ESP_LOGE(GATTC_TAG, "esp_ble_gattc_get_descr_by_char_handle error"); + free(descr_elem_result_a); + descr_elem_result_a = NULL; + break; } /* Every char has only one descriptor in our 'ESP_GATTS_DEMO' demo, so we used first 'descr_elem_result' */ @@ -286,6 +292,7 @@ static void gattc_profile_a_event_handler(esp_gattc_cb_event_t event, esp_gatt_i /* free descr_elem_result */ free(descr_elem_result_a); + descr_elem_result_a = NULL; } } else{ @@ -327,8 +334,8 @@ static void gattc_profile_a_event_handler(esp_gattc_cb_event_t event, esp_gatt_i case ESP_GATTC_SRVC_CHG_EVT: { esp_bd_addr_t bda; memcpy(bda, p_data->srvc_chg.remote_bda, sizeof(esp_bd_addr_t)); - ESP_LOGI(GATTC_TAG, "ESP_GATTC_SRVC_CHG_EVT, bd_addr:%08x%04x",(bda[0] << 24) + (bda[1] << 16) + (bda[2] << 8) + bda[3], - (bda[4] << 8) + bda[5]); + ESP_LOGI(GATTC_TAG, "ESP_GATTC_SRVC_CHG_EVT, bd_addr:%02x:%02x:%02x:%02x:%02x:%02x", + bda[0], bda[1], bda[2], bda[3], bda[4], bda[5]); break; } case ESP_GATTC_DISCONNECT_EVT: @@ -360,7 +367,8 @@ static void gattc_profile_b_event_handler(esp_gattc_cb_event_t event, esp_gatt_i //open failed, ignore the second device, connect the third device ESP_LOGE(GATTC_TAG, "connect device failed, status %d", p_data->open.status); conn_device_b = false; - //start_scan(); + Isconnecting = false; + start_scan(); break; } memcpy(gl_profile_tab[PROFILE_B_APP_ID].remote_bda, p_data->open.remote_bda, 6); @@ -372,6 +380,7 @@ static void gattc_profile_b_event_handler(esp_gattc_cb_event_t event, esp_gatt_i if (mtu_ret){ ESP_LOGE(GATTC_TAG, "config MTU error, error code = %x", mtu_ret); } + Isconnecting = false; break; case ESP_GATTC_CFG_MTU_EVT: if (param->cfg_mtu.status != ESP_GATT_OK){ @@ -538,8 +547,8 @@ static void gattc_profile_b_event_handler(esp_gattc_cb_event_t event, esp_gatt_i case ESP_GATTC_SRVC_CHG_EVT: { esp_bd_addr_t bda; memcpy(bda, p_data->srvc_chg.remote_bda, sizeof(esp_bd_addr_t)); - ESP_LOGI(GATTC_TAG, "ESP_GATTC_SRVC_CHG_EVT, bd_addr:%08x%04x",(bda[0] << 24) + (bda[1] << 16) + (bda[2] << 8) + bda[3], - (bda[4] << 8) + bda[5]); + ESP_LOGI(GATTC_TAG, "ESP_GATTC_SRVC_CHG_EVT, bd_addr:%02x:%02x:%02x:%02x:%02x:%02x", + bda[0], bda[1], bda[2], bda[3], bda[4], bda[5]); break; } case ESP_GATTC_DISCONNECT_EVT: @@ -568,7 +577,8 @@ static void gattc_profile_c_event_handler(esp_gattc_cb_event_t event, esp_gatt_i if (p_data->open.status != ESP_GATT_OK){ ESP_LOGE(GATTC_TAG, "connect device failed, status %d", p_data->open.status); conn_device_c = false; - //start_scan(); + Isconnecting = false; + start_scan(); break; } memcpy(gl_profile_tab[PROFILE_C_APP_ID].remote_bda, p_data->open.remote_bda, 6); @@ -580,6 +590,7 @@ static void gattc_profile_c_event_handler(esp_gattc_cb_event_t event, esp_gatt_i if (mtu_ret){ ESP_LOGE(GATTC_TAG, "config MTU error, error code = %x", mtu_ret); } + Isconnecting = false; break; case ESP_GATTC_CFG_MTU_EVT: if (param->cfg_mtu.status != ESP_GATT_OK){ @@ -746,8 +757,8 @@ static void gattc_profile_c_event_handler(esp_gattc_cb_event_t event, esp_gatt_i case ESP_GATTC_SRVC_CHG_EVT: { esp_bd_addr_t bda; memcpy(bda, p_data->srvc_chg.remote_bda, sizeof(esp_bd_addr_t)); - ESP_LOGI(GATTC_TAG, "ESP_GATTC_SRVC_CHG_EVT, bd_addr:%08x%04x",(bda[0] << 24) + (bda[1] << 16) + (bda[2] << 8) + bda[3], - (bda[4] << 8) + bda[5]); + ESP_LOGI(GATTC_TAG, "ESP_GATTC_SRVC_CHG_EVT, bd_addr:%02x:%02x:%02x:%02x:%02x:%02x", + bda[0], bda[1], bda[2], bda[3], bda[4], bda[5]); break; } case ESP_GATTC_DISCONNECT_EVT: diff --git a/examples/bluetooth/bluedroid/ble_50/ble50_security_server/main/ble50_sec_gatts_demo.c b/examples/bluetooth/bluedroid/ble_50/ble50_security_server/main/ble50_sec_gatts_demo.c index 2182465edef..3858acec646 100644 --- a/examples/bluetooth/bluedroid/ble_50/ble50_security_server/main/ble50_sec_gatts_demo.c +++ b/examples/bluetooth/bluedroid/ble_50/ble50_security_server/main/ble50_sec_gatts_demo.c @@ -287,7 +287,7 @@ static void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param esp_ble_gap_ext_adv_start(NUM_EXT_ADV_SET, &ext_adv[0]); break; case ESP_GAP_BLE_EXT_ADV_START_COMPLETE_EVT: - ESP_LOGI(GATTS_TABLE_TAG, "Extended advertising start, status %d", param->ext_adv_data_set.status); + ESP_LOGI(GATTS_TABLE_TAG, "Extended advertising start, status %d", param->ext_adv_start.status); break; case ESP_GAP_BLE_ADV_TERMINATED_EVT: ESP_LOGI(GATTS_TABLE_TAG, "Extended advertising terminated, status %d", param->adv_terminate.status); diff --git a/examples/bluetooth/bluedroid/ble_50/ble50_throughput/throughput_client/main/example_ble_client_throughput.c b/examples/bluetooth/bluedroid/ble_50/ble50_throughput/throughput_client/main/example_ble_client_throughput.c index aa8a727a650..ca1fd2c2c2d 100644 --- a/examples/bluetooth/bluedroid/ble_50/ble50_throughput/throughput_client/main/example_ble_client_throughput.c +++ b/examples/bluetooth/bluedroid/ble_50/ble50_throughput/throughput_client/main/example_ble_client_throughput.c @@ -163,6 +163,9 @@ static uint8_t check_sum(uint8_t *addr, uint16_t count) if (addr == NULL || count == 0) { return 0; } + if (count > (ESP_GATT_MAX_MTU_SIZE - 3U)) { + return 0; + } for(int i = 0; i < count; i++) { sum = sum + addr[i]; @@ -175,6 +178,15 @@ static uint8_t check_sum(uint8_t *addr, uint16_t count) return (uint8_t)~sum; } +static void throughput_client_resume_ext_scan(void) +{ + connect = false; + esp_err_t err = esp_ble_gap_start_ext_scan(EXT_SCAN_DURATION, EXT_SCAN_PERIOD); + if (err != ESP_OK) { + ESP_LOGE(GATTC_TAG, "start_ext_scan failed: %s", esp_err_to_name(err)); + } +} + static void gattc_profile_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param) { esp_ble_gattc_cb_param_t *p_data = (esp_ble_gattc_cb_param_t *)param; @@ -202,6 +214,7 @@ static void gattc_profile_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_ case ESP_GATTC_OPEN_EVT: if (param->open.status != ESP_GATT_OK){ ESP_LOGE(GATTC_TAG, "Open failed, status %d", p_data->open.status); + throughput_client_resume_ext_scan(); break; } ESP_LOGI(GATTC_TAG, "Open successfully, MTU %u", param->open.mtu); @@ -293,12 +306,13 @@ static void gattc_profile_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_ &count); if (ret_status != ESP_GATT_OK){ ESP_LOGE(GATTC_TAG, "esp_ble_gattc_get_attr_count error"); - } - if (count > 0){ + } else if (count == 0) { + ESP_LOGE(GATTC_TAG, "decsr not found"); + } else { descr_elem_result = malloc(sizeof(esp_gattc_descr_elem_t) * count); - if (!descr_elem_result){ + if (descr_elem_result == NULL){ ESP_LOGE(GATTC_TAG, "malloc error, gattc no mem"); - }else{ + } else { ret_status = esp_ble_gattc_get_descr_by_char_handle( gattc_if, gl_profile_tab[PROFILE_A_APP_ID].conn_id, p_data->reg_for_notify.handle, @@ -307,10 +321,10 @@ static void gattc_profile_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_ &count); if (ret_status != ESP_GATT_OK){ ESP_LOGE(GATTC_TAG, "esp_ble_gattc_get_descr_by_char_handle error"); - } - - /* Every char has only one descriptor in our 'throughput_server' demo, so we use first 'descr_elem_result' */ - if (count > 0 && descr_elem_result[0].uuid.len == ESP_UUID_LEN_16 && descr_elem_result[0].uuid.uuid.uuid16 == ESP_GATT_UUID_CHAR_CLIENT_CONFIG){ + } else if (count > 0 && + descr_elem_result[0].uuid.len == ESP_UUID_LEN_16 && + descr_elem_result[0].uuid.uuid.uuid16 == ESP_GATT_UUID_CHAR_CLIENT_CONFIG) { + /* Every char has only one descriptor in our 'throughput_server' demo, so we use first 'descr_elem_result' */ ret_status = esp_ble_gattc_write_char_descr( gattc_if, gl_profile_tab[PROFILE_A_APP_ID].conn_id, descr_elem_result[0].handle, @@ -318,29 +332,37 @@ static void gattc_profile_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_ (uint8_t *)¬ify_en, ESP_GATT_WRITE_TYPE_RSP, ESP_GATT_AUTH_REQ_NONE); + if (ret_status != ESP_GATT_OK){ + ESP_LOGE(GATTC_TAG, "esp_ble_gattc_write_char_descr error"); + } } - - if (ret_status != ESP_GATT_OK){ - ESP_LOGE(GATTC_TAG, "esp_ble_gattc_write_char_descr error"); - } - - /* free descr_elem_result */ free(descr_elem_result); + descr_elem_result = NULL; } } - else{ - ESP_LOGE(GATTC_TAG, "decsr not found"); - } - } break; } case ESP_GATTC_NOTIFY_EVT: { #if (CONFIG_GATTS_NOTIFY_THROUGHPUT) - if (p_data->notify.is_notify && - (p_data->notify.value[p_data->notify.value_len - 1] == - check_sum(p_data->notify.value, p_data->notify.value_len - 1))){ - notify_len += p_data->notify.value_len; + if (p_data->notify.is_notify) { + uint16_t vlen = p_data->notify.value_len; + uint8_t *val = p_data->notify.value; + /* value_len == 0 makes (vlen - 1) wrap; never index or pass to check_sum before validating. */ + const uint16_t max_notify_len = (uint16_t)(ESP_GATT_MAX_MTU_SIZE - 3U); + if (val == NULL) { + ESP_LOGW(GATTC_TAG, "notify ignored: null value"); + } else if (vlen == 0) { + ESP_LOGW(GATTC_TAG, "notify ignored: zero length"); + } else if (vlen < 2) { + ESP_LOGW(GATTC_TAG, "notify ignored: length too short for payload+checksum"); + } else if (vlen > max_notify_len) { + ESP_LOGW(GATTC_TAG, "notify ignored: length exceeds bound"); + } else if (val[vlen - 1] == check_sum(val, (uint16_t)(vlen - 1U))) { + notify_len += vlen; + } else { + ESP_LOGE(GATTC_TAG, "notify checksum mismatch"); + } } else { ESP_LOGE(GATTC_TAG, "Indication received, value:"); } @@ -385,8 +407,14 @@ static void gattc_profile_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_ current_time = 0; notify_len = 0; #endif /* #if (CONFIG_GATTS_NOTIFY_THROUGHPUT) */ +#if (CONFIG_GATTC_WRITE_THROUGHPUT) + /* Unblock throughput_client_task if it is waiting on gattc_semaphore while congested. */ + can_send_write = true; + xSemaphoreGive(gattc_semaphore); +#endif /* #if (CONFIG_GATTC_WRITE_THROUGHPUT) */ ESP_LOGI(GATTC_TAG, "Disconnected, remote "ESP_BD_ADDR_STR", reason 0x%02x", ESP_BD_ADDR_HEX(p_data->disconnect.remote_bda), p_data->disconnect.reason); + throughput_client_resume_ext_scan(); break; case ESP_GATTC_CONGEST_EVT: #if (CONFIG_GATTC_WRITE_THROUGHPUT) @@ -517,7 +545,6 @@ static void esp_gattc_cb(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp #if (CONFIG_GATTC_WRITE_THROUGHPUT) static void throughput_client_task(void *param) { - vTaskDelay(2000 / portTICK_PERIOD_MS); uint8_t sum = check_sum(write_data, sizeof(write_data) - 1); write_data[GATTC_WRITE_LEN - 1] = sum; @@ -618,6 +645,17 @@ void app_main(void) return; } +#if (CONFIG_GATTC_WRITE_THROUGHPUT) + /* Create the semaphore before registering the GATTC callback so that any + * xSemaphoreGive() invoked from the callback is guaranteed to see a valid handle. + */ + gattc_semaphore = xSemaphoreCreateBinary(); + if (gattc_semaphore == NULL) { + ESP_LOGE(GATTC_TAG, "%s: gattc semaphore create failed", __func__); + return; + } +#endif /* #if (CONFIG_GATTC_WRITE_THROUGHPUT) */ + //register the callback function to the gattc module ret = esp_ble_gattc_register_callback(esp_gattc_cb); if(ret){ @@ -635,20 +673,11 @@ void app_main(void) ESP_LOGE(GATTC_TAG, "set local MTU failed, error code = %x", local_mtu_ret); } #if (CONFIG_GATTC_WRITE_THROUGHPUT) - // The task is only created on the CPU core that Bluetooth is working on, - // preventing the sending task from using the un-updated Bluetooth state on another CPU. + /* Create the task only after the semaphore exists; never rely on priority or delays. */ xTaskCreatePinnedToCore(&throughput_client_task, "throughput_client_task", 4096, NULL, 10, NULL, BLUETOOTH_TASK_PINNED_TO_CORE); #endif #if (CONFIG_GATTS_NOTIFY_THROUGHPUT) xTaskCreatePinnedToCore(&throughput_cal_task, "throughput_cal_task", 4096, NULL, 9, NULL, BLUETOOTH_TASK_PINNED_TO_CORE); #endif - -#if (CONFIG_GATTC_WRITE_THROUGHPUT) - gattc_semaphore = xSemaphoreCreateBinary(); - if (!gattc_semaphore) { - ESP_LOGE(GATTC_TAG, "%s, init fail, the gattc semaphore create fail.", __func__); - return; - } -#endif /* #if (CONFIG_GATTC_WRITE_THROUGHPUT) */ } diff --git a/examples/bluetooth/bluedroid/ble_50/ble50_throughput/throughput_server/main/example_ble_server_throughput.c b/examples/bluetooth/bluedroid/ble_50/ble50_throughput/throughput_server/main/example_ble_server_throughput.c index 57c540ec116..05f7fd3250b 100644 --- a/examples/bluetooth/bluedroid/ble_50/ble50_throughput/throughput_server/main/example_ble_server_throughput.c +++ b/examples/bluetooth/bluedroid/ble_50/ble50_throughput/throughput_server/main/example_ble_server_throughput.c @@ -164,6 +164,18 @@ typedef struct { static prepare_type_env_t a_prepare_write_env; +static void prepare_write_env_clear(prepare_type_env_t *env) +{ + if (env == NULL) { + return; + } + if (env->prepare_buf != NULL) { + free(env->prepare_buf); + env->prepare_buf = NULL; + } + env->prepare_len = 0; +} + extern void esp_ble_switch_phy_coded(bool phy_500k); void example_write_event_env(esp_gatt_if_t gatts_if, prepare_type_env_t *prepare_write_env, esp_ble_gatts_cb_param_t *param); void example_exec_write_event_env(prepare_type_env_t *prepare_write_env, esp_ble_gatts_cb_param_t *param); @@ -175,6 +187,9 @@ static uint8_t check_sum(uint8_t *addr, uint16_t count) if (addr == NULL || count == 0) { return 0; } + if (count > (ESP_GATT_MAX_MTU_SIZE - 3U)) { + return 0; + } for(int i = 0; i < count; i++) { sum = sum + addr[i]; @@ -228,6 +243,8 @@ void example_write_event_env(esp_gatt_if_t gatts_if, prepare_type_env_t *prepare status = ESP_GATT_INVALID_OFFSET; } else if ((param->write.offset + param->write.len) > PREPARE_BUF_MAX_SIZE) { status = ESP_GATT_INVALID_ATTR_LEN; + } else if (param->write.len > ESP_GATT_MAX_ATTR_LEN) { + status = ESP_GATT_INVALID_ATTR_LEN; } if (status == ESP_GATT_OK && prepare_write_env->prepare_buf == NULL) { @@ -239,13 +256,19 @@ void example_write_event_env(esp_gatt_if_t gatts_if, prepare_type_env_t *prepare } } - esp_gatt_rsp_t *gatt_rsp = (esp_gatt_rsp_t *)malloc(sizeof(esp_gatt_rsp_t)); + esp_gatt_rsp_t *gatt_rsp = (esp_gatt_rsp_t *)calloc(1, sizeof(esp_gatt_rsp_t)); if (gatt_rsp) { - gatt_rsp->attr_value.len = param->write.len; gatt_rsp->attr_value.handle = param->write.handle; gatt_rsp->attr_value.offset = param->write.offset; gatt_rsp->attr_value.auth_req = ESP_GATT_AUTH_REQ_NONE; - memcpy(gatt_rsp->attr_value.value, param->write.value, param->write.len); + if (status == ESP_GATT_OK) { + if (param->write.value == NULL) { + status = ESP_GATT_INVALID_ATTR_LEN; + } else { + gatt_rsp->attr_value.len = param->write.len; + memcpy(gatt_rsp->attr_value.value, param->write.value, param->write.len); + } + } esp_err_t response_err = esp_ble_gatts_send_response(gatts_if, param->write.conn_id, param->write.trans_id, status, gatt_rsp); if (response_err != ESP_OK) { @@ -275,11 +298,7 @@ void example_exec_write_event_env(prepare_type_env_t *prepare_write_env, esp_ble if (param->exec_write.exec_write_flag != ESP_GATT_PREP_WRITE_EXEC){ ESP_LOGI(GATTS_TAG,"Prepare write cancel"); } - if (prepare_write_env->prepare_buf) { - free(prepare_write_env->prepare_buf); - prepare_write_env->prepare_buf = NULL; - } - prepare_write_env->prepare_len = 0; + prepare_write_env_clear(prepare_write_env); } static void gatts_profile_a_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param) { @@ -356,9 +375,22 @@ static void gatts_profile_a_event_handler(esp_gatts_cb_event_t event, esp_gatt_i #if (CONFIG_EXAMPLE_GATTC_WRITE_THROUGHPUT) if (param->write.handle == gl_profile_tab[PROFILE_A_APP_ID].char_handle) { // The last value byte is the checksum data, should used to check the data is received corrected or not. - if (param->write.value[param->write.len - 1] == - check_sum(param->write.value, param->write.len - 1)) { - write_len += param->write.len; + uint16_t wlen = param->write.len; + uint8_t *wval = param->write.value; + /* len==0 makes (wlen-1) wrap; cap len before indexing or passing to check_sum. */ + const uint16_t max_write_len = (uint16_t)(ESP_GATT_MAX_MTU_SIZE - 3U); + if (wval == NULL) { + ESP_LOGW(GATTS_TAG, "write ignored: null value"); + } else if (wlen == 0) { + ESP_LOGW(GATTS_TAG, "write ignored: zero length"); + } else if (wlen < 2) { + ESP_LOGW(GATTS_TAG, "write ignored: length too short for payload+checksum"); + } else if (wlen > max_write_len) { + ESP_LOGW(GATTS_TAG, "write ignored: length exceeds bound"); + } else if (wval[wlen - 1] == check_sum(wval, (uint16_t)(wlen - 1U))) { + write_len += wlen; + } else { + ESP_LOGE(GATTS_TAG, "write checksum mismatch"); } if (start == false) { @@ -451,10 +483,12 @@ static void gatts_profile_a_event_handler(esp_gatts_cb_event_t event, esp_gatt_i ESP_LOGI(GATTS_TAG, "Connected, conn_id %u, remote "ESP_BD_ADDR_STR"", param->connect.conn_id, ESP_BD_ADDR_HEX(param->connect.remote_bda)); gl_profile_tab[PROFILE_A_APP_ID].conn_id = param->connect.conn_id; + prepare_write_env_clear(&a_prepare_write_env); break; } case ESP_GATTS_DISCONNECT_EVT: is_connect = false; + prepare_write_env_clear(&a_prepare_write_env); ESP_LOGI(GATTS_TAG, "Disconnected, remote "ESP_BD_ADDR_STR", reason 0x%x", ESP_BD_ADDR_HEX(param->disconnect.remote_bda), param->disconnect.reason); esp_ble_gap_ext_adv_start(NUM_EXT_ADV_SET, &ext_adv[0]); diff --git a/examples/bluetooth/bluedroid/ble_50/ble_conn_subrating_central/main/main.c b/examples/bluetooth/bluedroid/ble_50/ble_conn_subrating_central/main/main.c index 2eb6989bb99..f1b3f47b3fe 100644 --- a/examples/bluetooth/bluedroid/ble_50/ble_conn_subrating_central/main/main.c +++ b/examples/bluetooth/bluedroid/ble_50/ble_conn_subrating_central/main/main.c @@ -96,6 +96,16 @@ static struct gattc_profile_inst gl_profile_tab[PROFILE_NUM] = { }, }; +/** Clear connect-in-progress flag and restart indefinite extended scan (after failed open or disconnect). */ +static void central_resume_ext_scan(void) +{ + connect = false; + esp_err_t err = esp_ble_gap_start_ext_scan(0, 0); + if (err != ESP_OK) { + ESP_LOGE(TAG, "start ext scan failed, error = 0x%x", err); + } +} + /** * @brief GATT client event handler */ @@ -112,6 +122,10 @@ static void gattc_profile_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_ case ESP_GATTC_REG_EVT: ESP_LOGI(TAG, "GATT client register, status %d, app_id %d, gattc_if %d", p_data->reg.status, p_data->reg.app_id, gattc_if); + if (p_data->reg.status != ESP_GATT_OK) { + ESP_LOGE(TAG, "GATT client register failed, status %d", p_data->reg.status); + break; + } gl_profile_tab[PROFILE_A_APP_ID].gattc_if = gattc_if; // Set default subrate parameters esp_ble_default_subrate_param_t default_subrate_params = { @@ -148,11 +162,19 @@ static void gattc_profile_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_ ESP_LOGI(TAG, "Subrate request sent successfully"); } break; + case ESP_GATTC_OPEN_EVT: + if (p_data->open.status != ESP_GATT_OK) { + ESP_LOGE(TAG, "Open failed, status %d", p_data->open.status); + central_resume_ext_scan(); + break; + } + ESP_LOGI(TAG, "GATT open OK, conn_id %d, MTU %u", p_data->open.conn_id, p_data->open.mtu); + break; case ESP_GATTC_DISCONNECT_EVT: - connect = false; g_conn_handle = 0xFFFF; ESP_LOGI(TAG, "Disconnected, remote "ESP_BD_ADDR_STR", reason 0x%02x", ESP_BD_ADDR_HEX(p_data->disconnect.remote_bda), p_data->disconnect.reason); + central_resume_ext_scan(); break; default: break; @@ -208,10 +230,9 @@ static void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param creat_conn_params.phy_1m_conn_params = &phy_1m_conn_params; creat_conn_params.phy_2m_conn_params = &phy_2m_conn_params; creat_conn_params.phy_coded_conn_params = &phy_coded_conn_params; - if (esp_ble_gattc_enh_open(gl_profile_tab[PROFILE_A_APP_ID].gattc_if, &creat_conn_params) != ESP_OK) - { - connect = false; + if (esp_ble_gattc_enh_open(gl_profile_tab[PROFILE_A_APP_ID].gattc_if, &creat_conn_params) != ESP_OK) { ESP_LOGE(TAG, "Failed to open connection"); + central_resume_ext_scan(); } } } diff --git a/examples/bluetooth/bluedroid/ble_50/ble_connection_central_with_cte/main/connection_central_with_cte.c b/examples/bluetooth/bluedroid/ble_50/ble_connection_central_with_cte/main/connection_central_with_cte.c index a3346407f04..627a55c1edf 100644 --- a/examples/bluetooth/bluedroid/ble_50/ble_connection_central_with_cte/main/connection_central_with_cte.c +++ b/examples/bluetooth/bluedroid/ble_50/ble_connection_central_with_cte/main/connection_central_with_cte.c @@ -37,6 +37,7 @@ #define REMOTE_NOTIFY_UUID 0xFF01 #define EXT_SCAN_DURATION 0 #define EXT_SCAN_PERIOD 0 +#define BLE_CONN_HDL_INVALID ((uint16_t)0xFFFF) ///Declare static functions static void esp_gap_cb(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param); @@ -95,7 +96,7 @@ const esp_ble_conn_params_t phy_coded_conn_params = { static uint8_t antenna_ids[2] = {0x00, 0x01}; esp_ble_cte_recv_params_params_t cte_recv_params = { - .conn_handle = 0xff, + .conn_handle = BLE_CONN_HDL_INVALID, .sampling_en = ESP_BLE_CTE_SAMPLING_ENABLE, .slot_dur = ESP_BLE_CTE_SLOT_DURATION_2US, .switching_pattern_len = sizeof(antenna_ids), @@ -103,14 +104,14 @@ esp_ble_cte_recv_params_params_t cte_recv_params = { }; static esp_ble_cte_req_en_params_t cte_conn_req_en = { - .conn_handle = 0xff, + .conn_handle = BLE_CONN_HDL_INVALID, .enable = ESP_BLE_CTE_SAMPLING_ENABLE, .cte_req_interval = 0x05, .req_cte_len = ESP_BLE_CTE_MAX_REQUESTED_CTE_LENGTH, .req_cte_Type = ESP_BLE_CTE_TYPE_AOA, }; -uint16_t cur_conn_hdl = 0xff; +uint16_t cur_conn_hdl = BLE_CONN_HDL_INVALID; #define PROFILE_NUM 1 #define PROFILE_A_APP_ID 0 @@ -210,6 +211,31 @@ static char *esp_auth_req_to_str(esp_ble_auth_req_t auth_req) return auth_str; } +/** After a failed open or disconnect: clear connect flag and restart extended scan. */ +static void cte_resume_ext_scan(void) +{ + connect = false; + esp_err_t err = esp_ble_gap_start_ext_scan(EXT_SCAN_DURATION, EXT_SCAN_PERIOD); + if (err != ESP_OK) { + ESP_LOGE(LOG_TAG, "start ext scan failed, error = 0x%x", err); + } +} + +/** Start CTE receive setup only after link is authenticated (see esp_gap_cb AUTH_CMPL). */ +static void cte_enable_connection_receive_after_encrypted(void) +{ + if (cur_conn_hdl == BLE_CONN_HDL_INVALID) { + ESP_LOGW(LOG_TAG, "Skip CTE receive params: no connection handle"); + return; + } + ESP_LOGI(LOG_TAG, "Set CTE connection receive params after encryption, conn_handle %d", cur_conn_hdl); + cte_recv_params.conn_handle = cur_conn_hdl; + esp_err_t cte_ret = esp_ble_cte_set_connection_receive_params(&cte_recv_params); + if (cte_ret != ESP_OK) { + ESP_LOGE(LOG_TAG, "CTE set connection receive params failed, 0x%x", cte_ret); + } +} + static void gattc_profile_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param) { esp_ble_gattc_cb_param_t *p_data = (esp_ble_gattc_cb_param_t *)param; @@ -227,6 +253,7 @@ static void gattc_profile_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_ case ESP_GATTC_OPEN_EVT: if (param->open.status != ESP_GATT_OK){ ESP_LOGE(LOG_TAG, "Open failed, status %x", p_data->open.status); + cte_resume_ext_scan(); break; } ESP_LOGI(LOG_TAG, "Open successfully, MTU %d", p_data->open.mtu); @@ -239,19 +266,14 @@ static void gattc_profile_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_ break; case ESP_GATTC_CFG_MTU_EVT: ESP_LOGI(LOG_TAG, "MTU exchange, status %d, MTU %d, conn_id %d", param->cfg_mtu.status, param->cfg_mtu.mtu, param->cfg_mtu.conn_id); - if (!param->cfg_mtu.status) { - ESP_LOGI(LOG_TAG, "Set CTE connection receive params, conn_handle %d", cur_conn_hdl); - cte_recv_params.conn_handle = cur_conn_hdl; - esp_ble_cte_set_connection_receive_params(&cte_recv_params); - } break; case ESP_GATTC_DIS_SRVC_CMPL_EVT: if (param->dis_srvc_cmpl.status != ESP_GATT_OK){ ESP_LOGE(LOG_TAG, "Service discover failed, status %d", param->dis_srvc_cmpl.status); break; } - ESP_LOGI(LOG_TAG, "Service discover complete, conn_id %d", param->dis_srvc_cmpl.conn_id); - esp_ble_gattc_search_service(gattc_if, param->cfg_mtu.conn_id, &remote_filter_service_uuid); + ESP_LOGI(LOG_TAG, "Service discover complete, conn_id %d", p_data->dis_srvc_cmpl.conn_id); + esp_ble_gattc_search_service(gattc_if, p_data->dis_srvc_cmpl.conn_id, &remote_filter_service_uuid); break; case ESP_GATTC_SEARCH_RES_EVT: { ESP_LOGI(LOG_TAG, "Service search result, conn_id %x, is primary service %d", p_data->search_res.conn_id, p_data->search_res.is_primary); @@ -314,9 +336,9 @@ static void gattc_profile_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_ case ESP_GATTC_DISCONNECT_EVT: ESP_LOGI(LOG_TAG, "Disconnected, remote "ESP_BD_ADDR_STR", reason 0x%02x", ESP_BD_ADDR_HEX(p_data->disconnect.remote_bda), p_data->disconnect.reason); - connect = false; get_service = false; - esp_ble_gap_start_ext_scan(EXT_SCAN_DURATION, EXT_SCAN_PERIOD); + cur_conn_hdl = BLE_CONN_HDL_INVALID; + cte_resume_ext_scan(); break; default: break; @@ -397,8 +419,7 @@ static void esp_gap_cb(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *par ESP_LOGI(LOG_TAG, "Pairing failed, reason 0x%x",param->ble_security.auth_cmpl.fail_reason); } else { ESP_LOGI(LOG_TAG, "Pairing successfully, auth mode %s",esp_auth_req_to_str(param->ble_security.auth_cmpl.auth_mode)); - // Enable CTE - + cte_enable_connection_receive_after_encrypted(); } break; } @@ -429,7 +450,10 @@ static void esp_gap_cb(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *par creat_conn_params.phy_1m_conn_params = &phy_1m_conn_params; creat_conn_params.phy_2m_conn_params = &phy_2m_conn_params; creat_conn_params.phy_coded_conn_params = &phy_coded_conn_params; - esp_ble_gattc_enh_open(gl_profile_tab[PROFILE_A_APP_ID].gattc_if, &creat_conn_params); + if (esp_ble_gattc_enh_open(gl_profile_tab[PROFILE_A_APP_ID].gattc_if, &creat_conn_params) != ESP_OK) { + ESP_LOGE(LOG_TAG, "Failed to open connection"); + cte_resume_ext_scan(); + } } break; @@ -453,14 +477,17 @@ static void cte_event_handler(esp_ble_cte_cb_event_t event, esp_ble_cte_cb_param case ESP_BLE_CTE_SET_CONN_TRANS_PARAMS_CMPL_EVT: ESP_LOGI(LOG_TAG, "CTE set connection transmit params, status %d", param->conn_trans_params_cmpl.status); break; - case ESP_BLE_CTE_SET_CONN_RECV_PARAMS_CMPL_EVT: - ESP_LOGI(LOG_TAG, "CTE set connection receive params, status %d", param->conn_recv_params_cmpl.status); + case ESP_BLE_CTE_SET_CONN_RECV_PARAMS_CMPL_EVT: { + uint16_t recv_cmpl_conn_hdl = param->conn_recv_params_cmpl.conn_handle; + ESP_LOGI(LOG_TAG, "CTE set connection receive params, status %d, conn_handle %d", + param->conn_recv_params_cmpl.status, recv_cmpl_conn_hdl); if (!param->conn_recv_params_cmpl.status) { - cte_conn_req_en.conn_handle = cur_conn_hdl; - ESP_LOGI(LOG_TAG, "Enable CTE request, conn_handle %d", cur_conn_hdl); + cte_conn_req_en.conn_handle = recv_cmpl_conn_hdl; + ESP_LOGI(LOG_TAG, "Enable CTE request, conn_handle %d", recv_cmpl_conn_hdl); esp_ble_cte_connection_cte_request_enable(&cte_conn_req_en); } break; + } case ESP_BLE_CTE_SET_CONN_REQ_ENABLE_CMPL_EVT: ESP_LOGI(LOG_TAG, "CTE set connection request enable, status %d", param->conn_req_en_cmpl.status); break; diff --git a/examples/bluetooth/bluedroid/ble_50/ble_connection_peripheral_with_cte/main/connection_peripheral_with_cte.c b/examples/bluetooth/bluedroid/ble_50/ble_connection_peripheral_with_cte/main/connection_peripheral_with_cte.c index e52ea3e5170..7ea6e22838a 100644 --- a/examples/bluetooth/bluedroid/ble_50/ble_connection_peripheral_with_cte/main/connection_peripheral_with_cte.c +++ b/examples/bluetooth/bluedroid/ble_50/ble_connection_peripheral_with_cte/main/connection_peripheral_with_cte.c @@ -35,6 +35,9 @@ #define GATTS_DEMO_CHAR_VAL_LEN_MAX 0x40 +/* Connection_Handle is 12-bit (0x0000..0x0EFF); 0xFFFF is outside the valid range. */ +#define BLE_CONN_HDL_INVALID ((uint16_t)0xFFFF) + #ifndef MIN #define MIN(a, b) (((a) < (b)) ? (a) : (b)) #endif @@ -68,18 +71,18 @@ esp_ble_gap_ext_adv_params_t ext_adv_params_2M = { static uint8_t antenna_ids[2] = {0x00, 0x01}; static esp_ble_cte_conn_trans_params_t cte_conn_trans_params = { - .conn_handle = 0xff, + .conn_handle = BLE_CONN_HDL_INVALID, .cte_types = ESP_BLE_CTE_TYPES_ALL, .switching_pattern_len = sizeof(antenna_ids), .antenna_ids = &antenna_ids[0], }; static esp_ble_cte_rsp_en_params_t cte_conn_rsp_en = { - .conn_handle = 0xff, + .conn_handle = BLE_CONN_HDL_INVALID, .enable = ESP_BLE_CTE_RESPONSE_FOR_CONNECTION_ENABLE, }; -uint16_t cur_conn_hdl = 0xff; +uint16_t cur_conn_hdl = BLE_CONN_HDL_INVALID; struct gatts_profile_inst { esp_gatts_cb_t gatts_cb; @@ -315,10 +318,14 @@ static void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param ESP_LOGI(LOG_TAG, "Pairing failed, reason 0x%x",param->ble_security.auth_cmpl.fail_reason); } else { ESP_LOGI(LOG_TAG, "Pairing successfully, auth_mode %s",esp_auth_req_to_str(param->ble_security.auth_cmpl.auth_mode)); - // Setting CTE connection transmit parameters - cte_conn_trans_params.conn_handle = cur_conn_hdl; - ESP_LOGI(LOG_TAG, "Set CTE connection transmit params, conn_handle %d", cur_conn_hdl); - esp_ble_cte_set_connection_transmit_params(&cte_conn_trans_params); + if (cur_conn_hdl != BLE_CONN_HDL_INVALID) { + // Setting CTE connection transmit parameters + cte_conn_trans_params.conn_handle = cur_conn_hdl; + ESP_LOGI(LOG_TAG, "Set CTE connection transmit params, conn_handle %d", cur_conn_hdl); + esp_ble_cte_set_connection_transmit_params(&cte_conn_trans_params); + } else { + ESP_LOGW(LOG_TAG, "Skip CTE transmit params: no active connection handle"); + } } break; } @@ -385,6 +392,7 @@ static void gatts_profile_event_handler(esp_gatts_cb_event_t event, case ESP_GATTS_DISCONNECT_EVT: ESP_LOGI(LOG_TAG, "Disconnected, remote "ESP_BD_ADDR_STR", reason 0x%x", ESP_BD_ADDR_HEX(param->disconnect.remote_bda), param->disconnect.reason); + cur_conn_hdl = BLE_CONN_HDL_INVALID; /* start advertising again when missing the connect */ esp_ble_gap_ext_adv_start(NUM_EXT_ADV_SET, &ext_adv[0]); break; @@ -399,18 +407,18 @@ static void gatts_profile_event_handler(esp_gatts_cb_event_t event, case ESP_GATTS_CONGEST_EVT: break; case ESP_GATTS_CREAT_ATTR_TAB_EVT: { - if (param->create.status == ESP_GATT_OK){ - if(param->add_attr_tab.num_handle == HRS_IDX_NB) { + if (param->add_attr_tab.status == ESP_GATT_OK) { + if (param->add_attr_tab.num_handle == HRS_IDX_NB) { ESP_LOGI(LOG_TAG, "Attribute table create successfully, num_handle %x", param->add_attr_tab.num_handle); memcpy(profile_handle_table, param->add_attr_tab.handles, - sizeof(profile_handle_table)); + sizeof(profile_handle_table)); esp_ble_gatts_start_service(profile_handle_table[IDX_SVC]); - }else{ + } else { ESP_LOGE(LOG_TAG, "Attribute table create abnormally, num_handle (%d) doesn't equal to HRS_IDX_NB(%d)", - param->add_attr_tab.num_handle, HRS_IDX_NB); + param->add_attr_tab.num_handle, HRS_IDX_NB); } - }else{ - ESP_LOGE(LOG_TAG, "Attribute table create failed, status %x", param->create.status); + } else { + ESP_LOGE(LOG_TAG, "Attribute table create failed, status %x", param->add_attr_tab.status); } break; } @@ -422,15 +430,17 @@ static void gatts_profile_event_handler(esp_gatts_cb_event_t event, static void cte_event_handler(esp_ble_cte_cb_event_t event, esp_ble_cte_cb_param_t *param) { switch (event) { - case ESP_BLE_CTE_SET_CONN_TRANS_PARAMS_CMPL_EVT: - ESP_LOGI(LOG_TAG, "CTE set connection transmit params, status %d", param->conn_trans_params_cmpl.status); + case ESP_BLE_CTE_SET_CONN_TRANS_PARAMS_CMPL_EVT: { + uint16_t trans_cmpl_conn_hdl = param->conn_trans_params_cmpl.conn_handle; + ESP_LOGI(LOG_TAG, "CTE set connection transmit params, status %d, conn_handle %d", + param->conn_trans_params_cmpl.status, trans_cmpl_conn_hdl); if (!param->conn_trans_params_cmpl.status) { - ESP_LOGI(LOG_TAG, "Setting CTE connection response enable"); - // Enable CTE response for connection - cte_conn_rsp_en.conn_handle = cur_conn_hdl; + ESP_LOGI(LOG_TAG, "Setting CTE connection response enable, conn_handle %d", trans_cmpl_conn_hdl); + cte_conn_rsp_en.conn_handle = trans_cmpl_conn_hdl; esp_ble_cte_connection_cte_response_enable(&cte_conn_rsp_en); } break; + } case ESP_BLE_CTE_SET_CONN_RECV_PARAMS_CMPL_EVT: ESP_LOGI(LOG_TAG, "CTE set connection receive params, status %d", param->conn_recv_params_cmpl.status); break; diff --git a/examples/bluetooth/bluedroid/ble_50/ble_pawr_advertiser/main/main.c b/examples/bluetooth/bluedroid/ble_50/ble_pawr_advertiser/main/main.c index c553dba6351..855536b2bd9 100644 --- a/examples/bluetooth/bluedroid/ble_50/ble_pawr_advertiser/main/main.c +++ b/examples/bluetooth/bluedroid/ble_50/ble_pawr_advertiser/main/main.c @@ -223,7 +223,11 @@ static void start_periodic_adv(void) { // Create static random address esp_bd_addr_t rand_addr; - esp_ble_gap_addr_create_static(rand_addr); + esp_err_t addr_ret = esp_ble_gap_addr_create_static(rand_addr); + if (addr_ret != ESP_OK) { + ESP_LOGE(TAG, "esp_ble_gap_addr_create_static failed: %s", esp_err_to_name(addr_ret)); + return; + } ESP_LOG_BUFFER_HEX(TAG, rand_addr, ESP_BD_ADDR_LEN); diff --git a/examples/bluetooth/bluedroid/ble_50/ble_pawr_synchronizer/main/ble_pawr_synchronizer_demo.c b/examples/bluetooth/bluedroid/ble_50/ble_pawr_synchronizer/main/ble_pawr_synchronizer_demo.c index e6d1b44e37d..f4cd6bdba4e 100644 --- a/examples/bluetooth/bluedroid/ble_50/ble_pawr_synchronizer/main/ble_pawr_synchronizer_demo.c +++ b/examples/bluetooth/bluedroid/ble_50/ble_pawr_synchronizer/main/ble_pawr_synchronizer_demo.c @@ -243,7 +243,9 @@ static void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param param->ext_adv_report.params.adv_data_len, ESP_BLE_AD_TYPE_NAME_CMPL, &adv_name_len); - if ((adv_name != NULL) && (memcmp(adv_name, remote_device_name, adv_name_len) == 0) && !periodic_sync) { + if ((adv_name != NULL) && (adv_name_len > 0) && + (adv_name_len == strlen(remote_device_name)) && + (memcmp(adv_name, remote_device_name, adv_name_len) == 0) && !periodic_sync) { // Note: If there are multiple devices with the same device name, the device may sync to an unintended one. // It is recommended to change the default device name to ensure it is unique. periodic_sync = true; diff --git a/examples/bluetooth/bluedroid/ble_50/ble_periodic_adv_with_cte/main/periodic_adv_with_cte_demo.c b/examples/bluetooth/bluedroid/ble_50/ble_periodic_adv_with_cte/main/periodic_adv_with_cte_demo.c index af7019f728b..7269ba8370b 100644 --- a/examples/bluetooth/bluedroid/ble_50/ble_periodic_adv_with_cte/main/periodic_adv_with_cte_demo.c +++ b/examples/bluetooth/bluedroid/ble_50/ble_periodic_adv_with_cte/main/periodic_adv_with_cte_demo.c @@ -41,16 +41,21 @@ #define FUNC_SEND_WAIT_SEM(func, sem) do {\ esp_err_t __err_rc = (func);\ if (__err_rc != ESP_OK) { \ - ESP_LOGE(LOG_TAG, "%s, message send fail, error = %d", __func__, __err_rc); \ + ESP_LOGE(LOG_TAG, "%s failed: %s", #func, esp_err_to_name(__err_rc)); \ + return; \ + } \ + xSemaphoreTake((sem), portMAX_DELAY); \ + if (last_ble_async_status != ESP_BT_STATUS_SUCCESS) { \ + ESP_LOGE(LOG_TAG, "Async completion after %s failed, status 0x%x", #func, last_ble_async_status); \ + return; \ } \ - xSemaphoreTake(sem, portMAX_DELAY); \ } while(0); #define EXT_ADV_HANDLE 0 #define NUM_EXT_ADV 1 static SemaphoreHandle_t test_sem = NULL; - +static esp_bt_status_t last_ble_async_status = ESP_BT_STATUS_SUCCESS; uint8_t addr_2m[6] = {0xc0, 0xde, 0x52, 0x00, 0x00, 0x02}; @@ -113,44 +118,56 @@ static esp_ble_cte_trans_enable_params_t cte_trans_enable = { static uint8_t periodic_adv_hdl = 0xff; +static void ble_async_complete_signal(esp_bt_status_t status) +{ + last_ble_async_status = status; + if (test_sem != NULL) { + xSemaphoreGive(test_sem); + } +} + static void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) { switch (event) { case ESP_GAP_BLE_EXT_ADV_SET_RAND_ADDR_COMPLETE_EVT: - xSemaphoreGive(test_sem); + ble_async_complete_signal(param->ext_adv_set_rand_addr.status); ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_EXT_ADV_SET_RAND_ADDR_COMPLETE_EVT, status %d", param->ext_adv_set_rand_addr.status); break; case ESP_GAP_BLE_EXT_ADV_SET_PARAMS_COMPLETE_EVT: - xSemaphoreGive(test_sem); + ble_async_complete_signal(param->ext_adv_set_params.status); ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_EXT_ADV_SET_PARAMS_COMPLETE_EVT, status %d", param->ext_adv_set_params.status); break; case ESP_GAP_BLE_EXT_ADV_DATA_SET_COMPLETE_EVT: - xSemaphoreGive(test_sem); + ble_async_complete_signal(param->ext_adv_data_set.status); ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_EXT_ADV_DATA_SET_COMPLETE_EVT, status %d", param->ext_adv_data_set.status); break; case ESP_GAP_BLE_EXT_SCAN_RSP_DATA_SET_COMPLETE_EVT: - xSemaphoreGive(test_sem); + ble_async_complete_signal(param->scan_rsp_set.status); ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_EXT_SCAN_RSP_DATA_SET_COMPLETE_EVT, status %d", param->scan_rsp_set.status); break; case ESP_GAP_BLE_EXT_ADV_START_COMPLETE_EVT: - xSemaphoreGive(test_sem); + ble_async_complete_signal(param->ext_adv_start.status); ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_EXT_ADV_START_COMPLETE_EVT, status %d", param->ext_adv_start.status); break; case ESP_GAP_BLE_EXT_ADV_STOP_COMPLETE_EVT: - xSemaphoreGive(test_sem); + ble_async_complete_signal(param->ext_adv_stop.status); ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_EXT_ADV_STOP_COMPLETE_EVT, status %d", param->ext_adv_stop.status); break; case ESP_GAP_BLE_PERIODIC_ADV_SET_PARAMS_COMPLETE_EVT: - periodic_adv_hdl = param->peroid_adv_set_params.instance; - xSemaphoreGive(test_sem); + if (param->peroid_adv_set_params.status == ESP_BT_STATUS_SUCCESS) { + periodic_adv_hdl = param->peroid_adv_set_params.instance; + } else { + ESP_LOGE(LOG_TAG, "periodic adv set params failed, not updating periodic_adv_hdl"); + } + ble_async_complete_signal(param->peroid_adv_set_params.status); ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_PERIODIC_ADV_SET_PARAMS_COMPLETE_EVT, status %d", param->peroid_adv_set_params.status); break; case ESP_GAP_BLE_PERIODIC_ADV_DATA_SET_COMPLETE_EVT: - xSemaphoreGive(test_sem); + ble_async_complete_signal(param->period_adv_data_set.status); ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_PERIODIC_ADV_DATA_SET_COMPLETE_EVT, status %d", param->period_adv_data_set.status); break; case ESP_GAP_BLE_PERIODIC_ADV_START_COMPLETE_EVT: - xSemaphoreGive(test_sem); + ble_async_complete_signal(param->period_adv_start.status); ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_PERIODIC_ADV_START_COMPLETE_EVT, status %d", param->period_adv_start.status); break; default: @@ -162,11 +179,11 @@ static void cte_event_handler(esp_ble_cte_cb_event_t event, esp_ble_cte_cb_param { switch (event) { case ESP_BLE_CTE_SET_CONNLESS_TRANS_PARAMS_CMPL_EVT: - xSemaphoreGive(test_sem); + ble_async_complete_signal(param->set_trans_params_cmpl.status); ESP_LOGI(LOG_TAG, "ESP_BLE_CTE_SET_CONNLESS_TRANS_PARAMS_CMPL_EVT, status %d", param->set_trans_params_cmpl.status); break; case ESP_BLE_CTE_SET_CONNLESS_TRANS_ENABLE_CMPL_EVT: - xSemaphoreGive(test_sem); + ble_async_complete_signal(param->set_trans_enable_cmpl.status); ESP_LOGI(LOG_TAG, "ESP_BLE_CTE_SET_CONNLESS_TRANS_ENABLE_CMPL_EVT, status %d", param->set_trans_enable_cmpl.status); break; case ESP_BLE_CTE_SET_CONNLESS_IQ_SAMPLING_ENABLE_CMPL_EVT: diff --git a/examples/bluetooth/bluedroid/ble_50/ble_periodic_sync_with_cte/main/periodic_sync_with_cte_demo.c b/examples/bluetooth/bluedroid/ble_50/ble_periodic_sync_with_cte/main/periodic_sync_with_cte_demo.c index d1cea760bdf..281984152f4 100644 --- a/examples/bluetooth/bluedroid/ble_50/ble_periodic_sync_with_cte/main/periodic_sync_with_cte_demo.c +++ b/examples/bluetooth/bluedroid/ble_50/ble_periodic_sync_with_cte/main/periodic_sync_with_cte_demo.c @@ -37,15 +37,16 @@ #include "esp_ble_cte_api.h" +#define LOG_TAG "PERIODIC_SYNC" + #define FUNC_SEND_WAIT_SEM(func, sem) do {\ esp_err_t __err_rc = (func);\ if (__err_rc != ESP_OK) { \ - ESP_LOGE(LOG_TAG, "%s, message send fail, error = %d", __func__, __err_rc); \ + ESP_LOGE(LOG_TAG, "%s failed: %s", #func, esp_err_to_name(__err_rc)); \ + return; \ } \ - xSemaphoreTake(sem, portMAX_DELAY); \ + xSemaphoreTake((sem), portMAX_DELAY); \ } while(0); - -#define LOG_TAG "PERIODIC_SYNC" #define EXT_SCAN_DURATION 0 #define EXT_SCAN_PERIOD 0 @@ -136,13 +137,16 @@ static void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param param->ext_adv_report.params.adv_data_len, ESP_BLE_AD_TYPE_NAME_CMPL, &adv_name_len); - if ((adv_name != NULL) && (memcmp(adv_name, remote_device_name, adv_name_len) == 0) && !periodic_sync) { + const size_t remote_cap = sizeof(remote_device_name); + size_t remote_len = strnlen(remote_device_name, remote_cap); + if ((adv_name != NULL) && (adv_name_len > 0) && + (adv_name_len <= remote_cap) && + (adv_name_len == remote_len) && + (memcmp(adv_name, remote_device_name, adv_name_len) == 0) && !periodic_sync) { // Note: If there are multiple devices with the same device name, the device may sync to an unintended one. // It is recommended to change the default device name to ensure it is unique. periodic_sync = true; - char adv_temp_name[30] = {'0'}; - memcpy(adv_temp_name, adv_name, adv_name_len); - ESP_LOGI(LOG_TAG, "Create sync with the peer device %s", adv_temp_name); + ESP_LOGI(LOG_TAG, "Create sync with the peer device %.*s", (int)adv_name_len, (const char *)adv_name); periodic_adv_sync_params.sid = param->ext_adv_report.params.sid; periodic_adv_sync_params.addr_type = param->ext_adv_report.params.addr_type; memcpy(periodic_adv_sync_params.addr, param->ext_adv_report.params.addr, sizeof(esp_bd_addr_t)); diff --git a/examples/bluetooth/bluedroid/ble_50/ble_power_control_central/main/main.c b/examples/bluetooth/bluedroid/ble_50/ble_power_control_central/main/main.c index 383dc270db1..84effffd80b 100644 --- a/examples/bluetooth/bluedroid/ble_50/ble_power_control_central/main/main.c +++ b/examples/bluetooth/bluedroid/ble_50/ble_power_control_central/main/main.c @@ -180,12 +180,12 @@ static void gattc_profile_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_ } break; case ESP_GATTC_CONNECT_EVT: - ESP_LOGI(TAG, "Connected, conn_id %d, remote "ESP_BD_ADDR_STR"", - p_data->connect.conn_id, ESP_BD_ADDR_HEX(p_data->connect.remote_bda)); + ESP_LOGI(TAG, "Connected, conn_id %d, hci_conn_handle %d, remote "ESP_BD_ADDR_STR"", + p_data->connect.conn_id, p_data->connect.conn_handle, + ESP_BD_ADDR_HEX(p_data->connect.remote_bda)); gl_profile_tab[PROFILE_A_APP_ID].conn_id = p_data->connect.conn_id; memcpy(gl_profile_tab[PROFILE_A_APP_ID].remote_bda, p_data->connect.remote_bda, sizeof(esp_bd_addr_t)); - conn_handle = p_data->connect.conn_id; - // Initialize power control after connection + conn_handle = p_data->connect.conn_handle; init_power_control(conn_handle); break; case ESP_GATTC_DISCONNECT_EVT: diff --git a/examples/bluetooth/bluedroid/ble_50/periodic_adv/main/periodic_adv_demo.c b/examples/bluetooth/bluedroid/ble_50/periodic_adv/main/periodic_adv_demo.c index 3c7ecfba5c9..aebc6115ad8 100644 --- a/examples/bluetooth/bluedroid/ble_50/periodic_adv/main/periodic_adv_demo.c +++ b/examples/bluetooth/bluedroid/ble_50/periodic_adv/main/periodic_adv_demo.c @@ -40,9 +40,10 @@ #define FUNC_SEND_WAIT_SEM(func, sem) do {\ esp_err_t __err_rc = (func);\ if (__err_rc != ESP_OK) { \ - ESP_LOGE(LOG_TAG, "%s, message send fail, error = %d", __func__, __err_rc); \ + ESP_LOGE(LOG_TAG, "%s failed: %s", #func, esp_err_to_name(__err_rc)); \ + return; \ } \ - xSemaphoreTake(sem, portMAX_DELAY); \ + xSemaphoreTake((sem), portMAX_DELAY); \ } while(0); #define EXT_ADV_HANDLE 0 @@ -95,43 +96,50 @@ static esp_ble_gap_ext_adv_t ext_adv[1] = { [0] = {EXT_ADV_HANDLE, 0, 0}, }; +static void periodic_adv_gap_sem_give(void) +{ + if (test_sem != NULL) { + xSemaphoreGive(test_sem); + } +} + static void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) { switch (event) { case ESP_GAP_BLE_EXT_ADV_SET_RAND_ADDR_COMPLETE_EVT: - xSemaphoreGive(test_sem); + periodic_adv_gap_sem_give(); ESP_LOGI(LOG_TAG, "Extended advertising random address set, status %d, instance %d", param->ext_adv_set_rand_addr.status, param->ext_adv_set_rand_addr.instance); break; case ESP_GAP_BLE_EXT_ADV_SET_PARAMS_COMPLETE_EVT: - xSemaphoreGive(test_sem); + periodic_adv_gap_sem_give(); ESP_LOGI(LOG_TAG, "Extended advertising params set, status %d, instance %d", param->ext_adv_set_params.status, param->ext_adv_set_params.instance); break; case ESP_GAP_BLE_EXT_ADV_DATA_SET_COMPLETE_EVT: - xSemaphoreGive(test_sem); + periodic_adv_gap_sem_give(); ESP_LOGI(LOG_TAG, "Extended advertising data set, status %d, instance %d", param->ext_adv_data_set.status, param->ext_adv_data_set.instance); break; case ESP_GAP_BLE_EXT_SCAN_RSP_DATA_SET_COMPLETE_EVT: - xSemaphoreGive(test_sem); + periodic_adv_gap_sem_give(); ESP_LOGI(LOG_TAG, "Extended advertising scan response data set, status %d, instance %d", param->scan_rsp_set.status, param->scan_rsp_set.instance); break; case ESP_GAP_BLE_EXT_ADV_START_COMPLETE_EVT: - xSemaphoreGive(test_sem); - ESP_LOGI(LOG_TAG, "Extended advertising start, status %d, instance numble %d", param->ext_adv_start.status, param->ext_adv_start.instance_num); + periodic_adv_gap_sem_give(); + ESP_LOGI(LOG_TAG, "Extended advertising start, status %d, instance number %d", param->ext_adv_start.status, param->ext_adv_start.instance_num); break; case ESP_GAP_BLE_EXT_ADV_STOP_COMPLETE_EVT: - xSemaphoreGive(test_sem); - ESP_LOGI(LOG_TAG, "Extended advertising start, status %d, instance numble %d", param->ext_adv_stop.status, param->ext_adv_stop.instance_num); + periodic_adv_gap_sem_give(); + ESP_LOGI(LOG_TAG, "Extended advertising stop, status %d, instance number %d", param->ext_adv_stop.status, param->ext_adv_stop.instance_num); break; case ESP_GAP_BLE_PERIODIC_ADV_SET_PARAMS_COMPLETE_EVT: - xSemaphoreGive(test_sem); + periodic_adv_gap_sem_give(); ESP_LOGI(LOG_TAG, "Periodic advertising params set, status %d, instance %d", param->peroid_adv_set_params.status, param->peroid_adv_set_params.instance); break; case ESP_GAP_BLE_PERIODIC_ADV_DATA_SET_COMPLETE_EVT: - xSemaphoreGive(test_sem); + periodic_adv_gap_sem_give(); ESP_LOGI(LOG_TAG, "Periodic advertising data set, status %d, instance %d", param->period_adv_data_set.status, param->period_adv_data_set.instance); break; case ESP_GAP_BLE_PERIODIC_ADV_START_COMPLETE_EVT: - xSemaphoreGive(test_sem); + periodic_adv_gap_sem_give(); ESP_LOGI(LOG_TAG, "Periodic advertising start, status %d, instance %d", param->period_adv_start.status, param->period_adv_start.instance); break; default: @@ -184,9 +192,18 @@ void app_main(void) ESP_LOGE(LOG_TAG, "%s enable bluetooth failed: %s", __func__, esp_err_to_name(ret)); return; } + + test_sem = xSemaphoreCreateBinary(); + if (test_sem == NULL) { + ESP_LOGE(LOG_TAG, "Failed to create semaphore"); + return; + } + ret = esp_ble_gap_register_callback(gap_event_handler); if (ret){ ESP_LOGE(LOG_TAG, "gap register error, error code = %x", ret); + vSemaphoreDelete(test_sem); + test_sem = NULL; return; } @@ -194,8 +211,6 @@ void app_main(void) esp_bd_addr_t rand_addr; esp_ble_gap_addr_create_static(rand_addr); - test_sem = xSemaphoreCreateBinary(); - // 2M phy extend adv, Non-Connectable and Non-Scannable Undirected advertising ESP_LOG_BUFFER_HEX(LOG_TAG, rand_addr, ESP_BD_ADDR_LEN); FUNC_SEND_WAIT_SEM(esp_ble_gap_ext_adv_set_params(EXT_ADV_HANDLE, &ext_adv_params_2M), test_sem); diff --git a/examples/bluetooth/bluedroid/ble_50/periodic_sync/main/periodic_sync_demo.c b/examples/bluetooth/bluedroid/ble_50/periodic_sync/main/periodic_sync_demo.c index f70bf1e8b25..f83cef27923 100644 --- a/examples/bluetooth/bluedroid/ble_50/periodic_sync/main/periodic_sync_demo.c +++ b/examples/bluetooth/bluedroid/ble_50/periodic_sync/main/periodic_sync_demo.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2021-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Unlicense OR CC0-1.0 */ @@ -84,10 +84,13 @@ static void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param break; case ESP_GAP_BLE_EXT_SCAN_STOP_COMPLETE_EVT: xSemaphoreGive(test_sem); - ESP_LOGI(LOG_TAG, "Extended scanning stop, status %d", param->period_adv_stop.status); + ESP_LOGI(LOG_TAG, "Extended scanning stop, status %d", param->ext_scan_stop.status); break; case ESP_GAP_BLE_PERIODIC_ADV_CREATE_SYNC_COMPLETE_EVT: ESP_LOGI(LOG_TAG, "Periodic advertising create sync, status %d", param->period_adv_create_sync.status); + if (param->period_adv_create_sync.status != ESP_BT_STATUS_SUCCESS) { + periodic_sync = false; + } break; case ESP_GAP_BLE_PERIODIC_ADV_SYNC_CANCEL_COMPLETE_EVT: ESP_LOGI(LOG_TAG, "Periodic advertising sync cancel, status %d", param->period_adv_sync_cancel.status); @@ -97,9 +100,13 @@ static void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param break; case ESP_GAP_BLE_PERIODIC_ADV_SYNC_LOST_EVT: ESP_LOGI(LOG_TAG, "Periodic advertising sync lost, sync handle %d", param->periodic_adv_sync_lost.sync_handle); + periodic_sync = false; break; case ESP_GAP_BLE_PERIODIC_ADV_SYNC_ESTAB_EVT: ESP_LOGI(LOG_TAG, "Periodic advertising sync establish, status %d", param->periodic_adv_sync_estab.status); + if (param->periodic_adv_sync_estab.status != ESP_BT_STATUS_SUCCESS) { + periodic_sync = false; + } ESP_LOGI(LOG_TAG, "address "ESP_BD_ADDR_STR"", ESP_BD_ADDR_HEX(param->periodic_adv_sync_estab.adv_addr)); ESP_LOGI(LOG_TAG, "sync handle %d sid %d perioic adv interval %d adv phy %d", param->periodic_adv_sync_estab.sync_handle, param->periodic_adv_sync_estab.sid, @@ -113,13 +120,16 @@ static void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param param->ext_adv_report.params.adv_data_len, ESP_BLE_AD_TYPE_NAME_CMPL, &adv_name_len); - if ((adv_name != NULL) && (memcmp(adv_name, remote_device_name, adv_name_len) == 0) && !periodic_sync) { + const size_t remote_cap = sizeof(remote_device_name); + size_t remote_len = strnlen(remote_device_name, remote_cap); + if ((adv_name != NULL) && (adv_name_len > 0) && + (adv_name_len <= remote_cap) && + (adv_name_len == remote_len) && + (memcmp(adv_name, remote_device_name, adv_name_len) == 0) && !periodic_sync) { // Note: If there are multiple devices with the same device name, the device may sync to an unintended one. // It is recommended to change the default device name to ensure it is unique. periodic_sync = true; - char adv_temp_name[30] = {'0'}; - memcpy(adv_temp_name, adv_name, adv_name_len); - ESP_LOGI(LOG_TAG, "Create sync with the peer device %s", adv_temp_name); + ESP_LOGI(LOG_TAG, "Create sync with the peer device %.*s", (int)adv_name_len, (const char *)adv_name); periodic_adv_sync_params.sid = param->ext_adv_report.params.sid; periodic_adv_sync_params.addr_type = param->ext_adv_report.params.addr_type; memcpy(periodic_adv_sync_params.addr, param->ext_adv_report.params.addr, sizeof(esp_bd_addr_t)); diff --git a/examples/bluetooth/bluedroid/coex/a2dp_gatts_coex/main/main.c b/examples/bluetooth/bluedroid/coex/a2dp_gatts_coex/main/main.c index de745686e66..98ec7adb488 100644 --- a/examples/bluetooth/bluedroid/coex/a2dp_gatts_coex/main/main.c +++ b/examples/bluetooth/bluedroid/coex/a2dp_gatts_coex/main/main.c @@ -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 */ @@ -81,6 +81,15 @@ typedef struct { static prepare_type_env_t a_prepare_write_env; static prepare_type_env_t b_prepare_write_env; +static void prepare_write_env_free(prepare_type_env_t *env) +{ + if (env->prepare_buf != NULL) { + free(env->prepare_buf); + env->prepare_buf = NULL; + } + env->prepare_len = 0; +} + //Declare the static function static void gatts_profile_a_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param); static void gatts_profile_b_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param); @@ -176,10 +185,21 @@ static void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param { switch (event) { case ESP_GAP_BLE_ADV_DATA_RAW_SET_COMPLETE_EVT: - //esp_ble_gap_start_advertising(&adv_params); + break; + case ESP_GAP_BLE_SET_LOCAL_PRIVACY_COMPLETE_EVT: + /* Configure adv data only after local privacy is set up (REG_EVT requests it). */ + if (param->local_privacy_cmpl.status != ESP_BT_STATUS_SUCCESS) { + ESP_LOGE(BT_BLE_COEX_TAG, "set local privacy failed, status %d", param->local_privacy_cmpl.status); + } else { + ble_init_adv_data(BLE_ADV_NAME); + } break; case ESP_GAP_BLE_SCAN_RSP_DATA_RAW_SET_COMPLETE_EVT: - esp_ble_gap_start_advertising(&adv_params); + if (param->scan_rsp_data_raw_cmpl.status != ESP_BT_STATUS_SUCCESS) { + ESP_LOGE(BT_BLE_COEX_TAG, "set raw scan rsp data failed, status %d", param->scan_rsp_data_raw_cmpl.status); + } else { + esp_ble_gap_start_advertising(&adv_params); + } break; case ESP_GAP_BLE_ADV_START_COMPLETE_EVT: //advertising start complete event to indicate advertising start successfully or failed @@ -264,11 +284,69 @@ void example_exec_write_event_env(prepare_type_env_t *prepare_write_env, esp_ble }else{ ESP_LOGI(BT_BLE_COEX_TAG,"ESP_GATT_PREP_WRITE_CANCEL"); } - if (prepare_write_env->prepare_buf) { - free(prepare_write_env->prepare_buf); - prepare_write_env->prepare_buf = NULL; + prepare_write_env_free(prepare_write_env); +} + +/* Profile A and B handle READ/WRITE identically (apart from per-profile state); these helpers + * avoid the copy-pasted bodies that existed in the previous version. */ +static void gatts_coex_read_evt(esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param) +{ + ESP_LOGI(BT_BLE_COEX_TAG, "GATT_READ_EVT, conn_id %d, trans_id %"PRIu32", handle %d", + param->read.conn_id, param->read.trans_id, param->read.handle); + esp_gatt_rsp_t rsp; + memset(&rsp, 0, sizeof(esp_gatt_rsp_t)); + rsp.attr_value.handle = param->read.handle; + rsp.attr_value.len = 4; + rsp.attr_value.value[0] = 0xde; + rsp.attr_value.value[1] = 0xed; + rsp.attr_value.value[2] = 0xbe; + rsp.attr_value.value[3] = 0xef; + esp_ble_gatts_send_response(gatts_if, param->read.conn_id, param->read.trans_id, + ESP_GATT_OK, &rsp); +} + +static void gatts_coex_write_evt(esp_gatt_if_t gatts_if, int profile_idx, prepare_type_env_t *prep_env, + esp_gatt_char_prop_t char_prop, esp_ble_gatts_cb_param_t *param) +{ + ESP_LOGI(BT_BLE_COEX_TAG, "GATT_WRITE_EVT, conn_id %d, trans_id %"PRIu32", handle %d", + param->write.conn_id, param->write.trans_id, param->write.handle); + + if (!param->write.is_prep) { + ESP_LOGI(BT_BLE_COEX_TAG, "GATT_WRITE_EVT, value len %d, value :", param->write.len); + ESP_LOG_BUFFER_HEX(BT_BLE_COEX_TAG, param->write.value, param->write.len); + if (gl_profile_tab[profile_idx].descr_handle == param->write.handle && param->write.len == 2) { + uint16_t descr_value = param->write.value[1] << 8 | param->write.value[0]; + if (descr_value == 0x0001) { + if (char_prop & ESP_GATT_CHAR_PROP_BIT_NOTIFY) { + ESP_LOGI(BT_BLE_COEX_TAG, "notify enable"); + uint8_t notify_data[15]; + for (int i = 0; i < sizeof(notify_data); ++i) { + notify_data[i] = i % 0xff; + } + //the size of notify_data[] need less than MTU size + esp_ble_gatts_send_indicate(gatts_if, param->write.conn_id, gl_profile_tab[profile_idx].char_handle, + sizeof(notify_data), notify_data, false); + } + } else if (descr_value == 0x0002) { + if (char_prop & ESP_GATT_CHAR_PROP_BIT_INDICATE) { + ESP_LOGI(BT_BLE_COEX_TAG, "indicate enable"); + uint8_t indicate_data[15]; + for (int i = 0; i < sizeof(indicate_data); ++i) { + indicate_data[i] = i % 0xff; + } + //the size of indicate_data[] need less than MTU size + esp_ble_gatts_send_indicate(gatts_if, param->write.conn_id, gl_profile_tab[profile_idx].char_handle, + sizeof(indicate_data), indicate_data, true); + } + } else if (descr_value == 0x0000) { + ESP_LOGI(BT_BLE_COEX_TAG, "notify/indicate disable "); + } else { + ESP_LOGE(BT_BLE_COEX_TAG, "unknown descr value"); + ESP_LOG_BUFFER_HEX(BT_BLE_COEX_TAG, param->write.value, param->write.len); + } + } } - prepare_write_env->prepare_len = 0; + example_write_event_env(gatts_if, prep_env, param); } static void gatts_profile_a_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param) @@ -281,68 +359,20 @@ static void gatts_profile_a_event_handler(esp_gatts_cb_event_t event, esp_gatt_i gl_profile_tab[PROFILE_A_APP_ID].service_id.id.inst_id = 0x00; gl_profile_tab[PROFILE_A_APP_ID].service_id.id.uuid.len = ESP_UUID_LEN_16; gl_profile_tab[PROFILE_A_APP_ID].service_id.id.uuid.uuid.uuid16 = GATTS_SERVICE_UUID_A; - //init BLE adv data and scan response data - ble_init_adv_data(BLE_ADV_NAME); + /* Adv raw data is set from gap_event_handler after ESP_GAP_BLE_SET_LOCAL_PRIVACY_COMPLETE_EVT. */ esp_ble_gatts_create_service(gatts_if, &gl_profile_tab[PROFILE_A_APP_ID].service_id, GATTS_NUM_HANDLE_A); break; - case ESP_GATTS_READ_EVT: { - ESP_LOGI(BT_BLE_COEX_TAG, "GATT_READ_EVT, conn_id %d, trans_id %"PRIu32", handle %d", param->read.conn_id, param->read.trans_id, param->read.handle); - esp_gatt_rsp_t rsp; - memset(&rsp, 0, sizeof(esp_gatt_rsp_t)); - rsp.attr_value.handle = param->read.handle; - rsp.attr_value.len = 4; - rsp.attr_value.value[0] = 0xde; - rsp.attr_value.value[1] = 0xed; - rsp.attr_value.value[2] = 0xbe; - rsp.attr_value.value[3] = 0xef; - esp_ble_gatts_send_response(gatts_if, param->read.conn_id, param->read.trans_id, - ESP_GATT_OK, &rsp); + case ESP_GATTS_READ_EVT: + gatts_coex_read_evt(gatts_if, param); break; - } - case ESP_GATTS_WRITE_EVT: { - ESP_LOGI(BT_BLE_COEX_TAG, "GATT_WRITE_EVT, conn_id %d, trans_id %"PRIu32", handle %d", param->write.conn_id, param->write.trans_id, param->write.handle); - if (!param->write.is_prep) { - ESP_LOGI(BT_BLE_COEX_TAG, "GATT_WRITE_EVT, value len %d, value :", param->write.len); - esp_log_buffer_hex(BT_BLE_COEX_TAG, param->write.value, param->write.len); - if (gl_profile_tab[PROFILE_A_APP_ID].descr_handle == param->write.handle && param->write.len == 2){ - uint16_t descr_value = param->write.value[1]<<8 | param->write.value[0]; - if (descr_value == 0x0001){ - if (a_property & ESP_GATT_CHAR_PROP_BIT_NOTIFY){ - ESP_LOGI(BT_BLE_COEX_TAG, "notify enable"); - uint8_t notify_data[15]; - for (int i = 0; i < sizeof(notify_data); ++i) { - notify_data[i] = i % 0xff; - } - //the size of notify_data[] need less than MTU size - esp_ble_gatts_send_indicate(gatts_if, param->write.conn_id, gl_profile_tab[PROFILE_A_APP_ID].char_handle, - sizeof(notify_data), notify_data, false); - } - } else if (descr_value == 0x0002) { - if (a_property & ESP_GATT_CHAR_PROP_BIT_INDICATE) { - ESP_LOGI(BT_BLE_COEX_TAG, "indicate enable"); - uint8_t indicate_data[15]; - for (int i = 0; i < sizeof(indicate_data); ++i) { - indicate_data[i] = i % 0xff; - } - //the size of indicate_data[] need less than MTU size - esp_ble_gatts_send_indicate(gatts_if, param->write.conn_id, gl_profile_tab[PROFILE_A_APP_ID].char_handle, - sizeof(indicate_data), indicate_data, true); - } - } else if (descr_value == 0x0000) { - ESP_LOGI(BT_BLE_COEX_TAG, "notify/indicate disable "); - } else { - ESP_LOGE(BT_BLE_COEX_TAG, "unknown descr value"); - esp_log_buffer_hex(BT_BLE_COEX_TAG, param->write.value, param->write.len); - } - - } - } - example_write_event_env(gatts_if, &a_prepare_write_env, param); + case ESP_GATTS_WRITE_EVT: + gatts_coex_write_evt(gatts_if, PROFILE_A_APP_ID, &a_prepare_write_env, a_property, param); break; - } case ESP_GATTS_EXEC_WRITE_EVT: ESP_LOGI(BT_BLE_COEX_TAG, "ESP_GATTS_EXEC_WRITE_EVT"); - esp_ble_gatts_send_response(gatts_if, param->write.conn_id, param->write.trans_id, ESP_GATT_OK, NULL); + /* Use exec_write union member, not write — these share offsets in the union but it + * was a latent bug in master to refer to param->write here. */ + esp_ble_gatts_send_response(gatts_if, param->exec_write.conn_id, param->exec_write.trans_id, ESP_GATT_OK, NULL); example_exec_write_event_env(&a_prepare_write_env, param); break; case ESP_GATTS_MTU_EVT: @@ -394,13 +424,11 @@ static void gatts_profile_a_event_handler(esp_gatts_cb_event_t event, esp_gatt_i break; case ESP_GATTS_STOP_EVT: break; - case ESP_GATTS_CONNECT_EVT: { - esp_ble_conn_update_params_t conn_params = {0}; - memcpy(conn_params.bda, param->connect.remote_bda, sizeof(esp_bd_addr_t)); + case ESP_GATTS_CONNECT_EVT: break; - } case ESP_GATTS_DISCONNECT_EVT: ESP_LOGI(BT_BLE_COEX_TAG, "ESP_GATTS_DISCONNECT_EVT"); + prepare_write_env_free(&a_prepare_write_env); esp_ble_gap_start_advertising(&adv_params); break; case ESP_GATTS_CONF_EVT: @@ -431,63 +459,15 @@ static void gatts_profile_b_event_handler(esp_gatts_cb_event_t event, esp_gatt_i esp_ble_gatts_create_service(gatts_if, &gl_profile_tab[PROFILE_B_APP_ID].service_id, GATTS_NUM_HANDLE_B); break; - case ESP_GATTS_READ_EVT: { - ESP_LOGI(BT_BLE_COEX_TAG, "GATT_READ_EVT, conn_id %d, trans_id %"PRIu32", handle %d", param->read.conn_id, param->read.trans_id, param->read.handle); - esp_gatt_rsp_t rsp; - memset(&rsp, 0, sizeof(esp_gatt_rsp_t)); - rsp.attr_value.handle = param->read.handle; - rsp.attr_value.len = 4; - rsp.attr_value.value[0] = 0xde; - rsp.attr_value.value[1] = 0xed; - rsp.attr_value.value[2] = 0xbe; - rsp.attr_value.value[3] = 0xef; - esp_ble_gatts_send_response(gatts_if, param->read.conn_id, param->read.trans_id, - ESP_GATT_OK, &rsp); + case ESP_GATTS_READ_EVT: + gatts_coex_read_evt(gatts_if, param); break; - } - case ESP_GATTS_WRITE_EVT: { - ESP_LOGI(BT_BLE_COEX_TAG, "GATT_WRITE_EVT, conn_id %d, trans_id %"PRIu32", handle %d", param->write.conn_id, param->write.trans_id, param->write.handle); - if (!param->write.is_prep) { - ESP_LOGI(BT_BLE_COEX_TAG, "GATT_WRITE_EVT, value len %d, value :", param->write.len); - esp_log_buffer_hex(BT_BLE_COEX_TAG, param->write.value, param->write.len); - if (gl_profile_tab[PROFILE_B_APP_ID].descr_handle == param->write.handle && param->write.len == 2){ - uint16_t descr_value= param->write.value[1]<<8 | param->write.value[0]; - if (descr_value == 0x0001){ - if (b_property & ESP_GATT_CHAR_PROP_BIT_NOTIFY){ - ESP_LOGI(BT_BLE_COEX_TAG, "notify enable"); - uint8_t notify_data[15]; - for (int i = 0; i < sizeof(notify_data); ++i) { - notify_data[i] = i % 0xff; - } - //the size of notify_data[] need less than MTU size - esp_ble_gatts_send_indicate(gatts_if, param->write.conn_id, gl_profile_tab[PROFILE_B_APP_ID].char_handle, - sizeof(notify_data), notify_data, false); - } - } else if (descr_value == 0x0002) { - if (b_property & ESP_GATT_CHAR_PROP_BIT_INDICATE) { - ESP_LOGI(BT_BLE_COEX_TAG, "indicate enable"); - uint8_t indicate_data[15]; - for (int i = 0; i < sizeof(indicate_data); ++i) { - indicate_data[i] = i % 0xff; - } - //the size of indicate_data[] need less than MTU size - esp_ble_gatts_send_indicate(gatts_if, param->write.conn_id, gl_profile_tab[PROFILE_B_APP_ID].char_handle, - sizeof(indicate_data), indicate_data, true); - } - } else if (descr_value == 0x0000) { - ESP_LOGI(BT_BLE_COEX_TAG, "notify/indicate disable "); - } else { - ESP_LOGE(BT_BLE_COEX_TAG, "unknown value"); - } - - } - } - example_write_event_env(gatts_if, &b_prepare_write_env, param); + case ESP_GATTS_WRITE_EVT: + gatts_coex_write_evt(gatts_if, PROFILE_B_APP_ID, &b_prepare_write_env, b_property, param); break; - } case ESP_GATTS_EXEC_WRITE_EVT: ESP_LOGI(BT_BLE_COEX_TAG, "ESP_GATTS_EXEC_WRITE_EVT"); - esp_ble_gatts_send_response(gatts_if, param->write.conn_id, param->write.trans_id, ESP_GATT_OK, NULL); + esp_ble_gatts_send_response(gatts_if, param->exec_write.conn_id, param->exec_write.trans_id, ESP_GATT_OK, NULL); example_exec_write_event_env(&b_prepare_write_env, param); break; case ESP_GATTS_MTU_EVT: @@ -551,6 +531,9 @@ static void gatts_profile_b_event_handler(esp_gatts_cb_event_t event, esp_gatt_i } break; case ESP_GATTS_DISCONNECT_EVT: + ESP_LOGI(BT_BLE_COEX_TAG, "ESP_GATTS_DISCONNECT_EVT (profile B)"); + prepare_write_env_free(&b_prepare_write_env); + break; case ESP_GATTS_OPEN_EVT: case ESP_GATTS_CANCEL_OPEN_EVT: case ESP_GATTS_CLOSE_EVT: diff --git a/examples/bluetooth/bluedroid/coex/gattc_gatts_coex/main/gattc_gatts_coex.c b/examples/bluetooth/bluedroid/coex/gattc_gatts_coex/main/gattc_gatts_coex.c index 927e784b838..087e8a4edb9 100644 --- a/examples/bluetooth/bluedroid/coex/gattc_gatts_coex/main/gattc_gatts_coex.c +++ b/examples/bluetooth/bluedroid/coex/gattc_gatts_coex/main/gattc_gatts_coex.c @@ -104,6 +104,15 @@ static esp_gatt_char_prop_t b_property = 0; static prepare_type_env_t a_prepare_write_env; static prepare_type_env_t b_prepare_write_env; static uint8_t adv_config_done = 0; + +static void prepare_write_env_free(prepare_type_env_t *env) +{ + if (env->prepare_buf != NULL) { + free(env->prepare_buf); + env->prepare_buf = NULL; + } + env->prepare_len = 0; +} static uint8_t char1_str[] = {0x11, 0x22, 0x33}; static bool connect = false; static bool get_server = false; @@ -292,11 +301,17 @@ static void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param case ESP_GAP_BLE_SCAN_RESULT_EVT: { esp_ble_gap_cb_param_t *scan_result = (esp_ble_gap_cb_param_t *)param; switch (scan_result->scan_rst.search_evt) { - case ESP_GAP_SEARCH_INQ_RES_EVT: + case ESP_GAP_SEARCH_INQ_RES_EVT: { + const uint16_t ble_adv_storage_max = ESP_BLE_ADV_DATA_LEN_MAX + ESP_BLE_SCAN_RSP_DATA_LEN_MAX; + uint32_t combined_len = (uint32_t)scan_result->scan_rst.adv_data_len + + (uint32_t)scan_result->scan_rst.scan_rsp_len; + uint16_t resolve_len = (combined_len > ble_adv_storage_max) + ? ble_adv_storage_max + : (uint16_t)combined_len; adv_name = esp_ble_resolve_adv_data_by_type(scan_result->scan_rst.ble_adv, - scan_result->scan_rst.adv_data_len + scan_result->scan_rst.scan_rsp_len, - ESP_BLE_AD_TYPE_NAME_CMPL, - &adv_name_len); + resolve_len, + ESP_BLE_AD_TYPE_NAME_CMPL, + &adv_name_len); if (adv_name != NULL) { if (strlen(remote_device_name) == adv_name_len && strncmp((char *)adv_name, remote_device_name, adv_name_len) == 0) { if (connect == false) { @@ -323,6 +338,7 @@ static void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param } } break; + } case ESP_GAP_SEARCH_INQ_CMPL_EVT: ESP_LOGI(COEX_TAG, "ESP_GAP_SEARCH_INQ_CMPL_EVT, scan stop"); break; @@ -562,6 +578,7 @@ static void gattc_profile_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_ // Update connect flag and get_server flag if peer device is a gatt server connect = false; get_server = false; + gattc_profile_tab[GATTC_PROFILE_C_APP_ID].conn_id = UINT16_MAX; } ESP_LOGI(COEX_TAG, "ESP_GATTC_DISCONNECT_EVT, reason = %d", p_data->disconnect.reason); break; @@ -576,9 +593,11 @@ static void example_write_event_env(esp_gatt_if_t gatts_if, prepare_type_env_t * esp_gatt_status_t status = ESP_GATT_OK; if (param->write.need_rsp) { if (param->write.is_prep) { - if (param->write.offset > PREPARE_BUF_MAX_SIZE) { + size_t w_off = param->write.offset; + size_t w_len = param->write.len; + if (w_off > PREPARE_BUF_MAX_SIZE) { status = ESP_GATT_INVALID_OFFSET; - } else if ((param->write.offset + param->write.len) > PREPARE_BUF_MAX_SIZE) { + } else if (w_len > ESP_GATT_MAX_ATTR_LEN || (w_off + w_len) > PREPARE_BUF_MAX_SIZE) { status = ESP_GATT_INVALID_ATTR_LEN; } if (status == ESP_GATT_OK && prepare_write_env->prepare_buf == NULL) { @@ -590,13 +609,19 @@ static void example_write_event_env(esp_gatt_if_t gatts_if, prepare_type_env_t * } } - esp_gatt_rsp_t *gatt_rsp = (esp_gatt_rsp_t *)malloc(sizeof(esp_gatt_rsp_t)); + esp_gatt_rsp_t *gatt_rsp = (esp_gatt_rsp_t *)calloc(1, sizeof(esp_gatt_rsp_t)); if (gatt_rsp) { - gatt_rsp->attr_value.len = param->write.len; gatt_rsp->attr_value.handle = param->write.handle; gatt_rsp->attr_value.offset = param->write.offset; gatt_rsp->attr_value.auth_req = ESP_GATT_AUTH_REQ_NONE; - memcpy(gatt_rsp->attr_value.value, param->write.value, param->write.len); + if (status == ESP_GATT_OK) { + if (param->write.value == NULL) { + status = ESP_GATT_INVALID_ATTR_LEN; + } else { + gatt_rsp->attr_value.len = param->write.len; + memcpy(gatt_rsp->attr_value.value, param->write.value, param->write.len); + } + } esp_err_t response_err = esp_ble_gatts_send_response(gatts_if, param->write.conn_id, param->write.trans_id, status, gatt_rsp); if (response_err != ESP_OK) { ESP_LOGE(COEX_TAG, "Send response error\n"); @@ -604,15 +629,29 @@ static void example_write_event_env(esp_gatt_if_t gatts_if, prepare_type_env_t * free(gatt_rsp); } else { ESP_LOGE(COEX_TAG, "%s, malloc failed", __func__); - status = ESP_GATT_NO_RESOURCES; + if (status == ESP_GATT_OK) { + status = ESP_GATT_NO_RESOURCES; + } + esp_err_t response_err = esp_ble_gatts_send_response(gatts_if, param->write.conn_id, + param->write.trans_id, status, NULL); + if (response_err != ESP_OK) { + ESP_LOGE(COEX_TAG, "Send response error\n"); + } } if (status != ESP_GATT_OK) { return; } - memcpy(prepare_write_env->prepare_buf + param->write.offset, + memcpy(prepare_write_env->prepare_buf + w_off, param->write.value, - param->write.len); - prepare_write_env->prepare_len += param->write.len; + w_len); + /* High-water end of written range (not sum of chunk lengths). */ + int chunk_end = (int)(w_off + w_len); + if (chunk_end > prepare_write_env->prepare_len) { + prepare_write_env->prepare_len = chunk_end; + } + if (prepare_write_env->prepare_len > PREPARE_BUF_MAX_SIZE) { + prepare_write_env->prepare_len = PREPARE_BUF_MAX_SIZE; + } } else { esp_ble_gatts_send_response(gatts_if, param->write.conn_id, param->write.trans_id, status, NULL); @@ -623,15 +662,17 @@ static void example_write_event_env(esp_gatt_if_t gatts_if, prepare_type_env_t * static void example_exec_write_event_env(prepare_type_env_t *prepare_write_env, esp_ble_gatts_cb_param_t *param) { if (param->exec_write.exec_write_flag == ESP_GATT_PREP_WRITE_EXEC) { - esp_log_buffer_hex(COEX_TAG, prepare_write_env->prepare_buf, prepare_write_env->prepare_len); + if (prepare_write_env->prepare_buf != NULL && prepare_write_env->prepare_len > 0) { + size_t log_len = (size_t)prepare_write_env->prepare_len; + if (log_len > PREPARE_BUF_MAX_SIZE) { + log_len = PREPARE_BUF_MAX_SIZE; + } + ESP_LOG_BUFFER_HEX(COEX_TAG, prepare_write_env->prepare_buf, log_len); + } } else { ESP_LOGI(COEX_TAG, "ESP_GATT_PREP_WRITE_CANCEL"); } - if (prepare_write_env->prepare_buf) { - free(prepare_write_env->prepare_buf); - prepare_write_env->prepare_buf = NULL; - } - prepare_write_env->prepare_len = 0; + prepare_write_env_free(prepare_write_env); } static void gatts_profile_a_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param) @@ -792,6 +833,7 @@ static void gatts_profile_a_event_handler(esp_gatts_cb_event_t event, esp_gatt_i } case ESP_GATTS_DISCONNECT_EVT: ESP_LOGI(COEX_TAG, "ESP_GATTS_DISCONNECT_EVT, disconnect reason 0x%x", param->disconnect.reason); + prepare_write_env_free(&a_prepare_write_env); if (memcmp(peer_gatts_addr, param->disconnect.remote_bda, sizeof(esp_bd_addr_t))) { // If the peer device is a GATT client, restart advertising esp_ble_gap_start_advertising(&adv_params); @@ -799,8 +841,8 @@ static void gatts_profile_a_event_handler(esp_gatts_cb_event_t event, esp_gatt_i break; case ESP_GATTS_CONF_EVT: ESP_LOGI(COEX_TAG, "ESP_GATTS_CONF_EVT, status %d attr_handle %d", param->conf.status, param->conf.handle); - if (param->conf.status != ESP_GATT_OK) { - esp_log_buffer_hex(COEX_TAG, param->conf.value, param->conf.len); + if (param->conf.status == ESP_GATT_OK && param->conf.value != NULL && param->conf.len > 0) { + ESP_LOG_BUFFER_HEX(COEX_TAG, param->conf.value, param->conf.len); } break; case ESP_GATTS_OPEN_EVT: @@ -940,11 +982,14 @@ static void gatts_profile_b_event_handler(esp_gatts_cb_event_t event, esp_gatt_i break; case ESP_GATTS_CONF_EVT: ESP_LOGI(COEX_TAG, "ESP_GATTS_CONF_EVT status %d attr_handle %d", param->conf.status, param->conf.handle); - if (param->conf.status != ESP_GATT_OK) { - esp_log_buffer_hex(COEX_TAG, param->conf.value, param->conf.len); + if (param->conf.status == ESP_GATT_OK && param->conf.value != NULL && param->conf.len > 0) { + ESP_LOG_BUFFER_HEX(COEX_TAG, param->conf.value, param->conf.len); } break; case ESP_GATTS_DISCONNECT_EVT: + ESP_LOGI(COEX_TAG, "ESP_GATTS_DISCONNECT_EVT, disconnect reason 0x%x", param->disconnect.reason); + prepare_write_env_free(&b_prepare_write_env); + break; case ESP_GATTS_OPEN_EVT: default: break;