diff --git a/components/bt/common/btc/core/btc_task.c b/components/bt/common/btc/core/btc_task.c index 07d4cf6a2e4..ee3ce1fe153 100644 --- a/components/bt/common/btc/core/btc_task.c +++ b/components/bt/common/btc/core/btc_task.c @@ -347,7 +347,10 @@ bt_status_t btc_transfer_context(btc_msg_t *msg, void *arg, int arg_len, btc_arg memcpy(lmsg, msg, sizeof(btc_msg_t)); if (arg) { - memset(lmsg->arg, 0x00, arg_len); //important, avoid arg which have no length + /* memcpy below covers exactly arg_len bytes, which is the full size of + * the destination buffer (it was sized as sizeof(btc_msg_t) + arg_len), + * so a prior memset would be redundant. Deep-copy callbacks must only + * read fields that were written by the caller-supplied arg. */ memcpy(lmsg->arg, arg, arg_len); if (copy_func) { copy_func(lmsg, lmsg->arg, arg); diff --git a/components/bt/esp_ble_mesh/core/bluedroid_host/adapter.c b/components/bt/esp_ble_mesh/core/bluedroid_host/adapter.c index 5dbb0ae4189..f5a1b835c4a 100644 --- a/components/bt/esp_ble_mesh/core/bluedroid_host/adapter.c +++ b/components/bt/esp_ble_mesh/core/bluedroid_host/adapter.c @@ -2315,7 +2315,11 @@ static void bt_mesh_bta_gattc_cb(tBTA_GATTC_EVT event, tBTA_GATTC *p_data) } break; case BTA_GATTC_CLOSE_EVT: - bta_gattc_clcb_dealloc_by_conn_id(p_data->close.conn_id); + /* CLCB lifetime is owned by BTA: bta_gattc_close() deallocates the + * CLCB right after invoking this synchronous callback. Calling + * bta_gattc_clcb_dealloc_by_conn_id() here would be a redundant + * double-dealloc (currently a no-op only because of NULL checks in + * bta_gattc_clcb_dealloc()). Keep this branch as a pure notification. */ BT_DBG("BTA_GATTC_CLOSE_EVT"); break; case BTA_GATTC_CONNECT_EVT: { diff --git a/components/bt/host/bluedroid/api/esp_ble_cte_api.c b/components/bt/host/bluedroid/api/esp_ble_cte_api.c index faeb58e8705..27f489d19e2 100644 --- a/components/bt/host/bluedroid/api/esp_ble_cte_api.c +++ b/components/bt/host/bluedroid/api/esp_ble_cte_api.c @@ -34,7 +34,7 @@ esp_ble_cte_cb_t esp_ble_cte_get_callback(void) #if (BLE_FEAT_CTE_CONNECTIONLESS_EN == TRUE) esp_err_t esp_ble_cte_set_connectionless_trans_params(esp_ble_cte_connless_trans_params_t *cte_trans_params) { - btc_msg_t msg; + btc_msg_t msg = {0}; btc_ble_cte_args_t arg; memset(&arg, 0, sizeof(arg)); @@ -42,7 +42,15 @@ esp_err_t esp_ble_cte_set_connectionless_trans_params(esp_ble_cte_connless_trans return ESP_ERR_INVALID_STATE; } - if ((cte_trans_params == NULL) || (cte_trans_params->antenna_ids == NULL)) { + if (cte_trans_params == NULL) { + return ESP_ERR_INVALID_ARG; + } + /* + * Per Core Spec, switching_pattern_len and Antenna_IDs is ignored when no switching pattern is used + * For AoA CTE type, the transmitter does not + * switch antenna, so Antenna_IDs may be omitted as well. + */ + if ((cte_trans_params->cte_type != ESP_BLE_CTE_TYPE_AOA) && (cte_trans_params->antenna_ids == NULL)) { return ESP_ERR_INVALID_ARG; } // The controller has performed parameter checking, and the host will no longer verify the validity of these parameters repeatedly. @@ -72,15 +80,24 @@ esp_err_t esp_ble_cte_set_connectionless_trans_params(esp_ble_cte_connless_trans arg.cte_trans_params.cte_len = cte_trans_params->cte_len; arg.cte_trans_params.cte_type = cte_trans_params->cte_type; arg.cte_trans_params.cte_count = cte_trans_params->cte_count; - arg.cte_trans_params.switching_pattern_len = cte_trans_params->switching_pattern_len; - arg.cte_trans_params.antenna_ids = cte_trans_params->antenna_ids; + /* For AoA CTE type, the transmitter does not switch antenna; normalize + * switching_pattern_len and antenna_ids so the BTC layer does not perform + * an unnecessary deep copy of data that the controller will ignore. + */ + if (cte_trans_params->cte_type != ESP_BLE_CTE_TYPE_AOA) { + arg.cte_trans_params.switching_pattern_len = cte_trans_params->switching_pattern_len; + arg.cte_trans_params.antenna_ids = cte_trans_params->antenna_ids; + } else { + arg.cte_trans_params.switching_pattern_len = 0; + arg.cte_trans_params.antenna_ids = NULL; + } return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_cte_args_t), btc_ble_cte_arg_deep_copy, btc_ble_cte_arg_deep_free) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); } esp_err_t esp_ble_cte_set_connectionless_trans_enable(esp_ble_cte_trans_enable_params_t *cte_trans_enable) { - btc_msg_t msg; + btc_msg_t msg = {0}; btc_ble_cte_args_t arg; memset(&arg, 0, sizeof(arg)); @@ -108,7 +125,7 @@ esp_err_t esp_ble_cte_set_connectionless_trans_enable(esp_ble_cte_trans_enable_p esp_err_t esp_ble_cte_set_connectionless_iq_sampling_enable(esp_ble_cte_iq_sampling_params_t *iq_sampling_en) { - btc_msg_t msg; + btc_msg_t msg = {0}; btc_ble_cte_args_t arg; memset(&arg, 0, sizeof(arg)); @@ -169,7 +186,7 @@ esp_err_t esp_ble_cte_set_connectionless_iq_sampling_enable(esp_ble_cte_iq_sampl #if (BLE_FEAT_CTE_CONNECTION_EN == TRUE) esp_err_t esp_ble_cte_set_connection_receive_params(esp_ble_cte_recv_params_params_t *cte_recv_params) { - btc_msg_t msg; + btc_msg_t msg = {0}; btc_ble_cte_args_t arg; memset(&arg, 0, sizeof(arg)); @@ -221,7 +238,7 @@ esp_err_t esp_ble_cte_set_connection_receive_params(esp_ble_cte_recv_params_para esp_err_t esp_ble_cte_set_connection_transmit_params(esp_ble_cte_conn_trans_params_t *cte_conn_trans_params) { - btc_msg_t msg; + btc_msg_t msg = {0}; btc_ble_cte_args_t arg; memset(&arg, 0, sizeof(arg)); @@ -229,9 +246,19 @@ esp_err_t esp_ble_cte_set_connection_transmit_params(esp_ble_cte_conn_trans_para return ESP_ERR_INVALID_STATE; } - if ((cte_conn_trans_params == NULL) || (cte_conn_trans_params->antenna_ids == NULL)) { + if (cte_conn_trans_params == NULL) { return ESP_ERR_INVALID_ARG; } + /* + * Per Core Spec, switching_pattern_len and Antenna_IDs is ignored when no switching pattern is used + * For AoA CTE type, the transmitter does not + * switch antenna, so Antenna_IDs may be omitted as well. + */ + if ((cte_conn_trans_params->cte_types & (ESP_BLE_CTE_TYPES_AOD_RESPONSE_WITH_1US | ESP_BLE_CTE_TYPES_AOD_RESPONSE_WITH_2US)) && + (cte_conn_trans_params->antenna_ids == NULL)) { + return ESP_ERR_INVALID_ARG; + } + // The controller has performed parameter checking, and the host will no longer verify the validity of these parameters repeatedly. #if (0) if ((cte_conn_trans_params->switching_pattern_len < ESP_BLE_CTE_MIN_SWITCHING_PATTERN_LENGTH) || @@ -251,15 +278,25 @@ esp_err_t esp_ble_cte_set_connection_transmit_params(esp_ble_cte_conn_trans_para arg.cte_conn_trans_params.conn_handle = cte_conn_trans_params->conn_handle; arg.cte_conn_trans_params.cte_types = cte_conn_trans_params->cte_types; - arg.cte_conn_trans_params.switching_pattern_len = cte_conn_trans_params->switching_pattern_len; - arg.cte_conn_trans_params.antenna_ids = cte_conn_trans_params->antenna_ids; + /* Antenna switching is only required for AoD CTE responses; when only AoA + * is enabled, normalize switching_pattern_len and antenna_ids so the BTC + * layer does not perform an unnecessary deep copy of data that the + * controller will ignore. + */ + if (cte_conn_trans_params->cte_types & (ESP_BLE_CTE_TYPES_AOD_RESPONSE_WITH_1US | ESP_BLE_CTE_TYPES_AOD_RESPONSE_WITH_2US)) { + arg.cte_conn_trans_params.switching_pattern_len = cte_conn_trans_params->switching_pattern_len; + arg.cte_conn_trans_params.antenna_ids = cte_conn_trans_params->antenna_ids; + } else { + arg.cte_conn_trans_params.switching_pattern_len = 0; + arg.cte_conn_trans_params.antenna_ids = NULL; + } return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_cte_args_t), btc_ble_cte_arg_deep_copy, btc_ble_cte_arg_deep_free) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); } esp_err_t esp_ble_cte_connection_cte_request_enable(esp_ble_cte_req_en_params_t *cte_conn_req_en) { - btc_msg_t msg; + btc_msg_t msg = {0}; btc_ble_cte_args_t arg; memset(&arg, 0, sizeof(arg)); @@ -302,7 +339,7 @@ esp_err_t esp_ble_cte_connection_cte_request_enable(esp_ble_cte_req_en_params_t esp_err_t esp_ble_cte_connection_cte_response_enable(esp_ble_cte_rsp_en_params_t *cte_conn_rsp_en) { - btc_msg_t msg; + btc_msg_t msg = {0}; btc_ble_cte_args_t arg; memset(&arg, 0, sizeof(arg)); @@ -334,7 +371,7 @@ esp_err_t esp_ble_cte_connection_cte_response_enable(esp_ble_cte_rsp_en_params_t esp_err_t esp_ble_cte_read_antenna_information(void) { - btc_msg_t msg; + btc_msg_t msg = {0}; if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { return ESP_ERR_INVALID_STATE; diff --git a/components/bt/host/bluedroid/api/esp_ble_iso_api.c b/components/bt/host/bluedroid/api/esp_ble_iso_api.c index 132b8880add..486c8676535 100644 --- a/components/bt/host/bluedroid/api/esp_ble_iso_api.c +++ b/components/bt/host/bluedroid/api/esp_ble_iso_api.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: Apache-2.0 */ @@ -63,7 +63,8 @@ esp_err_t esp_ble_iso_create_big(esp_ble_iso_big_creat_params_t *big_creat_param if (big_creat_param->rtn > 0x1E) { return ESP_ERR_INVALID_ARG; } - if ((big_creat_param->phy != 0x01) && (big_creat_param->phy != 0x02) && (big_creat_param->phy != 0x04)) { + /* phy is a bit field: bit0=1M, bit1=2M, bit2=Coded (HCI_LE_Create_BIG, Core Spec): at least one bit. */ + if ((big_creat_param->phy == 0) || (big_creat_param->phy & ~0x07)) { return ESP_ERR_INVALID_ARG; } if (big_creat_param->packing > 0x01) { @@ -118,7 +119,9 @@ esp_err_t esp_ble_iso_create_big_test(esp_ble_iso_big_creat_test_params_t *big_c if (big_creat_test_param->max_pdu < 0x0001 || big_creat_test_param->max_pdu > 0x00FB) { return ESP_ERR_INVALID_ARG; } - if ((big_creat_test_param->phy != 0x01) && (big_creat_test_param->phy != 0x02) && (big_creat_test_param->phy != 0x04)) { + /* HCI_LE_Create_BIG_Test (Core Spec): host shall set exactly one of 1M / 2M / Coded PHY. */ + if ((big_creat_test_param->phy != 0x01) && (big_creat_test_param->phy != 0x02) && + (big_creat_test_param->phy != 0x04)) { return ESP_ERR_INVALID_ARG; } if (big_creat_test_param->framing > BLE_ISO_FRAMING_FRAMED_PDU_UNSEGMENTABLE_MODE) { diff --git a/components/bt/host/bluedroid/api/esp_gap_ble_api.c b/components/bt/host/bluedroid/api/esp_gap_ble_api.c index 311c5d281ca..9ac5a99b3bc 100644 --- a/components/bt/host/bluedroid/api/esp_gap_ble_api.c +++ b/components/bt/host/bluedroid/api/esp_gap_ble_api.c @@ -15,6 +15,9 @@ #include "btc/btc_ble_storage.h" #include "esp_random.h" +/* Hard upper bound to prevent excessive allocations in BTC/BTA layers. */ +#define ESP_GAP_BLE_EXT_ADV_DATA_MAX_LEN 1650U + esp_err_t esp_ble_gap_register_callback(esp_gap_ble_cb_t callback) { ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); @@ -158,7 +161,22 @@ esp_err_t esp_ble_gap_update_conn_params(esp_ble_conn_update_params_t *params) ESP_BLE_IS_VALID_PARAM(params->max_int, BLE_CONN_INT_MIN_HOST_CHECK, ESP_BLE_CONN_INT_MAX) && ESP_BLE_IS_VALID_PARAM(params->timeout, ESP_BLE_CONN_SUP_TOUT_MIN, ESP_BLE_CONN_SUP_TOUT_MAX) && (params->latency <= ESP_BLE_CONN_LATENCY_MAX) && - ((params->timeout * 10) >= ((1 + params->latency) * ((params->max_int * 5) >> 1))) && params->min_int <= params->max_int) { + /* + * Core Spec (Vol 6, Part B, Section 4.5.2): + * supervision_timeout shall be strictly greater than + * (1 + connSlaveLatency) * connIntervalMax * 2. + * + * Here: + * - timeout is in 10 ms units + * - max_int is in 1.25 ms units + * + * Convert both sides into 0.5 ms units to avoid truncation: + * (timeout * 10 ms) -> timeout * 20 (0.5 ms units) + * (max_int * 1.25 ms * 2) -> max_int * 5 (0.5 ms units) + */ + (((uint32_t)params->timeout * 20U) > + ((uint32_t)(1U + (uint32_t)params->latency) * (uint32_t)params->max_int * 5U)) && + (params->min_int <= params->max_int)) { msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GAP_BLE; @@ -339,7 +357,7 @@ esp_err_t esp_ble_gap_update_whitelist(bool add_remove, esp_bd_addr_t remote_bda return ESP_ERR_INVALID_STATE; } if (!remote_bda){ - return ESP_ERR_INVALID_SIZE; + return ESP_ERR_INVALID_ARG; } msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GAP_BLE; @@ -369,7 +387,7 @@ esp_err_t esp_ble_gap_clear_whitelist(void) esp_err_t esp_ble_gap_get_whitelist_size(uint16_t *length) { if (length == NULL) { - return ESP_FAIL; + return ESP_ERR_INVALID_ARG; } ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); btc_get_whitelist_size(length); @@ -397,7 +415,9 @@ esp_err_t esp_ble_gap_set_prefer_conn_params(esp_bd_addr_t bd_addr, ESP_BLE_IS_VALID_PARAM(max_conn_int, BLE_CONN_INT_MIN_HOST_CHECK, ESP_BLE_CONN_INT_MAX) && ESP_BLE_IS_VALID_PARAM(supervision_tout, ESP_BLE_CONN_SUP_TOUT_MIN, ESP_BLE_CONN_SUP_TOUT_MAX) && (slave_latency <= ESP_BLE_CONN_LATENCY_MAX) && - ((supervision_tout * 10) >= ((1 + slave_latency) * ((max_conn_int * 5) >> 1))) && min_conn_int <= max_conn_int) { + (((uint32_t)supervision_tout * 20U) > + ((uint32_t)(1U + (uint32_t)slave_latency) * (uint32_t)max_conn_int * 5U)) && + (min_conn_int <= max_conn_int)) { msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GAP_BLE; @@ -678,7 +698,7 @@ esp_err_t esp_ble_gap_set_security_param(esp_ble_sm_param_t param_type, uint32_t passkey = 0; for(uint8_t i = 0; i < len; i++) { - passkey += (((uint8_t *)value)[i]<<(8*i)); + passkey += ((uint32_t)((const uint8_t *)value)[i] << (8U * (uint32_t)i)); } if(passkey > 999999) { return ESP_ERR_INVALID_ARG; @@ -846,6 +866,15 @@ esp_err_t esp_ble_get_bond_device_list(int *dev_num, esp_ble_bond_dev_t *dev_lis *dev_num = dev_num_total; } + /* + * The storage layer updates some fields using |= (e.g. key_mask). Ensure + * the caller-provided list is zero-initialized to avoid propagating + * uninitialized heap contents (including padding) back to the caller. + */ + if (*dev_num > 0) { + memset(dev_list, 0, sizeof(*dev_list) * (size_t)(*dev_num)); + } + ret = btc_storage_get_bonded_ble_devices_list(dev_list, *dev_num); return (ret == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); @@ -1293,6 +1322,10 @@ esp_err_t esp_ble_gap_config_ext_adv_data_raw(uint8_t instance, uint16_t length, return ESP_ERR_INVALID_ARG; } + if (length > ESP_GAP_BLE_EXT_ADV_DATA_MAX_LEN) { + return ESP_ERR_INVALID_ARG; + } + msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GAP_BLE; @@ -1319,6 +1352,10 @@ esp_err_t esp_ble_gap_config_ext_scan_rsp_data_raw(uint8_t instance, uint16_t le return ESP_ERR_INVALID_ARG; } + if (length > ESP_GAP_BLE_EXT_ADV_DATA_MAX_LEN) { + return ESP_ERR_INVALID_ARG; + } + msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GAP_BLE; msg.act = BTC_GAP_BLE_CFG_EXT_SCAN_RSP_DATA_RAW; @@ -1454,6 +1491,10 @@ esp_err_t esp_ble_gap_config_periodic_adv_data_raw(uint8_t instance, uint16_t le return ESP_ERR_INVALID_ARG; } + if (length > ESP_GAP_BLE_EXT_ADV_DATA_MAX_LEN) { + return ESP_ERR_INVALID_ARG; + } + msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GAP_BLE; @@ -1751,7 +1792,8 @@ esp_err_t esp_ble_gap_prefer_ext_connect_params_set(esp_bd_addr_t addr, ESP_BLE_IS_VALID_PARAM(phy_1m_conn_params->interval_max, BLE_CONN_INT_MIN_HOST_CHECK, ESP_BLE_CONN_INT_MAX) && ESP_BLE_IS_VALID_PARAM(phy_1m_conn_params->supervision_timeout, ESP_BLE_CONN_SUP_TOUT_MIN, ESP_BLE_CONN_SUP_TOUT_MAX) && (phy_1m_conn_params->latency <= ESP_BLE_CONN_LATENCY_MAX) && - ((phy_1m_conn_params->supervision_timeout * 10) >= ((1 + phy_1m_conn_params->latency) * ((phy_1m_conn_params->interval_max * 5) >> 1))) && + (((uint32_t)phy_1m_conn_params->supervision_timeout * 20U) > + ((uint32_t)(1U + (uint32_t)phy_1m_conn_params->latency) * (uint32_t)phy_1m_conn_params->interval_max * 5U)) && (phy_1m_conn_params->interval_min <= phy_1m_conn_params->interval_max)) { memcpy(&arg.set_ext_conn_params.phy_1m_conn_params, phy_1m_conn_params, sizeof(esp_ble_gap_conn_params_t)); @@ -1775,7 +1817,8 @@ esp_err_t esp_ble_gap_prefer_ext_connect_params_set(esp_bd_addr_t addr, ESP_BLE_IS_VALID_PARAM(phy_2m_conn_params->interval_max, BLE_CONN_INT_MIN_HOST_CHECK, ESP_BLE_CONN_INT_MAX) && ESP_BLE_IS_VALID_PARAM(phy_2m_conn_params->supervision_timeout, ESP_BLE_CONN_SUP_TOUT_MIN, ESP_BLE_CONN_SUP_TOUT_MAX) && (phy_2m_conn_params->latency <= ESP_BLE_CONN_LATENCY_MAX) && - ((phy_2m_conn_params->supervision_timeout * 10) >= ((1 + phy_2m_conn_params->latency) * ((phy_2m_conn_params->interval_max * 5) >> 1))) && + (((uint32_t)phy_2m_conn_params->supervision_timeout * 20U) > + ((uint32_t)(1U + (uint32_t)phy_2m_conn_params->latency) * (uint32_t)phy_2m_conn_params->interval_max * 5U)) && (phy_2m_conn_params->interval_min <= phy_2m_conn_params->interval_max)) { memcpy(&arg.set_ext_conn_params.phy_2m_conn_params, phy_2m_conn_params, sizeof(esp_ble_gap_conn_params_t)); @@ -1799,7 +1842,8 @@ esp_err_t esp_ble_gap_prefer_ext_connect_params_set(esp_bd_addr_t addr, ESP_BLE_IS_VALID_PARAM(phy_coded_conn_params->interval_max, BLE_CONN_INT_MIN_HOST_CHECK, ESP_BLE_CONN_INT_MAX) && ESP_BLE_IS_VALID_PARAM(phy_coded_conn_params->supervision_timeout, ESP_BLE_CONN_SUP_TOUT_MIN, ESP_BLE_CONN_SUP_TOUT_MAX) && (phy_coded_conn_params->latency <= ESP_BLE_CONN_LATENCY_MAX) && - ((phy_coded_conn_params->supervision_timeout * 10) >= ((1 + phy_coded_conn_params->latency) * ((phy_coded_conn_params->interval_max * 5) >> 1))) && + (((uint32_t)phy_coded_conn_params->supervision_timeout * 20U) > + ((uint32_t)(1U + (uint32_t)phy_coded_conn_params->latency) * (uint32_t)phy_coded_conn_params->interval_max * 5U)) && (phy_coded_conn_params->interval_min <= phy_coded_conn_params->interval_max)) { memcpy(&arg.set_ext_conn_params.phy_coded_conn_params, phy_coded_conn_params, sizeof(esp_ble_gap_conn_params_t)); diff --git a/components/bt/host/bluedroid/api/esp_gattc_api.c b/components/bt/host/bluedroid/api/esp_gattc_api.c index 2a61abeb053..ff4a0684e76 100644 --- a/components/bt/host/bluedroid/api/esp_gattc_api.c +++ b/components/bt/host/bluedroid/api/esp_gattc_api.c @@ -397,7 +397,9 @@ esp_err_t esp_ble_gattc_search_service(esp_gatt_if_t gattc_if, uint16_t conn_id, esp_gatt_status_t esp_ble_gattc_get_service(esp_gatt_if_t gattc_if, uint16_t conn_id, esp_bt_uuid_t *svc_uuid, esp_gattc_service_elem_t *result, uint16_t *count, uint16_t offset) { - ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_GATT_WRONG_STATE; + } if (result == NULL || count == NULL || *count == 0) { return ESP_GATT_INVALID_PDU; @@ -415,14 +417,16 @@ esp_gatt_status_t esp_ble_gattc_get_all_char(esp_gatt_if_t gattc_if, esp_gattc_char_elem_t *result, uint16_t *count, uint16_t offset) { - ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_GATT_WRONG_STATE; + } if (result == NULL || count == NULL || *count == 0) { return ESP_GATT_INVALID_PDU; } - if ((start_handle == 0) && (end_handle == 0)) { + if ((start_handle == 0 && end_handle == 0) || start_handle > end_handle) { *count = 0; return ESP_GATT_INVALID_HANDLE; } @@ -437,7 +441,9 @@ esp_gatt_status_t esp_ble_gattc_get_all_descr(esp_gatt_if_t gattc_if, esp_gattc_descr_elem_t *result, uint16_t *count, uint16_t offset) { - ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_GATT_WRONG_STATE; + } if (char_handle == 0) { return ESP_GATT_INVALID_HANDLE; @@ -459,13 +465,15 @@ esp_gatt_status_t esp_ble_gattc_get_char_by_uuid(esp_gatt_if_t gattc_if, esp_gattc_char_elem_t *result, uint16_t *count) { - ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_GATT_WRONG_STATE; + } if (result == NULL || count == NULL || *count == 0) { return ESP_GATT_INVALID_PDU; } - if (start_handle == 0 && end_handle == 0) { + if ((start_handle == 0 && end_handle == 0) || start_handle > end_handle) { *count = 0; return ESP_GATT_INVALID_HANDLE; } @@ -484,12 +492,19 @@ esp_gatt_status_t esp_ble_gattc_get_descr_by_uuid(esp_gatt_if_t gattc_if, esp_gattc_descr_elem_t *result, uint16_t *count) { - ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_GATT_WRONG_STATE; + } if (result == NULL || count == NULL || *count == 0) { return ESP_GATT_INVALID_PDU; } + if ((start_handle == 0 && end_handle == 0) || start_handle > end_handle) { + *count = 0; + return ESP_GATT_INVALID_HANDLE; + } + uint16_t conn_hdl = BTC_GATT_CREATE_CONN_ID(gattc_if, conn_id); return btc_ble_gattc_get_descr_by_uuid(conn_hdl, start_handle, end_handle, char_uuid, descr_uuid, result, count); } @@ -501,7 +516,9 @@ esp_gatt_status_t esp_ble_gattc_get_descr_by_char_handle(esp_gatt_if_t gattc_if, esp_gattc_descr_elem_t *result, uint16_t *count) { - ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_GATT_WRONG_STATE; + } if (result == NULL || count == NULL || *count == 0) { return ESP_GATT_INVALID_PDU; @@ -524,13 +541,15 @@ esp_gatt_status_t esp_ble_gattc_get_include_service(esp_gatt_if_t gattc_if, esp_gattc_incl_svc_elem_t *result, uint16_t *count) { - ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_GATT_WRONG_STATE; + } if (result == NULL || count == NULL || *count == 0) { return ESP_GATT_INVALID_PDU; } - if (start_handle == 0 && end_handle == 0) { + if ((start_handle == 0 && end_handle == 0) || start_handle > end_handle) { *count = 0; return ESP_GATT_INVALID_HANDLE; } @@ -547,13 +566,17 @@ esp_gatt_status_t esp_ble_gattc_get_attr_count(esp_gatt_if_t gattc_if, uint16_t char_handle, uint16_t *count) { - ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_GATT_WRONG_STATE; + } if (count == NULL) { return ESP_GATT_INVALID_PDU; } - if ((start_handle == 0 && end_handle == 0) && (type != ESP_GATT_DB_DESCRIPTOR)) { + /* start_handle/end_handle are ignored for ESP_GATT_DB_DESCRIPTOR (see esp_gattc_api.h). */ + if (type != ESP_GATT_DB_DESCRIPTOR && + ((start_handle == 0 && end_handle == 0) || start_handle > end_handle)) { *count = 0; return ESP_GATT_INVALID_HANDLE; } @@ -565,14 +588,16 @@ esp_gatt_status_t esp_ble_gattc_get_attr_count(esp_gatt_if_t gattc_if, esp_gatt_status_t esp_ble_gattc_get_db(esp_gatt_if_t gattc_if, uint16_t conn_id, uint16_t start_handle, uint16_t end_handle, esp_gattc_db_elem_t *db, uint16_t *count) { - ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_GATT_WRONG_STATE; + } if (db == NULL || count == NULL || *count == 0) { return ESP_GATT_INVALID_PDU; } - if (start_handle == 0 && end_handle == 0) { + if ((start_handle == 0 && end_handle == 0) || start_handle > end_handle) { *count = 0; return ESP_GATT_INVALID_HANDLE; } @@ -644,10 +669,14 @@ esp_err_t esp_ble_gattc_read_by_type (esp_gatt_if_t gattc_if, return ESP_FAIL; } - if (start_handle == 0 || end_handle == 0) { + if ((start_handle == 0 && end_handle == 0) || start_handle > end_handle) { return ESP_GATT_INVALID_HANDLE; } + if (start_handle == 0) { + start_handle = 1; + } + msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GATTC; msg.act = BTC_GATTC_ACT_READ_BY_TYPE; @@ -670,7 +699,12 @@ esp_err_t esp_ble_gattc_read_multiple(esp_gatt_if_t gattc_if, ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); - if ((read_multi == NULL) || (read_multi->num_attr == 0) || (read_multi->num_attr > ESP_GATT_MAX_READ_MULTI_HANDLES)) { + if (read_multi == NULL) { + return ESP_ERR_INVALID_ARG; + } + uint8_t num_attr = read_multi->num_attr; + + if ((num_attr == 0) || (num_attr > ESP_GATT_MAX_READ_MULTI_HANDLES)) { return ESP_ERR_INVALID_ARG; } @@ -689,10 +723,10 @@ esp_err_t esp_ble_gattc_read_multiple(esp_gatt_if_t gattc_if, msg.pid = BTC_PID_GATTC; msg.act = BTC_GATTC_ACT_READ_MULTIPLE_CHAR; arg.read_multiple.conn_id = BTC_GATT_CREATE_CONN_ID(gattc_if, conn_id); - arg.read_multiple.num_attr = read_multi->num_attr; + arg.read_multiple.num_attr = num_attr; arg.read_multiple.auth_req = auth_req; - memcpy(arg.read_multiple.handles, read_multi->handles, sizeof(uint16_t)*read_multi->num_attr); + memcpy(arg.read_multiple.handles, read_multi->handles, sizeof(uint16_t) * num_attr); return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gattc_args_t), NULL, NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); } @@ -707,7 +741,12 @@ esp_err_t esp_ble_gattc_read_multiple_variable(esp_gatt_if_t gattc_if, ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); - if ((read_multi == NULL) || (read_multi->num_attr == 0) || (read_multi->num_attr > ESP_GATT_MAX_READ_MULTI_HANDLES)) { + if (read_multi == NULL) { + return ESP_ERR_INVALID_ARG; + } + uint8_t num_attr = read_multi->num_attr; + + if ((num_attr == 0) || (num_attr > ESP_GATT_MAX_READ_MULTI_HANDLES)) { return ESP_ERR_INVALID_ARG; } @@ -726,9 +765,9 @@ esp_err_t esp_ble_gattc_read_multiple_variable(esp_gatt_if_t gattc_if, msg.pid = BTC_PID_GATTC; msg.act = BTC_GATTC_ACT_READ_MULTIPLE_VARIABLE_CHAR; arg.read_multiple.conn_id = BTC_GATT_CREATE_CONN_ID(gattc_if, conn_id); - arg.read_multiple.num_attr = read_multi->num_attr; + arg.read_multiple.num_attr = num_attr; arg.read_multiple.auth_req = auth_req; - memcpy(arg.read_multiple.handles, read_multi->handles, sizeof(uint16_t)*read_multi->num_attr); + memcpy(arg.read_multiple.handles, read_multi->handles, sizeof(uint16_t) * num_attr); return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gattc_args_t), NULL, NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); } @@ -829,6 +868,10 @@ esp_err_t esp_ble_gattc_write_char_descr (esp_gatt_if_t gattc_if, ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + if ((value_len > 0) && (value == NULL)) { + return ESP_ERR_INVALID_ARG; + } + tGATT_TCB *p_tcb = gatt_get_tcb_by_idx(conn_id); if (!gatt_check_connection_state_by_tcb(p_tcb)) { LOG_WARN("%s, The connection not created.", __func__); @@ -873,6 +916,10 @@ esp_err_t esp_ble_gattc_prepare_write(esp_gatt_if_t gattc_if, ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + if ((value_len > 0) && (value == NULL)) { + return ESP_ERR_INVALID_ARG; + } + tGATT_TCB *p_tcb = gatt_get_tcb_by_idx(conn_id); if (!gatt_check_connection_state_by_tcb(p_tcb)) { LOG_WARN("%s, The connection not created.", __func__); @@ -915,6 +962,10 @@ esp_err_t esp_ble_gattc_prepare_write_char_descr(esp_gatt_if_t gattc_if, ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + if ((value_len > 0) && (value == NULL)) { + return ESP_ERR_INVALID_ARG; + } + tGATT_TCB *p_tcb = gatt_get_tcb_by_idx(conn_id); if (!gatt_check_connection_state_by_tcb(p_tcb)) { LOG_WARN("%s, The connection not created.", __func__); diff --git a/components/bt/host/bluedroid/api/esp_gatts_api.c b/components/bt/host/bluedroid/api/esp_gatts_api.c index ceaf047875c..c93b2bdc96b 100644 --- a/components/bt/host/bluedroid/api/esp_gatts_api.c +++ b/components/bt/host/bluedroid/api/esp_gatts_api.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2015-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -384,7 +384,9 @@ esp_err_t esp_ble_gatts_set_attr_value(uint16_t attr_handle, uint16_t length, co esp_gatt_status_t esp_ble_gatts_get_attr_value(uint16_t attr_handle, uint16_t *length, const uint8_t **value) { - ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { + return ESP_GATT_WRONG_STATE; + } if (length == NULL || value == NULL) { return ESP_GATT_INVALID_PDU; @@ -463,20 +465,44 @@ esp_err_t esp_ble_gatts_send_service_change_indication(esp_gatt_if_t gatts_if, e static esp_err_t esp_ble_gatts_add_char_desc_param_check(esp_attr_value_t *char_val, esp_attr_control_t *control) { - if ((control != NULL) && ((control->auto_rsp != ESP_GATT_AUTO_RSP) && (control->auto_rsp != ESP_GATT_RSP_BY_APP))){ - LOG_ERROR("Error in %s, line=%d, control->auto_rsp should be set to ESP_GATT_AUTO_RSP or ESP_GATT_RSP_BY_APP\n",\ - __func__, __LINE__); - return ESP_ERR_INVALID_ARG; + if ((control != NULL) && + (control->auto_rsp != ESP_GATT_AUTO_RSP) && + (control->auto_rsp != ESP_GATT_RSP_BY_APP)) { + return ESP_ERR_INVALID_ARG; } - if ((control != NULL) && (control->auto_rsp == ESP_GATT_AUTO_RSP)){ - if (char_val == NULL){ - LOG_ERROR("Error in %s, line=%d, for stack respond attribute, char_val should not be NULL here\n",\ - __func__, __LINE__); + /* Validate attribute value regardless of auto_rsp to avoid deep-copy waste and leaks. */ + if (char_val != NULL) { + bool invalid = false; + + if (char_val->attr_max_len > ESP_GATT_MAX_ATTR_LEN) { + invalid = true; + } + if (char_val->attr_len > ESP_GATT_MAX_ATTR_LEN) { + invalid = true; + } + /* Always require attr_len <= attr_max_len to avoid leaks and wasteful allocations. */ + if (char_val->attr_len > char_val->attr_max_len) { + invalid = true; + } + + if (invalid) { + LOG_ERROR("%s bad attr len=%u/%u lim=%u", + __func__, + (unsigned)char_val->attr_len, + (unsigned)char_val->attr_max_len, + (unsigned)ESP_GATT_MAX_ATTR_LEN); return ESP_ERR_INVALID_ARG; - } else if (char_val->attr_max_len == 0){ - LOG_ERROR("Error in %s, line=%d, for stack respond attribute, attribute max length should not be 0\n",\ - __func__, __LINE__); + } + } + + if ((control != NULL) && (control->auto_rsp == ESP_GATT_AUTO_RSP)) { + if (char_val == NULL) { + return ESP_ERR_INVALID_ARG; + } + + /* For stack auto response, attr_max_len must be non-zero. */ + if (char_val->attr_max_len == 0) { return ESP_ERR_INVALID_ARG; } } diff --git a/components/bt/host/bluedroid/api/include/api/esp_ble_iso_api.h b/components/bt/host/bluedroid/api/include/api/esp_ble_iso_api.h index c2afa745c75..d8eeb5b82d6 100644 --- a/components/bt/host/bluedroid/api/include/api/esp_ble_iso_api.h +++ b/components/bt/host/bluedroid/api/include/api/esp_ble_iso_api.h @@ -69,7 +69,7 @@ typedef enum { #define BLE_ISO_WORST_CASE_SCA_LEVEL_20_PPM (0x07) #define BLE_ISO_PACKING_SEQUENTIAL (0x00) -#define BLE_ISO_PACKING_INTERLEAVED (0x00) +#define BLE_ISO_PACKING_INTERLEAVED (0x01) #define BLE_ISO_FRAMING_UNFRAMED_PDU (0x00) #define BLE_ISO_FRAMING_FRAMED_PDU_SEGMENTABLE_MODE (0x01) diff --git a/components/bt/host/bluedroid/api/include/api/esp_gap_ble_api.h b/components/bt/host/bluedroid/api/include/api/esp_gap_ble_api.h index c1ceb1e021e..3b4e10f9847 100644 --- a/components/bt/host/bluedroid/api/include/api/esp_gap_ble_api.h +++ b/components/bt/host/bluedroid/api/include/api/esp_gap_ble_api.h @@ -2532,7 +2532,6 @@ typedef union { */ struct ble_cs_read_local_supp_caps_evt { esp_bt_status_t status; /*!< Indicate channel sounding read local supported capabilities command successfully completed */ - uint16_t conn_handle; /*!< Connection Handle */ uint8_t num_config_supported; /*!< Number of CS configurations supported per connection */ uint16_t max_consecutive_proc_supported; /*!< 0x0000: Support for both a fixed number of consecutive CS procedures and for an indefinite number of CS procedures until termination 0x0001 to 0xFFFF: Maximum number of consecutive CS procedures supported */ @@ -3113,6 +3112,11 @@ esp_err_t esp_ble_gap_add_device_to_resolving_list(esp_bd_addr_t peer_addr, uint /** * @brief This function clears the random address for the application * + * @note This function shall not be used when: + * - Advertising is enabled, + * - Scanning is enabled, or + * - any LE connection exists / a create connection command is pending. + * * @return * - ESP_OK : success * - other : failed diff --git a/components/bt/host/bluedroid/api/include/api/esp_gattc_api.h b/components/bt/host/bluedroid/api/include/api/esp_gattc_api.h index 65979ba4abf..800bcf5c768 100644 --- a/components/bt/host/bluedroid/api/include/api/esp_gattc_api.h +++ b/components/bt/host/bluedroid/api/include/api/esp_gattc_api.h @@ -519,8 +519,12 @@ esp_err_t esp_ble_gattc_search_service(esp_gatt_if_t gattc_if, uint16_t conn_id, * 2. `esp_ble_gattc_cache_refresh` can be used to discover services again. * * @return - * - ESP_OK: Success - * - ESP_FAIL: Failure + * - ESP_GATT_OK: Success + * - ESP_GATT_WRONG_STATE: Bluedroid stack is not enabled + * - ESP_GATT_INVALID_PDU: NULL pointer to `result` or NULL pointer to `count` or the count value is 0 + * - ESP_GATT_NOT_FOUND: No matching service was found in the local cache + * - ESP_GATT_INVALID_OFFSET: `offset` is out of range of the matched services + * - ESP_GATT_NO_RESOURCES: Failed to allocate memory for the UUID lookup */ esp_gatt_status_t esp_ble_gattc_get_service(esp_gatt_if_t gattc_if, uint16_t conn_id, esp_bt_uuid_t *svc_uuid, esp_gattc_service_elem_t *result, uint16_t *count, uint16_t offset); @@ -538,13 +542,17 @@ esp_gatt_status_t esp_ble_gattc_get_service(esp_gatt_if_t gattc_if, uint16_t con * * @note * 1. This API does not trigger any event. - * 2. `start_handle` must be greater than 0, and smaller than `end_handle`. + * 2. `start_handle` must not be greater than `end_handle`, and `start_handle` and + * `end_handle` must not both be 0. 0 is allowed for `start_handle` alone and matches + * cached attributes from the beginning of the handle range. * * @return - * - ESP_OK: Success + * - ESP_GATT_OK: Success + * - ESP_GATT_WRONG_STATE: Bluedroid stack is not enabled * - ESP_GATT_INVALID_HANDLE: Invalid GATT `start_handle` or `end_handle` * - ESP_GATT_INVALID_PDU: NULL pointer to `result` or NULL pointer to `count` or the count value is 0 - * - ESP_FAIL: Failure due to other reasons + * - ESP_GATT_NOT_FOUND: No characteristic found in the given handle range + * - ESP_GATT_INVALID_OFFSET: `offset` is out of range of the matched characteristics */ esp_gatt_status_t esp_ble_gattc_get_all_char(esp_gatt_if_t gattc_if, uint16_t conn_id, @@ -568,10 +576,12 @@ esp_gatt_status_t esp_ble_gattc_get_all_char(esp_gatt_if_t gattc_if, * 2. `char_handle` must be greater than 0. * * @return - * - ESP_OK: Success + * - ESP_GATT_OK: Success + * - ESP_GATT_WRONG_STATE: Bluedroid stack is not enabled * - ESP_GATT_INVALID_HANDLE: Invalid GATT `char_handle` * - ESP_GATT_INVALID_PDU: NULL pointer to `result` or NULL pointer to `count` or the count value is 0 - * - ESP_FAIL: Failure due to other reasons + * - ESP_GATT_NOT_FOUND: No descriptor found under the given characteristic + * - ESP_GATT_INVALID_OFFSET: `offset` is out of range of the matched descriptors */ esp_gatt_status_t esp_ble_gattc_get_all_descr(esp_gatt_if_t gattc_if, uint16_t conn_id, @@ -592,13 +602,16 @@ esp_gatt_status_t esp_ble_gattc_get_all_descr(esp_gatt_if_t gattc_if, * * @note * 1. This API does not trigger any event. - * 2. `start_handle` must be greater than 0, and smaller than `end_handle`. + * 2. `start_handle` must not be greater than `end_handle`, and `start_handle` and + * `end_handle` must not both be 0. 0 is allowed for `start_handle` alone and matches + * cached attributes from the beginning of the handle range. * * @return - * - ESP_OK: Success + * - ESP_GATT_OK: Success + * - ESP_GATT_WRONG_STATE: Bluedroid stack is not enabled * - ESP_GATT_INVALID_HANDLE: Invalid GATT `start_handle` or `end_handle` * - ESP_GATT_INVALID_PDU: NULL pointer to `result` or NULL pointer to `count` or the count value is 0 - * - ESP_FAIL: Failure due to other reasons + * - ESP_GATT_NOT_FOUND: No characteristic matching `char_uuid` found in the handle range */ esp_gatt_status_t esp_ble_gattc_get_char_by_uuid(esp_gatt_if_t gattc_if, uint16_t conn_id, @@ -622,12 +635,16 @@ esp_gatt_status_t esp_ble_gattc_get_char_by_uuid(esp_gatt_if_t gattc_if, * * @note * 1. This API does not trigger any event. - * 2. `start_handle` must be greater than 0, and smaller than `end_handle`. + * 2. `start_handle` must not be greater than `end_handle`, and `start_handle` and + * `end_handle` must not both be 0. 0 is allowed for `start_handle` alone and matches + * cached attributes from the beginning of the handle range. * * @return - * - ESP_OK: Success + * - ESP_GATT_OK: Success + * - ESP_GATT_WRONG_STATE: Bluedroid stack is not enabled + * - ESP_GATT_INVALID_HANDLE: Invalid GATT `start_handle` or `end_handle` * - ESP_GATT_INVALID_PDU: NULL pointer to `result` or NULL pointer to `count` or the count value is 0 - * - ESP_FAIL: Failure due to other reasons + * - ESP_GATT_NOT_FOUND: No descriptor matching the given UUIDs found in the handle range */ esp_gatt_status_t esp_ble_gattc_get_descr_by_uuid(esp_gatt_if_t gattc_if, uint16_t conn_id, @@ -653,10 +670,11 @@ esp_gatt_status_t esp_ble_gattc_get_descr_by_uuid(esp_gatt_if_t gattc_if, * 2. `char_handle` must be greater than 0. * * @return - * - ESP_OK: Success + * - ESP_GATT_OK: Success + * - ESP_GATT_WRONG_STATE: Bluedroid stack is not enabled * - ESP_GATT_INVALID_HANDLE: Invalid GATT `char_handle` * - ESP_GATT_INVALID_PDU: NULL pointer to `result` or NULL pointer to `count` or the count value is 0 - * - ESP_FAIL: Failure due to other reasons + * - ESP_GATT_NOT_FOUND: No descriptor matching `descr_uuid` found under `char_handle` */ esp_gatt_status_t esp_ble_gattc_get_descr_by_char_handle(esp_gatt_if_t gattc_if, uint16_t conn_id, @@ -678,12 +696,16 @@ esp_gatt_status_t esp_ble_gattc_get_descr_by_char_handle(esp_gatt_if_t gattc_if, * * @note * 1. This API does not trigger any event. - * 2. `start_handle` must be greater than 0, and smaller than `end_handle`. + * 2. `start_handle` must not be greater than `end_handle`, and `start_handle` and + * `end_handle` must not both be 0. 0 is allowed for `start_handle` alone and matches + * cached attributes from the beginning of the handle range. * * @return - * - ESP_OK: Success + * - ESP_GATT_OK: Success + * - ESP_GATT_WRONG_STATE: Bluedroid stack is not enabled + * - ESP_GATT_INVALID_HANDLE: Invalid GATT `start_handle` or `end_handle` * - ESP_GATT_INVALID_PDU: NULL pointer to `result` or NULL pointer to `count` or the count value is 0 - * - ESP_FAIL: Failure due to other reasons + * - ESP_GATT_NOT_FOUND: No included service matching `incl_uuid` found in the handle range */ esp_gatt_status_t esp_ble_gattc_get_include_service(esp_gatt_if_t gattc_if, uint16_t conn_id, @@ -707,13 +729,15 @@ esp_gatt_status_t esp_ble_gattc_get_include_service(esp_gatt_if_t gattc_if, * * @note * 1. This API does not trigger any event. - * 2. `start_handle` must be greater than 0, and smaller than `end_handle` if the `type` is not `ESP_GATT_DB_DESCRIPTOR`. + * 2. If the `type` is not `ESP_GATT_DB_DESCRIPTOR`, `start_handle` must not be greater than + * `end_handle`, and `start_handle` and `end_handle` must not both be 0. 0 is allowed for + * `start_handle` alone and matches cached attributes from the beginning of the handle range. * * @return - * - ESP_OK: Success + * - ESP_GATT_OK: Success + * - ESP_GATT_WRONG_STATE: Bluedroid stack is not enabled * - ESP_GATT_INVALID_HANDLE: Invalid GATT `start_handle`, `end_handle` * - ESP_GATT_INVALID_PDU: NULL pointer to `count` - * - ESP_FAIL: Failure due to other reasons */ esp_gatt_status_t esp_ble_gattc_get_attr_count(esp_gatt_if_t gattc_if, uint16_t conn_id, @@ -735,13 +759,16 @@ esp_gatt_status_t esp_ble_gattc_get_attr_count(esp_gatt_if_t gattc_if, * * @note * 1. This API does not trigger any event. - * 2. `start_handle` must be greater than 0, and smaller than `end_handle`. + * 2. `start_handle` must not be greater than `end_handle`, and `start_handle` and + * `end_handle` must not both be 0. 0 is allowed for `start_handle` alone and matches + * cached attributes from the beginning of the handle range. * * @return - * - ESP_OK: Success + * - ESP_GATT_OK: Success + * - ESP_GATT_WRONG_STATE: Bluedroid stack is not enabled * - ESP_GATT_INVALID_HANDLE: Invalid GATT `start_handle`, `end_handle` * - ESP_GATT_INVALID_PDU: NULL pointer to `db` or NULL pointer to `count` or the count value is 0 - * - ESP_FAIL: Failure due to other reasons + * - ESP_GATT_NOT_FOUND: No GATT database element found in the given handle range * */ esp_gatt_status_t esp_ble_gattc_get_db(esp_gatt_if_t gattc_if, uint16_t conn_id, uint16_t start_handle, uint16_t end_handle, @@ -784,7 +811,9 @@ esp_err_t esp_ble_gattc_read_char (esp_gatt_if_t gattc_if, * @note * 1. This function triggers `ESP_GATTC_READ_CHAR_EVT`. * 2. This function should be called only after the connection has been established. - * 3. `start_handle` must be greater than 0, and smaller than `end_handle`. + * 3. `start_handle` must not be greater than `end_handle`, and `start_handle` and + * `end_handle` must not both be 0. 0 is allowed for `start_handle` alone and is + * treated as 1 on air. * * @return * - ESP_OK: Success diff --git a/components/bt/host/bluedroid/api/include/api/esp_gatts_api.h b/components/bt/host/bluedroid/api/include/api/esp_gatts_api.h index 875ddbc9a11..09e49b8733e 100644 --- a/components/bt/host/bluedroid/api/include/api/esp_gatts_api.h +++ b/components/bt/host/bluedroid/api/include/api/esp_gatts_api.h @@ -548,9 +548,11 @@ esp_err_t esp_ble_gatts_set_attr_value(uint16_t attr_handle, uint16_t length, co * 2. `attr_handle` must be greater than 0. * * @return - * - ESP_OK: Success + * - ESP_GATT_OK: Success + * - ESP_GATT_WRONG_STATE: Bluedroid stack is not enabled + * - ESP_GATT_INVALID_PDU: NULL pointer to `length` or `value` * - ESP_GATT_INVALID_HANDLE: Invalid `attr_handle` - * - ESP_FAIL: Failure due to other reasons + * - Other `esp_gatt_status_t` values: Failure due to other reasons */ esp_gatt_status_t esp_ble_gatts_get_attr_value(uint16_t attr_handle, uint16_t *length, const uint8_t **value); diff --git a/components/bt/host/bluedroid/bta/gatt/bta_gattc_act.c b/components/bt/host/bluedroid/bta/gatt/bta_gattc_act.c index fbc87a3c7b8..ab231ad8c6d 100644 --- a/components/bt/host/bluedroid/bta/gatt/bta_gattc_act.c +++ b/components/bt/host/bluedroid/bta/gatt/bta_gattc_act.c @@ -189,7 +189,7 @@ void bta_gattc_disable(tBTA_GATTC_CB *p_cb) *******************************************************************************/ void bta_gattc_register(tBTA_GATTC_CB *p_cb, tBTA_GATTC_DATA *p_data) { - tBTA_GATTC cb_data; + tBTA_GATTC cb_data = {0}; UINT8 i; tBT_UUID *p_app_uuid = &p_data->api_reg.app_uuid; tBTA_GATTC_INT_START_IF *p_buf; @@ -383,7 +383,7 @@ void bta_gattc_process_api_open_cancel (tBTA_GATTC_CB *p_cb, tBTA_GATTC_DATA *p_ UINT16 event = ((BT_HDR *)p_msg)->event; tBTA_GATTC_CLCB *p_clcb = NULL; tBTA_GATTC_RCB *p_clreg; - tBTA_GATTC cb_data; + tBTA_GATTC cb_data = {0}; UNUSED(p_cb); if (p_msg->api_cancel_conn.is_direct) { @@ -425,7 +425,7 @@ void bta_gattc_process_api_open_cancel (tBTA_GATTC_CB *p_cb, tBTA_GATTC_DATA *p_ void bta_gattc_process_enc_cmpl(tBTA_GATTC_CB *p_cb, tBTA_GATTC_DATA *p_msg) { tBTA_GATTC_RCB *p_clreg; - tBTA_GATTC cb_data; + tBTA_GATTC cb_data = {0}; UNUSED(p_cb); p_clreg = bta_gattc_cl_get_regcb(p_msg->enc_cmpl.client_if); @@ -451,7 +451,7 @@ void bta_gattc_process_enc_cmpl(tBTA_GATTC_CB *p_cb, tBTA_GATTC_DATA *p_msg) *******************************************************************************/ void bta_gattc_cancel_open_error(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data) { - tBTA_GATTC cb_data; + tBTA_GATTC cb_data = {0}; UNUSED(p_data); memset(&cb_data, 0, sizeof(cb_data)); @@ -513,6 +513,8 @@ void bta_gattc_open(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data) return; } + memset(&gattc_data, 0, sizeof(gattc_data)); + p_tcb = gatt_find_tcb_by_addr(p_data->api_conn.remote_bda, BT_TRANSPORT_LE); if(p_tcb) { found_app = gatt_find_specific_app_in_hold_link(p_tcb, p_clcb->p_rcb->client_if); @@ -584,6 +586,8 @@ void bta_gattc_init_bk_conn(tBTA_GATTC_API_OPEN *p_data, tBTA_GATTC_RCB *p_clreg tBTA_GATTC_CLCB *p_clcb; tBTA_GATTC_DATA gattc_data; + memset(&gattc_data, 0, sizeof(gattc_data)); + if (bta_gattc_mark_bg_conn(p_data->client_if, p_data->remote_bda, TRUE, FALSE)) { /* always call open to hold a connection */ if (!GATT_Connect(p_data->client_if, p_data->remote_bda, @@ -634,8 +638,7 @@ void bta_gattc_init_bk_conn(tBTA_GATTC_API_OPEN *p_data, tBTA_GATTC_RCB *p_clreg void bta_gattc_cancel_bk_conn(tBTA_GATTC_API_CANCEL_OPEN *p_data) { tBTA_GATTC_RCB *p_clreg; - tBTA_GATTC cb_data; - + tBTA_GATTC cb_data = {0}; memset(&cb_data, 0, sizeof(cb_data)); cb_data.cancel_open.status = BTA_GATT_ERROR; cb_data.cancel_open.client_if = p_data->client_if; @@ -667,7 +670,7 @@ void bta_gattc_cancel_bk_conn(tBTA_GATTC_API_CANCEL_OPEN *p_data) *******************************************************************************/ void bta_gattc_cancel_open_ok(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data) { - tBTA_GATTC cb_data; + tBTA_GATTC cb_data = {0}; UNUSED(p_data); if ( p_clcb->p_rcb->p_cback ) { @@ -691,8 +694,7 @@ void bta_gattc_cancel_open_ok(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data) *******************************************************************************/ void bta_gattc_cancel_open(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data) { - tBTA_GATTC cb_data; - + tBTA_GATTC cb_data = {0}; if (GATT_CancelConnect(p_clcb->p_rcb->client_if, p_data->api_cancel_conn.remote_bda, TRUE)) { bta_gattc_sm_execute(p_clcb, BTA_GATTC_INT_CANCEL_OPEN_OK_EVT, p_data); } else { @@ -759,12 +761,18 @@ void bta_gattc_conn(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data) bta_gattc_register_service_change_notify(p_clcb->bta_conn_id, p_clcb->bda); } else #endif - { /* cache is building */ - APPL_TRACE_DEBUG("%s cache not found, start discovery %u", __func__, bta_gattc_cb.auto_disc); + { /* cache miss or cache load failed */ + APPL_TRACE_DEBUG("%s cache not found, auto_disc=%u", __func__, bta_gattc_cb.auto_disc); if (bta_gattc_cb.auto_disc) { p_clcb->p_srcb->state = BTA_GATTC_SERV_DISC; /* cache load failure, start discovery */ bta_gattc_start_discover(p_clcb, NULL); + } else { + /* Auto discovery is disabled: roll the SRCB back to + * SERV_IDLE so it is not stuck in SERV_LOAD. The app is + * expected to drive service discovery explicitly via + * BTA_GATTC_ServiceSearchRequest() once it is ready. */ + p_clcb->p_srcb->state = BTA_GATTC_SERV_IDLE; } } } else { /* cache is building */ @@ -852,8 +860,7 @@ void bta_gattc_disconncback(tBTA_GATTC_RCB *p_rcb, tBTA_GATTC_DATA *p_data) *******************************************************************************/ void bta_gattc_close_fail(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data) { - tBTA_GATTC cb_data; - + tBTA_GATTC cb_data = {0}; if ( p_clcb->p_rcb->p_cback ) { memset(&cb_data, 0, sizeof(tBTA_GATTC)); cb_data.close.client_if = p_clcb->p_rcb->client_if; @@ -883,8 +890,7 @@ void bta_gattc_close(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data) } tBTA_GATTC_CBACK *p_cback = p_clcb->p_rcb->p_cback; tBTA_GATTC_RCB *p_clreg = p_clcb->p_rcb; - tBTA_GATTC cb_data; - + tBTA_GATTC cb_data = {0}; APPL_TRACE_DEBUG("bta_gattc_close conn_id=%d", p_clcb->bta_conn_id); cb_data.close.client_if = p_clcb->p_rcb->client_if; @@ -908,9 +914,13 @@ void bta_gattc_close(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data) (* p_cback)(BTA_GATTC_CLOSE_EVT, (tBTA_GATTC *)&cb_data); } - // Please note that BTA_GATTC_CLOSE_EVT will run in the BTC task. - // because bta_gattc_deregister_cmpl did not execute as expected(this is a known issue), - // we will run it again in bta_gattc_clcb_dealloc_by_conn_id. + /* + * Free the CLCB in the BTA task after the close callback has been copied to + * BTC. This keeps the original close-callback semantics while avoiding BTC + * task access to core stack control blocks without locking. + */ + bta_gattc_clcb_dealloc(p_clcb); + if (p_clreg->num_clcb == 0 && p_clreg->dereg_pending) { bta_gattc_deregister_cmpl(p_clreg); } @@ -1078,7 +1088,7 @@ void bta_gattc_start_discover(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data) APPL_TRACE_ERROR("discovery on server failed"); bta_gattc_reset_discover_st(p_clcb->p_srcb, p_clcb->status); //discover service complete, trigger callback - tBTA_GATTC cb_data; + tBTA_GATTC cb_data = {0}; cb_data.dis_cmpl.status = p_clcb->status; cb_data.dis_cmpl.conn_id = p_clcb->bta_conn_id; ( *p_clcb->p_rcb->p_cback)(BTA_GATTC_DIS_SRVC_CMPL_EVT, &cb_data); @@ -1380,7 +1390,7 @@ void bta_gattc_confirm(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data) void bta_gattc_read_cmpl(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_OP_CMPL *p_data) { UINT8 event; - tBTA_GATTC cb_data; + tBTA_GATTC cb_data = {0}; tBTA_GATT_UNFMT read_value; if (p_clcb->p_q_cmd == NULL) { @@ -1434,8 +1444,21 @@ void bta_gattc_write_cmpl(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_OP_CMPL *p_data) APPL_TRACE_ERROR("%s, p_data->p_cmpl is NULL", __func__); UINT16 handle = p_clcb->p_q_cmd->api_write.handle; tBTA_GATTC_EVT cmpl_evt = p_clcb->p_q_cmd->api_write.cmpl_evt; + tBTA_GATTC_CONN *p_conn = bta_gattc_conn_find(p_clcb->bda); bta_gattc_free_command_data(p_clcb); bta_gattc_pop_command_to_send(p_clcb); + /* If this completion belongs to the internal service-change CCC write, + * clear the in-progress flag and swallow the event so the application + * does not see a spurious BTA_GATTC_WRITE_DESCR_EVT, and a future + * bta_gattc_register_service_change_notify() can retry. */ + if (p_conn && + p_conn->write_remote_svc_change_ccc_in_progress && + p_conn->svc_change_descr_handle == handle) { + p_conn->write_remote_svc_change_ccc_in_progress = FALSE; + p_conn->write_remote_svc_change_ccc_done = FALSE; + APPL_TRACE_ERROR("svc chg ccc: p_cmpl NULL"); + return; + } cb_data.write.status = BTA_GATT_ERROR; cb_data.write.handle = handle; cb_data.write.conn_id = p_clcb->bta_conn_id; @@ -1452,7 +1475,7 @@ void bta_gattc_write_cmpl(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_OP_CMPL *p_data) p_clcb->p_q_cmd->api_write.write_type == BTA_GATTC_WRITE_PREPARE) { // Check if the parameters are valid // should not happen, but just in case - if (p_clcb->p_q_cmd->api_write.p_value == NULL) { + if (p_clcb->p_q_cmd->api_write.p_value == NULL && p_clcb->p_q_cmd->api_write.len != 0) { APPL_TRACE_ERROR("%s, p_clcb->p_q_cmd->api_write.p_value is NULL", __func__); bta_gattc_free_command_data(p_clcb); bta_gattc_pop_command_to_send(p_clcb); @@ -1461,10 +1484,21 @@ void bta_gattc_write_cmpl(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_OP_CMPL *p_data) ( *p_clcb->p_rcb->p_cback)(BTA_GATTC_PREP_WRITE_EVT, (tBTA_GATTC *)&cb_data); return; } - // Should check the value received from the peer device is correct or not. - if (memcmp(p_clcb->p_q_cmd->api_write.p_value, p_data->p_cmpl->att_value.value, - p_data->p_cmpl->att_value.len) != 0) { - cb_data.write.status = BTA_GATT_INVALID_PDU; + /* Rsp value is one ATT chunk (<= MTU-5), not necessarily full api_write.len. */ + { + UINT16 rsp_len = p_data->p_cmpl->att_value.len; + UINT16 req_len = p_clcb->p_q_cmd->api_write.len; + tGATT_VALUE *a = &p_data->p_cmpl->att_value; + tBTA_GATTC_API_WRITE *w = &p_clcb->p_q_cmd->api_write; + + if (a->handle != w->handle || a->offset != w->offset || rsp_len > req_len || + (req_len > 0 && rsp_len == 0) || + (rsp_len > 0 && w->p_value != NULL && + memcmp(w->p_value, a->value, rsp_len) != 0)) { + APPL_TRACE_ERROR("%s prep_write rsp bad h %u/%u o %u/%u len %u/%u", __func__, + a->handle, w->handle, a->offset, w->offset, rsp_len, req_len); + cb_data.write.status = BTA_GATT_INVALID_PDU; + } } event = BTA_GATTC_PREP_WRITE_EVT; @@ -1475,12 +1509,17 @@ void bta_gattc_write_cmpl(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_OP_CMPL *p_data) bta_gattc_free_command_data(p_clcb); bta_gattc_pop_command_to_send(p_clcb); cb_data.write.conn_id = p_clcb->bta_conn_id; - if (p_conn && p_conn->svc_change_descr_handle == cb_data.write.handle) { - if(cb_data.write.status != BTA_GATT_OK) { + if (p_conn && + p_conn->write_remote_svc_change_ccc_in_progress && + p_conn->svc_change_descr_handle == cb_data.write.handle) { + p_conn->write_remote_svc_change_ccc_in_progress = FALSE; + if (cb_data.write.status == BTA_GATT_OK) { + p_conn->write_remote_svc_change_ccc_done = TRUE; + } else { p_conn->write_remote_svc_change_ccc_done = FALSE; APPL_TRACE_ERROR("service change write ccc failed"); } - return; + return; /* internal CCC write: don't forward to application */ } /* write complete, callback */ ( *p_clcb->p_rcb->p_cback)(event, (tBTA_GATTC *)&cb_data); @@ -1497,7 +1536,7 @@ void bta_gattc_write_cmpl(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_OP_CMPL *p_data) *******************************************************************************/ void bta_gattc_exec_cmpl(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_OP_CMPL *p_data) { - tBTA_GATTC cb_data; + tBTA_GATTC cb_data = {0}; //free the command data store in the queue. bta_gattc_free_command_data(p_clcb); bta_gattc_pop_command_to_send(p_clcb); @@ -1522,7 +1561,7 @@ void bta_gattc_exec_cmpl(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_OP_CMPL *p_data) *******************************************************************************/ void bta_gattc_cfg_mtu_cmpl(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_OP_CMPL *p_data) { - tBTA_GATTC cb_data; + tBTA_GATTC cb_data = {0}; //free the command data store in the queue. bta_gattc_free_command_data(p_clcb); bta_gattc_pop_command_to_send(p_clcb); @@ -1650,7 +1689,7 @@ void bta_gattc_ignore_op_cmpl(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data) void bta_gattc_search(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data) { tBTA_GATT_STATUS status = GATT_INTERNAL_ERROR; - tBTA_GATTC cb_data; + tBTA_GATTC cb_data = {0}; APPL_TRACE_DEBUG("bta_gattc_search conn_id=%d", p_clcb->bta_conn_id); if (p_clcb->p_srcb && p_clcb->p_srcb->p_srvc_cache) { status = BTA_GATT_OK; @@ -1782,7 +1821,7 @@ void bta_gattc_deregister_cmpl(tBTA_GATTC_RCB *p_clreg) { tBTA_GATTC_CB *p_cb = &bta_gattc_cb; tBTA_GATTC_IF client_if = p_clreg->client_if; - tBTA_GATTC cb_data; + tBTA_GATTC cb_data = {0}; tBTA_GATTC_CBACK *p_cback = p_clreg->p_cback; memset(&cb_data, 0, sizeof(tBTA_GATTC)); @@ -1910,7 +1949,7 @@ static void bta_gattc_enc_cmpl_cback(tGATT_IF gattc_if, BD_ADDR bda) *******************************************************************************/ void bta_gattc_process_api_refresh(tBTA_GATTC_CB *p_cb, tBTA_GATTC_DATA *p_msg) { - tBTA_GATTC_SERV *p_srvc_cb = bta_gattc_find_srvr_cache(p_msg->api_refresh.remote_bda); + tBTA_GATTC_SERV *p_srvc_cb; tBTA_GATTC_CLCB *p_clcb = &bta_gattc_cb.clcb[0]; BOOLEAN found = FALSE; UINT8 i; @@ -1918,6 +1957,14 @@ void bta_gattc_process_api_refresh(tBTA_GATTC_CB *p_cb, tBTA_GATTC_DATA *p_msg) APPL_TRACE_DEBUG("%s", __func__); +#if (GATTC_CACHE_NVS == TRUE) + if (p_msg->api_refresh.erase_flash) { + bta_gattc_cache_reset(p_msg->api_refresh.remote_bda); + } +#endif + + p_srvc_cb = bta_gattc_find_srvr_cache(p_msg->api_refresh.remote_bda); + if (p_srvc_cb != NULL) { /* try to find a CLCB */ if (p_srvc_cb->connected && p_srvc_cb->num_clcb != 0) { @@ -2014,11 +2061,17 @@ void bta_gattc_process_api_cache_get_addr_list(tBTA_GATTC_CB *p_cb, tBTA_GATTC_D *******************************************************************************/ void bta_gattc_process_api_cache_clean(tBTA_GATTC_CB *p_cb, tBTA_GATTC_DATA *p_msg) { - tBTA_GATTC_SERV *p_srvc_cb = bta_gattc_find_srvr_cache(p_msg->api_clean.remote_bda); + tBTA_GATTC_SERV *p_srvc_cb; UNUSED(p_cb); APPL_TRACE_DEBUG("%s", __func__); +#if (GATTC_CACHE_NVS == TRUE) + bta_gattc_cache_reset(p_msg->api_clean.remote_bda); +#endif + + p_srvc_cb = bta_gattc_find_srvr_cache(p_msg->api_clean.remote_bda); + if (p_srvc_cb != NULL && p_srvc_cb->p_srvc_cache != NULL) { //mark it and delete the cache */ list_free(p_srvc_cb->p_srvc_cache); @@ -2094,6 +2147,8 @@ BOOLEAN bta_gattc_process_srvc_chg_ind(UINT16 conn_id, tBTA_GATTC_CONN *p_conn = bta_gattc_conn_find(p_clcb->bda); if(p_conn) { p_conn->write_remote_svc_change_ccc_done = FALSE; + p_conn->write_remote_svc_change_ccc_in_progress = FALSE; + p_conn->svc_change_descr_handle = 0; } bta_gattc_sm_execute(p_clcb, BTA_GATTC_INT_DISCOVER_EVT, NULL); } @@ -2300,7 +2355,7 @@ static void bta_gattc_cmpl_sendmsg(UINT16 conn_id, tGATTC_OPTYPE op, ********************************************************************************/ static void bta_gattc_cong_cback (UINT16 conn_id, BOOLEAN congested) { - tBTA_GATTC cb_data; + tBTA_GATTC cb_data = {0}; cb_data.congest.conn_id = conn_id; cb_data.congest.congested = congested; btc_gattc_congest_callback(&cb_data); @@ -2343,6 +2398,8 @@ void bta_gattc_init_clcb_conn(UINT8 cif, BD_ADDR remote_bda) tBTA_GATTC_DATA gattc_data; UINT16 conn_id; + memset(&gattc_data, 0, sizeof(gattc_data)); + /* should always get the connection ID */ if (GATT_GetConnIdIfConnected(cif, remote_bda, &conn_id, BTA_GATT_TRANSPORT_LE) == FALSE) { APPL_TRACE_ERROR("bta_gattc_init_clcb_conn ERROR: not a connected device"); @@ -2414,7 +2471,8 @@ tBTA_GATTC_FIND_SERVICE_CB bta_gattc_register_service_change_notify(UINT16 conn_ tBT_UUID gatt_service_change_uuid = {LEN_UUID_16, {GATT_UUID_GATT_SRV_CHGD}}; tBT_UUID gatt_ccc_uuid = {LEN_UUID_16, {GATT_UUID_CHAR_CLIENT_CONFIG}}; tBTA_GATTC_CONN *p_conn = bta_gattc_conn_find_alloc(remote_bda); - if(p_conn && p_conn->write_remote_svc_change_ccc_done) { + if (p_conn && (p_conn->write_remote_svc_change_ccc_done || + p_conn->write_remote_svc_change_ccc_in_progress)) { return SERVICE_CHANGE_CCC_WRITTEN_SUCCESS; } @@ -2481,17 +2539,26 @@ tBTA_GATTC_FIND_SERVICE_CB bta_gattc_register_service_change_notify(UINT16 conn_ } if (gatt_ccc_found == TRUE){ - if (p_conn) { + /* + * The in-progress flag is required so that bta_gattc_write_cmpl can + * recognize this internal CCC write and avoid forwarding the response + * to the application as a spurious BTA_GATTC_WRITE_DESCR_EVT. If the + * connection tracking slot could not be obtained (e.g. conn_track is + * full), skip the write entirely instead of issuing an untracked one. + */ + if (p_conn == NULL) { + APPL_TRACE_ERROR("%s: no conn_track, skip ccc", __func__); + result = SERVICE_CHANGE_WRITE_CCC_FAILED; + } else { p_conn->svc_change_descr_handle = p_desc->handle; - p_conn->write_remote_svc_change_ccc_done = TRUE; + p_conn->write_remote_svc_change_ccc_in_progress = TRUE; + result = SERVICE_CHANGE_CCC_WRITTEN_SUCCESS; + uint16_t indicate_value = GATT_CLT_CONFIG_INDICATION; + tBTA_GATT_UNFMT indicate_v; + indicate_v.len = 2; + indicate_v.p_value = (uint8_t *)&indicate_value; + BTA_GATTC_WriteCharDescr (conn_id, p_desc->handle, BTA_GATTC_TYPE_WRITE, &indicate_v, BTA_GATT_AUTH_REQ_NONE); } - result = SERVICE_CHANGE_CCC_WRITTEN_SUCCESS; - uint16_t indicate_value = GATT_CLT_CONFIG_INDICATION; - tBTA_GATT_UNFMT indicate_v; - indicate_v.len = 2; - indicate_v.p_value = (uint8_t *)&indicate_value; - BTA_GATTC_WriteCharDescr (conn_id, p_desc->handle, BTA_GATTC_TYPE_WRITE, &indicate_v, BTA_GATT_AUTH_REQ_NONE); - } else if (gatt_service_change_found == TRUE) { /* Gatt service char found, but service change char ccc not found, diff --git a/components/bt/host/bluedroid/bta/gatt/bta_gattc_api.c b/components/bt/host/bluedroid/bta/gatt/bta_gattc_api.c index b72c62c9ec2..6eb81b24311 100644 --- a/components/bt/host/bluedroid/bta/gatt/bta_gattc_api.c +++ b/components/bt/host/bluedroid/bta/gatt/bta_gattc_api.c @@ -91,6 +91,7 @@ void BTA_GATTC_AppRegister(tBT_UUID *p_app_uuid, tBTA_GATTC_CBACK *p_client_cb) } if ((p_buf = (tBTA_GATTC_API_REG *) osi_malloc(sizeof(tBTA_GATTC_API_REG))) != NULL) { + memset(p_buf, 0, sizeof(*p_buf)); p_buf->hdr.event = BTA_GATTC_API_REG_EVT; if (p_app_uuid != NULL) { memcpy(&p_buf->app_uuid, p_app_uuid, sizeof(tBT_UUID)); @@ -206,6 +207,7 @@ void BTA_GATTC_CancelOpen(tBTA_GATTC_IF client_if, BD_ADDR remote_bda, BOOLEAN i tBTA_GATTC_API_CANCEL_OPEN *p_buf; if ((p_buf = (tBTA_GATTC_API_CANCEL_OPEN *) osi_malloc(sizeof(tBTA_GATTC_API_CANCEL_OPEN))) != NULL) { + memset(p_buf, 0, sizeof(tBTA_GATTC_API_CANCEL_OPEN)); p_buf->hdr.event = BTA_GATTC_API_CANCEL_OPEN_EVT; p_buf->client_if = client_if; @@ -780,6 +782,7 @@ void BTA_GATTC_PrepareWrite (UINT16 conn_id, UINT16 handle, p_buf->handle = handle; p_buf->write_type = BTA_GATTC_WRITE_PREPARE; + p_buf->cmpl_evt = BTA_GATTC_PREP_WRITE_EVT; p_buf->offset = offset; p_buf->len = len; @@ -827,6 +830,7 @@ void BTA_GATTC_PrepareWriteCharDescr (UINT16 conn_id, UINT16 handle, p_buf->auth_req = auth_req; p_buf->handle = handle; p_buf->write_type = BTA_GATTC_WRITE_PREPARE; + p_buf->cmpl_evt = BTA_GATTC_PREP_WRITE_EVT; p_buf->offset = offset; if (p_data && p_data->len != 0) { @@ -1043,12 +1047,6 @@ tBTA_GATT_STATUS BTA_GATTC_DeregisterForNotifications (tBTA_GATTC_IF client_if, *******************************************************************************/ void BTA_GATTC_Refresh(BD_ADDR remote_bda, bool erase_flash) { -#if(GATTC_CACHE_NVS == TRUE) - if(erase_flash) { - /* used to reset cache in application */ - bta_gattc_cache_reset(remote_bda); - } -#endif //If the registration callback is NULL, return if(bta_sys_is_register(BTA_ID_GATTC) == FALSE) { return; @@ -1056,8 +1054,10 @@ void BTA_GATTC_Refresh(BD_ADDR remote_bda, bool erase_flash) tBTA_GATTC_API_CACHE_REFRESH *p_buf; if ((p_buf = (tBTA_GATTC_API_CACHE_REFRESH *) osi_malloc(sizeof(tBTA_GATTC_API_CACHE_REFRESH))) != NULL) { + memset(p_buf, 0, sizeof(tBTA_GATTC_API_CACHE_REFRESH)); p_buf->hdr.event = BTA_GATTC_API_REFRESH_EVT; memcpy(p_buf->remote_bda, remote_bda, BD_ADDR_LEN); + p_buf->erase_flash = erase_flash ? TRUE : FALSE; bta_sys_sendmsg(p_buf); } @@ -1106,11 +1106,6 @@ void BTA_GATTC_CacheGetAddrList(tBTA_GATTC_IF client_if) *******************************************************************************/ void BTA_GATTC_Clean(BD_ADDR remote_bda) { -#if(GATTC_CACHE_NVS == TRUE) - /* used to reset cache in application */ - bta_gattc_cache_reset(remote_bda); -#endif - tBTA_GATTC_API_CACHE_CLEAN *p_buf; if ((p_buf = (tBTA_GATTC_API_CACHE_CLEAN *) osi_malloc(sizeof(tBTA_GATTC_API_CACHE_CLEAN))) != NULL) { diff --git a/components/bt/host/bluedroid/bta/gatt/bta_gattc_cache.c b/components/bt/host/bluedroid/bta/gatt/bta_gattc_cache.c index aebffce50a8..76da820b701 100644 --- a/components/bt/host/bluedroid/bta/gatt/bta_gattc_cache.c +++ b/components/bt/host/bluedroid/bta/gatt/bta_gattc_cache.c @@ -29,6 +29,7 @@ //#if( defined GATTC_CACHE_NVS ) && (GATTC_CACHE_NVS == TRUE) #include +#include #include "bta/utl.h" #include "bta/bta_sys.h" #include "stack/sdp_api.h" @@ -45,6 +46,7 @@ // #include "osi/include/log.h" static void bta_gattc_char_dscpt_disc_cmpl(UINT16 conn_id, tBTA_GATTC_SERV *p_srvc_cb); +static tBTA_GATT_STATUS bta_gattc_incl_srvc_disc_cmpl(UINT16 conn_id, tBTA_GATTC_SERV *p_srvc_cb); extern void bta_to_btif_uuid(bt_uuid_t *p_dest, tBT_UUID *p_src); static size_t bta_gattc_get_db_size_with_type(list_t *services, bt_gatt_db_attribute_type_t type, @@ -66,15 +68,8 @@ void bta_gattc_fill_gatt_db_el(btgatt_db_element_t *p_attr, static tBTA_GATT_STATUS bta_gattc_sdp_service_disc(UINT16 conn_id, tBTA_GATTC_SERV *p_server_cb); #define BTA_GATT_SDP_DB_SIZE 4096 #endif ///SDP_INCLUDED == TRUE -#define GATT_CACHE_PREFIX "/data/misc/bluetooth/gatt_cache_" #define GATT_CACHE_VERSION 2 -static void bta_gattc_generate_cache_file_name(char *buffer, BD_ADDR bda) -{ - sprintf(buffer, "%s%02x%02x%02x%02x%02x%02x", GATT_CACHE_PREFIX, - bda[0], bda[1], bda[2], bda[3], bda[4], bda[5]); -} - /***************************************************************************** ** Constants and data types *****************************************************************************/ @@ -131,6 +126,10 @@ bool display_cache_service(void *data, void *context) *******************************************************************************/ static void bta_gattc_display_cache_server(list_t *p_cache) { + if (p_cache == NULL) { + APPL_TRACE_ERROR("Server Cache: (null)"); + return; + } APPL_TRACE_ERROR("<================Start Server Cache =============>"); list_foreach(p_cache, display_cache_service, NULL); APPL_TRACE_ERROR("<================End Server Cache =============>"); @@ -146,15 +145,57 @@ static void bta_gattc_display_cache_server(list_t *p_cache) ** Returns none. ** *******************************************************************************/ -static void bta_gattc_display_explore_record(tBTA_GATTC_ATTR_REC *p_rec, UINT8 num_rec) +static void bta_gattc_uuid_to_str(const tBT_UUID *uuid, char *buf, size_t buf_len) { - UINT8 i; + int x = 0; + + if (buf == NULL || buf_len == 0) { + return; + } + + if (uuid == NULL) { + snprintf(buf, buf_len, "NULL"); + return; + } + + if (uuid->len == LEN_UUID_16) { + snprintf(buf, buf_len, "0x%04x", uuid->uu.uuid16); + } else if (uuid->len == LEN_UUID_32) { + snprintf(buf, buf_len, "0x%08x", (unsigned int)uuid->uu.uuid32); + } else if (uuid->len == LEN_UUID_128) { + x += snprintf(&buf[x], buf_len - (size_t)x, + "0x%02x%02x%02x%02x%02x%02x%02x%02x", + uuid->uu.uuid128[15], uuid->uu.uuid128[14], + uuid->uu.uuid128[13], uuid->uu.uuid128[12], + uuid->uu.uuid128[11], uuid->uu.uuid128[10], + uuid->uu.uuid128[9], uuid->uu.uuid128[8]); + if ((size_t)x < buf_len) { + (void)snprintf(&buf[x], buf_len - (size_t)x, + "%02x%02x%02x%02x%02x%02x%02x%02x", + uuid->uu.uuid128[7], uuid->uu.uuid128[6], + uuid->uu.uuid128[5], uuid->uu.uuid128[4], + uuid->uu.uuid128[3], uuid->uu.uuid128[2], + uuid->uu.uuid128[1], uuid->uu.uuid128[0]); + } + } else { + snprintf(buf, buf_len, "Unknown UUID len=%u", (unsigned)uuid->len); + } +} + +static void bta_gattc_display_explore_record(tBTA_GATTC_ATTR_REC *p_rec, UINT16 num_rec) +{ + UINT16 i; tBTA_GATTC_ATTR_REC *pp = p_rec; APPL_TRACE_ERROR("<================Start Explore Queue =============>"); for (i = 0; i < num_rec; i ++, pp ++) { - APPL_TRACE_ERROR("\t rec[%d] uuid[0x%04x] s_handle[%d] e_handle[%d] is_primary[%d]", - i + 1, pp->uuid.uu.uuid16, pp->s_handle, pp->e_handle, pp->is_primary); + char uuid_buf[50]; + bta_gattc_uuid_to_str(&pp->uuid, uuid_buf, sizeof(uuid_buf)); + + APPL_TRACE_ERROR("\t rec[%u] uuid[%s] s_handle[%u] e_handle[%u] is_primary[%u]", + (unsigned)(i + 1), uuid_buf, + (unsigned)pp->s_handle, (unsigned)pp->e_handle, + (unsigned)pp->is_primary); } APPL_TRACE_ERROR("<================ End Explore Queue =============>"); APPL_TRACE_ERROR(" "); @@ -215,38 +256,66 @@ static void bta_gattc_free(void *ptr) osi_free(ptr); } -void bta_gattc_insert_sec_service_to_cache(list_t *services, tBTA_GATTC_SERVICE *p_new_srvc) +/* Bluedroid host operates under the design assumption that it must not run + * out of memory; OOM is treated as an unrecoverable condition handled + * upstream by the allocator policy. The list_append / list_prepend / + * list_insert_after results are therefore forwarded as-is via the bool + * return value (NULL preconditions also surface as `false`), leaving the + * decision to roll back the orphaned service or to propagate the failure + * up the call stack to the caller. */ +bool bta_gattc_insert_sec_service_to_cache(list_t *services, tBTA_GATTC_SERVICE *p_new_srvc) { - // services/p_new_srvc is NULL if (!services || !p_new_srvc) { APPL_TRACE_ERROR("%s services/p_new_srvc is NULL", __func__); - return; + return false; } - //list is empty + + /* Order by s_handle so traversals (e.g. bta_gattc_get_db_with_operation) may rely on + * monotonic s_handle; gap-based insert failed when ranges overlapped or nested. */ if (list_is_empty(services)) { - list_append(services, p_new_srvc); - } else { - //check the first service - list_node_t *sn = list_begin(services); + return list_append(services, p_new_srvc); + } + + list_node_t *prev = NULL; + for (list_node_t *sn = list_begin(services); sn != list_end(services); sn = list_next(sn)) { tBTA_GATTC_SERVICE *service = list_node(sn); - if(service && p_new_srvc->e_handle < service->s_handle) { - list_prepend(services, p_new_srvc); - } else { - for (list_node_t *sn = list_begin(services); sn != list_end(services); sn = list_next(sn)) { - list_node_t *next_sn = list_next(sn); - if(next_sn == list_end(services)) { - list_append(services, p_new_srvc); - return; - } - tBTA_GATTC_SERVICE *service = list_node(sn); - tBTA_GATTC_SERVICE *next_service = list_node(next_sn); - if (p_new_srvc->s_handle > service->e_handle && p_new_srvc->e_handle < next_service->s_handle) { - list_insert_after(services, sn, p_new_srvc); - return; - } + if (p_new_srvc->s_handle < service->s_handle) { + if (prev == NULL) { + return list_prepend(services, p_new_srvc); + } else { + return list_insert_after(services, prev, p_new_srvc); } } + prev = sn; } + return list_append(services, p_new_srvc); +} + +/******************************************************************************* +** +** Function bta_gattc_next_service_in_list +** +** Description Return the service node after |cur| in |services| list order. +** +** Returns Next service, or NULL if none. +** +*******************************************************************************/ +static tBTA_GATTC_SERVICE *bta_gattc_next_service_in_list(const list_t *services, + const tBTA_GATTC_SERVICE *cur) +{ + if (!services) { + return NULL; + } + for (list_node_t *sn = list_begin(services); sn != list_end(services); sn = list_next(sn)) { + if (list_node(sn) == cur) { + list_node_t *next_sn = list_next(sn); + if (next_sn == list_end(services)) { + return NULL; + } + return (tBTA_GATTC_SERVICE *)list_node(next_sn); + } + } + return NULL; } /******************************************************************************* @@ -282,6 +351,18 @@ static tBTA_GATT_STATUS bta_gattc_add_srvc_to_cache(tBTA_GATTC_SERV *p_srvc_cb, p_new_srvc->characteristics = list_new(characteristic_free); p_new_srvc->included_svc = list_new(bta_gattc_free); + if (!p_new_srvc->characteristics || !p_new_srvc->included_svc) { + APPL_TRACE_WARNING("%s(), no resource.", __func__); + if (p_new_srvc->characteristics) { + list_free(p_new_srvc->characteristics); + } + if (p_new_srvc->included_svc) { + list_free(p_new_srvc->included_svc); + } + osi_free(p_new_srvc); + return BTA_GATT_NO_RESOURCES; + } + if (p_srvc_cb->p_srvc_cache == NULL) { p_srvc_cb->p_srvc_cache = list_new(service_free); } @@ -298,11 +379,22 @@ static tBTA_GATT_STATUS bta_gattc_add_srvc_to_cache(tBTA_GATTC_SERV *p_srvc_cb, return BTA_GATT_NO_RESOURCES; } - if(is_primary) { - list_append(p_srvc_cb->p_srvc_cache, p_new_srvc); + bool inserted; + if (is_primary) { + inserted = list_append(p_srvc_cb->p_srvc_cache, p_new_srvc); } else { //add secondary service into list - bta_gattc_insert_sec_service_to_cache(p_srvc_cb->p_srvc_cache, p_new_srvc); + inserted = bta_gattc_insert_sec_service_to_cache(p_srvc_cb->p_srvc_cache, p_new_srvc); + } + if (!inserted) { + /* p_new_srvc still owns its characteristics/included_svc lists; release + * them here since service_free will not run for a node that never + * entered p_srvc_cache. */ + APPL_TRACE_WARNING("%s(), no resource.", __func__); + list_free(p_new_srvc->characteristics); + list_free(p_new_srvc->included_svc); + osi_free(p_new_srvc); + return BTA_GATT_NO_RESOURCES; } return BTA_GATT_OK; } @@ -329,7 +421,15 @@ static tBTA_GATT_STATUS bta_gattc_add_char_to_cache(tBTA_GATTC_SERV *p_srvc_cb, This is just a temporary workaround. */ if (service->e_handle < value_handle) { - service->e_handle = value_handle; + UINT16 new_e = value_handle; + tBTA_GATTC_SERVICE *next_srvc = bta_gattc_next_service_in_list(p_srvc_cb->p_srvc_cache, service); + if (next_srvc != NULL && next_srvc->s_handle > service->s_handle + && new_e >= next_srvc->s_handle && next_srvc->s_handle > 0) { + new_e = next_srvc->s_handle - 1; + } + if (new_e > service->e_handle) { + service->e_handle = new_e; + } } tBTA_GATTC_CHARACTERISTIC *characteristic = osi_malloc(sizeof(tBTA_GATTC_CHARACTERISTIC)); @@ -343,6 +443,11 @@ static tBTA_GATT_STATUS bta_gattc_add_char_to_cache(tBTA_GATTC_SERV *p_srvc_cb, memcpy(&characteristic->uuid, p_uuid, sizeof(tBT_UUID)); characteristic->service = service; + if (!characteristic->descriptors) { + APPL_TRACE_WARNING("%s(), no resource.", __func__); + osi_free(characteristic); + return BTA_GATT_NO_RESOURCES; + } if (!list_append(service->characteristics, characteristic)) { APPL_TRACE_WARNING("%s(), no resource.", __func__); if (characteristic->descriptors) { @@ -402,7 +507,11 @@ static tBTA_GATT_STATUS bta_gattc_add_attr_to_cache(tBTA_GATTC_SERV *p_srvc_cb, p_srvc_cb->update_incl_srvc = true; } - list_append(service->included_svc, isvc); + if (!service->included_svc || !list_append(service->included_svc, isvc)) { + APPL_TRACE_WARNING("%s(), no resource.", __func__); + osi_free(isvc); + return BTA_GATT_NO_RESOURCES; + } } else if (type == BTA_GATTC_ATTR_TYPE_CHAR_DESCR) { tBTA_GATTC_DESCRIPTOR *descriptor = osi_malloc(sizeof(tBTA_GATTC_DESCRIPTOR)); @@ -424,7 +533,11 @@ static tBTA_GATT_STATUS bta_gattc_add_attr_to_cache(tBTA_GATTC_SERV *p_srvc_cb, tBTA_GATTC_CHARACTERISTIC *char_node = list_back(service->characteristics); descriptor->characteristic = char_node; - list_append(char_node->descriptors, descriptor); + if (!char_node->descriptors || !list_append(char_node->descriptors, descriptor)) { + APPL_TRACE_WARNING("%s(), no resource.", __func__); + osi_free(descriptor); + return BTA_GATT_NO_RESOURCES; + } } return BTA_GATT_OK; } @@ -447,6 +560,17 @@ void bta_gattc_get_disc_range(tBTA_GATTC_SERV *p_srvc_cb, UINT16 *p_s_hdl, UINT1 *p_s_hdl = p_rec->s_handle; } else { p_rec = p_srvc_cb->p_srvc_list + p_srvc_cb->cur_char_idx; + if (p_rec->s_handle == 0xFFFF) { + /* Per BT Core Spec (Vol 3, Part G, 3.3): a Characteristic + * Declaration cannot reside at handle 0xFFFF because its + * mandatory Value Declaration must immediately follow it. + * Treat this as corrupted/unexpected cache data and signal + * an invalid range to the caller. */ + APPL_TRACE_ERROR("%s: char s_handle 0xFFFF", __func__); + *p_s_hdl = 0; + *p_e_hdl = 0; + return; + } *p_s_hdl = p_rec->s_handle + 1; } @@ -510,7 +634,9 @@ tBTA_GATT_STATUS bta_gattc_discover_procedure(UINT16 conn_id, tBTA_GATTC_SERV *p bta_gattc_get_disc_range(p_server_cb, ¶m.s_handle, ¶m.e_handle, is_service); - if (param.s_handle > param.e_handle) { + if (!GATT_HANDLE_IS_VALID(param.s_handle) || + !GATT_HANDLE_IS_VALID(param.e_handle) || + (param.s_handle > param.e_handle)) { return GATT_ERROR; } } @@ -569,7 +695,9 @@ void bta_gattc_update_include_service(const list_t *services) { } for (list_node_t *sn = list_begin(services); sn != list_end(services); sn = list_next(sn)) { tBTA_GATTC_SERVICE *service = list_node(sn); - if(!service || !service->included_svc || list_is_empty(service->included_svc)) break; + if (!service || !service->included_svc || list_is_empty(service->included_svc)) { + continue; + } for (list_node_t *sn = list_begin(service->included_svc); sn != list_end(service->included_svc);) { list_node_t *sn_next = list_next(sn); tBTA_GATTC_INCLUDED_SVC *include_service = list_node(sn); @@ -619,7 +747,38 @@ static void bta_gattc_explore_srvc(UINT16 conn_id, tBTA_GATTC_SERV *p_srvc_cb) &p_rec->uuid, p_rec->is_primary) == 0) { /* start discovering included services */ - bta_gattc_start_disc_include_srvc(conn_id, p_srvc_cb); + tBTA_GATT_STATUS status = bta_gattc_start_disc_include_srvc(conn_id, p_srvc_cb); + if (status == 0) { + return; + } + + /* + * If included service discovery cannot be started (e.g. busy/error), + * try to advance the state machine by directly kicking off + * characteristic discovery. If that also succeeds, the discovery + * will be driven forward by its async completion callback and we + * MUST NOT fall through to the finalization code below (doing so + * would reset the state machine while an async procedure is still + * pending). + */ + tBTA_GATT_STATUS disc_status = bta_gattc_incl_srvc_disc_cmpl(conn_id, p_srvc_cb); + if (disc_status == BTA_GATT_OK) { + return; + } + + /* + * Both included-service and characteristic discovery failed to + * start synchronously. Falling through to the finalization path + * below would (a) deliver BTA_GATTC_DIS_SRVC_CMPL_EVT with a + * stale (likely SUCCESS) status, (b) persist an incomplete cache + * to NVS when GATTC_CACHE_NVS is enabled, and (c) reset the SRCB + * with BTA_GATT_OK, masking the real failure. Mirror the handling + * in bta_gattc_disc_cmpl_cback() and surface the actual error to + * the upper layer immediately. + */ + APPL_TRACE_ERROR("%s: incl/char disc start failed, st=%d/%d", + __func__, status, disc_status); + bta_gattc_reset_discover_st(p_clcb->p_srcb, disc_status); return; } } @@ -661,15 +820,20 @@ static void bta_gattc_explore_srvc(UINT16 conn_id, tBTA_GATTC_SERV *p_srvc_cb) ** ** Description process the relationship discovery complete event ** -** Returns status +** Returns GATT_SUCCESS if the next discovery step (characteristic +** discovery) was successfully kicked off; otherwise the +** error returned by bta_gattc_start_disc_char so that the +** caller can decide whether to fall back to the discovery +** finalization path instead of stalling on a callback that +** will never come. ** *******************************************************************************/ -static void bta_gattc_incl_srvc_disc_cmpl(UINT16 conn_id, tBTA_GATTC_SERV *p_srvc_cb) +static tBTA_GATT_STATUS bta_gattc_incl_srvc_disc_cmpl(UINT16 conn_id, tBTA_GATTC_SERV *p_srvc_cb) { p_srvc_cb->cur_char_idx = p_srvc_cb->total_srvc; /* start discoverying characteristic */ - bta_gattc_start_disc_char(conn_id, p_srvc_cb); + return bta_gattc_start_disc_char(conn_id, p_srvc_cb); } /******************************************************************************* ** @@ -687,11 +851,24 @@ static void bta_gattc_char_disc_cmpl(UINT16 conn_id, tBTA_GATTC_SERV *p_srvc_cb) /* if there are characteristic needs to be explored */ if (p_srvc_cb->total_char > 0) { /* add the first characteristic into cache */ - bta_gattc_add_char_to_cache (p_srvc_cb, - p_rec->char_decl_handle, - p_rec->s_handle, - &p_rec->uuid, - p_rec->property); + tBTA_GATT_STATUS add_status = bta_gattc_add_char_to_cache(p_srvc_cb, + p_rec->char_decl_handle, + p_rec->s_handle, + &p_rec->uuid, + p_rec->property); + if (add_status != BTA_GATT_OK) { + /* Failed to cache the characteristic (e.g. OOM). Kicking off + * descriptor discovery now would either silently drop the + * descriptors or attach them to the previous characteristic + * via list_back() in bta_gattc_add_attr_to_cache(), leaving the + * cache structurally inconsistent. Abort the procedure so the + * application sees the failure rather than a partially + * populated cache. */ + APPL_TRACE_ERROR("%s: add_char_to_cache fail st=%d", + __func__, add_status); + bta_gattc_reset_discover_st(p_srvc_cb, add_status); + return; + } /* start discoverying characteristic descriptor , if failed, disc for next char */ bta_gattc_start_disc_char_dscp(conn_id, p_srvc_cb); @@ -713,17 +890,31 @@ static void bta_gattc_char_disc_cmpl(UINT16 conn_id, tBTA_GATTC_SERV *p_srvc_cb) static void bta_gattc_char_dscpt_disc_cmpl(UINT16 conn_id, tBTA_GATTC_SERV *p_srvc_cb) { tBTA_GATTC_ATTR_REC *p_rec = NULL; + tBTA_GATT_STATUS add_status = BTA_GATT_OK; /* Recursive function will cause BTU stack overflow when there are a large number of characteristic * without descriptor to discover. So replace it with while function */ while (--p_srvc_cb->total_char > 0) { p_rec = p_srvc_cb->p_srvc_list + (++ p_srvc_cb->cur_char_idx); /* add the next characteristic into cache */ - bta_gattc_add_char_to_cache (p_srvc_cb, - p_rec->char_decl_handle, - p_rec->s_handle, - &p_rec->uuid, - p_rec->property); + add_status = bta_gattc_add_char_to_cache(p_srvc_cb, + p_rec->char_decl_handle, + p_rec->s_handle, + &p_rec->uuid, + p_rec->property); + if (add_status != BTA_GATT_OK) { + /* If we failed to cache the current characteristic, any + * descriptors discovered for it would be appended to the + * previous characteristic (bta_gattc_add_attr_to_cache uses + * list_back() on the service's characteristic list), corrupting + * the cache. Stop the discovery state machine and surface the + * error to the upper layer so the application can react + * instead of silently using an inconsistent cache. */ + APPL_TRACE_ERROR("%s: add_char_to_cache fail st=%d", + __func__, add_status); + bta_gattc_reset_discover_st(p_srvc_cb, add_status); + return; + } /* start to discover next characteristic for descriptor */ if (bta_gattc_discover_procedure(conn_id, p_srvc_cb, GATT_DISC_CHAR_DSCPT) == 0) { /* send att req and wait for att rsp */ @@ -992,7 +1183,10 @@ void bta_gattc_disc_res_cback (UINT16 conn_id, tGATT_DISC_TYPE disc_type, tGATT_ p_srvc_cb = bta_gattc_find_scb_by_cid(conn_id); if (p_srvc_cb != NULL && p_clcb != NULL && p_clcb->state == BTA_GATTC_DISCOVER_ST) { - p_srvc_cb->total_attr++; + /* total_attr is UINT16; saturate so discovery cannot wrap the counter */ + if (p_srvc_cb->total_attr < UINT16_MAX) { + p_srvc_cb->total_attr++; + } switch (disc_type) { case GATT_DISC_SRVC_ALL: /* discover services result, add services into a service list */ @@ -1085,10 +1279,24 @@ void bta_gattc_disc_cmpl_cback (UINT16 conn_id, tGATT_DISC_TYPE disc_type, tGATT bta_gattc_explore_srvc(conn_id, p_srvc_cb); break; - case GATT_DISC_INC_SRVC: - bta_gattc_incl_srvc_disc_cmpl(conn_id, p_srvc_cb); - + case GATT_DISC_INC_SRVC: { + /* + * If chained characteristic discovery cannot be started + * synchronously (e.g. GATT_BUSY / GATT_NO_RESOURCES, or invalid + * handle range returned by bta_gattc_discover_procedure), no + * async completion will ever come back to drive the state + * machine forward. Mirror the handling in bta_gattc_explore_srvc() + * and finalize discovery now (propagating the actual error code) + * so the SRCB does not get stuck in BTA_GATTC_SERV_DISC_ACT until + * the link supervision timeout fires. + */ + tBTA_GATT_STATUS disc_status = bta_gattc_incl_srvc_disc_cmpl(conn_id, p_srvc_cb); + if (disc_status != GATT_SUCCESS) { + APPL_TRACE_ERROR("%s: char disc fail after incl, st=%d", __func__, disc_status); + bta_gattc_reset_discover_st(p_srvc_cb, disc_status); + } break; + } case GATT_DISC_CHAR: #if (defined BTA_GATT_DEBUG && BTA_GATT_DEBUG == TRUE) @@ -1209,6 +1417,9 @@ tBTA_GATTC_CHARACTERISTIC* bta_gattc_get_characteristic_srcb(tBTA_GATTC_SERV *p return NULL; } + if (!service->characteristics) { + return NULL; + } for (list_node_t *cn = list_begin(service->characteristics); cn != list_end(service->characteristics); cn = list_next(cn)) { tBTA_GATTC_CHARACTERISTIC *p_char = list_node(cn); @@ -1240,9 +1451,15 @@ tBTA_GATTC_DESCRIPTOR* bta_gattc_get_descriptor_srcb(tBTA_GATTC_SERV *p_srcb, U return NULL; } + if (!service->characteristics) { + return NULL; + } for (list_node_t *cn = list_begin(service->characteristics); cn != list_end(service->characteristics); cn = list_next(cn)) { tBTA_GATTC_CHARACTERISTIC *p_char = list_node(cn); + if (!p_char->descriptors) { + continue; + } for (list_node_t *dn = list_begin(p_char->descriptors); dn != list_end(p_char->descriptors); dn = list_next(dn)) { tBTA_GATTC_DESCRIPTOR *p_desc = list_node(dn); @@ -1335,6 +1552,39 @@ void bta_gattc_fill_gatt_db_el(btgatt_db_element_t *p_attr, bta_to_btif_uuid(&p_attr->uuid, &uuid); } +/******************************************************************************* +** +** Function bta_gattc_get_db_chk_room +** +** Description bta_gattc_get_db_with_operation allocates using a heuristic +** upper bound; the walk can still emit more elements than that +** bound. If filled >= max_elems, free the buffer and clear +** outputs so the caller returns an empty DB (same shape as no +** matches) instead of heap overflow. +** +** Returns TRUE if there is room for one more element, else FALSE. +** +*******************************************************************************/ +/* Static trip counter: incremented every time the DB element buffer would + * have overflowed. Useful as a "did this ever happen in the field?" smoke + * signal in addition to the ERROR log emitted on each trip. Not an API. */ +static unsigned int s_get_db_chk_room_trips = 0; + +static BOOLEAN bta_gattc_get_db_chk_room(size_t filled, size_t max_elems, void *buffer, + btgatt_db_element_t **char_db, UINT16 *count) +{ + if (filled >= max_elems) { + s_get_db_chk_room_trips++; + APPL_TRACE_ERROR("%s: cap exceeded filled=%u cap=%u trips=%u", + __func__, (unsigned)filled, (unsigned)max_elems, s_get_db_chk_room_trips); + osi_free(buffer); + *char_db = NULL; + *count = 0; + return FALSE; + } + return TRUE; +} + void bta_gattc_get_db_with_operation(UINT16 conn_id, bt_gatt_get_db_op_t op, UINT16 char_handle, @@ -1361,15 +1611,19 @@ void bta_gattc_get_db_with_operation(UINT16 conn_id, return; } - size_t db_size = ((end_handle - start_handle + 1) < p_srcb->total_attr) ? (end_handle - start_handle + 1) : p_srcb->total_attr; - if (!db_size) { + /* Allocation cap: min(handle span, total_attr). May be lower than actual + * matching rows; bta_gattc_get_db_chk_room guards each append. */ + const size_t max_elems = ((end_handle - start_handle + 1) < p_srcb->total_attr) + ? (end_handle - start_handle + 1) + : p_srcb->total_attr; + if (!max_elems) { APPL_TRACE_DEBUG("the db size is 0."); *count = 0; *char_db = NULL; return; } - void *buffer = osi_malloc(db_size*sizeof(btgatt_db_element_t)); + void *buffer = osi_malloc(max_elems * sizeof(btgatt_db_element_t)); if (!buffer) { APPL_TRACE_DEBUG("the buffer is NULL."); @@ -1378,7 +1632,7 @@ void bta_gattc_get_db_with_operation(UINT16 conn_id, return; } btgatt_db_element_t *curr_db_attr = buffer; - db_size = 0; + size_t filled = 0; /* number of elements written into buffer so far */ for (list_node_t *sn = list_begin(p_srcb->p_srvc_cache); sn != list_end(p_srcb->p_srvc_cache); sn = list_next(sn)) { tBTA_GATTC_SERVICE *p_cur_srvc = list_node(sn); @@ -1405,10 +1659,13 @@ void bta_gattc_get_db_with_operation(UINT16 conn_id, if (p_isvc->handle > end_handle) { *char_db = buffer; - *count = db_size; + *count = filled; return; } if (!incl_uuid || bta_gattc_uuid_compare(&p_isvc->uuid, incl_uuid, TRUE)) { + if (!bta_gattc_get_db_chk_room(filled, max_elems, buffer, char_db, count)) { + return; + } bta_gattc_fill_gatt_db_el(curr_db_attr, BTGATT_DB_INCLUDED_SERVICE, p_isvc->handle, @@ -1418,7 +1675,7 @@ void bta_gattc_get_db_with_operation(UINT16 conn_id, p_isvc->uuid, 0 /* property */); curr_db_attr++; - db_size++; + filled++; } } continue; @@ -1439,12 +1696,15 @@ void bta_gattc_get_db_with_operation(UINT16 conn_id, if (p_char->handle > end_handle) { *char_db = buffer; - *count = db_size; + *count = filled; return; } if ((op == GATT_OP_GET_ALL_CHAR || op == GATT_OP_GET_CHAR_BY_UUID) && (char_uuid == NULL || bta_gattc_uuid_compare(&p_char->uuid, char_uuid, TRUE))) { APPL_TRACE_DEBUG("%s(), uuid match.", __func__); + if (!bta_gattc_get_db_chk_room(filled, max_elems, buffer, char_db, count)) { + return; + } bta_gattc_fill_gatt_db_el(curr_db_attr, BTGATT_DB_CHARACTERISTIC, p_char->handle, @@ -1454,7 +1714,7 @@ void bta_gattc_get_db_with_operation(UINT16 conn_id, p_char->uuid, p_char->properties); curr_db_attr++; - db_size++; + filled++; continue; } @@ -1481,12 +1741,15 @@ void bta_gattc_get_db_with_operation(UINT16 conn_id, } if (p_desc->handle > end_handle) { *char_db = buffer; - *count = db_size; + *count = filled; return; } if (((op == GATT_OP_GET_ALL_DESCRI || op == GATT_OP_GET_DESCRI_BY_UUID) && (descr_uuid == NULL || bta_gattc_uuid_compare(&p_desc->uuid, descr_uuid, TRUE))) || (op == GATT_OP_GET_DESCRI_BY_HANDLE && bta_gattc_uuid_compare(&p_desc->uuid, descr_uuid, TRUE))) { + if (!bta_gattc_get_db_chk_room(filled, max_elems, buffer, char_db, count)) { + return; + } bta_gattc_fill_gatt_db_el(curr_db_attr, BTGATT_DB_DESCRIPTOR, p_desc->handle, @@ -1496,7 +1759,7 @@ void bta_gattc_get_db_with_operation(UINT16 conn_id, p_desc->uuid, 0 /* property */); curr_db_attr++; - db_size++; + filled++; } } } @@ -1505,7 +1768,7 @@ void bta_gattc_get_db_with_operation(UINT16 conn_id, } *char_db = buffer; - *count = db_size; + *count = filled; } static size_t bta_gattc_get_db_size_with_type(list_t *services, @@ -1568,19 +1831,23 @@ static size_t bta_gattc_get_db_size_with_type(list_t *services, cn != list_end(p_cur_srvc->characteristics); cn = list_next(cn)) { tBTA_GATTC_CHARACTERISTIC *p_char = list_node(cn); - if (p_char->handle < start_handle) { - continue; - } - if (p_char->handle > end_handle) { return db_size; } - if ((type == BTGATT_DB_CHARACTERISTIC) && bta_gattc_uuid_compare(&p_char->uuid, char_uuid, TRUE)) { + /* Count the characteristic only when its handle is in range. */ + if ((p_char->handle >= start_handle) && + (type == BTGATT_DB_CHARACTERISTIC) && + bta_gattc_uuid_compare(&p_char->uuid, char_uuid, TRUE)) { db_size++; continue; } + /* Descriptor handles are strictly greater than their parent characteristic + * declaration handle, so an in-range descriptor may still exist when the + * parent's handle is below start_handle. Iterate descriptors regardless of + * p_char->handle vs start_handle to stay consistent with the paired filler + * (bta_gattc_get_db_with_operation) which already iterates descriptors in that case. */ if (p_char->descriptors && (type == BTGATT_DB_DESCRIPTOR) && bta_gattc_uuid_compare(&p_char->uuid, char_uuid, TRUE)) { for (list_node_t *dn = list_begin(p_char->descriptors); dn != list_end(p_char->descriptors); dn = list_next(dn)) { @@ -1643,43 +1910,14 @@ static size_t bta_gattc_get_db_size(list_t *services, break; } - /* Count service only when declaration handle s_handle is within [start_handle, end_handle] (GATT spec). - * Skip counting this service and all its contents when s_handle < start_handle (consistent with bta_gattc_get_gatt_db_impl). */ - if (p_cur_srvc->s_handle < start_handle) { - continue; - } - db_size++; - - if (p_cur_srvc->characteristics && !list_is_empty(p_cur_srvc->characteristics)) { - for (list_node_t *cn = list_begin(p_cur_srvc->characteristics); - cn != list_end(p_cur_srvc->characteristics); cn = list_next(cn)) { - tBTA_GATTC_CHARACTERISTIC *p_char = list_node(cn); - - if (p_char->handle < start_handle) { - continue; - } - if (p_char->handle > end_handle) { - return db_size; - } - db_size++; - - if (p_char->descriptors) { - for (list_node_t *dn = list_begin(p_char->descriptors); - dn != list_end(p_char->descriptors); dn = list_next(dn)) { - tBTA_GATTC_DESCRIPTOR *p_desc = list_node(dn); - if (p_desc->handle < start_handle) { - continue; - } - if (p_desc->handle > end_handle) { - return db_size; - } - db_size++; - } - } - } + /* Count service declaration only when its handle lies in the requested range. Still count + * included services, characteristics, and descriptors in-range when s_handle is before start_handle. */ + if (p_cur_srvc->s_handle >= start_handle && p_cur_srvc->s_handle <= end_handle) { + db_size++; } - if (p_cur_srvc->included_svc) { + /* GATT attribute order: included service declarations before characteristic declarations. */ + if (p_cur_srvc->included_svc && !list_is_empty(p_cur_srvc->included_svc)) { for (list_node_t *isn = list_begin(p_cur_srvc->included_svc); isn != list_end(p_cur_srvc->included_svc); isn = list_next(isn)) { tBTA_GATTC_INCLUDED_SVC *p_isvc = list_node(isn); @@ -1689,11 +1927,45 @@ static size_t bta_gattc_get_db_size(list_t *services, } if (p_isvc->handle > end_handle) { - return db_size; + break; } db_size++; } } + + if (p_cur_srvc->characteristics && !list_is_empty(p_cur_srvc->characteristics)) { + for (list_node_t *cn = list_begin(p_cur_srvc->characteristics); + cn != list_end(p_cur_srvc->characteristics); cn = list_next(cn)) { + tBTA_GATTC_CHARACTERISTIC *p_char = list_node(cn); + + if (p_char->handle > end_handle) { + break; + } + + /* Count the characteristic declaration only when its handle is in range. + * Descriptors must still be examined when the parent characteristic's + * handle is below start_handle, because descriptor handles are strictly + * greater than the characteristic declaration handle and may themselves + * fall inside [start_handle, end_handle]. */ + if (p_char->handle >= start_handle) { + db_size++; + } + + if (p_char->descriptors) { + for (list_node_t *dn = list_begin(p_char->descriptors); + dn != list_end(p_char->descriptors); dn = list_next(dn)) { + tBTA_GATTC_DESCRIPTOR *p_desc = list_node(dn); + if (p_desc->handle < start_handle) { + continue; + } + if (p_desc->handle > end_handle) { + break; + } + db_size++; + } + } + } + } } return db_size; @@ -1778,6 +2050,15 @@ static void bta_gattc_get_gatt_db_impl(tBTA_GATTC_SERV *p_srvc_cb, size_t db_size = bta_gattc_get_db_size(p_srvc_cb->p_srvc_cache, start_handle, end_handle); + /* No attribute falls in the requested handle range. Return an empty result + * without invoking osi_malloc(0), which would yield NULL on ESP-IDF and + * be misreported as an allocation failure. */ + if (db_size == 0) { + *db = NULL; + *count = 0; + return; + } + void* buffer = osi_malloc(db_size * sizeof(btgatt_db_element_t)); if (!buffer) { APPL_TRACE_WARNING("%s(), no resource.", __func__); @@ -1799,47 +2080,69 @@ static void bta_gattc_get_gatt_db_impl(tBTA_GATTC_SERV *p_srvc_cb, break; } - /* Output service only when declaration handle s_handle is within [start_handle, end_handle] (consistent with count logic). */ - if (p_cur_srvc->s_handle < start_handle) { - continue; + /* Emit service declaration only when its handle lies in the requested range. */ + if (p_cur_srvc->s_handle >= start_handle && p_cur_srvc->s_handle <= end_handle) { + bta_gattc_fill_gatt_db_el(curr_db_attr, + p_cur_srvc->is_primary ? + BTGATT_DB_PRIMARY_SERVICE : + BTGATT_DB_SECONDARY_SERVICE, + 0 /* att_handle */, + p_cur_srvc->s_handle, + p_cur_srvc->e_handle, + p_cur_srvc->s_handle, + p_cur_srvc->uuid, + 0 /* prop */); + curr_db_attr++; } - bta_gattc_fill_gatt_db_el(curr_db_attr, - p_cur_srvc->is_primary ? - BTGATT_DB_PRIMARY_SERVICE : - BTGATT_DB_SECONDARY_SERVICE, - 0 /* att_handle */, - p_cur_srvc->s_handle, - p_cur_srvc->e_handle, - p_cur_srvc->s_handle, - p_cur_srvc->uuid, - 0 /* prop */); - curr_db_attr++; + /* GATT attribute order: included service declarations before characteristic declarations. */ + if (p_cur_srvc->included_svc && !list_is_empty(p_cur_srvc->included_svc)) { + for (list_node_t *isn = list_begin(p_cur_srvc->included_svc); + isn != list_end(p_cur_srvc->included_svc); isn = list_next(isn)) { + tBTA_GATTC_INCLUDED_SVC *p_isvc = list_node(isn); + + if (p_isvc->handle < start_handle) { + continue; + } + + if (p_isvc->handle > end_handle) { + break; + } + bta_gattc_fill_gatt_db_el(curr_db_attr, + BTGATT_DB_INCLUDED_SERVICE, + p_isvc->handle, + p_isvc->incl_srvc_s_handle, + p_isvc->incl_srvc_e_handle, + p_isvc->handle, + p_isvc->uuid, + 0 /* property */); + curr_db_attr++; + } + } if (p_cur_srvc->characteristics && !list_is_empty(p_cur_srvc->characteristics)) { - for (list_node_t *cn = list_begin(p_cur_srvc->characteristics); cn != list_end(p_cur_srvc->characteristics); cn = list_next(cn)) { tBTA_GATTC_CHARACTERISTIC *p_char = list_node(cn); - if (p_char->handle < start_handle) { - continue; + if (p_char->handle > end_handle) { + break; } - if (p_char->handle > end_handle) { - *db = buffer; - *count = db_size; - return; + /* Emit the characteristic declaration only when its handle is in range, + * but still emit its in-range descriptors. This mirrors bta_gattc_get_db_size + * so allocated db_size and emitted entries stay consistent. */ + if (p_char->handle >= start_handle) { + bta_gattc_fill_gatt_db_el(curr_db_attr, + BTGATT_DB_CHARACTERISTIC, + p_char->handle, + 0 /* s_handle */, + 0 /* e_handle */, + p_char->handle, + p_char->uuid, + p_char->properties); + curr_db_attr++; } - bta_gattc_fill_gatt_db_el(curr_db_attr, - BTGATT_DB_CHARACTERISTIC, - p_char->handle, - 0 /* s_handle */, - 0 /* e_handle */, - p_char->handle, - p_char->uuid, - p_char->properties); - curr_db_attr++; if (!p_char->descriptors || list_is_empty(p_char->descriptors)) { continue; @@ -1854,9 +2157,7 @@ static void bta_gattc_get_gatt_db_impl(tBTA_GATTC_SERV *p_srvc_cb, } if (p_desc->handle > end_handle) { - *db = buffer; - *count = db_size; - return; + break; } bta_gattc_fill_gatt_db_el(curr_db_attr, BTGATT_DB_DESCRIPTOR, @@ -1870,34 +2171,6 @@ static void bta_gattc_get_gatt_db_impl(tBTA_GATTC_SERV *p_srvc_cb, } } } - - if (!p_cur_srvc->included_svc || list_is_empty(p_cur_srvc->included_svc)) { - continue; - } - - for (list_node_t *isn = list_begin(p_cur_srvc->included_svc); - isn != list_end(p_cur_srvc->included_svc); isn = list_next(isn)) { - tBTA_GATTC_INCLUDED_SVC *p_isvc = list_node(isn); - - if (p_isvc->handle < start_handle) { - continue; - } - - if (p_isvc->handle > end_handle) { - *db = buffer; - *count = db_size; - return; - } - bta_gattc_fill_gatt_db_el(curr_db_attr, - BTGATT_DB_INCLUDED_SERVICE, - p_isvc->handle, - p_isvc->incl_srvc_s_handle, - p_isvc->incl_srvc_e_handle, - p_isvc->handle, - p_isvc->uuid, - 0 /* property */); - curr_db_attr++; - } } *db = buffer; @@ -1959,12 +2232,19 @@ void bta_gattc_get_gatt_db(UINT16 conn_id, UINT16 start_handle, UINT16 end_handl ** ** Parameters ** -** Returns None. +** Returns BTA_GATT_OK on success. On the first failure of any +** bta_gattc_add_*_to_cache() call (e.g. BTA_GATT_NO_RESOURCES +** on OOM, or GATT_WRONG_STATE on inconsistent NV layout), +** the partially built in-memory cache is freed and the +** underlying error is returned so the caller can fall back +** to a fresh discovery. ** *******************************************************************************/ -void bta_gattc_rebuild_cache(tBTA_GATTC_SERV *p_srvc_cb, UINT16 num_attr, - tBTA_GATTC_NV_ATTR *p_attr) +tBTA_GATT_STATUS bta_gattc_rebuild_cache(tBTA_GATTC_SERV *p_srvc_cb, UINT16 num_attr, + tBTA_GATTC_NV_ATTR *p_attr) { + tBTA_GATT_STATUS status = BTA_GATT_OK; + /* first attribute loading, initialize buffer */ APPL_TRACE_DEBUG("%s: bta_gattc_rebuild_cache, num_attr = %d", __func__, num_attr); @@ -1974,44 +2254,58 @@ void bta_gattc_rebuild_cache(tBTA_GATTC_SERV *p_srvc_cb, UINT16 num_attr, while (num_attr > 0 && p_attr != NULL) { switch (p_attr->attr_type) { case BTA_GATTC_ATTR_TYPE_SRVC: - bta_gattc_add_srvc_to_cache(p_srvc_cb, - p_attr->s_handle, - p_attr->e_handle, - &p_attr->uuid, - p_attr->is_primary); + status = bta_gattc_add_srvc_to_cache(p_srvc_cb, + p_attr->s_handle, + p_attr->e_handle, + &p_attr->uuid, + p_attr->is_primary); break; case BTA_GATTC_ATTR_TYPE_CHAR: //TODO(jpawlowski): store decl_handle properly. - bta_gattc_add_char_to_cache(p_srvc_cb, - p_attr->s_handle, - p_attr->s_handle, - &p_attr->uuid, - p_attr->prop); + status = bta_gattc_add_char_to_cache(p_srvc_cb, + p_attr->s_handle, + p_attr->s_handle, + &p_attr->uuid, + p_attr->prop); break; case BTA_GATTC_ATTR_TYPE_CHAR_DESCR: - bta_gattc_add_attr_to_cache(p_srvc_cb, - p_attr->s_handle, - &p_attr->uuid, - p_attr->prop, - p_attr->incl_srvc_s_handle, - p_attr->incl_srvc_e_handle, - p_attr->attr_type); + status = bta_gattc_add_attr_to_cache(p_srvc_cb, + p_attr->s_handle, + &p_attr->uuid, + p_attr->prop, + p_attr->incl_srvc_s_handle, + p_attr->incl_srvc_e_handle, + p_attr->attr_type); break; case BTA_GATTC_ATTR_TYPE_INCL_SRVC: - bta_gattc_add_attr_to_cache(p_srvc_cb, - p_attr->s_handle, - &p_attr->uuid, - p_attr->prop, - p_attr->incl_srvc_s_handle, - p_attr->incl_srvc_e_handle, - p_attr->attr_type); + status = bta_gattc_add_attr_to_cache(p_srvc_cb, + p_attr->s_handle, + &p_attr->uuid, + p_attr->prop, + p_attr->incl_srvc_s_handle, + p_attr->incl_srvc_e_handle, + p_attr->attr_type); break; } + if (status != BTA_GATT_OK) { + /* Partial rebuild leaves the cache in an inconsistent state + * (e.g. a service without its characteristics, or a char whose + * descriptors would now attach to the wrong parent). Drop the + * partially built cache and let the caller fall back to a fresh + * discovery instead of exposing a corrupted view to the app. */ + APPL_TRACE_ERROR("%s: rebuild abort t=%d st=%d", + __func__, p_attr->attr_type, status); + list_free(p_srvc_cb->p_srvc_cache); + p_srvc_cb->p_srvc_cache = NULL; + return status; + } p_attr ++; num_attr --; } + + return BTA_GATT_OK; } /******************************************************************************* @@ -2056,6 +2350,11 @@ void bta_gattc_cache_save(tBTA_GATTC_SERV *p_srvc_cb, UINT16 conn_id) /* i: current write index, equals actual count after loops; db_size: allocated slots from cache count */ size_t i = 0; size_t db_size = bta_gattc_get_db_size(p_srvc_cb->p_srvc_cache, 0x0000, 0xFFFF); + /* Nothing to persist; skip allocating a zero-byte buffer (osi_malloc(0) + * returns NULL on ESP-IDF and would be misreported as "no resource"). */ + if (db_size == 0) { + return; + } tBTA_GATTC_NV_ATTR *nv_attr = osi_malloc(db_size * sizeof(tBTA_GATTC_NV_ATTR)); // This step is very important, if not clear the memory, the hasy key base on the attribute case will be not correct. if (nv_attr != NULL) { @@ -2203,9 +2502,15 @@ bool bta_gattc_cache_load(tBTA_GATTC_CLCB *p_clcb) } size_t num_attr = length / sizeof(tBTA_GATTC_NV_ATTR); - //don't forget to set the total attribute number. - p_clcb->p_srcb->total_attr = num_attr; - APPL_TRACE_DEBUG("%s(), index = %x, num_attr = %d", __func__, index, num_attr); + /* total_attr and rebuild path are UINT16-wide; refuse oversize NV blobs + * (NVS reads the whole blob, and UINT16 cannot represent the count). */ + if (num_attr > UINT16_MAX) { + APPL_TRACE_ERROR("%s: NV attr cnt>%u", + __func__, (unsigned)UINT16_MAX); + return false; + } + p_clcb->p_srcb->total_attr = (UINT16)num_attr; + APPL_TRACE_DEBUG("%s(), index = %x, num_attr = %u", __func__, index, (unsigned)num_attr); if ((attr = osi_malloc(sizeof(tBTA_GATTC_NV_ATTR) * num_attr)) == NULL) { APPL_TRACE_ERROR("%s, No Memory.", __func__); return false; @@ -2216,9 +2521,21 @@ bool bta_gattc_cache_load(tBTA_GATTC_CLCB *p_clcb) return false; } p_clcb->searched_service_source = BTA_GATTC_SERVICE_INFO_FROM_NVS_FLASH; - bta_gattc_rebuild_cache(p_clcb->p_srcb, num_attr, attr); + status = bta_gattc_rebuild_cache(p_clcb->p_srcb, (UINT16)num_attr, attr); //free the attr buffer after used. osi_free(attr); + if (status != BTA_GATT_OK) { + /* The NV cache is either corrupted or we ran out of memory while + * rebuilding it. Either way the in-memory cache has already been + * cleared by bta_gattc_rebuild_cache(); drop the on-flash copy too + * so the next connection re-discovers from scratch instead of + * looping on a bad cache. */ + APPL_TRACE_ERROR("%s: rebuild fail st=%d, reset NV", + __func__, status); + bta_gattc_co_cache_reset(p_clcb->p_srcb->server_bda); + p_clcb->searched_service_source = BTA_GATTC_SERVICE_INFO_FROM_UNKNOWN; + return false; + } return true; } @@ -2256,10 +2573,7 @@ static void bta_gattc_cache_write(BD_ADDR server_bda, UINT16 num_attr, void bta_gattc_cache_reset(BD_ADDR server_bda) { BTIF_TRACE_DEBUG("%s", __func__); - char fname[255] = {0}; - bta_gattc_generate_cache_file_name(fname, server_bda); bta_gattc_co_cache_reset(server_bda); - //unlink(fname); } //#endif /* GATTC_CACHE_NVS */ diff --git a/components/bt/host/bluedroid/bta/gatt/bta_gattc_co.c b/components/bt/host/bluedroid/bta/gatt/bta_gattc_co.c index 80a10a8f2ad..9ebac8a30b8 100644 --- a/components/bt/host/bluedroid/bta/gatt/bta_gattc_co.c +++ b/components/bt/host/bluedroid/bta/gatt/bta_gattc_co.c @@ -15,9 +15,6 @@ * limitations under the License. * ******************************************************************************/ -#ifdef BT_SUPPORT_NVM -#include -#endif /* BT_SUPPORT_NVM */ #include #include #include "bta/bta_gattc_co.h" @@ -30,6 +27,8 @@ #include "osi/list.h" #include "esp_err.h" #include "osi/allocator.h" +#include "freertos/FreeRTOS.h" +#include "freertos/semphr.h" #if( defined BLE_INCLUDED ) && (BLE_INCLUDED == TRUE) #if( defined BTA_GATT_INCLUDED ) && (GATTC_INCLUDED == TRUE) @@ -40,42 +39,6 @@ #define MAX_DEVICE_IN_CACHE 50 #define MAX_ADDR_LIST_CACHE_BUF 2048 -#ifdef BT_SUPPORT_NVM -static FILE *sCacheFD = 0; -static void getFilename(char *buffer, BD_ADDR bda) -{ - sprintf(buffer, "%s%02x%02x%02x%02x%02x%02x", GATT_CACHE_PREFIX - , bda[0], bda[1], bda[2], bda[3], bda[4], bda[5]); -} - -static void cacheClose(void) -{ - if (sCacheFD != 0) { - fclose(sCacheFD); - sCacheFD = 0; - } -} - -static bool cacheOpen(BD_ADDR bda, bool to_save) -{ - char fname[255] = {0}; - getFilename(fname, bda); - - cacheClose(); - sCacheFD = fopen(fname, to_save ? "w" : "r"); - - return (sCacheFD != 0); -} - -static void cacheReset(BD_ADDR bda) -{ - char fname[255] = {0}; - getFilename(fname, bda); - unlink(fname); -} - -#else - static const char *cache_key = "gattc_cache_key"; static const char *cache_addr = "cache_addr_tab"; @@ -98,6 +61,60 @@ typedef struct { static cache_env_t *cache_env = NULL; +/* Protect |cache_env|; exported callouts re-enter each other (e.g. cacheOpen -> find_addr). + * + * The mutex is created exactly once from bta_gattc_co_cache_addr_init() on the + * BT host startup task before any other callout becomes reachable, so we do + * NOT need any spinlock around the creation. The *Static variant places the + * storage in .bss and avoids any allocation. */ +static StaticSemaphore_t s_cache_env_mutex_buf; +static SemaphoreHandle_t s_cache_env_mutex = NULL; + +/* Must be called from the BT host startup task (single-threaded context) + * before any other cache_env_* user becomes reachable. Idempotent. */ +static void cache_env_mutex_init_once(void) +{ + if (s_cache_env_mutex == NULL) { + s_cache_env_mutex = xSemaphoreCreateRecursiveMutexStatic(&s_cache_env_mutex_buf); + } +} + +static void cache_env_lock(void) +{ + if (s_cache_env_mutex != NULL) { + (void)xSemaphoreTakeRecursive(s_cache_env_mutex, portMAX_DELAY); + } +} + +static void cache_env_unlock(void) +{ + if (s_cache_env_mutex != NULL) { + xSemaphoreGiveRecursive(s_cache_env_mutex); + } +} + +/* Format a per-device GATT cache NVS namespace name into |buffer|. + * + * Output layout: GATT_CACHE_PREFIX (5 chars: "gatt_") + sizeof(hash_key_t)*2 + * hex chars (currently 8) + NUL = 14 bytes total. + * + * Contract: + * |buffer| MUST be at least NVS_KEY_NAME_MAX_SIZE (16) bytes, which is also + * the NVS limit for a namespace name. Today's 14-byte output leaves only + * 2 bytes of headroom -- if GATT_CACHE_PREFIX is ever lengthened or + * sizeof(hash_key_t) is ever increased so that + * + * strlen(GATT_CACHE_PREFIX) + 2*sizeof(hash_key_t) + 1 > NVS_KEY_NAME_MAX_SIZE + * + * this sprintf() will silently overflow the caller's stack buffer AND + * produce a namespace name that nvs_open() will reject. In that case + * switch to snprintf(buffer, NVS_KEY_NAME_MAX_SIZE, ...) and propagate + * the truncation as an error to callers. + * + * Callers (keep this list in sync if a new one is added): + * - cacheOpen() [open by current hash] + * - bta_gattc_co_cache_addr_save() [erase old namespace on hash change] + */ static void getFilename(char *buffer, hash_key_t hash) { sprintf(buffer, "%s%02x%02x%02x%02x", GATT_CACHE_PREFIX, @@ -107,6 +124,9 @@ static void getFilename(char *buffer, hash_key_t hash) static void cacheClose(BD_ADDR bda) { UINT8 index = 0; + if (cache_env == NULL) { + return; + } if ((index = bta_gattc_co_find_addr_in_cache(bda)) != INVALID_ADDR_NUM) { if (cache_env->cache_addr[index].is_open) { nvs_close(cache_env->cache_addr[index].cache_fp); @@ -117,8 +137,13 @@ static void cacheClose(BD_ADDR bda) static bool cacheOpen(BD_ADDR bda, bool to_save, UINT8 *index) { + if (cache_env == NULL) { + return false; + } UNUSED(to_save); - char fname[255] = {0}; + /* NVS namespace name is limited to (NVS_KEY_NAME_MAX_SIZE - 1) characters. + * getFilename() produces GATT_CACHE_PREFIX (5) + 8 hex chars + NUL = 14 bytes. */ + char fname[NVS_KEY_NAME_MAX_SIZE] = {0}; UINT8 *assoc_addr = NULL; esp_err_t status = ESP_FAIL; hash_key_t hash_key = {0}; @@ -141,8 +166,9 @@ static bool cacheOpen(BD_ADDR bda, bool to_save, UINT8 *index) static void cacheReset(BD_ADDR bda, BOOLEAN update) { - char fname[255] = {0}; - getFilename(fname, bda); + if (cache_env == NULL) { + return; + } UINT8 index = 0; //cache_env->cache_addr if ((index = bta_gattc_co_find_addr_in_cache(bda)) != INVALID_ADDR_NUM) { @@ -153,18 +179,21 @@ static void cacheReset(BD_ADDR bda, BOOLEAN update) nvs_close(cache_env->cache_addr[index].cache_fp); cache_env->cache_addr[index].is_open = FALSE; } else { - cacheOpen(bda, false, &index); - if (index == INVALID_ADDR_NUM) { - APPL_TRACE_ERROR("%s INVALID ADDR NUM", __func__); - return; - } + /* The entry exists but is not currently held open in this session; + * try to (re)open it best-effort so we can erase the on-flash blob. + * The recursive cache_env lock is held throughout, so cacheOpen()'s + * internal find_addr_in_cache(bda) cannot disagree with the outer + * find above, i.e. *index will remain valid here. We deliberately + * do NOT bail out on cacheOpen() failure: the entry must still be + * removed from RAM to keep num_addr/cache_addr[] consistent and to + * avoid OOB in callers. */ + (void)cacheOpen(bda, false, &index); if (cache_env->cache_addr[index].is_open) { nvs_erase_all(cache_env->cache_addr[index].cache_fp); nvs_close(cache_env->cache_addr[index].cache_fp); cache_env->cache_addr[index].is_open = FALSE; } else { - APPL_TRACE_ERROR("%s cacheOpen failed", __func__); - return; + APPL_TRACE_ERROR("%s: cacheOpen fail, evict RAM", __func__); } } if(cache_env->num_addr == 0) { @@ -180,6 +209,10 @@ static void cacheReset(BD_ADDR bda, BOOLEAN update) cache_env->cache_addr[index].assoc_addr = NULL; } + if (cache_env->num_addr > MAX_DEVICE_IN_CACHE) { + APPL_TRACE_WARNING("%s: num_addr %u exceeds max, clamping", __func__, cache_env->num_addr); + cache_env->num_addr = MAX_DEVICE_IN_CACHE; + } UINT8 num = cache_env->num_addr; //delete the server_bda in the addr_info list. for(UINT8 i = index; i < (num - 1); i++) { @@ -231,7 +264,6 @@ static void cacheReset(BD_ADDR bda, BOOLEAN update) } } -#endif /* BT_SUPPORT_NVM */ /***************************************************************************** ** Function Declarations *****************************************************************************/ @@ -253,13 +285,19 @@ static void cacheReset(BD_ADDR bda, BOOLEAN update) *******************************************************************************/ tBTA_GATT_STATUS bta_gattc_co_cache_open(BD_ADDR server_bda, BOOLEAN to_save, UINT8 *index) { + cache_env_lock(); /* open NV cache and send call in */ tBTA_GATT_STATUS status = BTA_GATT_OK; + if (cache_env == NULL) { + cache_env_unlock(); + return BTA_GATT_ERROR; + } if (!cacheOpen(server_bda, to_save, index)) { status = BTA_GATT_ERROR; } APPL_TRACE_DEBUG("%s() - status=%d", __func__, status); + cache_env_unlock(); return status; } @@ -280,11 +318,22 @@ tBTA_GATT_STATUS bta_gattc_co_cache_open(BD_ADDR server_bda, BOOLEAN to_save, UI *******************************************************************************/ tBTA_GATT_STATUS bta_gattc_co_cache_load(tBTA_GATTC_NV_ATTR *attr, UINT8 index) { + cache_env_lock(); #if (!CONFIG_BT_STACK_NO_LOG) UINT16 num_attr = 0; #endif tBTA_GATT_STATUS status = BTA_GATT_ERROR; size_t length = 0; + + if (cache_env == NULL) { + cache_env_unlock(); + return BTA_GATT_ERROR; + } + + if (index >= MAX_DEVICE_IN_CACHE) { + cache_env_unlock(); + return BTA_GATT_ERROR; + } // Read the size of memory space required for blob nvs_get_blob(cache_env->cache_addr[index].cache_fp, cache_key, NULL, &length); // Read previously saved blob if available @@ -296,18 +345,28 @@ tBTA_GATT_STATUS bta_gattc_co_cache_load(tBTA_GATTC_NV_ATTR *attr, UINT8 index) APPL_TRACE_DEBUG("%s() - read=%d, status=%d, err_code = %d", __func__, num_attr, status, err_code); + cache_env_unlock(); return status; } size_t bta_gattc_get_cache_attr_length(UINT8 index) { + cache_env_lock(); size_t length = 0; - if (index == INVALID_ADDR_NUM) { + + if (cache_env == NULL) { + cache_env_unlock(); + return 0; + } + + if (index == INVALID_ADDR_NUM || index >= MAX_DEVICE_IN_CACHE) { + cache_env_unlock(); return 0; } // Read the size of memory space required for blob nvs_get_blob(cache_env->cache_addr[index].cache_fp, cache_key, NULL, &length); + cache_env_unlock(); return length; } @@ -330,6 +389,13 @@ size_t bta_gattc_get_cache_attr_length(UINT8 index) void bta_gattc_co_cache_save (BD_ADDR server_bda, UINT16 num_attr, tBTA_GATTC_NV_ATTR *p_attr_list) { + cache_env_lock(); + + if (cache_env == NULL) { + cache_env_unlock(); + return; + } + tBTA_GATT_STATUS status = BTA_GATT_OK; hash_key_t hash_key = {0}; UINT8 index = INVALID_ADDR_NUM; @@ -350,6 +416,7 @@ void bta_gattc_co_cache_save (BD_ADDR server_bda, UINT16 num_attr, (void) status; #endif APPL_TRACE_DEBUG("%s() wrote hash_key = %x%x%x%x, num_attr = %d, status = %d.", __func__, hash_key[0], hash_key[1], hash_key[2], hash_key[3], num_attr, status); + cache_env_unlock(); } /******************************************************************************* @@ -367,14 +434,14 @@ void bta_gattc_co_cache_save (BD_ADDR server_bda, UINT16 num_attr, *******************************************************************************/ void bta_gattc_co_cache_close(BD_ADDR server_bda, UINT16 conn_id) { + cache_env_lock(); UNUSED(conn_id); -//#ifdef BT_SUPPORT_NVM cacheClose(server_bda); -//#endif /* BT_SUPPORT_NVM */ /* close NV when server cache is done saving or loading, does not need to do anything for now on Insight */ BTIF_TRACE_DEBUG("%s()", __FUNCTION__); + cache_env_unlock(); } /******************************************************************************* @@ -391,18 +458,22 @@ void bta_gattc_co_cache_close(BD_ADDR server_bda, UINT16 conn_id) *******************************************************************************/ void bta_gattc_co_cache_reset(BD_ADDR server_bda) { + cache_env_lock(); cacheReset(server_bda, TRUE); + cache_env_unlock(); } void bta_gattc_co_cache_addr_init(void) { + cache_env_mutex_init_once(); + cache_env_lock(); nvs_handle_t fp; esp_err_t err_code; - UINT8 num_addr; size_t length = MAX_ADDR_LIST_CACHE_BUF; UINT8 *p_buf = osi_malloc(MAX_ADDR_LIST_CACHE_BUF); if (p_buf == NULL) { APPL_TRACE_ERROR("%s malloc failed!", __func__); + cache_env_unlock(); return; } @@ -410,6 +481,7 @@ void bta_gattc_co_cache_addr_init(void) if (cache_env == NULL) { APPL_TRACE_ERROR("%s malloc failed!", __func__); osi_free(p_buf); + cache_env_unlock(); return; } @@ -420,19 +492,53 @@ void bta_gattc_co_cache_addr_init(void) cache_env->is_open = TRUE; // Read previously saved blob if available if ((err_code = nvs_get_blob(fp, cache_key, p_buf, &length)) != ESP_OK) { - if(err_code != ESP_ERR_NVS_NOT_FOUND) { + if (err_code == ESP_ERR_NVS_NOT_FOUND) { + length = 0; + } else { APPL_TRACE_ERROR("%s, Line = %d, nvs flash get blob data fail, err_code = 0x%x", __func__, __LINE__, err_code); + osi_free(p_buf); + if (cache_env->is_open) { + nvs_close(cache_env->addr_fp); + cache_env->is_open = FALSE; + } + osi_free(cache_env); + cache_env = NULL; + cache_env_unlock(); + return; } + } + const size_t rec_sz = sizeof(BD_ADDR) + sizeof(hash_key_t); + if (length == 0) { + cache_env->num_addr = 0; osi_free(p_buf); + cache_env_unlock(); return; } - num_addr = length / (sizeof(BD_ADDR) + sizeof(hash_key_t)); - cache_env->num_addr = num_addr; + if ((length % rec_sz) != 0) { + APPL_TRACE_ERROR("%s: bad blob len %zu", __func__, length); + (void)nvs_erase_key(fp, cache_key); + cache_env->num_addr = 0; + osi_free(p_buf); + if (cache_env->is_open) { + nvs_close(cache_env->addr_fp); + cache_env->is_open = FALSE; + } + osi_free(cache_env); + cache_env = NULL; + cache_env_unlock(); + return; + } + size_t n_entries = length / rec_sz; + const BOOLEAN truncated = (n_entries > (size_t)MAX_DEVICE_IN_CACHE) ? TRUE : FALSE; + if (truncated) { + APPL_TRACE_WARNING("%s: trunc %zu->%d", __func__, n_entries, MAX_DEVICE_IN_CACHE); + n_entries = MAX_DEVICE_IN_CACHE; + } + cache_env->num_addr = (UINT8)n_entries; //read the address from nvs flash to cache address list. - for (UINT8 i = 0; i < num_addr; i++) { - memcpy(cache_env->cache_addr[i].addr, p_buf + i*(sizeof(BD_ADDR) + sizeof(hash_key_t)), sizeof(BD_ADDR)); - memcpy(cache_env->cache_addr[i].hash_key, - p_buf + i*(sizeof(BD_ADDR) + sizeof(hash_key_t)) + sizeof(BD_ADDR), sizeof(hash_key_t)); + for (UINT8 i = 0; i < cache_env->num_addr; i++) { + memcpy(cache_env->cache_addr[i].addr, p_buf + i * rec_sz, sizeof(BD_ADDR)); + memcpy(cache_env->cache_addr[i].hash_key, p_buf + i * rec_sz + sizeof(BD_ADDR), sizeof(hash_key_t)); APPL_TRACE_DEBUG("cache_addr[%x] = %x:%x:%x:%x:%x:%x", i, cache_env->cache_addr[i].addr[0], cache_env->cache_addr[i].addr[1], cache_env->cache_addr[i].addr[2], cache_env->cache_addr[i].addr[3], cache_env->cache_addr[i].addr[4], cache_env->cache_addr[i].addr[5]); @@ -440,21 +546,30 @@ void bta_gattc_co_cache_addr_init(void) cache_env->cache_addr[i].hash_key[2], cache_env->cache_addr[i].hash_key[3]); bta_gattc_co_cache_new_assoc_list(cache_env->cache_addr[i].addr, i); } + if (truncated) { + UINT16 out_len = (UINT16)(cache_env->num_addr * rec_sz); + if (nvs_set_blob(fp, cache_key, p_buf, out_len) != ESP_OK) { + APPL_TRACE_WARNING("%s: nvs trunc fail", __func__); + } + } } else { APPL_TRACE_ERROR("%s, Line = %d, nvs flash open fail, err_code = %x", __func__, __LINE__, err_code); osi_free(p_buf); osi_free(cache_env); cache_env = NULL; + cache_env_unlock(); return; } osi_free(p_buf); - return; + cache_env_unlock(); } void bta_gattc_co_cache_addr_deinit(void) { + cache_env_lock(); if(cache_env == NULL) { + cache_env_unlock(); return; } @@ -463,112 +578,222 @@ void bta_gattc_co_cache_addr_deinit(void) if (!cache_env->is_open) { osi_free(cache_env); cache_env = NULL; + cache_env_unlock(); return; } nvs_close(cache_env->addr_fp); cache_env->is_open = false; - for(UINT8 i = 0; i< cache_env->num_addr; i++) { + UINT8 num = cache_env->num_addr; + if (num > MAX_DEVICE_IN_CACHE) { + num = MAX_DEVICE_IN_CACHE; + } + for (UINT8 i = 0; i < num; i++) { cache_addr_info_t *addr_info = &cache_env->cache_addr[i]; - if(addr_info) { + if (addr_info->is_open) { nvs_close(addr_info->cache_fp); addr_info->is_open = false; - if(addr_info->assoc_addr) { - list_free(addr_info->assoc_addr); - } + } + if (addr_info->assoc_addr != NULL) { + list_free(addr_info->assoc_addr); + addr_info->assoc_addr = NULL; } } osi_free(cache_env); cache_env = NULL; + cache_env_unlock(); } BOOLEAN bta_gattc_co_addr_in_cache(BD_ADDR bda) { + cache_env_lock(); + if (cache_env == NULL) { + cache_env_unlock(); + return FALSE; + } UINT8 addr_index = 0; UINT8 num = cache_env->num_addr; + if (num > MAX_DEVICE_IN_CACHE) { + num = MAX_DEVICE_IN_CACHE; + } cache_addr_info_t *addr_info = &cache_env->cache_addr[0]; - for (addr_index = 0; addr_index < num; addr_index++) { + for (addr_index = 0; addr_index < num; addr_index++, addr_info++) { if (!memcmp(addr_info->addr, bda, sizeof(BD_ADDR))) { + cache_env_unlock(); return TRUE; } } + cache_env_unlock(); return FALSE; } UINT8 bta_gattc_co_find_addr_in_cache(BD_ADDR bda) { + cache_env_lock(); + if (cache_env == NULL) { + cache_env_unlock(); + return INVALID_ADDR_NUM; + } UINT8 addr_index = 0; UINT8 num = cache_env->num_addr; + if (num > MAX_DEVICE_IN_CACHE) { + num = MAX_DEVICE_IN_CACHE; + } cache_addr_info_t *addr_info = &cache_env->cache_addr[0]; for (addr_index = 0; addr_index < num; addr_index++, addr_info++) { if (!memcmp(addr_info->addr, bda, sizeof(BD_ADDR))) { + cache_env_unlock(); return addr_index; } } + cache_env_unlock(); return INVALID_ADDR_NUM; } UINT8 bta_gattc_co_find_hash_in_cache(hash_key_t hash_key) { + cache_env_lock(); + if (cache_env == NULL) { + cache_env_unlock(); + return INVALID_ADDR_NUM; + } UINT8 index = 0; UINT8 num = cache_env->num_addr; + if (num > MAX_DEVICE_IN_CACHE) { + num = MAX_DEVICE_IN_CACHE; + } cache_addr_info_t *addr_info = &cache_env->cache_addr[0]; - for (index = 0; index < num; index++) { + for (index = 0; index < num; index++, addr_info++) { if (!memcmp(addr_info->hash_key, hash_key, sizeof(hash_key_t))) { + cache_env_unlock(); return index; } } + cache_env_unlock(); return INVALID_ADDR_NUM; } UINT8 bta_gattc_co_get_addr_num(void) { + cache_env_lock(); if (cache_env == NULL) { + cache_env_unlock(); return 0; } - return cache_env->num_addr; + if (cache_env->num_addr > MAX_DEVICE_IN_CACHE) { + cache_env_unlock(); + return MAX_DEVICE_IN_CACHE; + } + UINT8 n = cache_env->num_addr; + cache_env_unlock(); + return n; } void bta_gattc_co_get_addr_list(BD_ADDR *addr_list) { + cache_env_lock(); + if (cache_env == NULL || addr_list == NULL) { + cache_env_unlock(); + return; + } UINT8 num = cache_env->num_addr; + if (num > MAX_DEVICE_IN_CACHE) { + num = MAX_DEVICE_IN_CACHE; + } for (UINT8 i = 0; i < num; i++) { memcpy(addr_list[i], cache_env->cache_addr[i].addr, sizeof(BD_ADDR)); } + cache_env_unlock(); } void bta_gattc_co_cache_addr_save(BD_ADDR bd_addr, hash_key_t hash_key) { + cache_env_lock(); + if (cache_env == NULL) { + cache_env_unlock(); + return; + } esp_err_t err_code; UINT8 index = 0; UINT8 new_index = cache_env->num_addr; UINT8 *p_buf = osi_malloc(MAX_ADDR_LIST_CACHE_BUF); if (p_buf == NULL) { APPL_TRACE_ERROR("%s malloc failed!", __func__); + cache_env_unlock(); return; } + if (cache_env->num_addr > MAX_DEVICE_IN_CACHE) { + APPL_TRACE_WARNING("%s: num_addr %u exceeds max, clamping", __func__, cache_env->num_addr); + cache_env->num_addr = MAX_DEVICE_IN_CACHE; + } + // check the address list has the same address or not // for the same address, it's hash key may be change due to service change if ((index = bta_gattc_co_find_addr_in_cache(bd_addr)) != INVALID_ADDR_NUM) { APPL_TRACE_DEBUG("%s the bd_addr already in the cache list, index = %x", __func__, index); + /* If the hash key changed (service change on the same peer), the + * per-device NVS namespace is keyed by the OLD hash. Just overwriting + * the in-RAM hash would leave cache_fp/is_open pointing at the old + * namespace, so a subsequent cacheOpen() would short-circuit on + * is_open==TRUE and the new attribute blob would be written into the + * stale namespace. After reboot the addr table maps bd_addr to the + * NEW hash and the load path would miss (and the old namespace is + * orphaned in NVS). Erase the old namespace and clear is_open so + * the next cacheOpen() reopens with the new hash-derived filename. */ + if (memcmp(cache_env->cache_addr[index].hash_key, hash_key, sizeof(hash_key_t)) != 0) { + APPL_TRACE_WARNING("%s: hash chg "MACSTR" %02x%02x%02x%02x->%02x%02x%02x%02x, erase NVS", + __func__, MAC2STR(bd_addr), + cache_env->cache_addr[index].hash_key[0], cache_env->cache_addr[index].hash_key[1], + cache_env->cache_addr[index].hash_key[2], cache_env->cache_addr[index].hash_key[3], + hash_key[0], hash_key[1], hash_key[2], hash_key[3]); + if (cache_env->cache_addr[index].is_open) { + (void)nvs_erase_all(cache_env->cache_addr[index].cache_fp); + nvs_close(cache_env->cache_addr[index].cache_fp); + cache_env->cache_addr[index].is_open = FALSE; + } else { + /* Not currently held open in this session; transiently open + * the old namespace just to erase its blob. Best-effort: + * ignore failures so a missing/corrupt old namespace does + * not block writing the new one. */ + char fname_old[NVS_KEY_NAME_MAX_SIZE] = {0}; + nvs_handle_t old_fp; + getFilename(fname_old, cache_env->cache_addr[index].hash_key); + if (nvs_open(fname_old, NVS_READWRITE, &old_fp) == ESP_OK) { + (void)nvs_erase_all(old_fp); + nvs_close(old_fp); + } + } + } //if the bd_addr already in the address list, update the hash key in it. memcpy(cache_env->cache_addr[index].addr, bd_addr, sizeof(BD_ADDR)); memcpy(cache_env->cache_addr[index].hash_key, hash_key, sizeof(hash_key_t)); } else { - if (cache_env->num_addr >= MAX_DEVICE_IN_CACHE) { + while (cache_env->num_addr >= MAX_DEVICE_IN_CACHE) { APPL_TRACE_WARNING("%s cache list full and remove the oldest addr info", __func__); + UINT8 before = cache_env->num_addr; cacheReset(cache_env->cache_addr[0].addr, FALSE); + if (cache_env->num_addr >= before) { + APPL_TRACE_ERROR("%s: cache eviction failed", __func__); + osi_free(p_buf); + cache_env_unlock(); + return; + } } new_index = cache_env->num_addr; - assert(new_index < MAX_DEVICE_IN_CACHE); + if (new_index >= MAX_DEVICE_IN_CACHE) { + APPL_TRACE_ERROR("%s: invalid new_index %u", __func__, new_index); + osi_free(p_buf); + cache_env_unlock(); + return; + } memcpy(cache_env->cache_addr[new_index].addr, bd_addr, sizeof(BD_ADDR)); memcpy(cache_env->cache_addr[new_index].hash_key, hash_key, sizeof(hash_key_t)); cache_env->num_addr++; @@ -603,22 +828,45 @@ void bta_gattc_co_cache_addr_save(BD_ADDR bd_addr, hash_key_t hash_key) //free the buffer after used. osi_free(p_buf); - return; + cache_env_unlock(); } BOOLEAN bta_gattc_co_cache_new_assoc_list(BD_ADDR src_addr, UINT8 index) { + cache_env_lock(); + if (cache_env == NULL) { + cache_env_unlock(); + return FALSE; + } + UNUSED(src_addr); + if (index >= MAX_DEVICE_IN_CACHE) { + cache_env_unlock(); + return FALSE; + } cache_addr_info_t *addr_info = &cache_env->cache_addr[index]; + /* Idempotent: if a list already exists at this slot (e.g. caller invoked + * us twice on the same index), free it first so we don't leak the prior + * list_t. The current sole caller is bta_gattc_co_cache_addr_init() which + * runs after a fresh memset, so this path is normally a no-op; the guard + * just keeps the function safe to reuse. */ + if (addr_info->assoc_addr != NULL) { + list_free(addr_info->assoc_addr); + addr_info->assoc_addr = NULL; + } addr_info->assoc_addr = list_new(osi_free_func); - return (addr_info->assoc_addr != NULL ? TRUE : FALSE); + BOOLEAN ok = (addr_info->assoc_addr != NULL ? TRUE : FALSE); + cache_env_unlock(); + return ok; } BOOLEAN bta_gattc_co_cache_append_assoc_addr(BD_ADDR src_addr, BD_ADDR assoc_addr) { + cache_env_lock(); UINT8 addr_index = 0; cache_addr_info_t *addr_info; UINT8 *p_assoc_buf = osi_malloc(sizeof(BD_ADDR)); if(!p_assoc_buf) { + cache_env_unlock(); return FALSE; } memcpy(p_assoc_buf, assoc_addr, sizeof(BD_ADDR)); @@ -630,6 +878,7 @@ BOOLEAN bta_gattc_co_cache_append_assoc_addr(BD_ADDR src_addr, BD_ADDR assoc_add if (addr_info->assoc_addr == NULL) { APPL_TRACE_ERROR("assoc_addr list creation failed"); osi_free(p_assoc_buf); + cache_env_unlock(); return FALSE; } @@ -638,6 +887,7 @@ BOOLEAN bta_gattc_co_cache_append_assoc_addr(BD_ADDR src_addr, BD_ADDR assoc_add if (!memcmp(list_node(sn), assoc_addr, sizeof(BD_ADDR))) { APPL_TRACE_WARNING("Association already exists"); osi_free(p_assoc_buf); + cache_env_unlock(); return TRUE; } } @@ -645,19 +895,23 @@ BOOLEAN bta_gattc_co_cache_append_assoc_addr(BD_ADDR src_addr, BD_ADDR assoc_add if (!list_append(addr_info->assoc_addr, p_assoc_buf)) { APPL_TRACE_ERROR("Failed to append to assoc_addr list"); osi_free(p_assoc_buf); + cache_env_unlock(); return FALSE; } + cache_env_unlock(); return TRUE; } else { osi_free(p_assoc_buf); } + cache_env_unlock(); return FALSE; } BOOLEAN bta_gattc_co_cache_remove_assoc_addr(BD_ADDR src_addr, BD_ADDR assoc_addr) { + cache_env_lock(); UINT8 addr_index = 0; cache_addr_info_t *addr_info; if ((addr_index = bta_gattc_co_find_addr_in_cache(src_addr)) != INVALID_ADDR_NUM) { @@ -667,20 +921,34 @@ BOOLEAN bta_gattc_co_cache_remove_assoc_addr(BD_ADDR src_addr, BD_ADDR assoc_add sn != list_end(addr_info->assoc_addr); sn = list_next(sn)) { void *addr = list_node(sn); if (!memcmp(addr, assoc_addr, sizeof(BD_ADDR))) { - return list_remove(addr_info->assoc_addr, addr); + BOOLEAN removed = list_remove(addr_info->assoc_addr, addr); + cache_env_unlock(); + return removed; } } //return list_remove(addr_info->assoc_addr, assoc_addr); } else { + cache_env_unlock(); return FALSE; } } + cache_env_unlock(); return FALSE; } UINT8* bta_gattc_co_cache_find_src_addr(BD_ADDR assoc_addr, UINT8 *index) { + cache_env_lock(); + if (index == NULL) { + cache_env_unlock(); + return NULL; + } + if (cache_env == NULL) { + *index = INVALID_ADDR_NUM; + cache_env_unlock(); + return NULL; + } UINT8 num = (cache_env->num_addr > MAX_DEVICE_IN_CACHE) ? MAX_DEVICE_IN_CACHE : cache_env->num_addr; cache_addr_info_t *addr_info = &cache_env->cache_addr[0]; UINT8 *addr_data; @@ -695,30 +963,37 @@ UINT8* bta_gattc_co_cache_find_src_addr(BD_ADDR assoc_addr, UINT8 *index) addr_data = (UINT8 *)list_node(node); if (!memcmp(addr_data, assoc_addr, sizeof(BD_ADDR))) { *index = i; - return (UINT8 *)addr_info->addr; + UINT8 *ret = (UINT8 *)addr_info->addr; + cache_env_unlock(); + return ret; } } addr_info++; } *index = INVALID_ADDR_NUM; + cache_env_unlock(); return NULL; } BOOLEAN bta_gattc_co_cache_clear_assoc_addr(BD_ADDR src_addr) { + cache_env_lock(); UINT8 addr_index = 0; cache_addr_info_t *addr_info; if ((addr_index = bta_gattc_co_find_addr_in_cache(src_addr)) != INVALID_ADDR_NUM) { addr_info = &cache_env->cache_addr[addr_index]; if (addr_info->assoc_addr != NULL) { list_clear(addr_info->assoc_addr); + cache_env_unlock(); + return TRUE; } else { + cache_env_unlock(); return FALSE; } - return TRUE; } + cache_env_unlock(); return FALSE; } diff --git a/components/bt/host/bluedroid/bta/gatt/bta_gattc_main.c b/components/bt/host/bluedroid/bta/gatt/bta_gattc_main.c index bbf2b346bff..a29f9bd462e 100644 --- a/components/bt/host/bluedroid/bta/gatt/bta_gattc_main.c +++ b/components/bt/host/bluedroid/bta/gatt/bta_gattc_main.c @@ -491,6 +491,12 @@ static char *gattc_evt_code(tBTA_GATTC_INT_EVT evt_code) return "BTA_GATTC_API_READ_BY_TYPE_EVT"; case BTA_GATTC_API_READ_MULTI_VAR_EVT: return "BTA_GATTC_API_READ_MULTI_VAR_EVT"; + case BTA_GATTC_ENC_CMPL_EVT: + return "BTA_GATTC_ENC_CMPL_EVT"; + case BTA_GATTC_API_CACHE_ASSOC_EVT: + return "BTA_GATTC_API_CACHE_ASSOC_EVT"; + case BTA_GATTC_API_CACHE_GET_ADDR_LIST_EVT: + return "BTA_GATTC_API_CACHE_GET_ADDR_LIST_EVT"; default: return "unknown GATTC event code"; } @@ -541,7 +547,8 @@ uint8_t bta_gattc_cl_rcb_active_count(void) for (uint8_t i = 0; i < BTA_GATTC_CL_MAX; i ++) { if (bta_gattc_cb.cl_rcb[i].in_use && - memcmp(bta_gattc_cb.cl_rcb[i].app_uuid.uu.uuid128, dm_gattc_uuid, 16)) { + (bta_gattc_cb.cl_rcb[i].app_uuid.len != LEN_UUID_128 || + memcmp(bta_gattc_cb.cl_rcb[i].app_uuid.uu.uuid128, dm_gattc_uuid, LEN_UUID_128))) { count++; } } diff --git a/components/bt/host/bluedroid/bta/gatt/bta_gattc_utils.c b/components/bt/host/bluedroid/bta/gatt/bta_gattc_utils.c index 1786835e330..3d360c652a0 100644 --- a/components/bt/host/bluedroid/bta/gatt/bta_gattc_utils.c +++ b/components/bt/host/bluedroid/bta/gatt/bta_gattc_utils.c @@ -154,9 +154,12 @@ tBTA_GATTC_CLCB *bta_gattc_find_clcb_by_conn_id (UINT16 conn_id) tBTA_GATTC_CLCB *p_clcb = &bta_gattc_cb.clcb[0]; UINT8 i; + if (conn_id == 0 || conn_id == GATT_INVALID_CONN_ID) { + return NULL; + } + for (i = 0; i < BTA_GATTC_CLCB_MAX; i ++, p_clcb ++) { - if (p_clcb->in_use && - p_clcb->bta_conn_id == conn_id) { + if (p_clcb->in_use && p_clcb->bta_conn_id == conn_id) { return p_clcb; } } @@ -342,7 +345,7 @@ tBTA_GATTC_SERV *bta_gattc_find_srvr_cache(BD_ADDR bda) UINT8 i; for (i = 0; i < BTA_GATTC_KNOWN_SR_MAX; i ++, p_srcb ++) { - if (bdcmp(p_srcb->server_bda, bda) == 0) { + if (p_srcb->in_use && bdcmp(p_srcb->server_bda, bda) == 0) { return p_srcb; } } @@ -387,7 +390,7 @@ tBTA_GATTC_SERV *bta_gattc_srcb_alloc(BD_ADDR bda) if (!p_tcb->in_use) { found = TRUE; break; - } else if (!p_tcb->connected) { + } else if (!p_tcb->connected && p_tcb->num_clcb == 0) { p_recycle = p_tcb; } } @@ -417,6 +420,38 @@ tBTA_GATTC_SERV *bta_gattc_srcb_alloc(BD_ADDR bda) return p_tcb; } +/****************************************************************************** + * + * Fixed-length prefix size of a GATTC API message in the heap buffer passed + * to bta_gattc_enqueue. Must match osi_malloc sizes in bta_gattc_api.c. + * Variable payloads (write value, search UUID) are copied separately. + * + ******************************************************************************/ +static size_t bta_gattc_enqueue_api_fixed_size(UINT16 event) +{ + switch (event) { + case BTA_GATTC_API_READ_EVT: + case BTA_GATTC_API_READ_BY_TYPE_EVT: + return sizeof(tBTA_GATTC_API_READ); + case BTA_GATTC_API_WRITE_EVT: + return sizeof(tBTA_GATTC_API_WRITE); + case BTA_GATTC_API_EXEC_EVT: + return sizeof(tBTA_GATTC_API_EXEC); + case BTA_GATTC_API_CFG_MTU_EVT: + return sizeof(tBTA_GATTC_API_CFG_MTU); + case BTA_GATTC_API_SEARCH_EVT: + return sizeof(tBTA_GATTC_API_SEARCH); + case BTA_GATTC_API_CONFIRM_EVT: + return sizeof(tBTA_GATTC_API_CONFIRM); + case BTA_GATTC_API_READ_MULTI_EVT: + case BTA_GATTC_API_READ_MULTI_VAR_EVT: + return sizeof(tBTA_GATTC_API_READ_MULTI); + default: + APPL_TRACE_ERROR("%s: unexpected event 0x%x", __func__, event); + return 0; + } +} + static BOOLEAN bta_gattc_has_prepare_command_in_queue(tBTA_GATTC_CLCB *p_clcb) { assert(p_clcb != NULL); @@ -485,14 +520,29 @@ BOOLEAN bta_gattc_enqueue(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data) if (p_data->hdr.event == BTA_GATTC_API_WRITE_EVT) { len = p_data->api_write.len; - if ((cmd_data = (tBTA_GATTC_DATA *)osi_malloc(sizeof(tBTA_GATTC_DATA) + len)) != NULL) { - memset(cmd_data, 0, sizeof(tBTA_GATTC_DATA) + len); - memcpy(cmd_data, p_data, sizeof(tBTA_GATTC_DATA)); - cmd_data->api_write.p_value = (UINT8 *)(cmd_data + 1); - memcpy(cmd_data->api_write.p_value, p_data->api_write.p_value, len); + if (len > 0) { + if (p_data->api_write.p_value == NULL) { + APPL_TRACE_ERROR("%s(), write len=%u but p_value is NULL", __func__, len); + return FALSE; + } + if ((cmd_data = (tBTA_GATTC_DATA *)osi_malloc(sizeof(tBTA_GATTC_DATA) + len)) != NULL) { + memset(cmd_data, 0, sizeof(tBTA_GATTC_DATA) + len); + memcpy(cmd_data, p_data, sizeof(tBTA_GATTC_API_WRITE)); + cmd_data->api_write.p_value = (UINT8 *)(cmd_data + 1); + memcpy(cmd_data->api_write.p_value, p_data->api_write.p_value, len); + } else { + APPL_TRACE_ERROR("%s(), line = %d, alloc fail, no memory.", __func__, __LINE__); + return FALSE; + } } else { - APPL_TRACE_ERROR("%s(), line = %d, alloc fail, no memory.", __func__, __LINE__); - return FALSE; + /* len == 0: no payload to copy; keep p_value NULL like BTA_GATTC_API_SEARCH_EVT without UUID */ + if ((cmd_data = (tBTA_GATTC_DATA *)osi_malloc(sizeof(tBTA_GATTC_DATA))) != NULL) { + memset(cmd_data, 0, sizeof(tBTA_GATTC_DATA)); + memcpy(cmd_data, p_data, sizeof(tBTA_GATTC_API_WRITE)); + } else { + APPL_TRACE_ERROR("%s(), line = %d, alloc fail, no memory.", __func__, __LINE__); + return FALSE; + } } } else if (p_data->hdr.event == BTA_GATTC_API_SEARCH_EVT) { /* @@ -547,7 +597,7 @@ BOOLEAN bta_gattc_enqueue(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data) if ((cmd_data = (tBTA_GATTC_DATA *)osi_malloc(len)) != NULL) { memset(cmd_data, 0, len); /* Copy the structure */ - memcpy(cmd_data, p_data, sizeof(tBTA_GATTC_DATA)); + memcpy(cmd_data, p_data, sizeof(tBTA_GATTC_API_SEARCH)); /* Update pointer to point to the space after the structure */ cmd_data->api_search.p_srvc_uuid = (tBT_UUID *)(cmd_data + 1); /* Copy the UUID data */ @@ -561,16 +611,22 @@ BOOLEAN bta_gattc_enqueue(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data) /* p_srvc_uuid is NULL, no extra space needed (search all services) */ if ((cmd_data = (tBTA_GATTC_DATA *)osi_malloc(sizeof(tBTA_GATTC_DATA))) != NULL) { memset(cmd_data, 0, sizeof(tBTA_GATTC_DATA)); - memcpy(cmd_data, p_data, sizeof(tBTA_GATTC_DATA)); + memcpy(cmd_data, p_data, sizeof(tBTA_GATTC_API_SEARCH)); } else { APPL_TRACE_ERROR("%s(), line = %d, alloc fail, no memory.", __func__, __LINE__); return FALSE; } } } else { + size_t copy_sz = bta_gattc_enqueue_api_fixed_size(p_data->hdr.event); + if (copy_sz == 0) { + APPL_TRACE_ERROR("%s(), line = %d, unknown event for queue copy 0x%x.", __func__, __LINE__, + p_data->hdr.event); + return FALSE; + } if ((cmd_data = (tBTA_GATTC_DATA *)osi_malloc(sizeof(tBTA_GATTC_DATA))) != NULL) { memset(cmd_data, 0, sizeof(tBTA_GATTC_DATA)); - memcpy(cmd_data, p_data, sizeof(tBTA_GATTC_DATA)); + memcpy(cmd_data, p_data, copy_sz); } else { APPL_TRACE_ERROR("%s(), line = %d, alloc fail, no memory.", __func__, __LINE__); return FALSE; @@ -888,7 +944,10 @@ tBTA_GATTC_CONN *bta_gattc_conn_alloc(BD_ADDR remote_bda) #if BTA_GATT_DEBUG == TRUE APPL_TRACE_DEBUG("bta_gattc_conn_alloc: found conn_track[%d] available", i_conn); #endif - p_conn->in_use = TRUE; + p_conn->in_use = TRUE; + p_conn->svc_change_descr_handle = 0; + p_conn->write_remote_svc_change_ccc_in_progress = FALSE; + p_conn->write_remote_svc_change_ccc_done = FALSE; bdcpy(p_conn->remote_bda, remote_bda); return p_conn; } @@ -956,6 +1015,9 @@ BOOLEAN bta_gattc_conn_dealloc(BD_ADDR remote_bda) if (p_conn != NULL) { p_conn->in_use = FALSE; + p_conn->svc_change_descr_handle = 0; + p_conn->write_remote_svc_change_ccc_in_progress = FALSE; + p_conn->write_remote_svc_change_ccc_done = FALSE; memset(p_conn->remote_bda, 0, BD_ADDR_LEN); return TRUE; } diff --git a/components/bt/host/bluedroid/bta/gatt/bta_gatts_act.c b/components/bt/host/bluedroid/bta/gatt/bta_gatts_act.c index 9688b5d2bc9..ec8129e6847 100644 --- a/components/bt/host/bluedroid/bta/gatt/bta_gatts_act.c +++ b/components/bt/host/bluedroid/bta/gatt/bta_gatts_act.c @@ -173,11 +173,12 @@ void bta_gatts_api_disable(tBTA_GATTS_CB *p_cb) void bta_gatts_register(tBTA_GATTS_CB *p_cb, tBTA_GATTS_DATA *p_msg) { tBTA_GATTS_INT_START_IF *p_buf; - tBTA_GATTS cb_data; + tBTA_GATTS cb_data = {0}; tBTA_GATT_STATUS status = BTA_GATT_OK; UINT8 i, first_unuse = 0xff; - memset(&cb_data, 0, sizeof(tBTA_GATTS)); + cb_data.reg_oper.server_if = BTA_GATTS_INVALID_IF; + memcpy(&cb_data.reg_oper.uuid, &p_msg->api_reg.app_uuid, sizeof(tBT_UUID)); if (p_cb->enabled == FALSE) { bta_gatts_enable(p_cb); @@ -188,6 +189,7 @@ void bta_gatts_register(tBTA_GATTS_CB *p_cb, tBTA_GATTS_DATA *p_msg) if (gatt_uuid_compare(p_cb->rcb[i].app_uuid, p_msg->api_reg.app_uuid)) { APPL_TRACE_ERROR("application already registered.\n"); status = BTA_GATT_DUP_REG; + cb_data.reg_oper.server_if = p_cb->rcb[i].gatt_if; break; } } @@ -201,8 +203,6 @@ void bta_gatts_register(tBTA_GATTS_CB *p_cb, tBTA_GATTS_DATA *p_msg) } } - cb_data.reg_oper.server_if = BTA_GATTS_INVALID_IF; - memcpy(&cb_data.reg_oper.uuid, &p_msg->api_reg.app_uuid, sizeof(tBT_UUID)); if (first_unuse != 0xff) { APPL_TRACE_VERBOSE("register application first_unuse rcb_idx = %d", first_unuse); @@ -274,7 +274,8 @@ void bta_gatts_deregister(tBTA_GATTS_CB *p_cb, tBTA_GATTS_DATA *p_msg) tBTA_GATT_STATUS status = BTA_GATT_ERROR; tBTA_GATTS_CBACK *p_cback = NULL; UINT8 i; - tBTA_GATTS cb_data; + UINT8 j; + tBTA_GATTS cb_data = {0}; cb_data.reg_oper.server_if = p_msg->api_dereg.server_if; cb_data.reg_oper.status = status; @@ -287,6 +288,12 @@ void bta_gatts_deregister(tBTA_GATTS_CB *p_cb, tBTA_GATTS_DATA *p_msg) /* deregister the app */ GATT_Deregister(p_cb->rcb[i].gatt_if); + for (j = 0; j < BTA_GATTS_MAX_SRVC_NUM; j ++) { + if (p_cb->srvc_cb[j].in_use && p_cb->srvc_cb[j].rcb_idx == i) { + memset(&p_cb->srvc_cb[j], 0, sizeof(tBTA_GATTS_SRVC_CB)); + } + } + /* reset cb */ memset(&p_cb->rcb[i], 0, sizeof(tBTA_GATTS_RCB)); cb_data.reg_oper.status = status; @@ -323,6 +330,16 @@ void bta_gatts_create_srvc(tBTA_GATTS_CB *p_cb, tBTA_GATTS_DATA *p_msg) APPL_TRACE_DEBUG("create service rcb_idx = %d", rcb_idx); if (rcb_idx != BTA_GATTS_INVALID_APP) { + /* + * Populate callback data with the request context early so the app can + * identify which create-service request failed even if resource + * allocation fails before we call into GATT. + */ + cb_data.create.server_if = p_cb->rcb[rcb_idx].gatt_if; + cb_data.create.is_primary = p_msg->api_create_svc.is_pri; + memcpy(&cb_data.create.uuid, &p_msg->api_create_svc.service_uuid, sizeof(tBT_UUID)); + cb_data.create.svc_instance = p_msg->api_create_svc.inst; + if ((srvc_idx = bta_gatts_alloc_srvc_cb(p_cb, rcb_idx)) != BTA_GATTS_INVALID_APP) { /* create the service now */ service_id = GATTS_CreateService (p_cb->rcb[rcb_idx].gatt_if, @@ -345,7 +362,7 @@ void bta_gatts_create_srvc(tBTA_GATTS_CB *p_cb, tBTA_GATTS_DATA *p_msg) cb_data.create.server_if = p_cb->rcb[rcb_idx].gatt_if; } else { - cb_data.status = BTA_GATT_ERROR; + cb_data.create.server_if = p_cb->rcb[rcb_idx].gatt_if; memset(&p_cb->srvc_cb[srvc_idx], 0, sizeof(tBTA_GATTS_SRVC_CB)); APPL_TRACE_ERROR("service creation failed."); } @@ -374,7 +391,7 @@ void bta_gatts_add_include_srvc(tBTA_GATTS_SRVC_CB *p_srvc_cb, tBTA_GATTS_DATA * { tBTA_GATTS_RCB *p_rcb = &bta_gatts_cb.rcb[p_srvc_cb->rcb_idx]; UINT16 attr_id = 0; - tBTA_GATTS cb_data; + tBTA_GATTS cb_data = {0}; attr_id = GATTS_AddIncludeService(p_msg->api_add_incl_srvc.hdr.layer_specific, p_msg->api_add_incl_srvc.included_service_id); @@ -406,7 +423,7 @@ void bta_gatts_add_char(tBTA_GATTS_SRVC_CB *p_srvc_cb, tBTA_GATTS_DATA *p_msg) { tBTA_GATTS_RCB *p_rcb = &bta_gatts_cb.rcb[p_srvc_cb->rcb_idx]; UINT16 attr_id = 0; - tBTA_GATTS cb_data; + tBTA_GATTS cb_data = {0}; tGATT_ATTR_VAL *p_attr_val = NULL; tGATTS_ATTR_CONTROL *p_control = NULL; @@ -436,8 +453,9 @@ void bta_gatts_add_char(tBTA_GATTS_SRVC_CB *p_srvc_cb, tBTA_GATTS_DATA *p_msg) } else { cb_data.add_result.status = BTA_GATT_ERROR; } - if((p_attr_val != NULL) && (p_attr_val->attr_val != NULL)){ - osi_free(p_attr_val->attr_val); + if (p_msg->api_add_char.attr_val.attr_val != NULL) { + osi_free(p_msg->api_add_char.attr_val.attr_val); + p_msg->api_add_char.attr_val.attr_val = NULL; } if (p_rcb->p_cback) { @@ -458,7 +476,7 @@ void bta_gatts_add_char_descr(tBTA_GATTS_SRVC_CB *p_srvc_cb, tBTA_GATTS_DATA *p_ { tBTA_GATTS_RCB *p_rcb = &bta_gatts_cb.rcb[p_srvc_cb->rcb_idx]; UINT16 attr_id = 0; - tBTA_GATTS cb_data; + tBTA_GATTS cb_data = {0}; tGATT_ATTR_VAL *p_attr_val = NULL; tGATTS_ATTR_CONTROL *p_control = NULL; @@ -486,8 +504,9 @@ void bta_gatts_add_char_descr(tBTA_GATTS_SRVC_CB *p_srvc_cb, tBTA_GATTS_DATA *p_ } else { cb_data.add_result.status = BTA_GATT_ERROR; } - if((p_attr_val != NULL) && (p_attr_val->attr_val != NULL)){ - osi_free(p_attr_val->attr_val); + if (p_msg->api_add_char_descr.attr_val.attr_val != NULL) { + osi_free(p_msg->api_add_char_descr.attr_val.attr_val); + p_msg->api_add_char_descr.attr_val.attr_val = NULL; } if (p_rcb->p_cback) { @@ -509,7 +528,7 @@ void bta_gatts_set_attr_value(tBTA_GATTS_SRVC_CB *p_srvc_cb, tBTA_GATTS_DATA *p_ { tBTA_GATTS_RCB *p_rcb = &bta_gatts_cb.rcb[p_srvc_cb->rcb_idx]; UINT16 service_id = p_srvc_cb->service_id; - tBTA_GATTS cb_data; + tBTA_GATTS cb_data = {0}; tBTA_GATT_STATUS gatts_status; gatts_status = GATTS_SetAttributeValue(p_msg->api_set_val.hdr.layer_specific, p_msg->api_set_val.length, @@ -543,11 +562,27 @@ void bta_gatts_set_attr_value(tBTA_GATTS_SRVC_CB *p_srvc_cb, tBTA_GATTS_DATA *p_ tGATT_STATUS bta_gatts_get_attr_value(UINT16 attr_handle, UINT16 *length, UINT8 **value) { - if (GATTS_GetAttributeValueInternal(attr_handle, length, value) == 0) { - return 0; + tGATT_STATUS status = GATTS_GetAttributeValueInternal(attr_handle, length, value); + if (status == GATT_SUCCESS) { + return GATT_SUCCESS; } - return GATTS_GetAttributeValue(attr_handle, length, value); + /* + * Only fall back to the service database when the handle is not part of + * internal GAP/GATT services. For any other internal read error, preserve + * the original status to avoid masking failures as "success with len=0". + */ + if (status != GATT_NOT_FOUND) { + if (length) { + *length = 0; + } + if (value) { + *value = NULL; + } + return status; + } + + return GATTS_GetAttributeValue(attr_handle, length, value); } /******************************************************************************* @@ -562,10 +597,10 @@ tGATT_STATUS bta_gatts_get_attr_value(UINT16 attr_handle, UINT16 *length, UINT8 void bta_gatts_delete_service(tBTA_GATTS_SRVC_CB *p_srvc_cb, tBTA_GATTS_DATA *p_msg) { tBTA_GATTS_RCB *p_rcb = &bta_gatts_cb.rcb[p_srvc_cb->rcb_idx]; - tBTA_GATTS cb_data; + tBTA_GATTS cb_data = {0}; cb_data.srvc_oper.server_if = p_rcb->gatt_if; - cb_data.srvc_oper.service_id = p_msg->api_add_incl_srvc.hdr.layer_specific; + cb_data.srvc_oper.service_id = p_srvc_cb->service_id; if (GATTS_DeleteService(p_rcb->gatt_if, &p_srvc_cb->service_uuid, @@ -574,6 +609,12 @@ void bta_gatts_delete_service(tBTA_GATTS_SRVC_CB *p_srvc_cb, tBTA_GATTS_DATA *p_ memset(p_srvc_cb, 0, sizeof(tBTA_GATTS_SRVC_CB)); } else { cb_data.srvc_oper.status = BTA_GATT_ERROR; + /* + * GATTS_DeleteService() only fails when the service (or app registration) + * cannot be found in the stack. Keeping srvc_cb "in_use" would permanently + * leak a slot and eventually prevent creating new services. + */ + memset(p_srvc_cb, 0, sizeof(tBTA_GATTS_SRVC_CB)); } if (p_rcb->p_cback) { @@ -593,10 +634,10 @@ void bta_gatts_delete_service(tBTA_GATTS_SRVC_CB *p_srvc_cb, tBTA_GATTS_DATA *p_ void bta_gatts_start_service(tBTA_GATTS_SRVC_CB *p_srvc_cb, tBTA_GATTS_DATA *p_msg) { tBTA_GATTS_RCB *p_rcb = &bta_gatts_cb.rcb[p_srvc_cb->rcb_idx]; - tBTA_GATTS cb_data; + tBTA_GATTS cb_data = {0}; cb_data.srvc_oper.server_if = p_rcb->gatt_if; - cb_data.srvc_oper.service_id = p_msg->api_add_incl_srvc.hdr.layer_specific; + cb_data.srvc_oper.service_id = p_srvc_cb->service_id; if (GATTS_StartService(p_rcb->gatt_if, p_srvc_cb->service_id, @@ -624,7 +665,7 @@ void bta_gatts_start_service(tBTA_GATTS_SRVC_CB *p_srvc_cb, tBTA_GATTS_DATA *p_m void bta_gatts_stop_service(tBTA_GATTS_SRVC_CB *p_srvc_cb, tBTA_GATTS_DATA *p_msg) { tBTA_GATTS_RCB *p_rcb = &bta_gatts_cb.rcb[p_srvc_cb->rcb_idx]; - tBTA_GATTS cb_data; + tBTA_GATTS cb_data = {0}; UNUSED(p_msg); GATTS_StopService(p_srvc_cb->service_id); @@ -651,14 +692,65 @@ void bta_gatts_send_rsp (tBTA_GATTS_CB *p_cb, tBTA_GATTS_DATA *p_msg) { UNUSED(p_cb); - if (GATTS_SendRsp (p_msg->api_rsp.hdr.layer_specific, - p_msg->api_rsp.trans_id, - p_msg->api_rsp.status, - (tGATTS_RSP *)p_msg->api_rsp.p_rsp) != GATT_SUCCESS) { - APPL_TRACE_ERROR("Sending response failed\n"); + tGATT_STATUS ret = GATTS_SendRsp(p_msg->api_rsp.hdr.layer_specific, + p_msg->api_rsp.trans_id, + p_msg->api_rsp.status, + (tGATTS_RSP *)p_msg->api_rsp.p_rsp); + if (ret == GATT_CONGESTED) { + APPL_TRACE_WARNING("%s: rsp ok but congested", __func__); + } else if (ret != GATT_SUCCESS) { + APPL_TRACE_ERROR("%s: send rsp fail 0x%02x", __func__, ret); } } +/******************************************************************************* +** +** Function bta_gatts_send_conf_evt_to_app +** +** Description Build a BTA_GATTS_CONF_EVT and dispatch it to the application +** via the supplied RCB callback. The RCB callback is +** btc_gatts_inter_cb (registered by BTA_GATTS_AppRegister), which +** internally posts to the BTC task via btc_transfer_context, so +** delivery is asynchronous w.r.t. the BTA task. +** +** If |value| is non-NULL and |value_len| > 0, the buffer is +** duplicated for the duration of the callback and freed before +** this helper returns. On allocation failure the callback is +** still dispatched with value=NULL/data_len=0 so the application +** does not stall waiting for CONF_EVT. +** +** Returns void +** +*******************************************************************************/ +static void bta_gatts_send_conf_evt_to_app(tBTA_GATTS_RCB *p_rcb, + UINT16 conn_id, UINT16 handle, + tBTA_GATT_STATUS status, + const UINT8 *value, UINT16 value_len) +{ + if (p_rcb == NULL || p_rcb->p_cback == NULL) { + return; + } + tBTA_GATTS cb_data = {0}; + cb_data.req_data.status = status; + cb_data.req_data.conn_id = conn_id; + cb_data.req_data.handle = handle; + cb_data.req_data.value = NULL; + cb_data.req_data.data_len = 0; + if (value != NULL && value_len > 0) { + cb_data.req_data.value = (uint8_t *)osi_malloc(value_len); + if (cb_data.req_data.value != NULL) { + memcpy(cb_data.req_data.value, value, value_len); + cb_data.req_data.data_len = value_len; + } else { + APPL_TRACE_ERROR("%s, malloc(%u) failed", __func__, (unsigned)value_len); + } + } + (*p_rcb->p_cback)(BTA_GATTS_CONF_EVT, &cb_data); + if (cb_data.req_data.value != NULL) { + osi_free(cb_data.req_data.value); + } +} + /******************************************************************************* ** ** Function bta_gatts_indicate_handle @@ -672,71 +764,108 @@ void bta_gatts_indicate_handle (tBTA_GATTS_CB *p_cb, tBTA_GATTS_DATA *p_msg) { tBTA_GATTS_SRVC_CB *p_srvc_cb; tBTA_GATTS_RCB *p_rcb = NULL; + tBTA_GATTS_RCB *p_srvc_rcb = NULL; tBTA_GATT_STATUS status = BTA_GATT_ILLEGAL_PARAMETER; tGATT_IF gatt_if; BD_ADDR remote_bda; tBTA_TRANSPORT transport; - tBTA_GATTS cb_data; p_srvc_cb = bta_gatts_find_srvc_cb_by_attr_id (p_cb, p_msg->api_indicate.attr_id); if (p_srvc_cb ) { + p_srvc_rcb = &p_cb->rcb[p_srvc_cb->rcb_idx]; + if (GATT_GetConnectionInfor(p_msg->api_indicate.hdr.layer_specific, &gatt_if, remote_bda, &transport)) { p_rcb = bta_gatts_find_app_rcb_by_app_if(gatt_if); - if (p_msg->api_indicate.need_confirm) { - - status = GATTS_HandleValueIndication (p_msg->api_indicate.hdr.layer_specific, - p_msg->api_indicate.attr_id, - p_msg->api_indicate.len, - p_msg->api_indicate.value); + if (p_rcb != p_srvc_rcb) { + if (p_rcb == NULL) { + APPL_TRACE_ERROR("%s: no RCB for gatt_if %d", __func__, gatt_if); + } else { + APPL_TRACE_ERROR("%s: if mismatch svc owner", __func__); + } + if (!p_msg->api_indicate.need_confirm) { + l2ble_update_att_acl_pkt_num(L2CA_DECREASE_BTU_NUM, NULL); + } + status = BTA_GATT_ILLEGAL_PARAMETER; } else { - l2ble_update_att_acl_pkt_num(L2CA_DECREASE_BTU_NUM, NULL); - status = GATTS_HandleValueNotification (p_msg->api_indicate.hdr.layer_specific, - p_msg->api_indicate.attr_id, - p_msg->api_indicate.len, - p_msg->api_indicate.value); - } + + if (p_msg->api_indicate.need_confirm) { + + status = GATTS_HandleValueIndication (p_msg->api_indicate.hdr.layer_specific, + p_msg->api_indicate.attr_id, + p_msg->api_indicate.len, + p_msg->api_indicate.value); + } else { + l2ble_update_att_acl_pkt_num(L2CA_DECREASE_BTU_NUM, NULL); + status = GATTS_HandleValueNotification (p_msg->api_indicate.hdr.layer_specific, + p_msg->api_indicate.attr_id, + p_msg->api_indicate.len, + p_msg->api_indicate.value); + } #if (CLASSIC_BT_INCLUDED == TRUE) - /* if over BR_EDR, inform PM for mode change */ - if (transport == BTA_TRANSPORT_BR_EDR) { - bta_sys_busy(BTA_ID_GATTS, BTA_ALL_APP_ID, remote_bda); - bta_sys_idle(BTA_ID_GATTS, BTA_ALL_APP_ID, remote_bda); - } + /* if over BR_EDR, inform PM for mode change */ + if (transport == BTA_TRANSPORT_BR_EDR) { + bta_sys_busy(BTA_ID_GATTS, BTA_ALL_APP_ID, remote_bda); + bta_sys_idle(BTA_ID_GATTS, BTA_ALL_APP_ID, remote_bda); + } #endif // #if (CLASSIC_BT_INCLUDED == TRUE) + } } else { APPL_TRACE_ERROR("Unknown connection ID: %d fail sending notification", p_msg->api_indicate.hdr.layer_specific); + if (!p_msg->api_indicate.need_confirm) { + l2ble_update_att_acl_pkt_num(L2CA_DECREASE_BTU_NUM, NULL); + } } - if ((status != GATT_SUCCESS || !p_msg->api_indicate.need_confirm) && - p_rcb && p_cb->rcb[p_srvc_cb->rcb_idx].p_cback) { - cb_data.req_data.status = status; - cb_data.req_data.conn_id = p_msg->api_indicate.hdr.layer_specific; - cb_data.req_data.value = NULL; - cb_data.req_data.data_len = 0; - cb_data.req_data.handle = p_msg->api_indicate.attr_id; - - if (p_msg->api_indicate.len > 0) { - cb_data.req_data.value = (uint8_t *) osi_malloc(p_msg->api_indicate.len); - if (cb_data.req_data.value != NULL) { - memset(cb_data.req_data.value, 0, p_msg->api_indicate.len); - cb_data.req_data.data_len = p_msg->api_indicate.len; - memcpy(cb_data.req_data.value, p_msg->api_indicate.value, p_msg->api_indicate.len); - } else { - APPL_TRACE_ERROR("%s, malloc failed", __func__); - } - } - (*p_rcb->p_cback)(BTA_GATTS_CONF_EVT, &cb_data); - if (cb_data.req_data.value != NULL) { - osi_free(cb_data.req_data.value); - cb_data.req_data.value = NULL; - } + if (status != GATT_SUCCESS || !p_msg->api_indicate.need_confirm) { + /* BTA_GATTS_CONF_EVT must be delivered to the application that + * initiated the indicate/notify, i.e. the owner of the connection + * (p_rcb resolved from conn_id's gatt_if). Fall back to the + * service-owning RCB only when the conn_id could not be resolved + * (link already torn down) so the application does not stall + * waiting for CONF_EVT. */ + tBTA_GATTS_RCB *p_target_rcb = (p_rcb != NULL) ? p_rcb : p_srvc_rcb; + bta_gatts_send_conf_evt_to_app(p_target_rcb, + p_msg->api_indicate.hdr.layer_specific, + p_msg->api_indicate.attr_id, + status, + p_msg->api_indicate.value, + p_msg->api_indicate.len); } } else { APPL_TRACE_ERROR("Not a registered service attribute ID: 0x%04x", p_msg->api_indicate.attr_id); + if (!p_msg->api_indicate.need_confirm) { + /* Notifications increment the BTU counter in BTA_GATTS_HandleValueIndication(); + * indications do not. So only balance the counter on the notification path. */ + l2ble_update_att_acl_pkt_num(L2CA_DECREASE_BTU_NUM, NULL); + } else { + /* Indication path: the application is waiting for BTA_GATTS_CONF_EVT to know + * the request is finished. Make a best-effort attempt to deliver an error event + * so the app does not stall. Do NOT touch the BTU counter here, since indications + * never incremented it. */ + if (GATT_GetConnectionInfor(p_msg->api_indicate.hdr.layer_specific, + &gatt_if, remote_bda, &transport)) { + p_rcb = bta_gatts_find_app_rcb_by_app_if(gatt_if); + if (p_rcb && p_rcb->p_cback) { + bta_gatts_send_conf_evt_to_app(p_rcb, + p_msg->api_indicate.hdr.layer_specific, + p_msg->api_indicate.attr_id, + BTA_GATT_ILLEGAL_PARAMETER, + NULL, 0); + } else { + APPL_TRACE_ERROR("%s: no RCB if=%d, drop CONF", __func__, gatt_if); + } + } else { + /* conn_id is invalid (e.g. link already torn down). We have no gatt_if, so we + * cannot locate the owning RCB to deliver the callback. The application is + * expected to clean up pending indications on BTA_GATTS_DISCONNECT_EVT. */ + APPL_TRACE_ERROR("%s: bad conn_id %d, drop CONF", __func__, p_msg->api_indicate.hdr.layer_specific); + } + } } } @@ -916,13 +1045,11 @@ static void bta_gatts_send_request_cback (UINT16 conn_id, UINT32 trans_id, tGATTS_REQ_TYPE req_type, tGATTS_DATA *p_data) { - tBTA_GATTS cb_data; + tBTA_GATTS cb_data = {0}; tBTA_GATTS_RCB *p_rcb; tGATT_IF gatt_if; tBTA_GATT_TRANSPORT transport; - memset(&cb_data, 0 , sizeof(tBTA_GATTS)); - if (GATT_GetConnectionInfor(conn_id, &gatt_if, cb_data.req_data.remote_bda, &transport)) { p_rcb = bta_gatts_find_app_rcb_by_app_if(gatt_if); @@ -974,10 +1101,10 @@ static void bta_gatts_conn_cback (tGATT_IF gatt_if, BD_ADDR bda, UINT16 conn_id, gatt_if, conn_id, connected, reason); APPL_TRACE_DEBUG("bta_gatts_conn_cback bda :%02x-%02x-%02x-%02x-%02x-%02x ", bda[0], bda[1], bda[2], bda[3], bda[4], bda[5]); - + /* bt_bdaddr_t bdaddr; bdcpy(bdaddr.address, bda); - /* + if (connected) btif_debug_conn_state(bdaddr, BTIF_DEBUG_CONNECTED, GATT_CONN_UNKNOWN); else @@ -1033,7 +1160,7 @@ static void bta_gatts_conn_cback (tGATT_IF gatt_if, BD_ADDR bda, UINT16 conn_id, *******************************************************************************/ static void bta_gatts_cong_cback (UINT16 conn_id, BOOLEAN congested) { - tBTA_GATTS cb_data; + tBTA_GATTS cb_data = {0}; cb_data.congest.conn_id = conn_id; cb_data.congest.congested = congested; btc_congest_callback(&cb_data); diff --git a/components/bt/host/bluedroid/bta/gatt/bta_gatts_api.c b/components/bt/host/bluedroid/bta/gatt/bta_gatts_api.c index 358b6939913..f8774de77c5 100644 --- a/components/bt/host/bluedroid/bta/gatt/bta_gatts_api.c +++ b/components/bt/host/bluedroid/bta/gatt/bta_gatts_api.c @@ -93,6 +93,7 @@ void BTA_GATTS_AppRegister(const tBT_UUID * p_app_uuid, tBTA_GATTS_CBACK *p_cbac } if ((p_buf = (tBTA_GATTS_API_REG *) osi_malloc(sizeof(tBTA_GATTS_API_REG))) != NULL) { + memset(p_buf, 0, sizeof(*p_buf)); p_buf->hdr.event = BTA_GATTS_API_REG_EVT; if (p_app_uuid != NULL) { @@ -237,16 +238,23 @@ void BTA_GATTS_AddCharacteristic (UINT16 service_id, const tBT_UUID * p_char_u } if(attr_val != NULL){ - p_buf->attr_val.attr_len = attr_val->attr_len; p_buf->attr_val.attr_max_len = attr_val->attr_max_len; - if(len != 0){ - p_buf->attr_val.attr_val = (uint8_t *)osi_malloc(len); - if(p_buf->attr_val.attr_val != NULL){ - memcpy(p_buf->attr_val.attr_val, attr_val->attr_val, len); - } else { - p_buf->attr_val.attr_len = 0; - p_buf->attr_val.attr_max_len = 0; - APPL_TRACE_ERROR("Allocate fail for %s\n", __func__); + if (attr_val->attr_max_len == 0) { + p_buf->attr_val.attr_len = 0; + if (len != 0) { + APPL_TRACE_WARNING("%s: max_len 0, drop len %u", __func__, len); + } + } else { + p_buf->attr_val.attr_len = attr_val->attr_len; + if(len != 0){ + p_buf->attr_val.attr_val = (uint8_t *)osi_malloc(len); + if(p_buf->attr_val.attr_val != NULL){ + memcpy(p_buf->attr_val.attr_val, attr_val->attr_val, len); + } else { + p_buf->attr_val.attr_len = 0; + p_buf->attr_val.attr_max_len = 0; + APPL_TRACE_ERROR("alloc fail %s", __func__); + } } } } @@ -299,19 +307,27 @@ void BTA_GATTS_AddCharDescriptor (UINT16 service_id, } if(attr_val != NULL){ - p_buf->attr_val.attr_len = attr_val->attr_len; p_buf->attr_val.attr_max_len = attr_val->attr_max_len; - value_len = attr_val->attr_len; - if (value_len != 0){ - p_buf->attr_val.attr_val = (uint8_t*)osi_malloc(value_len); - if(p_buf->attr_val.attr_val != NULL){ - memcpy(p_buf->attr_val.attr_val, attr_val->attr_val, value_len); + if (attr_val->attr_max_len == 0) { + p_buf->attr_val.attr_len = 0; + value_len = attr_val->attr_len; + if (value_len != 0) { + APPL_TRACE_WARNING("%s: max_len 0, drop len %u", __func__, value_len); } - else{ - p_buf->attr_val.attr_len = 0; - p_buf->attr_val.attr_max_len = 0; - APPL_TRACE_ERROR("Allocate fail for %s\n", __func__); + } else { + p_buf->attr_val.attr_len = attr_val->attr_len; + value_len = attr_val->attr_len; + if (value_len != 0){ + p_buf->attr_val.attr_val = (uint8_t*)osi_malloc(value_len); + if(p_buf->attr_val.attr_val != NULL){ + memcpy(p_buf->attr_val.attr_val, attr_val->attr_val, value_len); + } + else{ + p_buf->attr_val.attr_len = 0; + p_buf->attr_val.attr_max_len = 0; + APPL_TRACE_ERROR("alloc fail %s", __func__); + } } } } @@ -501,8 +517,10 @@ void BTA_SetAttributeValue(UINT16 attr_handle, UINT16 length, UINT8 *value) memset(p_buf, 0, len); p_buf->hdr.event = BTA_GATTS_API_SET_ATTR_VAL_EVT; p_buf->hdr.layer_specific = attr_handle; - p_buf->length = length; - if(value != NULL){ + if (value == NULL) { + p_buf->length = 0; + } else { + p_buf->length = length; if((p_buf->value = (UINT8 *)osi_malloc(length)) != NULL){ memcpy(p_buf->value, value, length); } else { diff --git a/components/bt/host/bluedroid/bta/gatt/bta_gatts_main.c b/components/bt/host/bluedroid/bta/gatt/bta_gatts_main.c index 6444321437a..2c78d2df8ef 100644 --- a/components/bt/host/bluedroid/bta/gatt/bta_gatts_main.c +++ b/components/bt/host/bluedroid/bta/gatt/bta_gatts_main.c @@ -132,6 +132,16 @@ BOOLEAN bta_gatts_hdl_event(BT_HDR *p_msg) if (p_srvc_cb != NULL) { bta_gatts_srvc_build_act[p_msg->event - BTA_GATTS_API_ADD_INCL_SRVC_EVT](p_srvc_cb, (tBTA_GATTS_DATA *) p_msg); } else { + tBTA_GATTS_DATA *p_data = (tBTA_GATTS_DATA *)p_msg; + if (p_msg->event == BTA_GATTS_API_ADD_CHAR_EVT && + p_data->api_add_char.attr_val.attr_val != NULL) { + osi_free(p_data->api_add_char.attr_val.attr_val); + p_data->api_add_char.attr_val.attr_val = NULL; + } else if (p_msg->event == BTA_GATTS_API_ADD_DESCR_EVT && + p_data->api_add_char_descr.attr_val.attr_val != NULL) { + osi_free(p_data->api_add_char_descr.attr_val.attr_val); + p_data->api_add_char_descr.attr_val.attr_val = NULL; + } APPL_TRACE_ERROR("service not created\n"); } break; diff --git a/components/bt/host/bluedroid/bta/gatt/include/bta_gattc_int.h b/components/bt/host/bluedroid/bta/gatt/include/bta_gattc_int.h index e74b5759ee7..1c22f44ac81 100644 --- a/components/bt/host/bluedroid/bta/gatt/include/bta_gattc_int.h +++ b/components/bt/host/bluedroid/bta/gatt/include/bta_gattc_int.h @@ -211,6 +211,7 @@ typedef struct { typedef struct { BT_HDR hdr; BD_ADDR remote_bda; + BOOLEAN erase_flash; } tBTA_GATTC_API_CACHE_REFRESH; typedef struct { @@ -399,6 +400,8 @@ typedef struct { BOOLEAN in_use; BD_ADDR remote_bda; UINT16 svc_change_descr_handle; + /* Tracks the in-flight internal write to the Service Changed CCC descriptor */ + BOOLEAN write_remote_svc_change_ccc_in_progress; BOOLEAN write_remote_svc_change_ccc_done; } tBTA_GATTC_CONN; @@ -558,7 +561,7 @@ extern void bta_gattc_get_db_with_operation(UINT16 conn_id, extern void bta_gattc_get_gatt_db(UINT16 conn_id, UINT16 start_handle, UINT16 end_handle, btgatt_db_element_t **db, UINT16 *count); extern tBTA_GATT_STATUS bta_gattc_init_cache(tBTA_GATTC_SERV *p_srvc_cb); -extern void bta_gattc_rebuild_cache(tBTA_GATTC_SERV *p_srcv, UINT16 num_attr, tBTA_GATTC_NV_ATTR *attr); +extern tBTA_GATT_STATUS bta_gattc_rebuild_cache(tBTA_GATTC_SERV *p_srcv, UINT16 num_attr, tBTA_GATTC_NV_ATTR *attr); extern void bta_gattc_cache_save(tBTA_GATTC_SERV *p_srvc_cb, UINT16 conn_id); extern void bta_gattc_reset_discover_st(tBTA_GATTC_SERV *p_srcb, tBTA_GATT_STATUS status); diff --git a/components/bt/host/bluedroid/bta/include/bta/bta_gatt_api.h b/components/bt/host/bluedroid/bta/include/bta/bta_gatt_api.h index d18badf2d51..6aa59059f68 100644 --- a/components/bt/host/bluedroid/bta/include/bta/bta_gatt_api.h +++ b/components/bt/host/bluedroid/bta/include/bta/bta_gatt_api.h @@ -676,10 +676,17 @@ typedef union { add char : BTA_GATTS_ADD_CHAR_EVT add char descriptor: BTA_GATTS_ADD_CHAR_DESCR_EVT */ tBAT_GATTS_ATTR_VAL_RESULT attr_val; - tBTA_GATTS_REQ req_data; + tBTA_GATTS_REQ req_data; /* BTA_GATTS_READ_EVT, BTA_GATTS_WRITE_EVT, + BTA_GATTS_EXEC_WRITE_EVT, BTA_GATTS_MTU_EVT, + BTA_GATTS_CONF_EVT (handle/value/data_len + are carried here, not in `confirm`) */ tBTA_GATTS_CONN conn; /* BTA_GATTS_CONN_EVT */ tBTA_GATTS_CONGEST congest; /* BTA_GATTS_CONGEST_EVT callback data */ - tBTA_GATTS_CONF confirm; /* BTA_GATTS_CONF_EVT callback data */ + tBTA_GATTS_CONF confirm; /* Deprecated: retained for source/ABI compatibility + only. BTA_GATTS_CONF_EVT actually uses `req_data` + because handle/value/data_len are required by the + public API. Do NOT add new producers/consumers + that read or write this member. */ tBTA_GATTS_CLOSE close; /* BTA_GATTS_CLOSE_EVT callback data */ tBTA_GATTS_OPEN open; /* BTA_GATTS_OPEN_EVT callback data */ tBTA_GATTS_CANCEL_OPEN cancel_open; /* tBTA_GATTS_CANCEL_OPEN callback data */ diff --git a/components/bt/host/bluedroid/btc/core/btc_ble_storage.c b/components/bt/host/bluedroid/btc/core/btc_ble_storage.c index ac810a94bc3..a833a5ac91a 100644 --- a/components/bt/host/bluedroid/btc/core/btc_ble_storage.c +++ b/components/bt/host/bluedroid/btc/core/btc_ble_storage.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2015-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -90,6 +90,17 @@ void btc_storage_save(void) } #if (BLE_INCLUDED == TRUE) +static bool btc_storage_is_all_zeros(const void *buf, size_t len) +{ + const uint8_t *p = (const uint8_t *)buf; + for (size_t i = 0; i < len; i++) { + if (p[i] != 0) { + return false; + } + } + return true; +} + static bt_status_t _btc_storage_add_ble_bonding_key(bt_bdaddr_t *remote_bd_addr, char *key, uint8_t key_type, @@ -158,33 +169,48 @@ static bt_status_t _btc_storage_get_ble_bonding_key(bt_bdaddr_t *remote_bd_addr, { bdstr_t bdstr; bdaddr_to_string(remote_bd_addr, bdstr, sizeof(bdstr)); - const char* name; + const char *name; + size_t expected_len; switch (key_type) { case BTM_LE_KEY_PENC: name = BTC_BLE_STORAGE_LE_KEY_PENC_STR; + expected_len = sizeof(tBTM_LE_PENC_KEYS); break; case BTM_LE_KEY_PID: name = BTC_BLE_STORAGE_LE_KEY_PID_STR; + expected_len = sizeof(tBTM_LE_PID_KEYS); break; case BTM_LE_KEY_PCSRK: name = BTC_BLE_STORAGE_LE_KEY_PCSRK_STR; + expected_len = sizeof(tBTM_LE_PCSRK_KEYS); break; case BTM_LE_KEY_LENC: name = BTC_BLE_STORAGE_LE_KEY_LENC_STR; + expected_len = sizeof(tBTM_LE_LENC_KEYS); break; case BTM_LE_KEY_LCSRK: name = BTC_BLE_STORAGE_LE_KEY_LCSRK_STR; + expected_len = sizeof(tBTM_LE_LCSRK_KEYS); break; case BTM_LE_KEY_LID: - name = BTC_BLE_STORAGE_LE_KEY_LID_STR; + /* LID is a flag-only key; no payload is persisted. */ + name = BTC_BLE_STORAGE_LE_KEY_LID_STR; + expected_len = 0; break; default: return BT_STATUS_FAIL; } - size_t length = key_length; - int ret = btc_config_get_bin(bdstr, name, (uint8_t *)key_value, &length); - return ret ? BT_STATUS_SUCCESS : BT_STATUS_FAIL; + if (key_length < 0 || (size_t)key_length < expected_len) { + return BT_STATUS_FAIL; + } + + size_t length = (size_t)key_length; + bool ret = btc_config_get_bin(bdstr, name, (uint8_t *)key_value, &length); + if (!ret || length != expected_len) { + return BT_STATUS_FAIL; + } + return BT_STATUS_SUCCESS; } bt_status_t btc_storage_get_ble_bonding_key(bt_bdaddr_t *remote_bd_addr, @@ -237,17 +263,22 @@ static bt_status_t _btc_storage_remove_all_ble_keys(const char *name) void btc_storage_remove_unused_sections(uint8_t *cur_addr, tBTM_LE_PID_KEYS *del_pid_key) { bt_bdaddr_t bd_addr; - uint32_t device_type = 0; if (del_pid_key == NULL) { return; } + // Only use valid static address for de-duplication. + if (btc_storage_is_all_zeros(del_pid_key->static_addr, sizeof(del_pid_key->static_addr))) { + return; + } + btc_config_lock(); const btc_config_section_iter_t *iter = btc_config_section_begin(); while (iter != btc_config_section_end()) { + uint32_t device_type = 0; //store the next iter, if remove section, then will not loss the point const char *section = btc_config_section_name(iter); @@ -276,13 +307,19 @@ void btc_storage_remove_unused_sections(uint8_t *cur_addr, tBTM_LE_PID_KEYS *del char buffer[sizeof(tBTM_LE_KEY_VALUE)] = {0}; - if (_btc_storage_get_ble_bonding_key(&bd_addr, BTM_LE_KEY_PID, buffer, sizeof(tBTM_LE_PID_KEYS)) == BT_STATUS_SUCCESS) { + size_t pid_len = sizeof(tBTM_LE_PID_KEYS); + bool pid_ok = btc_config_get_bin(section, BTC_BLE_STORAGE_LE_KEY_PID_STR, (uint8_t *)buffer, &pid_len); + + if (pid_ok && pid_len >= sizeof(tBTM_LE_PID_KEYS)) { tBTM_LE_PID_KEYS *pid_key = (tBTM_LE_PID_KEYS *) buffer; iter = btc_config_section_next(iter); - if (memcmp(del_pid_key->static_addr, pid_key->static_addr, 6) == 0 && memcmp(cur_addr, bd_addr.address, 6) != 0 && del_pid_key->addr_type == pid_key->addr_type) { + if (del_pid_key->addr_type == pid_key->addr_type && + !btc_storage_is_all_zeros(pid_key->static_addr, sizeof(pid_key->static_addr)) && + memcmp(del_pid_key->static_addr, pid_key->static_addr, sizeof(pid_key->static_addr)) == 0 && + memcmp(cur_addr, bd_addr.address, sizeof(bd_addr.address)) != 0) { if (device_type == BT_DEVICE_TYPE_DUMO) { btc_config_set_int(section, BTC_BLE_STORAGE_DEV_TYPE_STR, BT_DEVICE_TYPE_BREDR); _btc_storage_remove_all_ble_keys(section); @@ -308,13 +345,13 @@ void btc_storage_delete_duplicate_ble_devices(void) char temp_buffer[sizeof(tBTM_LE_KEY_VALUE)] = {0}; tBTM_LE_PID_KEYS *pid_key; tBTM_LE_PID_KEYS *temp_pid_key; - uint32_t device_type = 0; bt_bdaddr_t temp_bd_addr; btc_config_lock(); for (const btc_config_section_iter_t *iter = btc_config_section_begin(); iter != btc_config_section_end(); iter = btc_config_section_next(iter)) { + uint32_t device_type = 0; const char *name = btc_config_section_name(iter); if (!string_is_bdaddr(name) || @@ -324,27 +361,36 @@ void btc_storage_delete_duplicate_ble_devices(void) } string_to_bdaddr(name, &bd_addr); - if (_btc_storage_get_ble_bonding_key(&bd_addr, BTM_LE_KEY_PID, buffer, sizeof(tBTM_LE_PID_KEYS)) == BT_STATUS_SUCCESS) + size_t pid_len = sizeof(tBTM_LE_PID_KEYS); + bool pid_ok = btc_config_get_bin(name, BTC_BLE_STORAGE_LE_KEY_PID_STR, (uint8_t *)buffer, &pid_len); + if (pid_ok && pid_len >= sizeof(tBTM_LE_PID_KEYS)) { pid_key = (tBTM_LE_PID_KEYS *) buffer; + if (btc_storage_is_all_zeros(pid_key->static_addr, sizeof(pid_key->static_addr))) { + continue; + } const btc_config_section_iter_t *temp_iter = btc_config_section_next(iter); while (temp_iter != NULL) { + uint32_t temp_device_type = 0; const char *temp_name = btc_config_section_name(temp_iter); - if (!string_is_bdaddr(temp_name) || !btc_config_get_int(temp_name, BTC_BLE_STORAGE_DEV_TYPE_STR, (int *)&device_type) || - ((device_type & BT_DEVICE_TYPE_BLE) != BT_DEVICE_TYPE_BLE)) { + if (!string_is_bdaddr(temp_name) || !btc_config_get_int(temp_name, BTC_BLE_STORAGE_DEV_TYPE_STR, (int *)&temp_device_type) || + ((temp_device_type & BT_DEVICE_TYPE_BLE) != BT_DEVICE_TYPE_BLE)) { temp_iter = btc_config_section_next(temp_iter); continue; } string_to_bdaddr(temp_name, &temp_bd_addr); - if (_btc_storage_get_ble_bonding_key(&temp_bd_addr, BTM_LE_KEY_PID, temp_buffer, sizeof(tBTM_LE_PID_KEYS)) == BT_STATUS_SUCCESS) + size_t temp_pid_len = sizeof(tBTM_LE_PID_KEYS); + bool temp_pid_ok = btc_config_get_bin(temp_name, BTC_BLE_STORAGE_LE_KEY_PID_STR, (uint8_t *)temp_buffer, &temp_pid_len); + if (temp_pid_ok && temp_pid_len >= sizeof(tBTM_LE_PID_KEYS)) { temp_pid_key = (tBTM_LE_PID_KEYS *) temp_buffer; - if (memcmp(pid_key->static_addr, temp_pid_key->static_addr, 6) == 0 && pid_key->addr_type == temp_pid_key->addr_type) { - const char *temp_name = btc_config_section_name(temp_iter); + if (pid_key->addr_type == temp_pid_key->addr_type && + !btc_storage_is_all_zeros(temp_pid_key->static_addr, sizeof(temp_pid_key->static_addr)) && + memcmp(pid_key->static_addr, temp_pid_key->static_addr, sizeof(pid_key->static_addr)) == 0) { temp_iter = btc_config_section_next(temp_iter); - if (device_type == BT_DEVICE_TYPE_DUMO) { + if (temp_device_type == BT_DEVICE_TYPE_DUMO) { btc_config_set_int(temp_name, BTC_BLE_STORAGE_DEV_TYPE_STR, BT_DEVICE_TYPE_BREDR); _btc_storage_remove_all_ble_keys(temp_name); } else { @@ -484,9 +530,11 @@ static bt_status_t _btc_storage_get_ble_local_key(uint8_t key_type, } size_t length = key_length; - int ret = btc_config_get_bin(BTC_BLE_STORAGE_LOCAL_ADAPTER_STR, name, (uint8_t *)key_value, &length); - - return ret ? BT_STATUS_SUCCESS : BT_STATUS_FAIL; + bool ret = btc_config_get_bin(BTC_BLE_STORAGE_LOCAL_ADAPTER_STR, name, (uint8_t *)key_value, &length); + if (!ret || length != (size_t)key_length) { + return BT_STATUS_FAIL; + } + return BT_STATUS_SUCCESS; } bt_status_t btc_storage_get_ble_local_key(uint8_t key_type, @@ -905,8 +953,8 @@ static void _btc_read_le_key(const uint8_t key_type, const size_t key_len, bt_bd } bt_status_t _btc_storage_in_fetch_bonded_ble_device(const char *remote_bd_addr, int add) { - uint32_t device_type; - int addr_type; + uint32_t device_type = 0; + int addr_type = BLE_ADDR_PUBLIC; bt_bdaddr_t bd_addr; BD_ADDR bta_bd_addr; bool device_added = false; @@ -1015,11 +1063,11 @@ bt_status_t btc_storage_get_bonded_ble_devices_list(esp_ble_bond_dev_t *bond_dev int btc_storage_get_num_ble_bond_devices(void) { int num_dev = 0; - uint32_t device_type = 0; btc_config_lock(); for (const btc_config_section_iter_t *iter = btc_config_section_begin(); iter != btc_config_section_end(); iter = btc_config_section_next(iter)) { + uint32_t device_type = 0; const char *name = btc_config_section_name(iter); if (!string_is_bdaddr(name) || !btc_config_get_int(name, BTC_BLE_STORAGE_DEV_TYPE_STR, (int *)&device_type) || @@ -1037,18 +1085,30 @@ int btc_storage_get_num_ble_bond_devices(void) bt_status_t btc_storage_get_gatt_cl_supp_feat(bt_bdaddr_t *remote_bd_addr, uint8_t *value, int len) { bdstr_t bdstr; - bdaddr_to_string(remote_bd_addr, bdstr, sizeof(bdstr)); - int ret = btc_config_get_bin(bdstr, BTC_BLE_STORAGE_GATT_CL_SUPP_FEAT_STR, value, (size_t *)&len); - return ret ? BT_STATUS_SUCCESS : BT_STATUS_FAIL; + bool ret; + size_t length = len; + + btc_config_lock(); + bdaddr_to_string(remote_bd_addr, bdstr, sizeof(bdstr_t)); + ret = btc_config_get_bin(bdstr, BTC_BLE_STORAGE_GATT_CL_SUPP_FEAT_STR, value, &length); + btc_config_unlock(); + + if (!ret || length != (size_t)len) { + return BT_STATUS_FAIL; + } + return BT_STATUS_SUCCESS; } bt_status_t btc_storage_set_gatt_cl_supp_feat(bt_bdaddr_t *remote_bd_addr, uint8_t *value, int len) { - int ret; + bool ret; bdstr_t bdstr; btc_config_lock(); bdaddr_to_string(remote_bd_addr, bdstr, sizeof(bdstr_t)); ret = btc_config_set_bin(bdstr, BTC_BLE_STORAGE_GATT_CL_SUPP_FEAT_STR, value, (size_t)len); + if (ret) { + _btc_storage_save(); + } btc_config_unlock(); if (ret == false) { return BT_STATUS_FAIL; @@ -1060,18 +1120,33 @@ bt_status_t btc_storage_set_gatt_cl_supp_feat(bt_bdaddr_t *remote_bd_addr, uint8 bt_status_t btc_storage_get_gatt_db_hash(bt_bdaddr_t *remote_bd_addr, uint8_t *value, int len) { bdstr_t bdstr; - bdaddr_to_string(remote_bd_addr, bdstr, sizeof(bdstr)); - int ret = btc_config_get_bin(bdstr, BTC_BLE_STORAGE_GATT_DB_HASH_STR, value, (size_t *)&len); - return ret ? BT_STATUS_SUCCESS : BT_STATUS_FAIL; + bool ret; + size_t length = len; + + btc_config_lock(); + bdaddr_to_string(remote_bd_addr, bdstr, sizeof(bdstr_t)); + ret = btc_config_get_bin(bdstr, BTC_BLE_STORAGE_GATT_DB_HASH_STR, value, &length); + btc_config_unlock(); + + if (!ret || length != (size_t)len) { + return BT_STATUS_FAIL; + } + return BT_STATUS_SUCCESS; } bt_status_t btc_storage_set_gatt_db_hash(bt_bdaddr_t *remote_bd_addr, uint8_t *value, int len) { - int ret; + bool ret; bdstr_t bdstr; + btc_config_lock(); bdaddr_to_string(remote_bd_addr, bdstr, sizeof(bdstr_t)); ret = btc_config_set_bin(bdstr, BTC_BLE_STORAGE_GATT_DB_HASH_STR, value, (size_t)len); + if (ret) { + _btc_storage_save(); + } + btc_config_unlock(); + if (ret == false) { return BT_STATUS_FAIL; } @@ -1084,9 +1159,11 @@ bt_status_t btc_storage_remove_gatt_cl_supp_feat(bt_bdaddr_t *remote_bd_addr) bool ret = true; bdstr_t bdstr; + btc_config_lock(); bdaddr_to_string(remote_bd_addr, bdstr, sizeof(bdstr)); - ret = btc_config_remove(bdstr, BTC_BLE_STORAGE_GATT_CL_SUPP_FEAT_STR); + btc_config_unlock(); + if (ret == false) { return BT_STATUS_FAIL; } @@ -1099,9 +1176,11 @@ bt_status_t btc_storage_remove_gatt_db_hash(bt_bdaddr_t *remote_bd_addr) bool ret = true; bdstr_t bdstr; + btc_config_lock(); bdaddr_to_string(remote_bd_addr, bdstr, sizeof(bdstr)); - ret = btc_config_remove(bdstr, BTC_BLE_STORAGE_GATT_DB_HASH_STR); + btc_config_unlock(); + if (ret == false) { return BT_STATUS_FAIL; } diff --git a/components/bt/host/bluedroid/btc/core/btc_config.c b/components/bt/host/bluedroid/btc/core/btc_config.c index c55b80023d5..d78615a7dfd 100644 --- a/components/bt/host/bluedroid/btc/core/btc_config.c +++ b/components/bt/host/bluedroid/btc/core/btc_config.c @@ -277,6 +277,7 @@ bool btc_config_set_bin(const char *section, const char *key, const uint8_t *val config_set_string(config, section, key, str, false); + memset(str, 0, length * 2 + 1); osi_free(str); return true; } diff --git a/components/bt/host/bluedroid/btc/core/btc_dm.c b/components/bt/host/bluedroid/btc/core/btc_dm.c index 5f91481b6fb..416c9f4c2c3 100644 --- a/components/bt/host/bluedroid/btc/core/btc_dm.c +++ b/components/bt/host/bluedroid/btc/core/btc_dm.c @@ -269,7 +269,7 @@ static void btc_dm_ble_auth_cmpl_evt (tBTA_DM_AUTH_CMPL *p_auth_cmpl) } #if BLE_SMP_BOND_NVS_FLASH - int addr_type; + int addr_type = BLE_ADDR_PUBLIC; if (btc_dm_cb.pairing_cb.ble.is_pid_key_rcvd) { // delete unused section in NVS diff --git a/components/bt/host/bluedroid/btc/profile/std/cte/btc_ble_cte.c b/components/bt/host/bluedroid/btc/profile/std/cte/btc_ble_cte.c index 1ea2920c63b..df1a9f2aade 100644 --- a/components/bt/host/bluedroid/btc/profile/std/cte/btc_ble_cte.c +++ b/components/bt/host/bluedroid/btc/profile/std/cte/btc_ble_cte.c @@ -28,7 +28,7 @@ static void btc_ble_cte_callback(tBTM_BLE_CTE_EVENT event, { esp_ble_cte_cb_param_t param = {0}; bt_status_t ret; - btc_msg_t msg; + btc_msg_t msg = {0}; msg.sig = BTC_SIG_API_CB; msg.pid = BTC_PID_BLE_CTE; @@ -42,7 +42,7 @@ static void btc_ble_cte_callback(tBTM_BLE_CTE_EVENT event, break; case BTA_BLE_CTE_SET_TRANS_ENABLE_EVT: msg.act = ESP_BLE_CTE_SET_CONNLESS_TRANS_ENABLE_CMPL_EVT; - param.set_trans_enable_cmpl.status = btc_btm_status_to_esp_status(params->cte_trans_params_cmpl.status); + param.set_trans_enable_cmpl.status = btc_btm_status_to_esp_status(params->cte_trans_en_cmpl.status); break; case BTA_BLE_CTE_SET_IQ_SAMP_ENABLE_EVT: msg.act = ESP_BLE_CTE_SET_CONNLESS_IQ_SAMPLING_ENABLE_CMPL_EVT; @@ -100,6 +100,7 @@ static void btc_ble_cte_callback(tBTM_BLE_CTE_EVENT event, case BTA_BLE_CTE_CONN_IQ_REPORT_EVT: msg.act = ESP_BLE_CTE_CONN_IQ_REPORT_EVT; param.conn_iq_rpt.conn_handle = params->cte_conn_iq_rpt.conn_handle; + param.conn_iq_rpt.rx_phy = params->cte_conn_iq_rpt.rx_phy; param.conn_iq_rpt.data_channel_idx = params->cte_conn_iq_rpt.data_channel_idx; param.conn_iq_rpt.rssi = params->cte_conn_iq_rpt.rssi; param.conn_iq_rpt.rssi_ant_id = params->cte_conn_iq_rpt.rssi_ant_id; @@ -163,6 +164,7 @@ void btc_ble_cte_arg_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src) dst->cte_trans_params.antenna_ids = NULL; } } else { + dst->cte_trans_params.switching_pattern_len = 0; dst->cte_trans_params.antenna_ids = NULL; } break; @@ -177,6 +179,7 @@ void btc_ble_cte_arg_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src) BTC_TRACE_ERROR("%s %d no mem\n",__func__, msg->act); } } else { + dst->cte_iq_sampling_en.switching_pattern_len = 0; dst->cte_iq_sampling_en.antenna_ids = NULL; } break; @@ -194,6 +197,7 @@ void btc_ble_cte_arg_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src) BTC_TRACE_ERROR("%s %d no mem\n",__func__, msg->act); } } else { + dst->cte_recv_params.switching_pattern_len = 0; dst->cte_recv_params.antenna_ids = NULL; } break; @@ -208,6 +212,7 @@ void btc_ble_cte_arg_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src) BTC_TRACE_ERROR("%s %d no mem\n",__func__, msg->act); } } else { + dst->cte_conn_trans_params.switching_pattern_len = 0; dst->cte_conn_trans_params.antenna_ids = NULL; } break; diff --git a/components/bt/host/bluedroid/btc/profile/std/gap/btc_gap_ble.c b/components/bt/host/bluedroid/btc/profile/std/gap/btc_gap_ble.c index 057a1248b60..568818c3942 100644 --- a/components/bt/host/bluedroid/btc/profile/std/gap/btc_gap_ble.c +++ b/components/bt/host/bluedroid/btc/profile/std/gap/btc_gap_ble.c @@ -490,7 +490,8 @@ static void btc_ble_start_advertising (esp_ble_adv_params_t *ble_adv_params) tBLE_BD_ADDR peer_addr; esp_bt_status_t status = ESP_BT_STATUS_SUCCESS; if (!BLE_ISVALID_PARAM(ble_adv_params->adv_int_min, BTM_BLE_ADV_INT_MIN, BTM_BLE_ADV_INT_MAX) || - !BLE_ISVALID_PARAM(ble_adv_params->adv_int_max, BTM_BLE_ADV_INT_MIN, BTM_BLE_ADV_INT_MAX)) { + !BLE_ISVALID_PARAM(ble_adv_params->adv_int_max, BTM_BLE_ADV_INT_MIN, BTM_BLE_ADV_INT_MAX) || + ble_adv_params->adv_int_min > ble_adv_params->adv_int_max) { status = ESP_BT_STATUS_PARM_INVALID; BTC_TRACE_ERROR("Invalid advertisting interval parameters.\n"); } @@ -563,7 +564,8 @@ static void btc_ble_set_scan_params(esp_ble_scan_params_t *scan_params) BLE_ISVALID_PARAM(scan_params->own_addr_type, BLE_ADDR_TYPE_PUBLIC, BLE_ADDR_TYPE_RPA_RANDOM) && BLE_ISVALID_PARAM(scan_params->scan_filter_policy, BLE_SCAN_FILTER_ALLOW_ALL, BLE_SCAN_FILTER_ALLOW_WLIST_RPA_DIR) && BLE_ISVALID_PARAM(scan_params->scan_duplicate, BLE_SCAN_DUPLICATE_DISABLE, BLE_SCAN_DUPLICATE_MAX -1) && - (scan_params->scan_type == BTM_BLE_SCAN_MODE_ACTI || scan_params->scan_type == BTM_BLE_SCAN_MODE_PASS)) { + (scan_params->scan_type == BTM_BLE_SCAN_MODE_ACTI || scan_params->scan_type == BTM_BLE_SCAN_MODE_PASS) && + scan_params->scan_window <= scan_params->scan_interval) { BTA_DmSetBleScanFilterParams(ESP_DEFAULT_GATT_IF, /*client_if*/ scan_params->scan_interval, scan_params->scan_window, @@ -936,7 +938,7 @@ static void btc_read_ble_rssi_cmpl_callback(void *p_data) static void btc_ble_read_channel_map_callback(void *p_data) { tBTA_BLE_CH_MAP_RESULTS *result = (tBTA_BLE_CH_MAP_RESULTS *)p_data; - esp_ble_gap_cb_param_t param; + esp_ble_gap_cb_param_t param = {0}; bt_status_t ret; btc_msg_t msg = {0}; @@ -1229,7 +1231,14 @@ void btc_ble_5_gap_callback(tBTA_DM_BLE_5_GAP_EVENT event, #if (BLE_50_EXTEND_SCAN_EN == TRUE) case BTA_DM_BLE_5_GAP_EXT_ADV_REPORT_EVT: msg.act = ESP_GAP_BLE_EXT_ADV_REPORT_EVT; - memcpy(¶m.ext_adv_report.params, ¶ms->ext_adv_report, sizeof(esp_ble_gap_ext_adv_report_t)); + memcpy(¶m.ext_adv_report.params, ¶ms->ext_adv_report, sizeof(tBTM_BLE_EXT_ADV_REPORT)); + /* The source struct ends with a pointer (UINT8 *adv_data) while the destination + * ends with a fixed array (uint8_t adv_data[251]). The memcpy above leaves the + * raw pointer bytes at the start of adv_data[]. Clear it before copying the real + * advertising payload to avoid leaking stale pointer bytes when adv_data is NULL + * or adv_data_len is smaller than sizeof(void *). */ + memset(param.ext_adv_report.params.adv_data, 0, + sizeof(param.ext_adv_report.params.adv_data)); if (params->ext_adv_report.adv_data) { memcpy(param.ext_adv_report.params.adv_data, params->ext_adv_report.adv_data, params->ext_adv_report.adv_data_len); @@ -1483,7 +1492,6 @@ void btc_ble_5_gap_callback(tBTA_DM_BLE_5_GAP_EVENT event, case BTA_BLE_GAP_CS_READ_LOCAL_SUPP_CAPS_EVT: msg.act = ESP_GAP_BLE_CS_READ_LOCAL_SUPP_CAPS_EVT; param.cs_read_local_supp_caps.status = btc_btm_status_to_esp_status(params->cs_read_local_supp_caps.status); - param.cs_read_local_supp_caps.conn_handle = params->cs_read_local_supp_caps.conn_handle; param.cs_read_local_supp_caps.num_config_supported = params->cs_read_local_supp_caps.num_config_supported; param.cs_read_local_supp_caps.max_consecutive_proc_supported = params->cs_read_local_supp_caps.max_consecutive_proc_supported; param.cs_read_local_supp_caps.num_ant_supported = params->cs_read_local_supp_caps.num_ant_supported; @@ -1648,12 +1656,12 @@ void btc_ble_5_gap_callback(tBTA_DM_BLE_5_GAP_EVENT event, #endif // #if (BLE_50_FEATURE_SUPPORT == TRUE) #if ((BLE_42_DTM_TEST_EN == TRUE) || (BLE_50_DTM_TEST_EN == TRUE)) -void btc_dtm_tx_start_callback(void *p1) +void btc_dtm_tx_start_callback(UINT8 *p1, UINT16 len) { UINT8 status; UINT8 *p; p = (UINT8*) p1; - if (p1) { + if (p1 && len >= 1) { STREAM_TO_UINT8(status, p); BTC_TRACE_DEBUG("DTM TX start, status 0x%x\n", status); esp_ble_gap_cb_param_t param; @@ -1675,13 +1683,13 @@ void btc_dtm_tx_start_callback(void *p1) } } -void btc_dtm_rx_start_callback(void *p1) +void btc_dtm_rx_start_callback(UINT8 *p1, UINT16 len) { UINT8 status; UINT8 *p; p = (UINT8*) p1; - if (p1) { + if (p1 && len >= 1) { STREAM_TO_UINT8(status, p); BTC_TRACE_DEBUG("DTM RX start, status 0x%x\n", status); esp_ble_gap_cb_param_t param; @@ -1705,13 +1713,13 @@ void btc_dtm_rx_start_callback(void *p1) #endif // #if ((BLE_42_DTM_TEST_EN == TRUE) || (BLE_50_DTM_TEST_EN == TRUE)) #if ((BLE_42_DTM_TEST_EN == TRUE) || (BLE_50_DTM_TEST_EN == TRUE)) -void btc_dtm_stop_callback(void *p1) +void btc_dtm_stop_callback(UINT8 *p1, UINT16 len) { UINT8 status; UINT16 num_pkt; UINT8 *p; p = (UINT8*) p1; - if (p1) { + if (p1 && len >= 3) { STREAM_TO_UINT8(status, p); STREAM_TO_UINT16(num_pkt, p); BTC_TRACE_DEBUG("DTM stop, status 0x%x num_pkt %d\n", status, num_pkt); @@ -1876,6 +1884,13 @@ static void btc_ble_vendor_hci_event_callback(UINT8 subevt_code, UINT8 param_len btc_msg_t msg = {0}; esp_ble_vendor_evt_param_t *evt_param = ¶m.vendor_hci_evt.param; bool copy_param = false; + bool parse_ok = true; + + if (param_len && params == NULL) { + BTC_TRACE_WARNING("%s vendor evt NULL: sub=0x%02x len=%u", + __func__, subevt_code, param_len); + return; + } msg.sig = BTC_SIG_API_CB; msg.pid = BTC_PID_GAP_BLE; @@ -1887,6 +1902,12 @@ static void btc_ble_vendor_hci_event_callback(UINT8 subevt_code, UINT8 param_len switch (subevt_code) { case BLE_VENDOR_PDU_RECV_EVT: param.vendor_hci_evt.subevt_code = ESP_BLE_VENDOR_PDU_RECV_EVT; + if (param_len < (UINT8)(1 + 1 + 1 + BD_ADDR_LEN)) { + BTC_TRACE_WARNING("%s vendor trunc: sub=0x%02x len=%u<%u", + __func__, subevt_code, param_len, (unsigned)(1 + 1 + 1 + BD_ADDR_LEN)); + parse_ok = false; + break; + } STREAM_TO_UINT8(evt_param->pdu_recv.type, params); STREAM_TO_UINT8(evt_param->pdu_recv.handle, params); STREAM_TO_UINT8(evt_param->pdu_recv.addr_type, params); @@ -1894,6 +1915,12 @@ static void btc_ble_vendor_hci_event_callback(UINT8 subevt_code, UINT8 param_len break; case BLE_VENDOR_CHMAP_UPDATE_EVT: param.vendor_hci_evt.subevt_code = ESP_BLE_VENDOR_CHAN_MAP_UPDATE_EVT; + if (param_len < (UINT8)(1 + 2 + ESP_GAP_BLE_CHANNELS_LEN)) { + BTC_TRACE_WARNING("%s vendor trunc: sub=0x%02x len=%u<%u", + __func__, subevt_code, param_len, (unsigned)(1 + 2 + ESP_GAP_BLE_CHANNELS_LEN)); + parse_ok = false; + break; + } STREAM_TO_UINT8(evt_param->chan_map_update.status, params); STREAM_TO_UINT16(evt_param->chan_map_update.conn_handle, params); REVERSE_STREAM_TO_ARRAY(evt_param->chan_map_update.ch_map, params, ESP_GAP_BLE_CHANNELS_LEN); @@ -1907,6 +1934,17 @@ static void btc_ble_vendor_hci_event_callback(UINT8 subevt_code, UINT8 param_len break; } + if (!parse_ok) { + /* Malformed/truncated parameters: keep the mapped esp_ble_vendor_evt_t + * already assigned in the switch (do NOT restore the raw HCI subevent + * code, which is outside esp_ble_vendor_evt_t and would break the + * public API contract). Zero the structured fields and suppress the + * internal raw buffer so the callback delivers a deterministic event; + * the truncation has already been logged above. */ + memset(evt_param, 0, sizeof(*evt_param)); + copy_param = false; + } + if (copy_param) { param.vendor_hci_evt.param_len = param_len; param.vendor_hci_evt.param_buf = (param_len) ? params : NULL; @@ -2140,20 +2178,38 @@ void btc_gap_ble_arg_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src) btc_ble_gap_args_t *src = (btc_ble_gap_args_t *)p_src; btc_ble_gap_args_t *dst = (btc_ble_gap_args_t *) p_dest; - if (src->cfg_adv_data.adv_data.p_manufacturer_data) { + /* btc_transfer_context() does a shallow memcpy first; always null-out + * pointer fields so deep_free() never frees non-owned memory on OOM. */ + dst->cfg_adv_data.adv_data.p_manufacturer_data = NULL; + dst->cfg_adv_data.adv_data.p_service_data = NULL; + dst->cfg_adv_data.adv_data.p_service_uuid = NULL; + + if (src->cfg_adv_data.adv_data.p_manufacturer_data && src->cfg_adv_data.adv_data.manufacturer_len) { dst->cfg_adv_data.adv_data.p_manufacturer_data = osi_malloc(src->cfg_adv_data.adv_data.manufacturer_len); - memcpy(dst->cfg_adv_data.adv_data.p_manufacturer_data, src->cfg_adv_data.adv_data.p_manufacturer_data, - src->cfg_adv_data.adv_data.manufacturer_len); + if (dst->cfg_adv_data.adv_data.p_manufacturer_data) { + memcpy(dst->cfg_adv_data.adv_data.p_manufacturer_data, src->cfg_adv_data.adv_data.p_manufacturer_data, + src->cfg_adv_data.adv_data.manufacturer_len); + } else { + BTC_TRACE_WARNING("%s no mem, manu drop %u", __func__, src->cfg_adv_data.adv_data.manufacturer_len); + } } - if (src->cfg_adv_data.adv_data.p_service_data) { + if (src->cfg_adv_data.adv_data.p_service_data && src->cfg_adv_data.adv_data.service_data_len) { dst->cfg_adv_data.adv_data.p_service_data = osi_malloc(src->cfg_adv_data.adv_data.service_data_len); - memcpy(dst->cfg_adv_data.adv_data.p_service_data, src->cfg_adv_data.adv_data.p_service_data, src->cfg_adv_data.adv_data.service_data_len); + if (dst->cfg_adv_data.adv_data.p_service_data) { + memcpy(dst->cfg_adv_data.adv_data.p_service_data, src->cfg_adv_data.adv_data.p_service_data, src->cfg_adv_data.adv_data.service_data_len); + } else { + BTC_TRACE_WARNING("%s no mem, svc_data drop %u", __func__, src->cfg_adv_data.adv_data.service_data_len); + } } - if (src->cfg_adv_data.adv_data.p_service_uuid) { + if (src->cfg_adv_data.adv_data.p_service_uuid && src->cfg_adv_data.adv_data.service_uuid_len) { dst->cfg_adv_data.adv_data.p_service_uuid = osi_malloc(src->cfg_adv_data.adv_data.service_uuid_len); - memcpy(dst->cfg_adv_data.adv_data.p_service_uuid, src->cfg_adv_data.adv_data.p_service_uuid, src->cfg_adv_data.adv_data.service_uuid_len); + if (dst->cfg_adv_data.adv_data.p_service_uuid) { + memcpy(dst->cfg_adv_data.adv_data.p_service_uuid, src->cfg_adv_data.adv_data.p_service_uuid, src->cfg_adv_data.adv_data.service_uuid_len); + } else { + BTC_TRACE_WARNING("%s no mem, svc_uuid drop %u", __func__, src->cfg_adv_data.adv_data.service_uuid_len); + } } break; } @@ -2161,10 +2217,13 @@ void btc_gap_ble_arg_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src) btc_ble_gap_args_t *src = (btc_ble_gap_args_t *)p_src; btc_ble_gap_args_t *dst = (btc_ble_gap_args_t *) p_dest; + dst->cfg_adv_data_raw.raw_adv = NULL; + dst->cfg_adv_data_raw.raw_adv_len = 0; if (src && src->cfg_adv_data_raw.raw_adv && src->cfg_adv_data_raw.raw_adv_len > 0) { dst->cfg_adv_data_raw.raw_adv = osi_malloc(src->cfg_adv_data_raw.raw_adv_len); if (dst->cfg_adv_data_raw.raw_adv) { memcpy(dst->cfg_adv_data_raw.raw_adv, src->cfg_adv_data_raw.raw_adv, src->cfg_adv_data_raw.raw_adv_len); + dst->cfg_adv_data_raw.raw_adv_len = src->cfg_adv_data_raw.raw_adv_len; } } break; @@ -2173,10 +2232,13 @@ void btc_gap_ble_arg_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src) btc_ble_gap_args_t *src = (btc_ble_gap_args_t *)p_src; btc_ble_gap_args_t *dst = (btc_ble_gap_args_t *) p_dest; + dst->cfg_scan_rsp_data_raw.raw_scan_rsp = NULL; + dst->cfg_scan_rsp_data_raw.raw_scan_rsp_len = 0; if (src && src->cfg_scan_rsp_data_raw.raw_scan_rsp && src->cfg_scan_rsp_data_raw.raw_scan_rsp_len > 0) { dst->cfg_scan_rsp_data_raw.raw_scan_rsp = osi_malloc(src->cfg_scan_rsp_data_raw.raw_scan_rsp_len); if (dst->cfg_scan_rsp_data_raw.raw_scan_rsp) { memcpy(dst->cfg_scan_rsp_data_raw.raw_scan_rsp, src->cfg_scan_rsp_data_raw.raw_scan_rsp, src->cfg_scan_rsp_data_raw.raw_scan_rsp_len); + dst->cfg_scan_rsp_data_raw.raw_scan_rsp_len = src->cfg_scan_rsp_data_raw.raw_scan_rsp_len; } } break; @@ -2187,6 +2249,7 @@ void btc_gap_ble_arg_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src) btc_ble_gap_args_t *src = (btc_ble_gap_args_t *)p_src; btc_ble_gap_args_t *dst = (btc_ble_gap_args_t *) p_dest; uint8_t length = 0; + dst->set_security_param.value = NULL; if (src->set_security_param.value) { length = dst->set_security_param.len; dst->set_security_param.value = osi_malloc(length); @@ -2194,6 +2257,7 @@ void btc_gap_ble_arg_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src) memcpy(dst->set_security_param.value, src->set_security_param.value, length); } else { BTC_TRACE_ERROR("%s %d no mem\n",__func__, msg->act); + dst->set_security_param.len = 0; } } break; @@ -2202,6 +2266,7 @@ void btc_gap_ble_arg_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src) btc_ble_gap_args_t *src = (btc_ble_gap_args_t *)p_src; btc_ble_gap_args_t *dst = (btc_ble_gap_args_t *) p_dest; uint8_t length = 0; + dst->oob_req_reply.p_value = NULL; if (src->oob_req_reply.p_value) { length = dst->oob_req_reply.len; dst->oob_req_reply.p_value = osi_malloc(length); @@ -2209,6 +2274,7 @@ void btc_gap_ble_arg_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src) memcpy(dst->oob_req_reply.p_value, src->oob_req_reply.p_value, length); } else { BTC_TRACE_ERROR("%s %d no mem\n",__func__, msg->act); + dst->oob_req_reply.len = 0; } } break; @@ -2216,6 +2282,8 @@ void btc_gap_ble_arg_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src) case BTC_GAP_BLE_SC_OOB_REQ_REPLY_EVT: { btc_ble_gap_args_t *src = (btc_ble_gap_args_t *)p_src; btc_ble_gap_args_t *dst = (btc_ble_gap_args_t *)p_dest; + dst->sc_oob_req_reply.p_c = NULL; + dst->sc_oob_req_reply.p_r = NULL; if (src->sc_oob_req_reply.p_c) { dst->sc_oob_req_reply.p_c = osi_malloc(BT_OCTET16_LEN); if (dst->sc_oob_req_reply.p_c) { @@ -2236,17 +2304,38 @@ void btc_gap_ble_arg_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src) } #if (BLE_50_FEATURE_SUPPORT == TRUE) #if (BLE_50_EXTEND_ADV_EN == TRUE) - case BTC_GAP_BLE_CFG_EXT_ADV_DATA_RAW: - case BTC_GAP_BLE_CFG_EXT_SCAN_RSP_DATA_RAW: { + case BTC_GAP_BLE_CFG_EXT_ADV_DATA_RAW: { btc_ble_5_gap_args_t *src = (btc_ble_5_gap_args_t *)p_src; btc_ble_5_gap_args_t *dst = (btc_ble_5_gap_args_t *)p_dest; uint16_t length = 0; + dst->ext_adv_cfg_data.data = NULL; + dst->ext_adv_cfg_data.length = 0; if (src->ext_adv_cfg_data.data) { length = src->ext_adv_cfg_data.length; dst->ext_adv_cfg_data.data = osi_malloc(length); if (dst->ext_adv_cfg_data.data) { memcpy(dst->ext_adv_cfg_data.data, src->ext_adv_cfg_data.data, length); + dst->ext_adv_cfg_data.length = length; + } else { + BTC_TRACE_ERROR("%s %d no mem\n",__func__, msg->act); + } + } + break; + } + case BTC_GAP_BLE_CFG_EXT_SCAN_RSP_DATA_RAW: { + btc_ble_5_gap_args_t *src = (btc_ble_5_gap_args_t *)p_src; + btc_ble_5_gap_args_t *dst = (btc_ble_5_gap_args_t *)p_dest; + uint16_t length = 0; + + dst->cfg_scan_rsp.data = NULL; + dst->cfg_scan_rsp.length = 0; + if (src->cfg_scan_rsp.data) { + length = src->cfg_scan_rsp.length; + dst->cfg_scan_rsp.data = osi_malloc(length); + if (dst->cfg_scan_rsp.data) { + memcpy(dst->cfg_scan_rsp.data, src->cfg_scan_rsp.data, length); + dst->cfg_scan_rsp.length = length; } else { BTC_TRACE_ERROR("%s %d no mem\n",__func__, msg->act); } @@ -2260,17 +2349,17 @@ void btc_gap_ble_arg_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src) btc_ble_5_gap_args_t *dst = (btc_ble_5_gap_args_t *)p_dest; uint16_t length = 0; + dst->periodic_adv_cfg_data.data = NULL; + dst->periodic_adv_cfg_data.len = 0; if (src->periodic_adv_cfg_data.data) { length = src->periodic_adv_cfg_data.len; dst->periodic_adv_cfg_data.data = osi_malloc(length); if (dst->periodic_adv_cfg_data.data) { memcpy(dst->periodic_adv_cfg_data.data, src->periodic_adv_cfg_data.data, length); + dst->periodic_adv_cfg_data.len = length; } else { BTC_TRACE_ERROR("%s %d no mem\n",__func__, msg->act); } - } else { - dst->periodic_adv_cfg_data.data = NULL; - dst->periodic_adv_cfg_data.len = 0; } break; } @@ -2308,10 +2397,13 @@ void btc_gap_ble_arg_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src) case BTC_GAP_BLE_ACT_VENDOR_HCI_CMD_EVT: { btc_ble_gap_args_t *src = (btc_ble_gap_args_t *)p_src; btc_ble_gap_args_t *dst = (btc_ble_gap_args_t *)p_dest; + dst->vendor_cmd_send.p_param_buf = NULL; + dst->vendor_cmd_send.param_len = 0; if (src->vendor_cmd_send.param_len) { dst->vendor_cmd_send.p_param_buf = osi_malloc(src->vendor_cmd_send.param_len); if (dst->vendor_cmd_send.p_param_buf) { memcpy(dst->vendor_cmd_send.p_param_buf, src->vendor_cmd_send.p_param_buf, src->vendor_cmd_send.param_len); + dst->vendor_cmd_send.param_len = src->vendor_cmd_send.param_len; } else { BTC_TRACE_ERROR("%s %d no mem\n",__func__, msg->act); } @@ -2398,11 +2490,14 @@ void btc_gap_ble_cb_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src) switch (msg->act) { case ESP_GAP_BLE_VENDOR_CMD_COMPLETE_EVT: { + dst->vendor_cmd_cmpl.p_param_buf = NULL; + dst->vendor_cmd_cmpl.param_len = 0; if (src->vendor_cmd_cmpl.param_len) { dst->vendor_cmd_cmpl.p_param_buf = osi_malloc(src->vendor_cmd_cmpl.param_len); if (dst->vendor_cmd_cmpl.p_param_buf) { memcpy(dst->vendor_cmd_cmpl.p_param_buf, src->vendor_cmd_cmpl.p_param_buf, src->vendor_cmd_cmpl.param_len); + dst->vendor_cmd_cmpl.param_len = src->vendor_cmd_cmpl.param_len; } else { BTC_TRACE_ERROR("%s, malloc failed\n", __func__); } @@ -2410,11 +2505,14 @@ void btc_gap_ble_cb_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src) break; } case ESP_GAP_BLE_VENDOR_HCI_EVT: { + dst->vendor_hci_evt.param_buf = NULL; + dst->vendor_hci_evt.param_len = 0; if (src->vendor_hci_evt.param_len) { dst->vendor_hci_evt.param_buf = osi_malloc(src->vendor_hci_evt.param_len); if (dst->vendor_hci_evt.param_buf) { memcpy(dst->vendor_hci_evt.param_buf, src->vendor_hci_evt.param_buf, src->vendor_hci_evt.param_len); + dst->vendor_hci_evt.param_len = src->vendor_hci_evt.param_len; } else { BTC_TRACE_ERROR("%s, malloc failed\n", __func__); } @@ -2476,7 +2574,7 @@ void btc_gap_ble_cb_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src) } } } else { - BTC_TRACE_ERROR("%s, pa_rsp_info, no enough memory.", __func__); + BTC_TRACE_ERROR("%s, step_info, no enough memory.", __func__); } } break; @@ -2499,7 +2597,7 @@ void btc_gap_ble_cb_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src) } } } else { - BTC_TRACE_ERROR("%s, pa_rsp_info, no enough memory.", __func__); + BTC_TRACE_ERROR("%s, continue step_info, no enough memory.", __func__); } } break; @@ -2574,14 +2672,20 @@ void btc_gap_ble_arg_deep_free(btc_msg_t *msg) } #if (BLE_50_FEATURE_SUPPORT == TRUE) #if (BLE_50_EXTEND_ADV_EN == TRUE) - case BTC_GAP_BLE_CFG_EXT_ADV_DATA_RAW: - case BTC_GAP_BLE_CFG_EXT_SCAN_RSP_DATA_RAW: { + case BTC_GAP_BLE_CFG_EXT_ADV_DATA_RAW: { uint8_t *value = ((btc_ble_5_gap_args_t *)msg->arg)->ext_adv_cfg_data.data; if (value) { osi_free(value); } break; } + case BTC_GAP_BLE_CFG_EXT_SCAN_RSP_DATA_RAW: { + uint8_t *value = ((btc_ble_5_gap_args_t *)msg->arg)->cfg_scan_rsp.data; + if (value) { + osi_free(value); + } + break; + } #endif // #if (BLE_50_EXTEND_ADV_EN == TRUE) #if (BLE_50_PERIODIC_ADV_EN == TRUE) case BTC_GAP_BLE_CFG_PERIODIC_ADV_DATA_RAW: { @@ -3033,9 +3137,9 @@ void btc_gap_ble_call_handler(btc_msg_t *msg) break; case BTC_GAP_BLE_CFG_EXT_SCAN_RSP_DATA_RAW: BTC_TRACE_DEBUG("BTC_GAP_BLE_CFG_EXT_SCAN_RSP_DATA_RAW"); - BTA_DmBleGapConfigExtAdvDataRaw(TRUE, arg_5->ext_adv_cfg_data.instance, - arg_5->ext_adv_cfg_data.length, - (const UINT8 *)arg_5->ext_adv_cfg_data.data); + BTA_DmBleGapConfigExtAdvDataRaw(TRUE, arg_5->cfg_scan_rsp.instance, + arg_5->cfg_scan_rsp.length, + (const UINT8 *)arg_5->cfg_scan_rsp.data); break; case BTC_GAP_BLE_EXT_ADV_START: { BTC_TRACE_DEBUG("BTC_GAP_BLE_EXT_ADV_START"); diff --git a/components/bt/host/bluedroid/btc/profile/std/gatt/btc_gattc.c b/components/bt/host/bluedroid/btc/profile/std/gatt/btc_gattc.c index 4882584baa4..21f1c8cbf52 100644 --- a/components/bt/host/bluedroid/btc/profile/std/gatt/btc_gattc.c +++ b/components/bt/host/bluedroid/btc/profile/std/gatt/btc_gattc.c @@ -203,7 +203,7 @@ static void btc_gattc_cback(tBTA_GATTC_EVT event, tBTA_GATTC *p_data) static void btc_gattc_app_register(btc_ble_gattc_args_t *arg) { - tBT_UUID app_uuid; + tBT_UUID app_uuid = {0}; app_uuid.len = 2; app_uuid.uu.uuid16 = arg->app_reg.app_id; BTA_GATTC_AppRegister(&app_uuid, btc_gattc_cback); @@ -991,8 +991,6 @@ void btc_gattc_cb_handler(btc_msg_t *msg) case BTA_GATTC_CLOSE_EVT: { tBTA_GATTC_CLOSE *close = &arg->close; - // Free gattc clcb in BTC task to avoid race condition - bta_gattc_clcb_dealloc_by_conn_id(close->conn_id); gattc_if = close->client_if; param.close.status = close->status; param.close.conn_id = BTC_GATT_GET_CONN_ID(close->conn_id); diff --git a/components/bt/host/bluedroid/btc/profile/std/gatt/btc_gatts.c b/components/bt/host/bluedroid/btc/profile/std/gatt/btc_gatts.c index af060c80b65..b816eb3d6d2 100644 --- a/components/bt/host/bluedroid/btc/profile/std/gatt/btc_gatts.c +++ b/components/bt/host/bluedroid/btc/profile/std/gatt/btc_gatts.c @@ -244,7 +244,7 @@ static void btc_gatts_act_create_attr_tab(esp_gatts_attr_db_t *gatts_attr_db, { uint16_t uuid = 0; future_t *future_p; - esp_ble_gatts_cb_param_t param; + esp_ble_gatts_cb_param_t param = {0}; param.add_attr_tab.status = ESP_GATT_OK; param.add_attr_tab.num_handle = max_nb_attr; @@ -509,11 +509,43 @@ static esp_gatt_status_t btc_gatts_check_valid_attr_tab(esp_gatts_attr_db_t *gat uint16_t uuid = 0; for(int i = 0; i < max_nb_attr; i++) { - if(gatts_attr_db[i].att_desc.uuid_length != ESP_UUID_LEN_16) { + const esp_attr_desc_t *desc = &gatts_attr_db[i].att_desc; + + /* Reject absurd attribute sizes regardless of row type. Mirrors the per-call + * guard in esp_ble_gatts_add_char_desc_param_check() so that the attribute + * table API enforces the same upper bound. */ + if (desc->max_length > ESP_GATT_MAX_ATTR_LEN || + desc->length > ESP_GATT_MAX_ATTR_LEN) { + BTC_TRACE_ERROR("%s attr[%d] len %u/max %u>%u", + __func__, i, + (unsigned)desc->length, + (unsigned)desc->max_length, + (unsigned)ESP_GATT_MAX_ATTR_LEN); + return ESP_GATT_INVALID_ATTR_LEN; + } + + /* When max_length is non-zero the row carries a stack-stored attribute + * value: gatts_add_char_descr()/gatts_add_characteristic() will allocate + * max_length bytes and memcpy length bytes into it. Reject length > max_length + * here so the table API matches esp_ble_gatts_add_char_desc_param_check(), + * fails fast with a clear error to the app, and also avoids the BTA-layer + * out-of-bounds read in BTA_GATTS_AddCharDescriptor() when the caller's + * source buffer is sized to max_length. Rows with max_length == 0 carry + * declarations (service UUID, char property byte, include-service descriptor) + * whose length is unrelated to max_length, so they are skipped. */ + if (desc->max_length != 0 && desc->length > desc->max_length) { + BTC_TRACE_ERROR("%s attr[%d] len %u>max %u", + __func__, i, + (unsigned)desc->length, + (unsigned)desc->max_length); + return ESP_GATT_INVALID_ATTR_LEN; + } + + if(desc->uuid_length != ESP_UUID_LEN_16) { continue; } - uuid = (gatts_attr_db[i].att_desc.uuid_p[1] << 8) + (gatts_attr_db[i].att_desc.uuid_p[0]); + uuid = (desc->uuid_p[1] << 8) + (desc->uuid_p[0]); switch(uuid) { case ESP_GATT_UUID_PRI_SERVICE: case ESP_GATT_UUID_SEC_SERVICE: @@ -594,9 +626,56 @@ esp_gatt_status_t btc_gatts_show_local_database(void) return ESP_GATT_OK; } +/* BTA passes a pointer to the active tBTA_GATTS union member (often a small stack + * object). Only copy that member's size — sizeof(tBTA_GATTS) would read past it. */ +static int btc_gatts_cb_event_param_len(tBTA_GATTS_EVT event) +{ + switch (event) { + case BTA_GATTS_REG_EVT: + case BTA_GATTS_DEREG_EVT: + return (int)sizeof(tBTA_GATTS_REG_OPER); + case BTA_GATTS_READ_EVT: + case BTA_GATTS_WRITE_EVT: + case BTA_GATTS_EXEC_WRITE_EVT: + case BTA_GATTS_MTU_EVT: + case BTA_GATTS_CONF_EVT: + return (int)sizeof(tBTA_GATTS_REQ); + case BTA_GATTS_CREATE_EVT: + return (int)sizeof(tBTA_GATTS_CREATE); + case BTA_GATTS_ADD_INCL_SRVC_EVT: + case BTA_GATTS_ADD_CHAR_EVT: + case BTA_GATTS_ADD_CHAR_DESCR_EVT: + return (int)sizeof(tBTA_GATTS_ADD_RESULT); + case BTA_GATTS_DELELTE_EVT: + case BTA_GATTS_START_EVT: + case BTA_GATTS_STOP_EVT: + return (int)sizeof(tBTA_GATTS_SRVC_OPER); + case BTA_GATTS_SET_ATTR_VAL_EVT: + return (int)sizeof(tBAT_GATTS_ATTR_VAL_RESULT); + case BTA_GATTS_CONNECT_EVT: + case BTA_GATTS_DISCONNECT_EVT: + return (int)sizeof(tBTA_GATTS_CONN); + case BTA_GATTS_OPEN_EVT: + return (int)sizeof(tBTA_GATTS_OPEN); + case BTA_GATTS_CANCEL_OPEN_EVT: + return (int)sizeof(tBTA_GATTS_CANCEL_OPEN); + case BTA_GATTS_CLOSE_EVT: + return (int)sizeof(tBTA_GATTS_CLOSE); + case BTA_GATTS_LISTEN_EVT: + return (int)sizeof(tBTA_GATT_STATUS); + case BTA_GATTS_CONGEST_EVT: + return (int)sizeof(tBTA_GATTS_CONGEST); + case BTA_GATTS_SEND_SERVICE_CHANGE_EVT: + return (int)sizeof(tBTA_GATTS_SERVICE_CHANGE); + default: + return (int)sizeof(tBTA_GATTS); + } +} + static void btc_gatts_cb_param_copy_req(btc_msg_t *msg, void *p_dest, void *p_src) { uint16_t event = msg->act; + int copy_len = btc_gatts_cb_event_param_len((tBTA_GATTS_EVT)event); tBTA_GATTS *p_dest_data = (tBTA_GATTS *) p_dest; tBTA_GATTS *p_src_data = (tBTA_GATTS *) p_src; @@ -605,8 +684,7 @@ static void btc_gatts_cb_param_copy_req(btc_msg_t *msg, void *p_dest, void *p_sr return; } - // Copy basic structure first - memcpy(p_dest_data, p_src_data, sizeof(tBTA_GATTS)); + memcpy(p_dest_data, p_src_data, (size_t)copy_len); // Allocate buffer for request data if necessary switch (event) { @@ -622,6 +700,21 @@ static void btc_gatts_cb_param_copy_req(btc_msg_t *msg, void *p_dest, void *p_sr BTC_TRACE_ERROR("%s %d no mem\n", __func__, msg->act); } break; + case BTA_GATTS_CONF_EVT: + /* bta_gatts_indicate_handle frees req_data.value after returning from this + * callback; duplicate the buffer so the queued BTC handler does not UAF. */ + if (p_src_data->req_data.value != NULL && p_src_data->req_data.data_len > 0) { + p_dest_data->req_data.value = (uint8_t *)osi_malloc(p_src_data->req_data.data_len); + if (p_dest_data->req_data.value != NULL) { + memcpy(p_dest_data->req_data.value, p_src_data->req_data.value, + p_src_data->req_data.data_len); + } else { + BTC_TRACE_ERROR("%s CONF_EVT no mem", __func__); + p_dest_data->req_data.value = NULL; + p_dest_data->req_data.data_len = 0; + } + } + break; default: break; @@ -643,6 +736,10 @@ static void btc_gatts_cb_param_copy_free(btc_msg_t *msg) } break; case BTA_GATTS_CONF_EVT: + if (p_data && p_data->req_data.value) { + osi_free(p_data->req_data.value); + p_data->req_data.value = NULL; + } break; default: break; @@ -709,7 +806,7 @@ static void btc_gatts_inter_cb(tBTA_GATTS_EVT event, tBTA_GATTS *p_data) future_ready(btc_creat_tab_env.complete_future, result); return; } - status = btc_transfer_context(&msg, p_data, sizeof(tBTA_GATTS), + status = btc_transfer_context(&msg, p_data, btc_gatts_cb_event_param_len(event), btc_gatts_cb_param_copy_req, btc_gatts_cb_param_copy_free); if (status != BT_STATUS_SUCCESS) { @@ -786,7 +883,7 @@ void btc_gatts_call_handler(btc_msg_t *msg) arg->send_ind.value_len, arg->send_ind.value, arg->send_ind.need_confirm); break; case BTC_GATTS_ACT_SEND_RESPONSE: { - esp_ble_gatts_cb_param_t param; + esp_ble_gatts_cb_param_t param = {0}; esp_gatt_rsp_t *p_rsp = arg->send_rsp.rsp; if (p_rsp) { @@ -1099,7 +1196,7 @@ void btc_gatts_cb_handler(btc_msg_t *msg) void btc_congest_callback(tBTA_GATTS *param) { - esp_ble_gatts_cb_param_t esp_param; + esp_ble_gatts_cb_param_t esp_param = {0}; esp_gatt_if_t gatts_if = BTC_GATT_GET_GATT_IF(param->congest.conn_id); esp_param.congest.conn_id = BTC_GATT_GET_CONN_ID(param->congest.conn_id); esp_param.congest.congested = param->congest.congested; diff --git a/components/bt/host/bluedroid/btc/profile/std/iso/btc_iso_ble.c b/components/bt/host/bluedroid/btc/profile/std/iso/btc_iso_ble.c index 572d7cc24ae..64e0cd74969 100644 --- a/components/bt/host/bluedroid/btc/profile/std/iso/btc_iso_ble.c +++ b/components/bt/host/bluedroid/btc/profile/std/iso/btc_iso_ble.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: Apache-2.0 */ @@ -29,9 +29,10 @@ static void btc_ble_iso_callback(tBTM_BLE_ISO_EVENT event, { esp_ble_iso_cb_param_t param = {0}; bt_status_t ret; - btc_msg_t msg; + btc_msg_t msg = {0}; msg.sig = BTC_SIG_API_CB; msg.pid = BTC_PID_ISO_BLE; + msg.act = ESP_BLE_ISO_EVT_MAX; switch(event) { #if (BLE_FEAT_ISO_BIG_BROADCASTER_EN == TRUE) @@ -208,6 +209,11 @@ static void btc_ble_iso_callback(tBTM_BLE_ISO_EVENT event, break; } + if (msg.act == ESP_BLE_ISO_EVT_MAX) { + BTC_TRACE_ERROR("%s unk ISO evt %d", __func__, event); + return; + } + ret = btc_transfer_context(&msg, ¶m, sizeof(esp_ble_iso_cb_param_t), NULL, NULL); @@ -341,13 +347,14 @@ void btc_iso_ble_call_handler(btc_msg_t *msg) (uint8_t *)&set_cig_params->cis_params[0]); break; } - case BTC_ISO_ACT_SET_CIG_PARAMS_TEST: + case BTC_ISO_ACT_SET_CIG_PARAMS_TEST: { struct set_cig_params_test_arg *set_cig_params_test = (struct set_cig_params_test_arg *)arg; BTA_DmBleIsoSetCigParamsTest(set_cig_params_test->cig_id, set_cig_params_test->sdu_int_c_to_p, set_cig_params_test->sdu_int_p_to_c, set_cig_params_test->ft_c_to_p, set_cig_params_test->ft_p_to_c, set_cig_params_test->iso_interval, set_cig_params_test->worse_case_SCA, set_cig_params_test->packing, set_cig_params_test->framing, set_cig_params_test->cis_cnt, (uint8_t *)&set_cig_params_test->cis_params_test[0]); break; + } case BTC_ISO_ACT_CREATE_CIS: { struct creat_cis_arg * create_cis = (struct creat_cis_arg *)arg; BTA_DmBleIsoCreateCis(create_cis->cis_count, (uint8_t *)&create_cis->cis_hdls[0]); diff --git a/components/bt/host/bluedroid/hci/ble_hci_iso.c b/components/bt/host/bluedroid/hci/ble_hci_iso.c index 92db66d3110..8032f378793 100644 --- a/components/bt/host/bluedroid/hci/ble_hci_iso.c +++ b/components/bt/host/bluedroid/hci/ble_hci_iso.c @@ -17,6 +17,22 @@ * under the License. */ +/* + * ============================================================================ + * WARNING + * ============================================================================ + * NOTE: The code in this file is for INTERNAL TEMPORARY TESTING ONLY. + * + * - DO NOT use it in any product code or user application. + * - It is not part of the public/stable API and provides NO compatibility + * guarantees of any kind (behavior, ABI, function signatures, side effects). + * - It may be changed, refactored, or REMOVED at any time without notice. + * + * This file will be deleted in a future release. Any external dependency on + * the symbols defined here is unsupported and will break. + * ============================================================================ + */ + #include #include #include "hci/ble_hci_iso.h" diff --git a/components/bt/host/bluedroid/stack/btm/btm_acl.c b/components/bt/host/bluedroid/stack/btm/btm_acl.c index 5b7b2618680..890c60aa84e 100644 --- a/components/bt/host/bluedroid/stack/btm/btm_acl.c +++ b/components/bt/host/bluedroid/stack/btm/btm_acl.c @@ -2279,7 +2279,7 @@ void btm_acl_pkt_types_changed(UINT8 status, UINT16 handle, UINT16 pkt_types) tBTM_STATUS BTM_ReadChannelMap(BD_ADDR remote_bda) { tACL_CONN *p; - tBTM_BLE_CH_MAP_RESULTS result; + tBTM_BLE_CH_MAP_RESULTS result = {0}; tBTM_BLE_LEGACY_GAP_CB_PARAMS cb_params; UINT8 status; @@ -2322,7 +2322,7 @@ void BTM_BleGetWhiteListSize(uint16_t *length) { tBTM_BLE_CB *p_cb = &btm_cb.ble_ctr_cb; if (p_cb->white_list_avail_size == 0) { - BTM_TRACE_WARNING("%s Whitelist full.", __func__); + BTM_TRACE_WARNING("%s Whitelist size is 0.", __func__); } *length = p_cb->white_list_avail_size; return; @@ -2356,7 +2356,7 @@ void BTM_BleGetPeriodicAdvListSize(uint8_t *size) *******************************************************************************/ void btm_read_channel_map_complete(UINT8 *p) { - tBTM_BLE_CH_MAP_RESULTS results; + tBTM_BLE_CH_MAP_RESULTS results = {0}; UINT16 handle; tACL_CONN *p_acl_cb = NULL; diff --git a/components/bt/host/bluedroid/stack/btm/btm_ble.c b/components/bt/host/bluedroid/stack/btm/btm_ble.c index 95b44c35889..23ddc7b99d5 100644 --- a/components/bt/host/bluedroid/stack/btm/btm_ble.c +++ b/components/bt/host/bluedroid/stack/btm/btm_ble.c @@ -736,7 +736,7 @@ BOOLEAN BTM_ReadConnectedTransportAddress(BD_ADDR remote_bda, tBT_TRANSPORT tran ** p_cmd_cmpl_cback - Command Complete callback ** *******************************************************************************/ -void BTM_BleReceiverTest(UINT8 rx_freq, tBTM_CMPL_CB *p_cmd_cmpl_cback) +void BTM_BleReceiverTest(UINT8 rx_freq, tBTM_DTM_CMD_CMPL_CBACK *p_cmd_cmpl_cback) { btm_cb.devcb.p_le_test_cmd_cmpl_cb = p_cmd_cmpl_cback; @@ -758,7 +758,7 @@ void BTM_BleReceiverTest(UINT8 rx_freq, tBTM_CMPL_CB *p_cmd_cmpl_cback) ** *******************************************************************************/ void BTM_BleTransmitterTest(UINT8 tx_freq, UINT8 test_data_len, - UINT8 packet_payload, tBTM_CMPL_CB *p_cmd_cmpl_cback) + UINT8 packet_payload, tBTM_DTM_CMD_CMPL_CBACK *p_cmd_cmpl_cback) { btm_cb.devcb.p_le_test_cmd_cmpl_cb = p_cmd_cmpl_cback; if (btsnd_hcic_ble_transmitter_test(tx_freq, test_data_len, packet_payload) == FALSE) { @@ -776,7 +776,7 @@ void BTM_BleTransmitterTest(UINT8 tx_freq, UINT8 test_data_len, ** Parameter p_cmd_cmpl_cback - Command complete callback ** *******************************************************************************/ -void BTM_BleTestEnd(tBTM_CMPL_CB *p_cmd_cmpl_cback) +void BTM_BleTestEnd(tBTM_DTM_CMD_CMPL_CBACK *p_cmd_cmpl_cback) { btm_cb.devcb.p_le_test_cmd_cmpl_cb = p_cmd_cmpl_cback; @@ -788,14 +788,14 @@ void BTM_BleTestEnd(tBTM_CMPL_CB *p_cmd_cmpl_cback) /******************************************************************************* ** Internal Functions *******************************************************************************/ -void btm_ble_test_command_complete(UINT8 *p) +void btm_ble_test_command_complete(UINT8 *p, UINT16 len) { - tBTM_CMPL_CB *p_cb = btm_cb.devcb.p_le_test_cmd_cmpl_cb; + tBTM_DTM_CMD_CMPL_CBACK *p_cb = btm_cb.devcb.p_le_test_cmd_cmpl_cb; btm_cb.devcb.p_le_test_cmd_cmpl_cb = NULL; if (p_cb) { - (*p_cb)(p); + (*p_cb)(p, len); } } #endif // #if ((BLE_42_DTM_TEST_EN == TRUE) || (BLE_50_DTM_TEST_EN == TRUE)) @@ -813,7 +813,7 @@ void btm_ble_test_command_complete(UINT8 *p) ** p_cmd_cmpl_cback - Command Complete callback ** *******************************************************************************/ -void BTM_BleEnhancedReceiverTest(UINT8 rx_freq, UINT8 phy, UINT8 modulation_index, tBTM_CMPL_CB *p_cmd_cmpl_cback) +void BTM_BleEnhancedReceiverTest(UINT8 rx_freq, UINT8 phy, UINT8 modulation_index, tBTM_DTM_CMD_CMPL_CBACK *p_cmd_cmpl_cback) { btm_cb.devcb.p_le_test_cmd_cmpl_cb = p_cmd_cmpl_cback; @@ -836,7 +836,7 @@ void BTM_BleEnhancedReceiverTest(UINT8 rx_freq, UINT8 phy, UINT8 modulation_inde ** *******************************************************************************/ void BTM_BleEnhancedTransmitterTest(UINT8 tx_freq, UINT8 test_data_len, - UINT8 packet_payload, UINT8 phy, tBTM_CMPL_CB *p_cmd_cmpl_cback) + UINT8 packet_payload, UINT8 phy, tBTM_DTM_CMD_CMPL_CBACK *p_cmd_cmpl_cback) { btm_cb.devcb.p_le_test_cmd_cmpl_cb = p_cmd_cmpl_cback; if (btsnd_hcic_ble_enhand_tx_test(tx_freq, test_data_len, packet_payload, phy) == FALSE) { diff --git a/components/bt/host/bluedroid/stack/btm/btm_ble_5_gap.c b/components/bt/host/bluedroid/stack/btm/btm_ble_5_gap.c index 40498baddc3..e15cae411b1 100644 --- a/components/bt/host/bluedroid/stack/btm/btm_ble_5_gap.c +++ b/components/bt/host/bluedroid/stack/btm/btm_ble_5_gap.c @@ -334,13 +334,13 @@ tBTM_STATUS BTM_BleConfigExtendedAdvDataRaw(BOOLEAN is_scan_rsp, UINT8 instance, } } if (!is_scan_rsp) { - if ((err = btsnd_hcic_ble_set_ext_adv_data(instance, operation, 0, send_data_len, &data[data_offset])) != HCI_SUCCESS) { + if ((err = btsnd_hcic_ble_set_ext_adv_data(instance, operation, 0, send_data_len, (data == NULL) ? NULL : &data[data_offset])) != HCI_SUCCESS) { BTM_TRACE_ERROR("LE EA SetAdvData: cmd err=0x%x", err); status = BTM_HCI_ERROR | err; break; } } else { - if ((err = btsnd_hcic_ble_set_ext_adv_scan_rsp_data(instance, operation, 0, send_data_len, &data[data_offset])) != HCI_SUCCESS) { + if ((err = btsnd_hcic_ble_set_ext_adv_scan_rsp_data(instance, operation, 0, send_data_len, (data == NULL) ? NULL : &data[data_offset])) != HCI_SUCCESS) { BTM_TRACE_ERROR("LE EA SetScanRspData: cmd err=0x%x", err); status = BTM_HCI_ERROR | err; break; @@ -1900,7 +1900,6 @@ void btm_ble_cs_read_local_supp_caps_cmpl_evt(uint8_t *p) goto _error; } - STREAM_TO_UINT16(cb_params.cs_read_local_supp_caps.conn_handle, p); STREAM_TO_UINT8(cb_params.cs_read_local_supp_caps.num_config_supported, p); STREAM_TO_UINT16(cb_params.cs_read_local_supp_caps.max_consecutive_proc_supported, p); STREAM_TO_UINT8(cb_params.cs_read_local_supp_caps.num_ant_supported, p); @@ -2011,6 +2010,7 @@ void btm_ble_cs_read_remote_fae_table_cmd_status(UINT8 status) tBTM_BLE_CS_READ_REMOTE_FAE_TAB_CMPL_EVT cs_read_remote_fae_tab = {0}; if (status != HCI_SUCCESS) { cs_read_remote_fae_tab.status = (status | BTM_HCI_ERROR); + cs_read_remote_fae_tab.conn_handle = 0xFFFF; BTM_ExtBleCallbackTrigger(BTM_BLE_GAP_CS_READ_REMOTE_FAE_TABLE_CMPL_EVT, (tBTM_BLE_5_GAP_CB_PARAMS *)&cs_read_remote_fae_tab); } } diff --git a/components/bt/host/bluedroid/stack/btm/btm_ble_privacy.c b/components/bt/host/bluedroid/stack/btm/btm_ble_privacy.c index 45f3af5bcd3..2a3fab1b09b 100644 --- a/components/bt/host/bluedroid/stack/btm/btm_ble_privacy.c +++ b/components/bt/host/bluedroid/stack/btm/btm_ble_privacy.c @@ -1180,6 +1180,9 @@ void btm_ble_resolving_list_cleanup(void) { tBTM_BLE_RESOLVE_Q *p_q = &btm_cb.ble_ctr_cb.resolving_list_pend_q; + p_q->q_next = 0; + p_q->q_pending = 0; + if (p_q->resolve_q_random_pseudo) { osi_free(p_q->resolve_q_random_pseudo); p_q->resolve_q_random_pseudo = NULL; diff --git a/components/bt/host/bluedroid/stack/btm/btm_devctl.c b/components/bt/host/bluedroid/stack/btm/btm_devctl.c index 1cc41f7e688..9b6052f9adb 100644 --- a/components/bt/host/bluedroid/stack/btm/btm_devctl.c +++ b/components/bt/host/bluedroid/stack/btm/btm_devctl.c @@ -120,13 +120,11 @@ void btm_dev_init (void) *******************************************************************************/ static void btm_db_reset (void) { - tBTM_CMPL_CB *p_cb; - tBTM_STATUS status = BTM_DEV_RESET; - btm_inq_db_reset(); #if (CLASSIC_BT_INCLUDED == TRUE) if (btm_cb.devcb.p_rln_cmpl_cb) { + tBTM_CMPL_CB *p_cb; p_cb = btm_cb.devcb.p_rln_cmpl_cb; btm_cb.devcb.p_rln_cmpl_cb = NULL; @@ -137,12 +135,14 @@ static void btm_db_reset (void) #endif // (CLASSIC_BT_INCLUDED == TRUE) if (btm_cb.devcb.p_rssi_cmpl_cb) { - p_cb = btm_cb.devcb.p_rssi_cmpl_cb; - btm_cb.devcb.p_rssi_cmpl_cb = NULL; + tBTM_CMPL_CB *p_cb = btm_cb.devcb.p_rssi_cmpl_cb; + tBTM_RSSI_RESULTS results = {0}; - if (p_cb) { - (*p_cb)((tBTM_RSSI_RESULTS *) &status); - } + results.status = BTM_DEV_RESET; + btm_cb.devcb.p_rssi_cmpl_cb = NULL; + btu_stop_timer(&btm_cb.devcb.rssi_timer); + + (*p_cb)(&results); } } diff --git a/components/bt/host/bluedroid/stack/btm/include/btm_ble_int.h b/components/bt/host/bluedroid/stack/btm/include/btm_ble_int.h index 02fa86b47e2..3ecd7cb16f0 100644 --- a/components/bt/host/bluedroid/stack/btm/include/btm_ble_int.h +++ b/components/bt/host/bluedroid/stack/btm/include/btm_ble_int.h @@ -444,7 +444,7 @@ void btm_ble_increment_sign_ctr(BD_ADDR bd_addr, BOOLEAN is_local ); BOOLEAN btm_get_local_div (BD_ADDR bd_addr, UINT16 *p_div); BOOLEAN btm_ble_get_enc_key_type(BD_ADDR bd_addr, UINT8 *p_key_types); -void btm_ble_test_command_complete(UINT8 *p); +void btm_ble_test_command_complete(UINT8 *p, UINT16 len); void btm_ble_rand_enc_complete (UINT8 *p, UINT16 op_code, tBTM_RAND_ENC_CB *p_enc_cplt_cback); void btm_sec_save_le_key(BD_ADDR bd_addr, tBTM_LE_KEY_TYPE key_type, tBTM_LE_KEY_VALUE *p_keys, BOOLEAN pass_to_application); diff --git a/components/bt/host/bluedroid/stack/btm/include/btm_int.h b/components/bt/host/bluedroid/stack/btm/include/btm_int.h index c0a1e2f2e24..f0a275f23b6 100644 --- a/components/bt/host/bluedroid/stack/btm/include/btm_int.h +++ b/components/bt/host/bluedroid/stack/btm/include/btm_int.h @@ -291,8 +291,8 @@ DEV_CLASS dev_class; /* Local device class TIMER_LIST_ENT ble_channels_timer; -tBTM_CMPL_CB *p_le_test_cmd_cmpl_cb; /* Callback function to be called when - LE test mode command has been sent successfully */ +tBTM_DTM_CMD_CMPL_CBACK *p_le_test_cmd_cmpl_cb; /* Callback function to be called when + LE test mode command has been sent successfully */ BD_ADDR read_tx_pwr_addr; /* read TX power target address */ diff --git a/components/bt/host/bluedroid/stack/btu/btu_hcif.c b/components/bt/host/bluedroid/stack/btu/btu_hcif.c index d780395f1c5..d9e34dcd10d 100644 --- a/components/bt/host/bluedroid/stack/btu/btu_hcif.c +++ b/components/bt/host/bluedroid/stack/btu/btu_hcif.c @@ -50,7 +50,7 @@ #include "stack/btu.h" extern void btm_process_cancel_complete(UINT8 status, UINT8 mode); -extern void btm_ble_test_command_complete(UINT8 *p); +extern void btm_ble_test_command_complete(UINT8 *p, UINT16 len); #if (BT_BLE_FEAT_CHANNEL_SOUNDING == TRUE) // BLE Channel Sounding parameter validation macros per BLE spec @@ -1331,11 +1331,17 @@ static void btu_hcif_hdl_command_complete (UINT16 opcode, UINT8 *p, UINT16 evt_l btm_ble_create_ll_conn_complete(*p); break; +#if ((BLE_42_DTM_TEST_EN == TRUE) || (BLE_50_DTM_TEST_EN == TRUE)) + case HCI_BLE_TRANSMITTER_TEST: + case HCI_BLE_RECEIVER_TEST: + case HCI_BLE_TEST_END: + /* Forward raw parameters + length; upper layers validate before parsing. */ + btm_ble_test_command_complete(p, evt_len); + break; +#else case HCI_BLE_TRANSMITTER_TEST: case HCI_BLE_RECEIVER_TEST: -#if ((BLE_42_DTM_TEST_EN == TRUE) || (BLE_50_DTM_TEST_EN == TRUE)) case HCI_BLE_TEST_END: - btm_ble_test_command_complete(p); break; #endif // #if ((BLE_42_DTM_TEST_EN == TRUE) || (BLE_50_DTM_TEST_EN == TRUE)) case HCI_BLE_CREATE_CONN_CANCEL: @@ -1397,7 +1403,7 @@ static void btu_hcif_hdl_command_complete (UINT16 opcode, UINT8 *p, UINT16 evt_l #if (BLE_50_DTM_TEST_EN == TRUE) case HCI_BLE_ENH_RX_TEST: case HCI_BLE_ENH_TX_TEST: - btm_ble_test_command_complete(p); + btm_ble_test_command_complete(p, evt_len); break; #endif // #if (BLE_50_DTM_TEST_EN == TRUE) diff --git a/components/bt/host/bluedroid/stack/gatt/att_protocol.c b/components/bt/host/bluedroid/stack/gatt/att_protocol.c index 686820071a8..704e1e969f0 100644 --- a/components/bt/host/bluedroid/stack/gatt/att_protocol.c +++ b/components/bt/host/bluedroid/stack/gatt/att_protocol.c @@ -390,6 +390,18 @@ tGATT_STATUS attp_send_msg_to_l2cap(tGATT_TCB *p_tcb, BT_HDR *p_toL2CAP) if (p_tcb->att_lcid == L2CAP_ATT_CID) { + /* L2CA_SendFixedChnlData() silently drops (osi_free) the buffer when the + * ATT fixed channel is already in cong_sent state, yet still returns + * L2CAP_DW_CONGESTED. Without distinguishing this from the post-enqueue + * congestion case, the upper layer would treat a dropped PDU as "sent" + * and wait for a response that never arrives. Detect the drop path + * up-front, release the buffer here and surface it as GATT_BUSY so that + * callers go through their failure path instead. */ + if (L2CA_CheckIsCongest(L2CAP_ATT_CID, p_tcb->peer_bda)) { + GATT_TRACE_WARNING("ATT fixed channel already congested, drop PDU"); + osi_free(p_toL2CAP); + return GATT_BUSY; + } l2cap_ret = L2CA_SendFixedChnlData (L2CAP_ATT_CID, p_tcb->peer_bda, p_toL2CAP); } else { #if (CLASSIC_BT_INCLUDED == TRUE) @@ -403,6 +415,8 @@ tGATT_STATUS attp_send_msg_to_l2cap(tGATT_TCB *p_tcb, BT_HDR *p_toL2CAP) GATT_TRACE_DEBUG("ATT failed to pass msg to L2CAP"); return GATT_INTERNAL_ERROR; } else if (l2cap_ret == L2CAP_DW_CONGESTED) { + /* Buffer was enqueued by L2CAP before congestion was reported; + * L2CAP retains ownership of it. */ GATT_TRACE_DEBUG("ATT congested, message accepted"); return GATT_CONGESTED; } diff --git a/components/bt/host/bluedroid/stack/gatt/gatt_api.c b/components/bt/host/bluedroid/stack/gatt/gatt_api.c index ddab5b986f8..3ade9f7f388 100644 --- a/components/bt/host/bluedroid/stack/gatt/gatt_api.c +++ b/components/bt/host/bluedroid/stack/gatt/gatt_api.c @@ -1750,9 +1750,22 @@ tGATT_STATUS GATTS_HandleMultiValueNotification (UINT16 conn_id, tGATT_HLV *tupl return GATT_ILLEGAL_PARAMETER; } + { + UINT32 new_len = (UINT32)notif.len + 4U + (UINT32)p_hlv->length; + if (new_len > (UINT32)GATT_MAX_ATTR_LEN) { + GATT_TRACE_ERROR("%s: len %u>MAX_ATTR_LEN", __func__, (unsigned)new_len); + return GATT_ILLEGAL_PARAMETER; + } + } + UINT16_TO_STREAM(p, p_hlv->handle); //handle UINT16_TO_STREAM(p, p_hlv->length); //length - memcpy (p, p_hlv->value, p_hlv->length); //value + if (p_hlv->length > 0) { + if (p_hlv->value == NULL) { + return GATT_ILLEGAL_PARAMETER; + } + memcpy (p, p_hlv->value, p_hlv->length); //value + } GATT_TRACE_DEBUG("%s handle %x, length %u", __func__, p_hlv->handle, p_hlv->length); p += p_hlv->length; notif.len += 4 + p_hlv->length; diff --git a/components/bt/host/bluedroid/stack/gatt/gatt_auth.c b/components/bt/host/bluedroid/stack/gatt/gatt_auth.c index 0a033e1ae04..19ad2bc4cad 100644 --- a/components/bt/host/bluedroid/stack/gatt/gatt_auth.c +++ b/components/bt/host/bluedroid/stack/gatt/gatt_auth.c @@ -171,7 +171,7 @@ void gatt_sec_check_complete(BOOLEAN sec_check_ok, tGATT_CLCB *p_clcb, UINT8 s void gatt_enc_cmpl_cback(BD_ADDR bd_addr, tBT_TRANSPORT transport, void *p_ref_data, tBTM_STATUS result) { tGATT_TCB *p_tcb; - UINT8 sec_flag; + UINT8 sec_flag = 0; BOOLEAN status = FALSE; UNUSED(p_ref_data); @@ -185,9 +185,8 @@ void gatt_enc_cmpl_cback(BD_ADDR bd_addr, tBT_TRANSPORT transport, void *p_ref_d if (p_buf != NULL) { if (result == BTM_SUCCESS) { if (gatt_get_sec_act(p_tcb) == GATT_SEC_ENCRYPT_MITM ) { - BTM_GetSecurityFlagsByTransport(bd_addr, &sec_flag, transport); - - if (sec_flag & BTM_SEC_FLAG_LKEY_AUTHED) { + if (BTM_GetSecurityFlagsByTransport(bd_addr, &sec_flag, transport) && + (sec_flag & BTM_SEC_FLAG_LKEY_AUTHED)) { status = TRUE; } } else { @@ -305,7 +304,7 @@ tGATT_SEC_ACTION gatt_get_sec_act(tGATT_TCB *p_tcb) tGATT_SEC_ACTION gatt_determine_sec_act(tGATT_CLCB *p_clcb ) { tGATT_SEC_ACTION act = GATT_SEC_OK; - UINT8 sec_flag; + UINT8 sec_flag = 0; tGATT_TCB *p_tcb = p_clcb->p_tcb; tGATT_AUTH_REQ auth_req = p_clcb->auth_req; BOOLEAN is_link_encrypted = FALSE; diff --git a/components/bt/host/bluedroid/stack/gatt/gatt_cl.c b/components/bt/host/bluedroid/stack/gatt/gatt_cl.c index 5d7c7e2bb39..28a50c4ac35 100644 --- a/components/bt/host/bluedroid/stack/gatt/gatt_cl.c +++ b/components/bt/host/bluedroid/stack/gatt/gatt_cl.c @@ -1124,10 +1124,14 @@ BOOLEAN gatt_cl_send_next_cmd_inq(tGATT_TCB *p_tcb) if (att_ret == GATT_SUCCESS || att_ret == GATT_CONGESTED) { sent = TRUE; p_cmd->to_send = FALSE; - if(p_cmd->p_cmd) { - osi_free(p_cmd->p_cmd); - p_cmd->p_cmd = NULL; - } + /* On GATT_SUCCESS / GATT_CONGESTED, L2CAP has taken ownership of + * p_cmd->p_cmd (it was either accepted normally, or enqueued just + * before the channel turned congested). The "already congested" + * drop path inside L2CA_SendFixedChnlData() is filtered out earlier + * by attp_send_msg_to_l2cap() and returned as GATT_BUSY, which + * falls into the error branch below. So we must not free the + * buffer here. */ + p_cmd->p_cmd = NULL; /* dequeue the request if is write command or sign write */ if (p_cmd->op_code != GATT_CMD_WRITE && p_cmd->op_code != GATT_SIGN_CMD_WRITE) { @@ -1145,13 +1149,22 @@ BOOLEAN gatt_cl_send_next_cmd_inq(tGATT_TCB *p_tcb) gatt_end_operation(p_clcb, att_ret, NULL); } } else { - GATT_TRACE_ERROR("gatt_cl_send_next_cmd_inq: L2CAP sent error"); + GATT_TRACE_ERROR("gatt_cl_send_next_cmd_inq: L2CAP sent error, status=%d", att_ret); /* attp_send_msg_to_l2cap() already freed p_cmd->p_cmd on failure */ p_cmd->p_cmd = NULL; - memset(p_cmd, 0, sizeof(tGATT_CMD_Q)); - p_tcb->pending_cl_req ++; - p_tcb->pending_cl_req %= GATT_CL_MAX_LCB; + p_cmd->to_send = FALSE; + /* Dequeue the failing command so pending_cl_req is advanced and the + * associated p_clcb can be retrieved. */ + p_clcb = gatt_cmd_dequeue(p_tcb, &rsp_code); p_cmd = &p_tcb->cl_cmd_q[p_tcb->pending_cl_req]; + /* Notify the upper layer about the failure. Without this the + * response timer is never armed (non-write ops) and the write + * completion callback is never fired, leaving the application + * stuck waiting for a callback that will never come. The p_clcb + * would also leak. */ + if (p_clcb != NULL) { + gatt_end_operation(p_clcb, att_ret, NULL); + } } } diff --git a/components/bt/host/bluedroid/stack/gatt/gatt_sr.c b/components/bt/host/bluedroid/stack/gatt/gatt_sr.c index 62b170b1a26..b30fd173f0e 100644 --- a/components/bt/host/bluedroid/stack/gatt/gatt_sr.c +++ b/components/bt/host/bluedroid/stack/gatt/gatt_sr.c @@ -754,8 +754,7 @@ void gatt_process_read_multi_req (tGATT_TCB *p_tcb, UINT8 op_code, UINT16 len, U for (ll = 0; ll < p_tcb->sr_cmd.multi_req.num_handles; ll ++) { if ((p_msg = (tGATTS_RSP *)osi_malloc(sizeof(tGATTS_RSP))) != NULL) { - memset(p_msg, 0, sizeof(tGATTS_RSP)) - ; + memset(p_msg, 0, sizeof(tGATTS_RSP)); handle = p_tcb->sr_cmd.multi_req.handles[ll]; i_rcb = gatt_sr_find_i_rcb_by_handle(handle); @@ -1496,10 +1495,17 @@ void gatt_attr_process_prepare_write (tGATT_TCB *p_tcb, UINT8 i_rcb, UINT16 hand } } + /* sr_cmd enqueued at handle but no attribute branch ran (null DB/list or no exact handle). */ + if (trans_id != 0 && !is_need_prepare_write_rsp && !is_need_queue_data && + status == GATT_SUCCESS) { + status = GATT_INVALID_HANDLE; + } + if (is_need_queue_data){ queue_data = (tGATT_PREPARE_WRITE_QUEUE_DATA *)osi_malloc(len + sizeof(tGATT_PREPARE_WRITE_QUEUE_DATA)); if (queue_data == NULL){ status = GATT_PREPARE_Q_FULL; + is_need_prepare_write_rsp = FALSE; } else { queue_data->p_attr = p_attr_temp; queue_data->len = len; @@ -1509,7 +1515,16 @@ void gatt_attr_process_prepare_write (tGATT_TCB *p_tcb, UINT8 i_rcb, UINT16 hand if (prepare_record->queue == NULL) { prepare_record->queue = fixed_queue_new(QUEUE_SIZE_MAX); } - fixed_queue_enqueue(prepare_record->queue, queue_data, FIXED_QUEUE_MAX_TIMEOUT); + if (prepare_record->queue == NULL || + fixed_queue_length(prepare_record->queue) >= + fixed_queue_capacity(prepare_record->queue)) { + osi_free(queue_data); + queue_data = NULL; + status = GATT_PREPARE_Q_FULL; + is_need_prepare_write_rsp = FALSE; + } else { + fixed_queue_enqueue(prepare_record->queue, queue_data, FIXED_QUEUE_MAX_TIMEOUT); + } } } diff --git a/components/bt/host/bluedroid/stack/gatt/gatt_utils.c b/components/bt/host/bluedroid/stack/gatt/gatt_utils.c index 0782a32c16e..552177df82c 100644 --- a/components/bt/host/bluedroid/stack/gatt/gatt_utils.c +++ b/components/bt/host/bluedroid/stack/gatt/gatt_utils.c @@ -690,6 +690,9 @@ BOOLEAN gatt_add_an_item_to_list(tGATT_HDL_LIST_INFO *p_list, tGATT_HDL_LIST_ELE p_new->p_prev = p_old->p_prev; p_new->p_next = p_old; + if (p_old->p_prev != NULL) { + p_old->p_prev->p_next = p_new; + } p_old->p_prev = p_new; break; @@ -1231,6 +1234,10 @@ BOOLEAN gatt_parse_uuid_from_cmd(tBT_UUID *p_uuid_rec, UINT16 uuid_size, UINT8 * void gatt_start_rsp_timer(UINT16 clcb_idx) { tGATT_CLCB *p_clcb = gatt_clcb_find_by_idx(clcb_idx); + if (p_clcb == NULL) { + GATT_TRACE_ERROR("%s: no CLCB for clcb_idx=0x%x", __func__, clcb_idx); + return; + } p_clcb->rsp_timer_ent.param = (TIMER_PARAM_TYPE)p_clcb; btu_start_timer (&p_clcb->rsp_timer_ent, BTU_TTYPE_ATT_WAIT_FOR_RSP, GATT_WAIT_FOR_RSP_TOUT); @@ -2305,10 +2312,17 @@ void gatt_cleanup_upon_disc(BD_ADDR bda, UINT16 reason, tBT_TRANSPORT transport) UINT8 i; UINT16 conn_id; tGATT_REG *p_reg = NULL; - +#if (GATTS_INCLUDED == TRUE) + BD_ADDR bda_local; +#endif GATT_TRACE_DEBUG ("gatt_cleanup_upon_disc "); +#if (GATTS_INCLUDED == TRUE) + /* Copy in case bda points into p_tcb->peer_bda, which is invalid after gatt_tcb_free. */ + memcpy(bda_local, bda, BD_ADDR_LEN); +#endif + if ((p_tcb = gatt_find_tcb_by_addr(bda, transport)) != NULL) { GATT_TRACE_DEBUG ("found p_tcb "); gatt_set_ch_state(p_tcb, GATT_CH_CLOSE); @@ -2357,7 +2371,7 @@ void gatt_cleanup_upon_disc(BD_ADDR bda, UINT16 reason, tBT_TRANSPORT transport) BTM_Recovery_Pre_State(); } #if (GATTS_INCLUDED == TRUE) - gatt_delete_dev_from_srv_chg_clt_list(bda); + gatt_delete_dev_from_srv_chg_clt_list(bda_local); #endif // (GATTS_INCLUDED == TRUE) } /******************************************************************************* diff --git a/components/bt/host/bluedroid/stack/hcic/hciblecmds.c b/components/bt/host/bluedroid/stack/hcic/hciblecmds.c index 901c672d20e..8c5494b7f04 100644 --- a/components/bt/host/bluedroid/stack/hcic/hciblecmds.c +++ b/components/bt/host/bluedroid/stack/hcic/hciblecmds.c @@ -3059,7 +3059,8 @@ UINT8 btsnd_hcic_ble_set_periodic_adv_subevt_data(UINT8 adv_handle, UINT8 num_su { BT_HDR *p; UINT8 *pp; - uint8_t param_len = 0; + unsigned total_len; + UINT8 param_len; HCI_TRACE_DEBUG("hci set PA subevent data, adv_handle %d num_subevents_with_data %d", adv_handle, num_subevents_with_data); @@ -3067,10 +3068,19 @@ UINT8 btsnd_hcic_ble_set_periodic_adv_subevt_data(UINT8 adv_handle, UINT8 num_su HCI_TRACE_ERROR("%s error\n", __func__); return HCI_ERR_ILLEGAL_PARAMETER_FMT; } - param_len += HCIC_PARAM_SIZE_SET_PA_SUBEVT_DATA_PARAMS_LEN; + total_len = HCIC_PARAM_SIZE_SET_PA_SUBEVT_DATA_PARAMS_LEN; for (UINT8 i = 0; i < num_subevents_with_data; i++) { + if (subevent_params[i].subevent_data_len > sizeof(subevent_params[i].data)) { + HCI_TRACE_ERROR("%s sub_data_len %u>%u", __func__, + (unsigned)subevent_params[i].subevent_data_len, + (unsigned)sizeof(subevent_params[i].data)); + return HCI_ERR_ILLEGAL_PARAMETER_FMT; + } + + unsigned add_len = 4u + (unsigned)subevent_params[i].subevent_data_len; + HCI_TRACE_DEBUG("subevent_params: subevent %d response_slot_start %d response_slot_count %d subevent_data_len %d", subevent_params[i].subevent, subevent_params[i].response_slot_start, subevent_params[i].response_slot_count, subevent_params[i].subevent_data_len); @@ -3079,9 +3089,17 @@ UINT8 btsnd_hcic_ble_set_periodic_adv_subevt_data(UINT8 adv_handle, UINT8 num_su esp_log_buffer_hex_internal("data", subevent_params[i].data, subevent_params[i].subevent_data_len, ESP_LOG_DEBUG); } - param_len += (4 + subevent_params[i].subevent_data_len); + /* Avoid unsigned wrap when add_len > HCI_COMMAND_SIZE. */ + if (total_len > (unsigned)HCI_COMMAND_SIZE || + add_len > (unsigned)HCI_COMMAND_SIZE - total_len) { + HCI_TRACE_ERROR("%s total>HCI_CMD_SZ", __func__); + return HCI_ERR_ILLEGAL_PARAMETER_FMT; + } + total_len += add_len; } + param_len = (UINT8)total_len; + HCIC_BLE_CMD_CREATED_U8(p, pp, param_len); pp = (UINT8 *)(p + 1); @@ -3113,6 +3131,11 @@ UINT8 btsnd_hcic_ble_set_periodic_adv_rsp_data(UINT16 sync_handle, UINT16 req_ev HCI_TRACE_DEBUG("hci set PA rsp data, sync_handle %d req_evt %d req_subevt %d rsp_subevt %d rsp_slot %d rsp_data_len %d", sync_handle, req_evt, req_subevt, rsp_subevt, rsp_slot, rsp_data_len); + if (rsp_data_len > HCIC_PA_RSP_DATA_PAYLOAD_MAX) { + HCI_TRACE_ERROR("%s rsp_len %u>%u", __func__, rsp_data_len, HCIC_PA_RSP_DATA_PAYLOAD_MAX); + return HCI_ERR_ILLEGAL_PARAMETER_FMT; + } + HCIC_BLE_CMD_CREATED_U8(p, pp, HCIC_PARAM_SIZE_SET_PA_RESPONSE_DATA_PARAMS_LEN + rsp_data_len); pp = (UINT8 *)(p + 1); @@ -3141,6 +3164,12 @@ UINT8 btsnd_hcic_ble_set_periodic_sync_subevt(UINT16 sync_handle, UINT16 periodi HCI_TRACE_DEBUG("hci set PA sync subevent, sync_handle %d periodic_adv_properties %d num_subevents_to_sync %d", sync_handle, periodic_adv_properties, num_subevents_to_sync); + + if (num_subevents_to_sync > HCIC_PA_SYNC_SUBEVT_NUM_MAX) { + HCI_TRACE_ERROR("%s n_sync %u>%u", __func__, num_subevents_to_sync, HCIC_PA_SYNC_SUBEVT_NUM_MAX); + return HCI_ERR_ILLEGAL_PARAMETER_FMT; + } + for (UINT8 i = 0; i < num_subevents_to_sync; i++) { HCI_TRACE_DEBUG("subevt[%d] = %d", i, subevt[i]); diff --git a/components/bt/host/bluedroid/stack/include/stack/btm_api.h b/components/bt/host/bluedroid/stack/include/stack/btm_api.h index 15dccaa115b..b90baf478e8 100644 --- a/components/bt/host/bluedroid/stack/include/stack/btm_api.h +++ b/components/bt/host/bluedroid/stack/include/stack/btm_api.h @@ -173,7 +173,14 @@ typedef void (tBTM_VSC_CMPL_CB) (tBTM_VSC_CMPL *p1); */ // typedef UINT8 (tBTM_FILTER_CB) (BD_ADDR bd_addr, DEV_CLASS dc); -typedef void (tBTM_DTM_CMD_CMPL_CBACK) (void *p1); +/* + * DTM (Direct Test Mode) command complete callback. + * + * The controller returns a variable-length parameter block depending on the + * specific LE test command. Propagate the parameter length so upper layers can + * validate before parsing and avoid OOB reads on malformed/truncated responses. + */ +typedef void (tBTM_DTM_CMD_CMPL_CBACK) (UINT8 *p, UINT16 len); typedef void (tBTM_SET_RAND_ADDR_CBACK) (UINT8 status); diff --git a/components/bt/host/bluedroid/stack/include/stack/btm_ble_api.h b/components/bt/host/bluedroid/stack/include/stack/btm_ble_api.h index 6084ffa6afa..7b17ba89314 100644 --- a/components/bt/host/bluedroid/stack/include/stack/btm_ble_api.h +++ b/components/bt/host/bluedroid/stack/include/stack/btm_ble_api.h @@ -1528,7 +1528,6 @@ typedef struct { typedef struct { UINT8 status; - UINT16 conn_handle; UINT8 num_config_supported; UINT16 max_consecutive_proc_supported; UINT8 num_ant_supported; @@ -2726,7 +2725,7 @@ void BTM_BleClearWhitelist(void); ** p_cmd_cmpl_cback - Command Complete callback ** *******************************************************************************/ -void BTM_BleReceiverTest(UINT8 rx_freq, tBTM_CMPL_CB *p_cmd_cmpl_cback); +void BTM_BleReceiverTest(UINT8 rx_freq, tBTM_DTM_CMD_CMPL_CBACK *p_cmd_cmpl_cback); /******************************************************************************* @@ -2742,7 +2741,7 @@ void BTM_BleReceiverTest(UINT8 rx_freq, tBTM_CMPL_CB *p_cmd_cmpl_cback); ** *******************************************************************************/ void BTM_BleTransmitterTest(UINT8 tx_freq, UINT8 test_data_len, - UINT8 packet_payload, tBTM_CMPL_CB *p_cmd_cmpl_cback); + UINT8 packet_payload, tBTM_DTM_CMD_CMPL_CBACK *p_cmd_cmpl_cback); /******************************************************************************* ** @@ -2753,7 +2752,7 @@ void BTM_BleTransmitterTest(UINT8 tx_freq, UINT8 test_data_len, ** Parameter p_cmd_cmpl_cback - Command complete callback ** *******************************************************************************/ -void BTM_BleTestEnd(tBTM_CMPL_CB *p_cmd_cmpl_cback); +void BTM_BleTestEnd(tBTM_DTM_CMD_CMPL_CBACK *p_cmd_cmpl_cback); /******************************************************************************* ** @@ -3087,9 +3086,9 @@ tBTM_STATUS BTM_BleEnableMonitorAdv(UINT8 enable); #endif // #if (BLE_50_FEATURE_SUPPORT == TRUE) #if (BLE_50_DTM_TEST_EN == TRUE) -void BTM_BleEnhancedReceiverTest(UINT8 rx_freq, UINT8 phy, UINT8 modulation_index, tBTM_CMPL_CB *p_cmd_cmpl_cback); +void BTM_BleEnhancedReceiverTest(UINT8 rx_freq, UINT8 phy, UINT8 modulation_index, tBTM_DTM_CMD_CMPL_CBACK *p_cmd_cmpl_cback); -void BTM_BleEnhancedTransmitterTest(UINT8 tx_freq, UINT8 test_data_len, UINT8 packet_payload, UINT8 phy, tBTM_CMPL_CB *p_cmd_cmpl_cback); +void BTM_BleEnhancedTransmitterTest(UINT8 tx_freq, UINT8 test_data_len, UINT8 packet_payload, UINT8 phy, tBTM_DTM_CMD_CMPL_CBACK *p_cmd_cmpl_cback); #endif // #if (BLE_50_DTM_TEST_EN == TRUE) #if (BLE_FEAT_PERIODIC_ADV_SYNC_TRANSFER == TRUE) diff --git a/components/bt/host/bluedroid/stack/include/stack/hcimsgs.h b/components/bt/host/bluedroid/stack/include/stack/hcimsgs.h index e82a3178abf..897d0f636e7 100644 --- a/components/bt/host/bluedroid/stack/include/stack/hcimsgs.h +++ b/components/bt/host/bluedroid/stack/include/stack/hcimsgs.h @@ -1285,6 +1285,11 @@ typedef struct { #define HCIC_PARAM_SIZE_SET_PA_RESPONSE_DATA_PARAMS_LEN 8 #define HCIC_PARAM_SIZE_SET_PA_SYNC_SUBEVT_PARAMS_LEN 5 +/** Max rsp_data octets in LE Set Periodic Advertising Response Data (HCI command param total ≤ HCI_COMMAND_SIZE). */ +#define HCIC_PA_RSP_DATA_PAYLOAD_MAX (HCI_COMMAND_SIZE - HCIC_PARAM_SIZE_SET_PA_RESPONSE_DATA_PARAMS_LEN) +/** Max Num_Subevents_To_Sync in LE Set Periodic Advertising Sync Subevents (BT Core Spec §7.8.127: 0x01–0x80). */ +#define HCIC_PA_SYNC_SUBEVT_NUM_MAX 128 + #define HCIC_PARAM_SIZE_SET_PERIODIC_ADV_PARAMS_V2 12 UINT8 btsnd_hcic_ble_set_periodic_adv_params_v2(UINT8 adv_handle, UINT16 interval_min, UINT16 interval_max, UINT16 propertics, UINT8 num_subevents, UINT8 subevent_interval, 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/cent/sdkconfig.defaults b/examples/bluetooth/bluedroid/ble/ble_acl_latency/cent/sdkconfig.defaults index a218a4da6a1..fa0bf673cfb 100644 --- a/examples/bluetooth/bluedroid/ble/ble_acl_latency/cent/sdkconfig.defaults +++ b/examples/bluetooth/bluedroid/ble/ble_acl_latency/cent/sdkconfig.defaults @@ -5,3 +5,10 @@ CONFIG_BT_ENABLED=y # CONFIG_BT_BLE_50_FEATURES_SUPPORTED is not set CONFIG_BT_BLE_42_FEATURES_SUPPORTED=y # CONFIG_BT_LE_50_FEATURE_SUPPORT is not set +CONFIG_PARTITION_TABLE_SINGLE_APP_LARGE=y + +# Disable unused Bluedroid host features for GATT client only example +# CONFIG_BT_GATTS_ENABLE is not set +# CONFIG_BT_BLE_SMP_ENABLE is not set +# CONFIG_BT_BLE_42_ADV_EN is not set +# CONFIG_BT_BLE_42_DTM_TEST_EN is not set 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_acl_latency/periph/sdkconfig.defaults b/examples/bluetooth/bluedroid/ble/ble_acl_latency/periph/sdkconfig.defaults index a218a4da6a1..c9b1dccee9c 100644 --- a/examples/bluetooth/bluedroid/ble/ble_acl_latency/periph/sdkconfig.defaults +++ b/examples/bluetooth/bluedroid/ble/ble_acl_latency/periph/sdkconfig.defaults @@ -5,3 +5,10 @@ CONFIG_BT_ENABLED=y # CONFIG_BT_BLE_50_FEATURES_SUPPORTED is not set CONFIG_BT_BLE_42_FEATURES_SUPPORTED=y # CONFIG_BT_LE_50_FEATURE_SUPPORT is not set +CONFIG_PARTITION_TABLE_SINGLE_APP_LARGE=y + +# Disable unused Bluedroid host features for GATT server only example +# CONFIG_BT_GATTC_ENABLE is not set +# CONFIG_BT_BLE_SMP_ENABLE is not set +# CONFIG_BT_BLE_42_SCAN_EN is not set +# CONFIG_BT_BLE_42_DTM_TEST_EN is not set 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 813a677b51d..5eef3e6d9eb 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); } @@ -131,7 +134,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); @@ -142,7 +148,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; @@ -197,9 +203,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"; @@ -213,6 +219,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 d92ce164f87..7fbd39d34ff 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" @@ -52,6 +53,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 @@ -190,8 +192,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 --; @@ -254,12 +256,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: 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)) { - // copy 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; @@ -639,6 +680,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_ancs/sdkconfig.defaults b/examples/bluetooth/bluedroid/ble/ble_ancs/sdkconfig.defaults index 1bf8906ea7d..1602e1c68ab 100644 --- a/examples/bluetooth/bluedroid/ble/ble_ancs/sdkconfig.defaults +++ b/examples/bluetooth/bluedroid/ble/ble_ancs/sdkconfig.defaults @@ -6,3 +6,10 @@ CONFIG_BT_BLE_50_FEATURES_SUPPORTED=n CONFIG_BT_BLE_42_FEATURES_SUPPORTED=y # CONFIG_BT_LE_50_FEATURE_SUPPORT is not used on ESP32, ESP32-C3 and ESP32-S3. CONFIG_BT_LE_50_FEATURE_SUPPORT=n + +CONFIG_PARTITION_TABLE_SINGLE_APP_LARGE=y +# Disable unused Bluedroid host features for ANCS example +# (advertises as peripheral and acts as GATT client to ANCS service) +# CONFIG_BT_GATTS_ENABLE is not set +# CONFIG_BT_BLE_42_SCAN_EN is not set +# CONFIG_BT_BLE_42_DTM_TEST_EN is not set 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 8ed1fef08ed..3ebc06a954a 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). * *********************************************************************************/ @@ -65,6 +66,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 @@ -441,7 +443,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}; @@ -562,7 +568,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: @@ -581,6 +587,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_compatibility_test/sdkconfig.defaults b/examples/bluetooth/bluedroid/ble/ble_compatibility_test/sdkconfig.defaults index 1bf8906ea7d..42586d5002a 100644 --- a/examples/bluetooth/bluedroid/ble/ble_compatibility_test/sdkconfig.defaults +++ b/examples/bluetooth/bluedroid/ble/ble_compatibility_test/sdkconfig.defaults @@ -6,3 +6,9 @@ CONFIG_BT_BLE_50_FEATURES_SUPPORTED=n CONFIG_BT_BLE_42_FEATURES_SUPPORTED=y # CONFIG_BT_LE_50_FEATURE_SUPPORT is not used on ESP32, ESP32-C3 and ESP32-S3. CONFIG_BT_LE_50_FEATURE_SUPPORT=n +CONFIG_PARTITION_TABLE_SINGLE_APP_LARGE=y + +# Disable unused Bluedroid host features for compatibility test (server with SMP) +# CONFIG_BT_GATTC_ENABLE is not set +# CONFIG_BT_BLE_42_SCAN_EN is not set +# CONFIG_BT_BLE_42_DTM_TEST_EN is not set diff --git a/examples/bluetooth/bluedroid/ble/ble_eddystone_receiver/sdkconfig.defaults b/examples/bluetooth/bluedroid/ble/ble_eddystone_receiver/sdkconfig.defaults index 1bf8906ea7d..fa011a220e0 100644 --- a/examples/bluetooth/bluedroid/ble/ble_eddystone_receiver/sdkconfig.defaults +++ b/examples/bluetooth/bluedroid/ble/ble_eddystone_receiver/sdkconfig.defaults @@ -5,4 +5,12 @@ CONFIG_BT_ENABLED=y CONFIG_BT_BLE_50_FEATURES_SUPPORTED=n CONFIG_BT_BLE_42_FEATURES_SUPPORTED=y # CONFIG_BT_LE_50_FEATURE_SUPPORT is not used on ESP32, ESP32-C3 and ESP32-S3. -CONFIG_BT_LE_50_FEATURE_SUPPORT=n +# CONFIG_BT_LE_50_FEATURE_SUPPORT is not set +CONFIG_PARTITION_TABLE_SINGLE_APP_LARGE=y + +# Disable unused Bluedroid host features for Eddystone receiver (scanning only) +# CONFIG_BT_GATTS_ENABLE is not set +# CONFIG_BT_GATTC_ENABLE is not set +# CONFIG_BT_BLE_SMP_ENABLE is not set +# CONFIG_BT_BLE_42_ADV_EN is not set +# CONFIG_BT_BLE_42_DTM_TEST_EN is not set 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_eddystone_sender/sdkconfig.defaults b/examples/bluetooth/bluedroid/ble/ble_eddystone_sender/sdkconfig.defaults index 93b417c68f4..268532f2cc0 100644 --- a/examples/bluetooth/bluedroid/ble/ble_eddystone_sender/sdkconfig.defaults +++ b/examples/bluetooth/bluedroid/ble/ble_eddystone_sender/sdkconfig.defaults @@ -9,3 +9,11 @@ CONFIG_BT_BLE_42_FEATURES_SUPPORTED=y CONFIG_EXMAPLE_EDDYSTONE_SEND_UID=y CONFIG_EXMAPLE_EDDYSTONE_SEND_URL=n CONFIG_EXMAPLE_EDDYSTONE_SEND_TLM=n +CONFIG_PARTITION_TABLE_SINGLE_APP_LARGE=y + +# Disable unused Bluedroid host features for Eddystone sender (advertising only) +# CONFIG_BT_GATTS_ENABLE is not set +# CONFIG_BT_GATTC_ENABLE is not set +# CONFIG_BT_BLE_SMP_ENABLE is not set +# CONFIG_BT_BLE_42_SCAN_EN is not set +# CONFIG_BT_BLE_42_DTM_TEST_EN is not set 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_cent/sdkconfig.defaults b/examples/bluetooth/bluedroid/ble/ble_enc_adv_data/enc_adv_data_cent/sdkconfig.defaults index ae0ee308988..7b5d67ee671 100644 --- a/examples/bluetooth/bluedroid/ble/ble_enc_adv_data/enc_adv_data_cent/sdkconfig.defaults +++ b/examples/bluetooth/bluedroid/ble/ble_enc_adv_data/enc_adv_data_cent/sdkconfig.defaults @@ -11,3 +11,9 @@ CONFIG_BT_BLE_SMP_ENABLE=y # Select crypto library for EAD (Encrypted Advertising Data) # Options: CONFIG_BT_SMP_CRYPTO_STACK_TINYCRYPT or CONFIG_BT_SMP_CRYPTO_STACK_MBEDTLS CONFIG_BT_SMP_CRYPTO_STACK_TINYCRYPT=y +CONFIG_PARTITION_TABLE_SINGLE_APP_LARGE=y + +# Disable unused Bluedroid host features for encrypted adv data central (client with SMP) +# CONFIG_BT_GATTS_ENABLE is not set +# CONFIG_BT_BLE_42_ADV_EN is not set +# CONFIG_BT_BLE_42_DTM_TEST_EN is not set 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_enc_adv_data/enc_adv_data_prph/sdkconfig.defaults b/examples/bluetooth/bluedroid/ble/ble_enc_adv_data/enc_adv_data_prph/sdkconfig.defaults index ae0ee308988..dafa5f42994 100644 --- a/examples/bluetooth/bluedroid/ble/ble_enc_adv_data/enc_adv_data_prph/sdkconfig.defaults +++ b/examples/bluetooth/bluedroid/ble/ble_enc_adv_data/enc_adv_data_prph/sdkconfig.defaults @@ -11,3 +11,9 @@ CONFIG_BT_BLE_SMP_ENABLE=y # Select crypto library for EAD (Encrypted Advertising Data) # Options: CONFIG_BT_SMP_CRYPTO_STACK_TINYCRYPT or CONFIG_BT_SMP_CRYPTO_STACK_MBEDTLS CONFIG_BT_SMP_CRYPTO_STACK_TINYCRYPT=y +CONFIG_PARTITION_TABLE_SINGLE_APP_LARGE=y + +# Disable unused Bluedroid host features for encrypted adv data peripheral (server with SMP) +# CONFIG_BT_GATTC_ENABLE is not set +# CONFIG_BT_BLE_42_SCAN_EN is not set +# CONFIG_BT_BLE_42_DTM_TEST_EN is not set 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_hid_device_demo/sdkconfig.defaults b/examples/bluetooth/bluedroid/ble/ble_hid_device_demo/sdkconfig.defaults index 1bf8906ea7d..2580dc36e6d 100644 --- a/examples/bluetooth/bluedroid/ble/ble_hid_device_demo/sdkconfig.defaults +++ b/examples/bluetooth/bluedroid/ble/ble_hid_device_demo/sdkconfig.defaults @@ -5,4 +5,10 @@ CONFIG_BT_ENABLED=y CONFIG_BT_BLE_50_FEATURES_SUPPORTED=n CONFIG_BT_BLE_42_FEATURES_SUPPORTED=y # CONFIG_BT_LE_50_FEATURE_SUPPORT is not used on ESP32, ESP32-C3 and ESP32-S3. -CONFIG_BT_LE_50_FEATURE_SUPPORT=n +# CONFIG_BT_LE_50_FEATURE_SUPPORT is not set +CONFIG_PARTITION_TABLE_SINGLE_APP_LARGE=y + +# Disable unused Bluedroid host features for HID device example (server with SMP) +# CONFIG_BT_GATTC_ENABLE is not set +# CONFIG_BT_BLE_42_SCAN_EN is not set +# CONFIG_BT_BLE_42_DTM_TEST_EN is not set diff --git a/examples/bluetooth/bluedroid/ble/ble_ibeacon/sdkconfig.defaults b/examples/bluetooth/bluedroid/ble/ble_ibeacon/sdkconfig.defaults index 1bf8906ea7d..7a916b469af 100644 --- a/examples/bluetooth/bluedroid/ble/ble_ibeacon/sdkconfig.defaults +++ b/examples/bluetooth/bluedroid/ble/ble_ibeacon/sdkconfig.defaults @@ -6,3 +6,10 @@ CONFIG_BT_BLE_50_FEATURES_SUPPORTED=n CONFIG_BT_BLE_42_FEATURES_SUPPORTED=y # CONFIG_BT_LE_50_FEATURE_SUPPORT is not used on ESP32, ESP32-C3 and ESP32-S3. CONFIG_BT_LE_50_FEATURE_SUPPORT=n +# CONFIG_BT_LE_50_FEATURE_SUPPORT is not set + +# Disable unused Bluedroid host features for iBeacon (advertising/scanning only, no GATT/SMP) +# CONFIG_BT_GATTS_ENABLE is not set +# CONFIG_BT_GATTC_ENABLE is not set +# CONFIG_BT_BLE_SMP_ENABLE is not set +# CONFIG_BT_BLE_42_DTM_TEST_EN is not set 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_cent/sdkconfig.defaults b/examples/bluetooth/bluedroid/ble/ble_multi_conn/ble_multi_conn_cent/sdkconfig.defaults index d6755c993c9..eda43c68c2e 100644 --- a/examples/bluetooth/bluedroid/ble/ble_multi_conn/ble_multi_conn_cent/sdkconfig.defaults +++ b/examples/bluetooth/bluedroid/ble/ble_multi_conn/ble_multi_conn_cent/sdkconfig.defaults @@ -6,3 +6,9 @@ CONFIG_BT_ENABLED=y CONFIG_BT_BLE_42_FEATURES_SUPPORTED=y # CONFIG_BT_BLE_50_FEATURES_SUPPORTED is not set CONFIG_BT_MULTI_CONNECTION_ENBALE=y +CONFIG_PARTITION_TABLE_SINGLE_APP_LARGE=y + +# Disable unused Bluedroid host features for multi-connection central +# (acts as both GATT server and GATT client, but no SMP) +# CONFIG_BT_BLE_SMP_ENABLE is not set +# CONFIG_BT_BLE_42_DTM_TEST_EN is not set 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_multi_conn/ble_multi_conn_prph/sdkconfig.defaults b/examples/bluetooth/bluedroid/ble/ble_multi_conn/ble_multi_conn_prph/sdkconfig.defaults index 319fcc0996e..355af902721 100644 --- a/examples/bluetooth/bluedroid/ble/ble_multi_conn/ble_multi_conn_prph/sdkconfig.defaults +++ b/examples/bluetooth/bluedroid/ble/ble_multi_conn/ble_multi_conn_prph/sdkconfig.defaults @@ -8,3 +8,10 @@ CONFIG_BT_BLE_42_FEATURES_SUPPORTED=y CONFIG_BT_ACL_CONNECTIONS=50 CONFIG_BT_ALARM_MAX_NUM=150 CONFIG_BT_MULTI_CONNECTION_ENBALE=y +CONFIG_PARTITION_TABLE_SINGLE_APP_LARGE=y + +# Disable unused Bluedroid host features for GATT server only example +# CONFIG_BT_GATTC_ENABLE is not set +# CONFIG_BT_BLE_SMP_ENABLE is not set +# CONFIG_BT_BLE_42_SCAN_EN is not set +# CONFIG_BT_BLE_42_DTM_TEST_EN is not set 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_client/sdkconfig.defaults b/examples/bluetooth/bluedroid/ble/ble_spp_client/sdkconfig.defaults index 1bf8906ea7d..d3b87b3423b 100644 --- a/examples/bluetooth/bluedroid/ble/ble_spp_client/sdkconfig.defaults +++ b/examples/bluetooth/bluedroid/ble/ble_spp_client/sdkconfig.defaults @@ -5,4 +5,11 @@ CONFIG_BT_ENABLED=y CONFIG_BT_BLE_50_FEATURES_SUPPORTED=n CONFIG_BT_BLE_42_FEATURES_SUPPORTED=y # CONFIG_BT_LE_50_FEATURE_SUPPORT is not used on ESP32, ESP32-C3 and ESP32-S3. -CONFIG_BT_LE_50_FEATURE_SUPPORT=n +# CONFIG_BT_LE_50_FEATURE_SUPPORT is not set +CONFIG_PARTITION_TABLE_SINGLE_APP_LARGE=y + +# Disable unused Bluedroid host features for GATT client only example +# CONFIG_BT_GATTS_ENABLE is not set +# CONFIG_BT_BLE_SMP_ENABLE is not set +# CONFIG_BT_BLE_42_ADV_EN is not set +# CONFIG_BT_BLE_42_DTM_TEST_EN is not set 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 aa3d911e808..0c6b5f2b54d 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 @@ -59,7 +59,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; @@ -108,19 +108,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); @@ -271,67 +277,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; } } @@ -449,22 +462,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); + } } } } @@ -474,13 +491,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); @@ -499,7 +518,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); } @@ -518,7 +537,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; } @@ -556,15 +575,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"); @@ -588,9 +609,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)) { @@ -612,14 +635,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: @@ -644,18 +668,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; @@ -670,6 +696,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_spp_server/sdkconfig.defaults b/examples/bluetooth/bluedroid/ble/ble_spp_server/sdkconfig.defaults index 1bf8906ea7d..8aee1cf3112 100644 --- a/examples/bluetooth/bluedroid/ble/ble_spp_server/sdkconfig.defaults +++ b/examples/bluetooth/bluedroid/ble/ble_spp_server/sdkconfig.defaults @@ -6,3 +6,11 @@ CONFIG_BT_BLE_50_FEATURES_SUPPORTED=n CONFIG_BT_BLE_42_FEATURES_SUPPORTED=y # CONFIG_BT_LE_50_FEATURE_SUPPORT is not used on ESP32, ESP32-C3 and ESP32-S3. CONFIG_BT_LE_50_FEATURE_SUPPORT=n +# CONFIG_BT_LE_50_FEATURE_SUPPORT is not set +CONFIG_PARTITION_TABLE_SINGLE_APP_LARGE=y + +# Disable unused Bluedroid host features for GATT server only example +# CONFIG_BT_GATTC_ENABLE is not set +# CONFIG_BT_BLE_SMP_ENABLE is not set +# CONFIG_BT_BLE_42_SCAN_EN is not set +# CONFIG_BT_BLE_42_DTM_TEST_EN is not set 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 9ce779a3894..40d1ec517d6 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 @@ -126,6 +126,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]; @@ -288,6 +291,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{ @@ -299,10 +303,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:"); } @@ -347,6 +364,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; @@ -405,7 +427,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; @@ -540,9 +562,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"); } @@ -600,6 +627,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){ @@ -625,12 +663,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_client/sdkconfig.defaults b/examples/bluetooth/bluedroid/ble/ble_throughput/throughput_client/sdkconfig.defaults index b0a386fc2ca..70e6b8943db 100644 --- a/examples/bluetooth/bluedroid/ble/ble_throughput/throughput_client/sdkconfig.defaults +++ b/examples/bluetooth/bluedroid/ble/ble_throughput/throughput_client/sdkconfig.defaults @@ -8,3 +8,10 @@ CONFIG_BT_BLE_42_FEATURES_SUPPORTED=y CONFIG_BT_LE_50_FEATURE_SUPPORT=n CONFIG_EXAMPLE_GATTS_NOTIFY_THROUGHPUT=y CONFIG_EXAMPLE_GATTC_WRITE_THROUGHPUT=n +CONFIG_PARTITION_TABLE_SINGLE_APP_LARGE=y + +# Disable unused Bluedroid host features for GATT client only example +# CONFIG_BT_GATTS_ENABLE is not set +# CONFIG_BT_BLE_SMP_ENABLE is not set +# CONFIG_BT_BLE_42_ADV_EN is not set +# CONFIG_BT_BLE_42_DTM_TEST_EN is not set 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 f27ca2a48f3..d3bb3e19710 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 @@ -59,6 +59,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; @@ -204,6 +205,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; @@ -211,6 +224,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]; @@ -334,14 +350,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) { @@ -442,17 +458,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) */ @@ -462,11 +493,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); @@ -538,10 +571,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); @@ -637,11 +694,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/ble_throughput/throughput_server/sdkconfig.defaults b/examples/bluetooth/bluedroid/ble/ble_throughput/throughput_server/sdkconfig.defaults index 1decbd1f1d6..f813648ada6 100644 --- a/examples/bluetooth/bluedroid/ble/ble_throughput/throughput_server/sdkconfig.defaults +++ b/examples/bluetooth/bluedroid/ble/ble_throughput/throughput_server/sdkconfig.defaults @@ -7,4 +7,11 @@ CONFIG_BT_ENABLED=y CONFIG_BT_BLE_50_FEATURES_SUPPORTED=n CONFIG_BT_BLE_42_FEATURES_SUPPORTED=y # CONFIG_BT_LE_50_FEATURE_SUPPORT is not used on ESP32, ESP32-C3 and ESP32-S3. -CONFIG_BT_LE_50_FEATURE_SUPPORT=n +# CONFIG_BT_LE_50_FEATURE_SUPPORT is not set +CONFIG_PARTITION_TABLE_SINGLE_APP_LARGE=y + +# Disable unused Bluedroid host features for GATT server only example +# CONFIG_BT_GATTC_ENABLE is not set +# CONFIG_BT_BLE_SMP_ENABLE is not set +# CONFIG_BT_BLE_42_SCAN_EN is not set +# CONFIG_BT_BLE_42_DTM_TEST_EN is not set 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 2010caa35d6..dbc6b19f517 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_client/sdkconfig.defaults b/examples/bluetooth/bluedroid/ble/gatt_client/sdkconfig.defaults index 1bf8906ea7d..d3b87b3423b 100644 --- a/examples/bluetooth/bluedroid/ble/gatt_client/sdkconfig.defaults +++ b/examples/bluetooth/bluedroid/ble/gatt_client/sdkconfig.defaults @@ -5,4 +5,11 @@ CONFIG_BT_ENABLED=y CONFIG_BT_BLE_50_FEATURES_SUPPORTED=n CONFIG_BT_BLE_42_FEATURES_SUPPORTED=y # CONFIG_BT_LE_50_FEATURE_SUPPORT is not used on ESP32, ESP32-C3 and ESP32-S3. -CONFIG_BT_LE_50_FEATURE_SUPPORT=n +# CONFIG_BT_LE_50_FEATURE_SUPPORT is not set +CONFIG_PARTITION_TABLE_SINGLE_APP_LARGE=y + +# Disable unused Bluedroid host features for GATT client only example +# CONFIG_BT_GATTS_ENABLE is not set +# CONFIG_BT_BLE_SMP_ENABLE is not set +# CONFIG_BT_BLE_42_ADV_EN is not set +# CONFIG_BT_BLE_42_DTM_TEST_EN is not set 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_client/sdkconfig.defaults b/examples/bluetooth/bluedroid/ble/gatt_security_client/sdkconfig.defaults index 1bf8906ea7d..8377ae83231 100644 --- a/examples/bluetooth/bluedroid/ble/gatt_security_client/sdkconfig.defaults +++ b/examples/bluetooth/bluedroid/ble/gatt_security_client/sdkconfig.defaults @@ -5,4 +5,10 @@ CONFIG_BT_ENABLED=y CONFIG_BT_BLE_50_FEATURES_SUPPORTED=n CONFIG_BT_BLE_42_FEATURES_SUPPORTED=y # CONFIG_BT_LE_50_FEATURE_SUPPORT is not used on ESP32, ESP32-C3 and ESP32-S3. -CONFIG_BT_LE_50_FEATURE_SUPPORT=n +# CONFIG_BT_LE_50_FEATURE_SUPPORT is not set +CONFIG_PARTITION_TABLE_SINGLE_APP_LARGE=y + +# Disable unused Bluedroid host features for GATT security client example +# CONFIG_BT_GATTS_ENABLE is not set +# CONFIG_BT_BLE_42_ADV_EN is not set +# CONFIG_BT_BLE_42_DTM_TEST_EN is not set 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 ca6d939f14f..85ac16ebf80 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 @@ -178,9 +178,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"; @@ -257,12 +257,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; @@ -280,12 +284,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; @@ -461,7 +469,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, @@ -472,7 +480,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_security_server/sdkconfig.defaults b/examples/bluetooth/bluedroid/ble/gatt_security_server/sdkconfig.defaults index 1bf8906ea7d..8cc772f6cf9 100644 --- a/examples/bluetooth/bluedroid/ble/gatt_security_server/sdkconfig.defaults +++ b/examples/bluetooth/bluedroid/ble/gatt_security_server/sdkconfig.defaults @@ -5,4 +5,10 @@ CONFIG_BT_ENABLED=y CONFIG_BT_BLE_50_FEATURES_SUPPORTED=n CONFIG_BT_BLE_42_FEATURES_SUPPORTED=y # CONFIG_BT_LE_50_FEATURE_SUPPORT is not used on ESP32, ESP32-C3 and ESP32-S3. -CONFIG_BT_LE_50_FEATURE_SUPPORT=n +# CONFIG_BT_LE_50_FEATURE_SUPPORT is not set +CONFIG_PARTITION_TABLE_SINGLE_APP_LARGE=y + +# Disable unused Bluedroid host features for GATT security server example +# CONFIG_BT_GATTC_ENABLE is not set +# CONFIG_BT_BLE_42_SCAN_EN is not set +# CONFIG_BT_BLE_42_DTM_TEST_EN is not set 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 57d51cb85a2..9ce65bc8e94 100644 --- a/examples/bluetooth/bluedroid/ble/gatt_server/main/gatts_demo.c +++ b/examples/bluetooth/bluedroid/ble/gatt_server/main/gatts_demo.c @@ -198,6 +198,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); @@ -206,27 +218,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; @@ -240,7 +268,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; } @@ -274,7 +302,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"); @@ -305,7 +333,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); @@ -315,15 +350,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) { @@ -546,12 +585,13 @@ 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){ + 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; @@ -692,11 +732,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){ + 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/sdkconfig.defaults b/examples/bluetooth/bluedroid/ble/gatt_server/sdkconfig.defaults index 1bf8906ea7d..925c754be65 100644 --- a/examples/bluetooth/bluedroid/ble/gatt_server/sdkconfig.defaults +++ b/examples/bluetooth/bluedroid/ble/gatt_server/sdkconfig.defaults @@ -5,4 +5,11 @@ CONFIG_BT_ENABLED=y CONFIG_BT_BLE_50_FEATURES_SUPPORTED=n CONFIG_BT_BLE_42_FEATURES_SUPPORTED=y # CONFIG_BT_LE_50_FEATURE_SUPPORT is not used on ESP32, ESP32-C3 and ESP32-S3. -CONFIG_BT_LE_50_FEATURE_SUPPORT=n +# CONFIG_BT_LE_50_FEATURE_SUPPORT is not set +CONFIG_PARTITION_TABLE_SINGLE_APP_LARGE=y + +# Disable unused Bluedroid host features for GATT server only example +# CONFIG_BT_GATTC_ENABLE is not set +# CONFIG_BT_BLE_SMP_ENABLE is not set +# CONFIG_BT_BLE_42_SCAN_EN is not set +# CONFIG_BT_BLE_42_DTM_TEST_EN is not set 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 18297b33449..6a3a7c7a9ff 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" @@ -57,8 +58,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[] = { @@ -288,7 +302,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__); @@ -321,21 +335,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) @@ -459,6 +484,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/gatt_server_service_table/sdkconfig.defaults b/examples/bluetooth/bluedroid/ble/gatt_server_service_table/sdkconfig.defaults index 1bf8906ea7d..925c754be65 100644 --- a/examples/bluetooth/bluedroid/ble/gatt_server_service_table/sdkconfig.defaults +++ b/examples/bluetooth/bluedroid/ble/gatt_server_service_table/sdkconfig.defaults @@ -5,4 +5,11 @@ CONFIG_BT_ENABLED=y CONFIG_BT_BLE_50_FEATURES_SUPPORTED=n CONFIG_BT_BLE_42_FEATURES_SUPPORTED=y # CONFIG_BT_LE_50_FEATURE_SUPPORT is not used on ESP32, ESP32-C3 and ESP32-S3. -CONFIG_BT_LE_50_FEATURE_SUPPORT=n +# CONFIG_BT_LE_50_FEATURE_SUPPORT is not set +CONFIG_PARTITION_TABLE_SINGLE_APP_LARGE=y + +# Disable unused Bluedroid host features for GATT server only example +# CONFIG_BT_GATTC_ENABLE is not set +# CONFIG_BT_BLE_SMP_ENABLE is not set +# CONFIG_BT_BLE_42_SCAN_EN is not set +# CONFIG_BT_BLE_42_DTM_TEST_EN is not set 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 213523d05ce..ef6b27dd545 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/gattc_multi_connect/sdkconfig.defaults b/examples/bluetooth/bluedroid/ble/gattc_multi_connect/sdkconfig.defaults index 1bf8906ea7d..d3b87b3423b 100644 --- a/examples/bluetooth/bluedroid/ble/gattc_multi_connect/sdkconfig.defaults +++ b/examples/bluetooth/bluedroid/ble/gattc_multi_connect/sdkconfig.defaults @@ -5,4 +5,11 @@ CONFIG_BT_ENABLED=y CONFIG_BT_BLE_50_FEATURES_SUPPORTED=n CONFIG_BT_BLE_42_FEATURES_SUPPORTED=y # CONFIG_BT_LE_50_FEATURE_SUPPORT is not used on ESP32, ESP32-C3 and ESP32-S3. -CONFIG_BT_LE_50_FEATURE_SUPPORT=n +# CONFIG_BT_LE_50_FEATURE_SUPPORT is not set +CONFIG_PARTITION_TABLE_SINGLE_APP_LARGE=y + +# Disable unused Bluedroid host features for GATT client only example +# CONFIG_BT_GATTS_ENABLE is not set +# CONFIG_BT_BLE_SMP_ENABLE is not set +# CONFIG_BT_BLE_42_ADV_EN is not set +# CONFIG_BT_BLE_42_DTM_TEST_EN is not set diff --git a/examples/bluetooth/bluedroid/ble_50/ble50_security_client/sdkconfig.defaults b/examples/bluetooth/bluedroid/ble_50/ble50_security_client/sdkconfig.defaults index d1004c7abde..b2530c1ec90 100644 --- a/examples/bluetooth/bluedroid/ble_50/ble50_security_client/sdkconfig.defaults +++ b/examples/bluetooth/bluedroid/ble_50/ble50_security_client/sdkconfig.defaults @@ -2,3 +2,7 @@ # Espressif IoT Development Framework (ESP-IDF) Project Minimal Configuration # CONFIG_BT_ENABLED=y + +# Disable unused Bluedroid host features (GATT security client, extended scan only) +# CONFIG_BT_GATTS_ENABLE is not set +# CONFIG_BT_BLE_50_DTM_TEST_EN is not set 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_security_server/sdkconfig.defaults b/examples/bluetooth/bluedroid/ble_50/ble50_security_server/sdkconfig.defaults index d1004c7abde..e0161417835 100644 --- a/examples/bluetooth/bluedroid/ble_50/ble50_security_server/sdkconfig.defaults +++ b/examples/bluetooth/bluedroid/ble_50/ble50_security_server/sdkconfig.defaults @@ -2,3 +2,7 @@ # Espressif IoT Development Framework (ESP-IDF) Project Minimal Configuration # CONFIG_BT_ENABLED=y + +# Disable unused Bluedroid host features (GATT security server, extended adv only) +# CONFIG_BT_GATTC_ENABLE is not set +# CONFIG_BT_BLE_50_DTM_TEST_EN is not set 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 234735f6a95..f817f297347 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_client/sdkconfig.defaults b/examples/bluetooth/bluedroid/ble_50/ble50_throughput/throughput_client/sdkconfig.defaults index 5976aa8c259..8b023e864c9 100644 --- a/examples/bluetooth/bluedroid/ble_50/ble50_throughput/throughput_client/sdkconfig.defaults +++ b/examples/bluetooth/bluedroid/ble_50/ble50_throughput/throughput_client/sdkconfig.defaults @@ -8,3 +8,8 @@ CONFIG_BT_BLE_42_FEATURES_SUPPORTED=n # CONFIG_BT_LE_50_FEATURE_SUPPORT=n CONFIG_GATTS_NOTIFY_THROUGHPUT=y CONFIG_GATTC_WRITE_THROUGHPUT=n + +# Disable unused Bluedroid host features (GATT client only) +# CONFIG_BT_GATTS_ENABLE is not set +# CONFIG_BT_BLE_SMP_ENABLE is not set +# CONFIG_BT_BLE_50_DTM_TEST_EN is not set 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 9d993b88542..94f2d7a945b 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/ble50_throughput/throughput_server/sdkconfig.defaults b/examples/bluetooth/bluedroid/ble_50/ble50_throughput/throughput_server/sdkconfig.defaults index 83bac6e78ac..521d85b1514 100644 --- a/examples/bluetooth/bluedroid/ble_50/ble50_throughput/throughput_server/sdkconfig.defaults +++ b/examples/bluetooth/bluedroid/ble_50/ble50_throughput/throughput_server/sdkconfig.defaults @@ -9,3 +9,8 @@ CONFIG_BT_BLE_50_FEATURES_SUPPORTED=y CONFIG_BT_BLE_42_FEATURES_SUPPORTED=n # CONFIG_BT_LE_50_FEATURE_SUPPORT is not used on ESP32, ESP32-C3 and ESP32-S3. # CONFIG_BT_LE_50_FEATURE_SUPPORT=n + +# Disable unused Bluedroid host features (GATT server only) +# CONFIG_BT_GATTC_ENABLE is not set +# CONFIG_BT_BLE_SMP_ENABLE is not set +# CONFIG_BT_BLE_50_DTM_TEST_EN is not set 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_conn_subrating_central/sdkconfig.defaults b/examples/bluetooth/bluedroid/ble_50/ble_conn_subrating_central/sdkconfig.defaults index 4fcdd7ebcc1..e32cd09388c 100644 --- a/examples/bluetooth/bluedroid/ble_50/ble_conn_subrating_central/sdkconfig.defaults +++ b/examples/bluetooth/bluedroid/ble_50/ble_conn_subrating_central/sdkconfig.defaults @@ -25,3 +25,4 @@ CONFIG_BT_BLE_50_EXTEND_ADV_EN=n CONFIG_BT_BLE_50_DTM_TEST_EN=n CONFIG_BT_BLE_50_EXTEND_SYNC_EN=n +CONFIG_PARTITION_TABLE_SINGLE_APP_LARGE=y diff --git a/examples/bluetooth/bluedroid/ble_50/ble_conn_subrating_peripheral/sdkconfig.defaults b/examples/bluetooth/bluedroid/ble_50/ble_conn_subrating_peripheral/sdkconfig.defaults index c806ef0519b..510c23942b4 100644 --- a/examples/bluetooth/bluedroid/ble_50/ble_conn_subrating_peripheral/sdkconfig.defaults +++ b/examples/bluetooth/bluedroid/ble_50/ble_conn_subrating_peripheral/sdkconfig.defaults @@ -25,3 +25,4 @@ CONFIG_BT_BLE_50_PERIODIC_ADV_EN=n CONFIG_BT_BLE_50_EXTEND_SCAN_EN=n CONFIG_BT_BLE_50_DTM_TEST_EN=n +CONFIG_PARTITION_TABLE_SINGLE_APP_LARGE=y 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_central_with_cte/sdkconfig.defaults b/examples/bluetooth/bluedroid/ble_50/ble_connection_central_with_cte/sdkconfig.defaults index 6272796eefd..ecb634a0783 100644 --- a/examples/bluetooth/bluedroid/ble_50/ble_connection_central_with_cte/sdkconfig.defaults +++ b/examples/bluetooth/bluedroid/ble_50/ble_connection_central_with_cte/sdkconfig.defaults @@ -20,3 +20,5 @@ CONFIG_BT_BLE_50_EXTEND_ADV_EN=n CONFIG_BT_BLE_50_EXTEND_SYNC_EN=n CONFIG_BT_BLE_50_DTM_TEST_EN=n + +CONFIG_PARTITION_TABLE_SINGLE_APP_LARGE=y 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_connection_peripheral_with_cte/sdkconfig.defaults b/examples/bluetooth/bluedroid/ble_50/ble_connection_peripheral_with_cte/sdkconfig.defaults index 3d045fb9883..f6ad0662f23 100644 --- a/examples/bluetooth/bluedroid/ble_50/ble_connection_peripheral_with_cte/sdkconfig.defaults +++ b/examples/bluetooth/bluedroid/ble_50/ble_connection_peripheral_with_cte/sdkconfig.defaults @@ -23,3 +23,5 @@ CONFIG_BT_BLE_50_EXTEND_SCAN_EN=n CONFIG_BT_BLE_50_EXTEND_SYNC_EN=n CONFIG_BT_BLE_50_DTM_TEST_EN=n + +CONFIG_PARTITION_TABLE_SINGLE_APP_LARGE=y 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_advertiser/sdkconfig.defaults b/examples/bluetooth/bluedroid/ble_50/ble_pawr_advertiser/sdkconfig.defaults index fa70c13f46a..416d51fc277 100644 --- a/examples/bluetooth/bluedroid/ble_50/ble_pawr_advertiser/sdkconfig.defaults +++ b/examples/bluetooth/bluedroid/ble_50/ble_pawr_advertiser/sdkconfig.defaults @@ -34,3 +34,5 @@ CONFIG_BT_GATTS_ENABLE=n CONFIG_BT_BLE_50_EXTEND_SCAN_EN=n CONFIG_BT_BLE_50_DTM_TEST_EN=n + +CONFIG_PARTITION_TABLE_SINGLE_APP_LARGE=y diff --git a/examples/bluetooth/bluedroid/ble_50/ble_pawr_advertiser_conn/sdkconfig.defaults b/examples/bluetooth/bluedroid/ble_50/ble_pawr_advertiser_conn/sdkconfig.defaults index b0b55071e50..b740991bb8e 100644 --- a/examples/bluetooth/bluedroid/ble_50/ble_pawr_advertiser_conn/sdkconfig.defaults +++ b/examples/bluetooth/bluedroid/ble_50/ble_pawr_advertiser_conn/sdkconfig.defaults @@ -32,3 +32,5 @@ CONFIG_BT_BLE_50_DTM_TEST_EN=n CONFIG_BT_BLE_SMP_ENABLE=n CONFIG_BT_GATTS_ENABLE=n + +CONFIG_PARTITION_TABLE_SINGLE_APP_LARGE=y 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_pawr_synchronizer/sdkconfig.defaults b/examples/bluetooth/bluedroid/ble_50/ble_pawr_synchronizer/sdkconfig.defaults index 7b444caaf14..810004f14ba 100644 --- a/examples/bluetooth/bluedroid/ble_50/ble_pawr_synchronizer/sdkconfig.defaults +++ b/examples/bluetooth/bluedroid/ble_50/ble_pawr_synchronizer/sdkconfig.defaults @@ -12,3 +12,6 @@ CONFIG_BT_GATTC_ENABLE=n CONFIG_BT_BLE_SMP_ENABLE=n CONFIG_BT_BLE_50_EXTEND_ADV_EN=n + +CONFIG_PARTITION_TABLE_SINGLE_APP_LARGE=y +CONFIG_BT_BLE_50_DTM_TEST_EN=n 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_adv_with_cte/sdkconfig.defaults b/examples/bluetooth/bluedroid/ble_50/ble_periodic_adv_with_cte/sdkconfig.defaults index fa787c9290c..714f390bbfa 100644 --- a/examples/bluetooth/bluedroid/ble_50/ble_periodic_adv_with_cte/sdkconfig.defaults +++ b/examples/bluetooth/bluedroid/ble_50/ble_periodic_adv_with_cte/sdkconfig.defaults @@ -24,3 +24,5 @@ CONFIG_BT_BLE_50_PERIODIC_ADV_EN=y CONFIG_BT_BLE_50_EXTEND_SCAN_EN=n CONFIG_BT_BLE_50_DTM_TEST_EN=n + +CONFIG_PARTITION_TABLE_SINGLE_APP_LARGE=y 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_periodic_sync_with_cte/sdkconfig.defaults b/examples/bluetooth/bluedroid/ble_50/ble_periodic_sync_with_cte/sdkconfig.defaults index 8e462e05234..06d12a5a754 100644 --- a/examples/bluetooth/bluedroid/ble_50/ble_periodic_sync_with_cte/sdkconfig.defaults +++ b/examples/bluetooth/bluedroid/ble_50/ble_periodic_sync_with_cte/sdkconfig.defaults @@ -22,3 +22,5 @@ CONFIG_BT_BLE_50_EXTEND_ADV_EN=n CONFIG_BT_BLE_50_EXTEND_SCAN_EN=y CONFIG_BT_BLE_50_DTM_TEST_EN=n + +CONFIG_PARTITION_TABLE_SINGLE_APP_LARGE=y 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/ble_power_control_central/sdkconfig.defaults b/examples/bluetooth/bluedroid/ble_50/ble_power_control_central/sdkconfig.defaults index 6370d701cdb..8e0aee8bc40 100644 --- a/examples/bluetooth/bluedroid/ble_50/ble_power_control_central/sdkconfig.defaults +++ b/examples/bluetooth/bluedroid/ble_50/ble_power_control_central/sdkconfig.defaults @@ -29,3 +29,5 @@ CONFIG_BT_BLE_50_EXTEND_ADV_EN=n CONFIG_BT_BLE_50_EXTEND_SYNC_EN=n CONFIG_BT_BLE_50_DTM_TEST_EN=n + +CONFIG_PARTITION_TABLE_SINGLE_APP_LARGE=y diff --git a/examples/bluetooth/bluedroid/ble_50/ble_power_control_peripheral/sdkconfig.defaults b/examples/bluetooth/bluedroid/ble_50/ble_power_control_peripheral/sdkconfig.defaults index a55da6e3629..069b8682e7b 100644 --- a/examples/bluetooth/bluedroid/ble_50/ble_power_control_peripheral/sdkconfig.defaults +++ b/examples/bluetooth/bluedroid/ble_50/ble_power_control_peripheral/sdkconfig.defaults @@ -30,3 +30,5 @@ CONFIG_BT_BLE_50_EXTEND_SCAN_EN=y CONFIG_BT_BLE_50_EXTEND_SYNC_EN=n CONFIG_BT_BLE_50_DTM_TEST_EN=n + +CONFIG_PARTITION_TABLE_SINGLE_APP_LARGE=y diff --git a/examples/bluetooth/bluedroid/ble_50/multi-adv/sdkconfig.defaults b/examples/bluetooth/bluedroid/ble_50/multi-adv/sdkconfig.defaults index 6777f92fd54..7553f5a56e6 100644 --- a/examples/bluetooth/bluedroid/ble_50/multi-adv/sdkconfig.defaults +++ b/examples/bluetooth/bluedroid/ble_50/multi-adv/sdkconfig.defaults @@ -2,4 +2,20 @@ # Espressif IoT Development Framework (ESP-IDF) Project Minimal Configuration # CONFIG_BT_ENABLED=y +CONFIG_BT_BLUEDROID_ENABLED=y +CONFIG_BT_BLE_ENABLED=y +CONFIG_BT_BLE_50_FEATURES_SUPPORTED=y +CONFIG_BT_BLE_42_FEATURES_SUPPORTED=n + +# Multiple extended advertisers only CONFIG_BT_LE_MAX_EXT_ADV_INSTANCES=4 +CONFIG_PARTITION_TABLE_SINGLE_APP_LARGE=y +CONFIG_BT_BLE_50_EXTEND_ADV_EN=y +CONFIG_BT_BLE_50_EXTEND_SCAN_EN=n +CONFIG_BT_BLE_50_EXTEND_SYNC_EN=n +CONFIG_BT_BLE_50_PERIODIC_ADV_EN=n + +# CONFIG_BT_GATTS_ENABLE is not set +# CONFIG_BT_GATTC_ENABLE is not set +# CONFIG_BT_BLE_SMP_ENABLE is not set +# CONFIG_BT_BLE_50_DTM_TEST_EN is not set 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_adv/sdkconfig.defaults b/examples/bluetooth/bluedroid/ble_50/periodic_adv/sdkconfig.defaults index d1004c7abde..c2c8c906cd1 100644 --- a/examples/bluetooth/bluedroid/ble_50/periodic_adv/sdkconfig.defaults +++ b/examples/bluetooth/bluedroid/ble_50/periodic_adv/sdkconfig.defaults @@ -2,3 +2,19 @@ # Espressif IoT Development Framework (ESP-IDF) Project Minimal Configuration # CONFIG_BT_ENABLED=y +CONFIG_PARTITION_TABLE_SINGLE_APP_LARGE=y +CONFIG_BT_BLUEDROID_ENABLED=y +CONFIG_BT_BLE_ENABLED=y +CONFIG_BT_BLE_50_FEATURES_SUPPORTED=y +CONFIG_BT_BLE_42_FEATURES_SUPPORTED=n + +# Periodic extended advertiser only (no scan / sync / GATT) +CONFIG_BT_BLE_50_EXTEND_ADV_EN=y +CONFIG_BT_BLE_50_PERIODIC_ADV_EN=y +CONFIG_BT_BLE_50_EXTEND_SCAN_EN=n +CONFIG_BT_BLE_50_EXTEND_SYNC_EN=n + +# CONFIG_BT_GATTS_ENABLE is not set +# CONFIG_BT_GATTC_ENABLE is not set +# CONFIG_BT_BLE_SMP_ENABLE is not set +# CONFIG_BT_BLE_50_DTM_TEST_EN is not set 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/ble_50/periodic_sync/sdkconfig.defaults b/examples/bluetooth/bluedroid/ble_50/periodic_sync/sdkconfig.defaults index d1004c7abde..c54620a9513 100644 --- a/examples/bluetooth/bluedroid/ble_50/periodic_sync/sdkconfig.defaults +++ b/examples/bluetooth/bluedroid/ble_50/periodic_sync/sdkconfig.defaults @@ -2,3 +2,19 @@ # Espressif IoT Development Framework (ESP-IDF) Project Minimal Configuration # CONFIG_BT_ENABLED=y +CONFIG_PARTITION_TABLE_SINGLE_APP_LARGE=y +CONFIG_BT_BLUEDROID_ENABLED=y +CONFIG_BT_BLE_ENABLED=y +CONFIG_BT_BLE_50_FEATURES_SUPPORTED=y +CONFIG_BT_BLE_42_FEATURES_SUPPORTED=n + +# Periodic advertising sync role: extended scan + sync only (no extended adv / no periodic adv in host) +CONFIG_BT_BLE_50_EXTEND_SCAN_EN=y +CONFIG_BT_BLE_50_EXTEND_SYNC_EN=y +# CONFIG_BT_BLE_50_EXTEND_ADV_EN is not set +# CONFIG_BT_BLE_50_PERIODIC_ADV_EN is not set + +# CONFIG_BT_GATTS_ENABLE is not set +# CONFIG_BT_GATTC_ENABLE is not set +# CONFIG_BT_BLE_SMP_ENABLE is not set +# CONFIG_BT_BLE_50_DTM_TEST_EN is not set 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 e5988c921ad..75cddcbcc29 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 @@ -265,11 +285,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) @@ -282,68 +360,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: @@ -395,13 +425,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: @@ -432,63 +460,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: @@ -552,6 +532,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 6d7c9b279e0..3c0bbba56ff 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 @@ -105,6 +105,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; @@ -293,11 +302,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) { @@ -324,6 +339,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; @@ -563,6 +579,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; @@ -577,9 +594,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) { @@ -591,13 +610,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"); @@ -605,15 +630,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); @@ -624,15 +663,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) @@ -793,6 +834,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); @@ -800,7 +842,7 @@ 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) { + 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; @@ -941,11 +983,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) { + 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;