mirror of
https://github.com/espressif/esp-idf.git
synced 2026-10-02 11:10:54 +03:00
feat(ble_audio): Support BLE ISO & BLE Audio deinitialization
This commit is contained in:
@@ -6,8 +6,14 @@
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <zephyr/logging/log.h>
|
||||
|
||||
#include "esp_ble_iso_common_api.h"
|
||||
|
||||
LOG_MODULE_REGISTER(ISO_API, CONFIG_BT_ISO_LOG_LEVEL);
|
||||
|
||||
esp_err_t esp_ble_iso_data_parse(const uint8_t ltv[], size_t size,
|
||||
bool (*func)(uint8_t type,
|
||||
const uint8_t *data,
|
||||
@@ -128,6 +134,7 @@ esp_err_t esp_ble_iso_chan_connect(esp_ble_iso_connect_param_t *param,
|
||||
|
||||
conn = bt_le_acl_conn_find(conn_handle);
|
||||
if (conn == NULL) {
|
||||
LOG_WRN("AclUnknown[%u]", conn_handle);
|
||||
ret = ESP_ERR_NOT_FOUND;
|
||||
goto unlock;
|
||||
}
|
||||
@@ -266,6 +273,7 @@ esp_err_t esp_ble_iso_big_create(uint8_t adv_handle,
|
||||
|
||||
adv = bt_le_ext_adv_find(adv_handle);
|
||||
if (adv == NULL) {
|
||||
LOG_WRN("ExtAdvUnknown[%u]", adv_handle);
|
||||
ret = ESP_ERR_NOT_FOUND;
|
||||
goto unlock;
|
||||
}
|
||||
@@ -294,6 +302,7 @@ esp_err_t esp_ble_iso_big_sync(uint16_t sync_handle,
|
||||
|
||||
per_adv_sync = bt_le_per_adv_sync_find(sync_handle);
|
||||
if (per_adv_sync == NULL) {
|
||||
LOG_WRN("PaSyncUnknown[%u]", sync_handle);
|
||||
ret = ESP_ERR_NOT_FOUND;
|
||||
goto unlock;
|
||||
}
|
||||
@@ -434,6 +443,44 @@ unregister_gap:
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
esp_err_t esp_ble_iso_common_deinit(const esp_ble_iso_deinit_info_t *info)
|
||||
{
|
||||
bool reset_ext_adv = true;
|
||||
bool reset_pa_sync = true;
|
||||
int err;
|
||||
|
||||
if (info) {
|
||||
if (!info->reset_acl_conn) {
|
||||
LOG_WRN("ConnKeepUnsupported");
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
reset_ext_adv = info->reset_ext_adv;
|
||||
reset_pa_sync = info->reset_pa_sync;
|
||||
}
|
||||
|
||||
if (bt_le_host_check_idle()) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
err = bt_le_host_deinit();
|
||||
if (err) {
|
||||
return ESP_ERR_TIMEOUT;
|
||||
}
|
||||
|
||||
if (reset_ext_adv) {
|
||||
bt_le_ext_adv_state_reset();
|
||||
}
|
||||
|
||||
if (reset_pa_sync) {
|
||||
bt_le_per_adv_sync_state_reset();
|
||||
}
|
||||
|
||||
bt_le_gap_app_cb_unregister();
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
#if CONFIG_BT_BLUEDROID_ENABLED
|
||||
uint8_t esp_ble_iso_bluedroid_get_gattc_if(void)
|
||||
{
|
||||
|
||||
@@ -367,6 +367,10 @@ esp_err_t esp_ble_iso_cig_terminate(esp_ble_iso_cig_t *cig);
|
||||
* @param conn_handle Connection handle.
|
||||
* @param count Number of channels to connect.
|
||||
*
|
||||
* @note ISO only knows links whose connection event it saw, so one
|
||||
* established before esp_ble_iso_common_init() returns
|
||||
* ESP_ERR_NOT_FOUND until it is handed back.
|
||||
*
|
||||
* @return ESP_OK on success, or an error code on failure.
|
||||
*/
|
||||
esp_err_t esp_ble_iso_chan_connect(esp_ble_iso_connect_param_t *param,
|
||||
@@ -578,13 +582,38 @@ typedef struct {
|
||||
/**
|
||||
* @brief Post an application-layer GAP event for ISO internal usage.
|
||||
*
|
||||
* @note This function is only needed while using NimBLE Host.
|
||||
* @note NimBLE-only: NimBLE has no global GAP callback, so ISO sees only what
|
||||
* the application forwards here. An event left out is dropped silently.
|
||||
* Bluedroid's ISO layer hooks the stack directly.
|
||||
*
|
||||
* @param type Event type.
|
||||
* @param param Event parameters.
|
||||
* @note Forward every event below that the application's flows can produce.
|
||||
* Forwarding one ISO does not use is harmless; omitting one leaves the
|
||||
* matching ISO state uncreated and fails later and elsewhere:
|
||||
* - BLE_GAP_EVENT_EXT_DISC extended advertising report
|
||||
* - BLE_GAP_EVENT_CONNECT ACL established or failed
|
||||
* - BLE_GAP_EVENT_DISCONNECT ACL closed
|
||||
* - BLE_GAP_EVENT_ENC_CHANGE encryption changed
|
||||
* - BLE_GAP_EVENT_PERIODIC_SYNC PA sync established
|
||||
* - BLE_GAP_EVENT_PERIODIC_SYNC_LOST PA sync lost
|
||||
* - BLE_GAP_EVENT_PERIODIC_REPORT periodic advertising report
|
||||
* - BLE_GAP_EVENT_PERIODIC_TRANSFER PA sync received over PAST
|
||||
* - BLE_GAP_EVENT_PERIODIC_TRANSFER_V2 as above, PAwR
|
||||
*
|
||||
* BIGInfo needs no forwarding: ISO takes it from the controller itself
|
||||
* and delivers it as ESP_BLE_ISO_GAP_EVENT_BIGINFO_RECV.
|
||||
*
|
||||
* @param type Event type, i.e. ble_gap_event::type.
|
||||
* @param param Event parameters, i.e. the struct ble_gap_event pointer.
|
||||
*/
|
||||
void esp_ble_iso_gap_app_post_event(uint16_t type, void *param);
|
||||
|
||||
/** Which of ISO's records of application-owned objects deinit clears. */
|
||||
typedef struct {
|
||||
bool reset_ext_adv; /*!< Clear the advertising sets added for BIG */
|
||||
bool reset_pa_sync; /*!< Clear the periodic advertising syncs */
|
||||
bool reset_acl_conn; /*!< Clear the ACL links. Must be true - see below */
|
||||
} esp_ble_iso_deinit_info_t;
|
||||
|
||||
/**
|
||||
* @brief Initialize ISO common functionality.
|
||||
*
|
||||
@@ -594,6 +623,34 @@ void esp_ble_iso_gap_app_post_event(uint16_t type, void *param);
|
||||
*/
|
||||
esp_err_t esp_ble_iso_common_init(esp_ble_iso_init_info_t *info);
|
||||
|
||||
/**
|
||||
* @brief Deinitialize ISO common functionality.
|
||||
*
|
||||
* The application must first disconnect all CIS/BIS and terminate every CIG/BIG.
|
||||
* If any is still up this returns ESP_ERR_INVALID_STATE and releases nothing,
|
||||
* logging each offending item at ERROR level.
|
||||
*
|
||||
* ACL links, advertising sets and periodic advertising syncs belong to the
|
||||
* application: they neither block deinit nor get torn down, only ISO's record of
|
||||
* them goes. @p info chooses which records to drop - a dropped one is logged at
|
||||
* WARNING level and must be handed back after the next init. NULL drops all three.
|
||||
*
|
||||
* reset_acl_conn must be true for now: on Bluedroid the GATT teardown drops ISO's
|
||||
* reference, so a link ISO alone held is disconnected while a shared one stays up,
|
||||
* and ISO cannot yet tell the two apart.
|
||||
*
|
||||
* @param info Which records to clear, or NULL to clear all.
|
||||
*
|
||||
* @note Must not be called from an ISO callback (they run on the ISO task,
|
||||
* which this stops).
|
||||
*
|
||||
* @return ESP_OK on success,
|
||||
* ESP_ERR_INVALID_STATE if something ISO owns is still active,
|
||||
* ESP_ERR_TIMEOUT if the ISO task could not be stopped.
|
||||
* Nothing is released unless ESP_OK is returned.
|
||||
*/
|
||||
esp_err_t esp_ble_iso_common_deinit(const esp_ble_iso_deinit_info_t *info);
|
||||
|
||||
#if CONFIG_BT_BLUEDROID_ENABLED
|
||||
/**
|
||||
* @brief Get the engine's internal GATTC interface handle (Bluedroid only).
|
||||
|
||||
@@ -212,7 +212,7 @@ void bt_le_bluedroid_gap_post_event(uint16_t event, void *param)
|
||||
|
||||
err = bt_le_iso_task_post(ISO_QUEUE_ITEM_TYPE_GAP_EVENT, qev, sizeof(*qev));
|
||||
if (err) {
|
||||
LOG_ERR("[B]GapPostEvtFail[%d][%u]", err, qev->type);
|
||||
ISO_POST_FAIL_LOG(err, "[B]GapPostEvtFail[%d][%u]", err, qev->type);
|
||||
free(qev);
|
||||
}
|
||||
}
|
||||
@@ -409,12 +409,8 @@ static void bt_le_bluedroid_gap_post_event_bta(tBTA_DM_BLE_5_GAP_EVENT event,
|
||||
|
||||
err = bt_le_iso_task_post(q_type, qev, sizeof(*qev));
|
||||
if (err) {
|
||||
/* Floodable reports drop by design when the queue is full; only a
|
||||
* failure on the reliable (normal-queue) path is a real error. */
|
||||
if (q_type == ISO_QUEUE_ITEM_TYPE_GAP_EVENT) {
|
||||
LOG_ERR("[B]GapPostEvtBtaFail[%d][%u]", err, qev->type);
|
||||
} else {
|
||||
LOG_DBG("[B]GapRptDrop[%u]", qev->type);
|
||||
ISO_POST_FAIL_LOG(err, "[B]GapPostEvtBtaFail[%d][%u]", err, qev->type);
|
||||
}
|
||||
goto free;
|
||||
}
|
||||
@@ -422,26 +418,7 @@ static void bt_le_bluedroid_gap_post_event_bta(tBTA_DM_BLE_5_GAP_EVENT event,
|
||||
return;
|
||||
|
||||
free:
|
||||
/* Mirror nimble/gap.c cleanup: free nested data buffers carried by
|
||||
* specific event types before freeing the qev container itself. */
|
||||
switch (qev->type) {
|
||||
case BT_LE_GAP_APP_PARAM_EXT_SCAN_RECV:
|
||||
if (qev->ext_scan_recv.data) {
|
||||
free(qev->ext_scan_recv.data);
|
||||
qev->ext_scan_recv.data = NULL;
|
||||
}
|
||||
break;
|
||||
case BT_LE_GAP_APP_PARAM_PA_SYNC_RECV:
|
||||
if (qev->pa_sync_recv.data) {
|
||||
free(qev->pa_sync_recv.data);
|
||||
qev->pa_sync_recv.data = NULL;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
free(qev);
|
||||
bt_le_gap_event_free(qev);
|
||||
}
|
||||
|
||||
int bt_le_bluedroid_scan_start(const struct bt_le_scan_param *param)
|
||||
|
||||
@@ -476,7 +476,7 @@ static void gattc_connect_event_handler(tBTA_GATTC_CONNECT *connect)
|
||||
|
||||
err = bt_le_iso_task_post(ISO_QUEUE_ITEM_TYPE_GATT_EVENT, qev, sizeof(*qev));
|
||||
if (err) {
|
||||
LOG_ERR("[B]GattcConnPostFail[%d]", err);
|
||||
ISO_POST_FAIL_LOG(err, "[B]GattcConnPostFail[%d]", err);
|
||||
free(qev);
|
||||
}
|
||||
}
|
||||
@@ -496,7 +496,7 @@ static void gattc_disconnect_event_handler(tBTA_GATTC_DISCONNECT *disconnect)
|
||||
|
||||
err = bt_le_iso_task_post(ISO_QUEUE_ITEM_TYPE_GATT_EVENT, qev, sizeof(*qev));
|
||||
if (err) {
|
||||
LOG_ERR("[B]GattcDiscPostFail[%d]", err);
|
||||
ISO_POST_FAIL_LOG(err, "[B]GattcDiscPostFail[%d]", err);
|
||||
free(qev);
|
||||
}
|
||||
}
|
||||
@@ -516,7 +516,7 @@ static void gattc_open_event_handler(tBTA_GATTC_OPEN *open)
|
||||
|
||||
err = bt_le_iso_task_post(ISO_QUEUE_ITEM_TYPE_GATT_EVENT, qev, sizeof(*qev));
|
||||
if (err) {
|
||||
LOG_ERR("[B]GattcOpenPostFail[%d]", err);
|
||||
ISO_POST_FAIL_LOG(err, "[B]GattcOpenPostFail[%d]", err);
|
||||
free(qev);
|
||||
}
|
||||
}
|
||||
@@ -537,7 +537,7 @@ static void gattc_mtu_event_handler(tBTA_GATTC_CFG_MTU *cfg_mtu)
|
||||
|
||||
err = bt_le_iso_task_post(ISO_QUEUE_ITEM_TYPE_GATT_EVENT, qev, sizeof(*qev));
|
||||
if (err) {
|
||||
LOG_ERR("[B]GattcMtuPostFail[%d]", err);
|
||||
ISO_POST_FAIL_LOG(err, "[B]GattcMtuPostFail[%d]", err);
|
||||
free(qev);
|
||||
}
|
||||
}
|
||||
@@ -557,7 +557,7 @@ static void gattc_disc_cmpl_event_handler(tBTA_GATTC_DIS_CMPL *disc_cmpl)
|
||||
|
||||
err = bt_le_iso_task_post(ISO_QUEUE_ITEM_TYPE_GATT_EVENT, qev, sizeof(*qev));
|
||||
if (err) {
|
||||
LOG_ERR("[B]GattcDiscCmplPostFail[%d]", err);
|
||||
ISO_POST_FAIL_LOG(err, "[B]GattcDiscCmplPostFail[%d]", err);
|
||||
free(qev);
|
||||
}
|
||||
}
|
||||
@@ -589,11 +589,8 @@ static void gattc_read_chrc_event_handler(tBTA_GATTC_READ *read)
|
||||
|
||||
err = bt_le_iso_task_post(ISO_QUEUE_ITEM_TYPE_GATT_EVENT, qev, sizeof(*qev));
|
||||
if (err) {
|
||||
LOG_ERR("[B]GattcReadChrcPostFail[%d]", err);
|
||||
if (qev->gattc_read_chrc.value) {
|
||||
free(qev->gattc_read_chrc.value);
|
||||
}
|
||||
free(qev);
|
||||
ISO_POST_FAIL_LOG(err, "[B]GattcReadChrcPostFail[%d]", err);
|
||||
bt_le_gatt_event_free(qev);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -614,7 +611,7 @@ static void gattc_write_chrc_event_handler(tBTA_GATTC_WRITE *write)
|
||||
|
||||
err = bt_le_iso_task_post(ISO_QUEUE_ITEM_TYPE_GATT_EVENT, qev, sizeof(*qev));
|
||||
if (err) {
|
||||
LOG_ERR("[B]GattcWriteChrcPostFail[%d]", err);
|
||||
ISO_POST_FAIL_LOG(err, "[B]GattcWriteChrcPostFail[%d]", err);
|
||||
free(qev);
|
||||
}
|
||||
}
|
||||
@@ -641,7 +638,7 @@ static void gatts_notify_tx_event_handler(tBTA_GATTS_REQ *req)
|
||||
|
||||
err = bt_le_iso_task_post(ISO_QUEUE_ITEM_TYPE_GATT_EVENT, qev, sizeof(*qev));
|
||||
if (err) {
|
||||
LOG_ERR("[B]GattsNotifyTxPostFail[%d]", err);
|
||||
ISO_POST_FAIL_LOG(err, "[B]GattsNotifyTxPostFail[%d]", err);
|
||||
free(qev);
|
||||
}
|
||||
}
|
||||
@@ -671,7 +668,7 @@ static void gattc_notify_rx_event_handler(tBTA_GATTC_NOTIFY *notify)
|
||||
|
||||
err = bt_le_iso_task_post(ISO_QUEUE_ITEM_TYPE_GATT_EVENT, qev, sizeof(*qev));
|
||||
if (err) {
|
||||
LOG_ERR("[B]GattcNotifyRxPostFail[%d]", err);
|
||||
ISO_POST_FAIL_LOG(err, "[B]GattcNotifyRxPostFail[%d]", err);
|
||||
if (qev->gattc_notify_rx.value) {
|
||||
free(qev->gattc_notify_rx.value);
|
||||
}
|
||||
@@ -697,7 +694,7 @@ static void gatts_connect_event_handler(tBTA_GATTS_CONN *connect)
|
||||
|
||||
err = bt_le_iso_task_post(ISO_QUEUE_ITEM_TYPE_GATT_EVENT, qev, sizeof(*qev));
|
||||
if (err) {
|
||||
LOG_ERR("[B]GattsConnPostFail[%d]", err);
|
||||
ISO_POST_FAIL_LOG(err, "[B]GattsConnPostFail[%d]", err);
|
||||
free(qev);
|
||||
}
|
||||
}
|
||||
@@ -717,7 +714,7 @@ static void gatts_disconnect_event_handler(tBTA_GATTS_CONN *disconnect)
|
||||
|
||||
err = bt_le_iso_task_post(ISO_QUEUE_ITEM_TYPE_GATT_EVENT, qev, sizeof(*qev));
|
||||
if (err) {
|
||||
LOG_ERR("[B]GattsDiscPostFail[%d]", err);
|
||||
ISO_POST_FAIL_LOG(err, "[B]GattsDiscPostFail[%d]", err);
|
||||
free(qev);
|
||||
}
|
||||
}
|
||||
@@ -738,7 +735,7 @@ static void gatts_mtu_event_handler(tBTA_GATTS_REQ *req)
|
||||
|
||||
err = bt_le_iso_task_post(ISO_QUEUE_ITEM_TYPE_GATT_EVENT, qev, sizeof(*qev));
|
||||
if (err) {
|
||||
LOG_ERR("[B]GattsMtuPostFail[%d]", err);
|
||||
ISO_POST_FAIL_LOG(err, "[B]GattsMtuPostFail[%d]", err);
|
||||
free(qev);
|
||||
}
|
||||
}
|
||||
@@ -763,7 +760,7 @@ static void gatts_read_req_handler(tBTA_GATTS_REQ *req)
|
||||
|
||||
err = bt_le_iso_task_post(ISO_QUEUE_ITEM_TYPE_GATT_EVENT, qev, sizeof(*qev));
|
||||
if (err) {
|
||||
LOG_ERR("[B]GattsReadPostFail[%d]", err);
|
||||
ISO_POST_FAIL_LOG(err, "[B]GattsReadPostFail[%d]", err);
|
||||
free(qev);
|
||||
}
|
||||
}
|
||||
@@ -798,11 +795,8 @@ static void gatts_write_req_handler(tBTA_GATTS_REQ *req)
|
||||
|
||||
err = bt_le_iso_task_post(ISO_QUEUE_ITEM_TYPE_GATT_EVENT, qev, sizeof(*qev));
|
||||
if (err) {
|
||||
LOG_ERR("[B]GattsWritePostFail[%d]", err);
|
||||
if (qev->gatts_write.value) {
|
||||
free(qev->gatts_write.value);
|
||||
}
|
||||
free(qev);
|
||||
ISO_POST_FAIL_LOG(err, "[B]GattsWritePostFail[%d]", err);
|
||||
bt_le_gatt_event_free(qev);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -822,13 +816,20 @@ static void gatts_exec_write_req_handler(tBTA_GATTS_REQ *req)
|
||||
|
||||
err = bt_le_iso_task_post(ISO_QUEUE_ITEM_TYPE_GATT_EVENT, qev, sizeof(*qev));
|
||||
if (err) {
|
||||
LOG_ERR("[B]GattsExecPostFail[%d]", err);
|
||||
ISO_POST_FAIL_LOG(err, "[B]GattsExecPostFail[%d]", err);
|
||||
free(qev);
|
||||
}
|
||||
}
|
||||
|
||||
static void gatts_app_cb(tBTA_GATTS_EVT event, tBTA_GATTS *p_data)
|
||||
{
|
||||
/* BTA_GATTS_AppDeregister is async, so its events land after deinit returns.
|
||||
* Drop on the flag, not gatts_if - a re-register can hand back the same if. */
|
||||
if (gatt_shutting_down) {
|
||||
LOG_INF("[B]GattsDropEvt[%u]", event);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (event) {
|
||||
case BTA_GATTS_REG_EVT:
|
||||
LOG_DBG("[B]GattsRegEvt[%u][%u]",
|
||||
@@ -838,10 +839,6 @@ static void gatts_app_cb(tBTA_GATTS_EVT event, tBTA_GATTS *p_data)
|
||||
p_data->reg_oper.uuid.uu.uuid128[0],
|
||||
p_data->reg_oper.uuid.uu.uuid128[1]);
|
||||
|
||||
if (gatt_shutting_down) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (p_data->reg_oper.status == BTA_GATT_OK &&
|
||||
memcmp(&p_data->reg_oper.uuid, &gatts_app_uuid, sizeof(tBT_UUID)) == 0) {
|
||||
gatts_if = p_data->reg_oper.server_if;
|
||||
@@ -869,7 +866,7 @@ static void gatts_app_cb(tBTA_GATTS_EVT event, tBTA_GATTS *p_data)
|
||||
p_data->conn.conn_params.timeout);
|
||||
|
||||
if (p_data->conn.server_if != gatts_if) {
|
||||
LOG_ERR("[B]GattsConnUnknownIf[%u]", p_data->conn.server_if);
|
||||
LOG_ERR("[B]GattsConnUnknownIf[%u][%u]", p_data->conn.server_if, gatts_if);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -885,7 +882,7 @@ static void gatts_app_cb(tBTA_GATTS_EVT event, tBTA_GATTS *p_data)
|
||||
p_data->conn.remote_bda[4], p_data->conn.remote_bda[5]);
|
||||
|
||||
if (p_data->conn.server_if != gatts_if) {
|
||||
LOG_ERR("[B]GattsDisconnUnknownIf[%u]", p_data->conn.server_if);
|
||||
LOG_ERR("[B]GattsDisconnUnknownIf[%u][%u]", p_data->conn.server_if, gatts_if);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -913,8 +910,8 @@ static void gatts_app_cb(tBTA_GATTS_EVT event, tBTA_GATTS *p_data)
|
||||
p_data->req_data.p_data ? p_data->req_data.p_data->mtu : 0);
|
||||
|
||||
if (BTC_GATT_GET_GATT_IF(p_data->req_data.conn_id) != gatts_if) {
|
||||
LOG_ERR("[B]GattsMtuUnknownIf[%u]",
|
||||
BTC_GATT_GET_GATT_IF(p_data->req_data.conn_id));
|
||||
LOG_ERR("[B]GattsMtuUnknownIf[%u][%u]",
|
||||
BTC_GATT_GET_GATT_IF(p_data->req_data.conn_id), gatts_if);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -955,7 +952,7 @@ static void gatts_app_cb(tBTA_GATTS_EVT event, tBTA_GATTS *p_data)
|
||||
p_data->create.uuid.uu.uuid128[1]);
|
||||
|
||||
if (p_data->create.server_if != gatts_if) {
|
||||
LOG_ERR("[B]GattsCreateUnknownIf[%u]", p_data->create.server_if);
|
||||
LOG_ERR("[B]GattsCreateUnknownIf[%u][%u]", p_data->create.server_if, gatts_if);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -972,7 +969,7 @@ static void gatts_app_cb(tBTA_GATTS_EVT event, tBTA_GATTS *p_data)
|
||||
p_data->add_result.attr_id, p_data->add_result.service_id);
|
||||
|
||||
if (p_data->add_result.server_if != gatts_if) {
|
||||
LOG_ERR("[B]GattsAddInclSvcUnknownIf[%u]", p_data->add_result.server_if);
|
||||
LOG_ERR("[B]GattsAddInclSvcUnknownIf[%u][%u]", p_data->add_result.server_if, gatts_if);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -990,7 +987,7 @@ static void gatts_app_cb(tBTA_GATTS_EVT event, tBTA_GATTS *p_data)
|
||||
p_data->add_result.char_uuid.len, p_data->add_result.char_uuid.uu.uuid16);
|
||||
|
||||
if (p_data->add_result.server_if != gatts_if) {
|
||||
LOG_ERR("[B]GattsAddCharUnknownIf[%u]", p_data->add_result.server_if);
|
||||
LOG_ERR("[B]GattsAddCharUnknownIf[%u][%u]", p_data->add_result.server_if, gatts_if);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1008,7 +1005,7 @@ static void gatts_app_cb(tBTA_GATTS_EVT event, tBTA_GATTS *p_data)
|
||||
p_data->add_result.char_uuid.len, p_data->add_result.char_uuid.uu.uuid16);
|
||||
|
||||
if (p_data->add_result.server_if != gatts_if) {
|
||||
LOG_ERR("[B]GattsAddDescrUnknownIf[%u]", p_data->add_result.server_if);
|
||||
LOG_ERR("[B]GattsAddDescrUnknownIf[%u][%u]", p_data->add_result.server_if, gatts_if);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1034,8 +1031,8 @@ static void gatts_app_cb(tBTA_GATTS_EVT event, tBTA_GATTS *p_data)
|
||||
p_data->req_data.p_data->read_req.need_rsp);
|
||||
|
||||
if (BTC_GATT_GET_GATT_IF(p_data->req_data.conn_id) != gatts_if) {
|
||||
LOG_ERR("[B]GattsReadUnknownIf[%u]",
|
||||
BTC_GATT_GET_GATT_IF(p_data->req_data.conn_id));
|
||||
LOG_ERR("[B]GattsReadUnknownIf[%u][%u]",
|
||||
BTC_GATT_GET_GATT_IF(p_data->req_data.conn_id), gatts_if);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1058,8 +1055,8 @@ static void gatts_app_cb(tBTA_GATTS_EVT event, tBTA_GATTS *p_data)
|
||||
}
|
||||
|
||||
if (BTC_GATT_GET_GATT_IF(p_data->req_data.conn_id) != gatts_if) {
|
||||
LOG_ERR("[B]GattsWriteUnknownIf[%u]",
|
||||
BTC_GATT_GET_GATT_IF(p_data->req_data.conn_id));
|
||||
LOG_ERR("[B]GattsWriteUnknownIf[%u][%u]",
|
||||
BTC_GATT_GET_GATT_IF(p_data->req_data.conn_id), gatts_if);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1078,8 +1075,8 @@ static void gatts_app_cb(tBTA_GATTS_EVT event, tBTA_GATTS *p_data)
|
||||
}
|
||||
|
||||
if (BTC_GATT_GET_GATT_IF(p_data->req_data.conn_id) != gatts_if) {
|
||||
LOG_ERR("[B]GattsExecUnknownIf[%u]",
|
||||
BTC_GATT_GET_GATT_IF(p_data->req_data.conn_id));
|
||||
LOG_ERR("[B]GattsExecUnknownIf[%u][%u]",
|
||||
BTC_GATT_GET_GATT_IF(p_data->req_data.conn_id), gatts_if);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1117,6 +1114,12 @@ static void gatts_app_cb(tBTA_GATTS_EVT event, tBTA_GATTS *p_data)
|
||||
|
||||
static void gattc_app_cb(tBTA_GATTC_EVT event, tBTA_GATTC *p_data)
|
||||
{
|
||||
/* See gatts_app_cb. */
|
||||
if (gatt_shutting_down) {
|
||||
LOG_INF("[B]GattcDropEvt[%u]", event);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (event) {
|
||||
case BTA_GATTC_REG_EVT:
|
||||
LOG_DBG("[B]GattcRegEvt[%u][%u]",
|
||||
@@ -1126,10 +1129,6 @@ static void gattc_app_cb(tBTA_GATTC_EVT event, tBTA_GATTC *p_data)
|
||||
p_data->reg_oper.app_uuid.uu.uuid128[0],
|
||||
p_data->reg_oper.app_uuid.uu.uuid128[1]);
|
||||
|
||||
if (gatt_shutting_down) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (p_data->reg_oper.status == BTA_GATT_OK &&
|
||||
memcmp(&p_data->reg_oper.app_uuid, &gattc_app_uuid, sizeof(tBT_UUID)) == 0) {
|
||||
gattc_if = p_data->reg_oper.client_if;
|
||||
@@ -1156,7 +1155,7 @@ static void gattc_app_cb(tBTA_GATTC_EVT event, tBTA_GATTC *p_data)
|
||||
p_data->connect.conn_params.latency, p_data->connect.conn_params.timeout);
|
||||
|
||||
if (p_data->connect.client_if != gattc_if) {
|
||||
LOG_ERR("[B]GattcConnUnknownIf[%u]", p_data->connect.client_if);
|
||||
LOG_ERR("[B]GattcConnUnknownIf[%u][%u]", p_data->connect.client_if, gattc_if);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1172,7 +1171,7 @@ static void gattc_app_cb(tBTA_GATTC_EVT event, tBTA_GATTC *p_data)
|
||||
p_data->disconnect.remote_bda[4], p_data->disconnect.remote_bda[5]);
|
||||
|
||||
if (p_data->disconnect.client_if != gattc_if) {
|
||||
LOG_ERR("[B]GattcDisconnUnknownIf[%u]", p_data->disconnect.client_if);
|
||||
LOG_ERR("[B]GattcDisconnUnknownIf[%u][%u]", p_data->disconnect.client_if, gattc_if);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1189,7 +1188,7 @@ static void gattc_app_cb(tBTA_GATTC_EVT event, tBTA_GATTC *p_data)
|
||||
p_data->open.remote_bda[4], p_data->open.remote_bda[5]);
|
||||
|
||||
if (p_data->open.client_if != gattc_if) {
|
||||
LOG_ERR("[B]GattcOpenUnknownIf[%u]", p_data->open.client_if);
|
||||
LOG_ERR("[B]GattcOpenUnknownIf[%u][%u]", p_data->open.client_if, gattc_if);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1213,8 +1212,8 @@ static void gattc_app_cb(tBTA_GATTC_EVT event, tBTA_GATTC *p_data)
|
||||
p_data->cfg_mtu.status, p_data->cfg_mtu.conn_id, p_data->cfg_mtu.mtu);
|
||||
|
||||
if (BTC_GATT_GET_GATT_IF(p_data->cfg_mtu.conn_id) != gattc_if) {
|
||||
LOG_ERR("[B]GattcMtuUnknownIf[%u]",
|
||||
BTC_GATT_GET_GATT_IF(p_data->cfg_mtu.conn_id));
|
||||
LOG_ERR("[B]GattcMtuUnknownIf[%u][%u]",
|
||||
BTC_GATT_GET_GATT_IF(p_data->cfg_mtu.conn_id), gattc_if);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1235,8 +1234,8 @@ static void gattc_app_cb(tBTA_GATTC_EVT event, tBTA_GATTC *p_data)
|
||||
p_data->dis_cmpl.status, p_data->dis_cmpl.conn_id);
|
||||
|
||||
if (BTC_GATT_GET_GATT_IF(p_data->dis_cmpl.conn_id) != gattc_if) {
|
||||
LOG_ERR("[B]GattcDiscSvcCmplUnknownIf[%u]",
|
||||
BTC_GATT_GET_GATT_IF(p_data->dis_cmpl.conn_id));
|
||||
LOG_ERR("[B]GattcDiscSvcCmplUnknownIf[%u][%u]",
|
||||
BTC_GATT_GET_GATT_IF(p_data->dis_cmpl.conn_id), gattc_if);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1254,8 +1253,8 @@ static void gattc_app_cb(tBTA_GATTC_EVT event, tBTA_GATTC *p_data)
|
||||
p_data->srvc_res.service_uuid.inst_id);
|
||||
|
||||
if (BTC_GATT_GET_GATT_IF(p_data->srvc_res.conn_id) != gattc_if) {
|
||||
LOG_ERR("[B]GattcSearchResUnknownIf[%u]",
|
||||
BTC_GATT_GET_GATT_IF(p_data->srvc_res.conn_id));
|
||||
LOG_ERR("[B]GattcSearchResUnknownIf[%u][%u]",
|
||||
BTC_GATT_GET_GATT_IF(p_data->srvc_res.conn_id), gattc_if);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
@@ -1267,8 +1266,8 @@ static void gattc_app_cb(tBTA_GATTC_EVT event, tBTA_GATTC *p_data)
|
||||
BTA_GATTC_SERVICE_INFO_FROM_REMOTE_DEVICE) ? "Remote" : "NVS");
|
||||
|
||||
if (BTC_GATT_GET_GATT_IF(p_data->search_cmpl.conn_id) != gattc_if) {
|
||||
LOG_ERR("[B]GattcSearchCmplUnknownIf[%u]",
|
||||
BTC_GATT_GET_GATT_IF(p_data->search_cmpl.conn_id));
|
||||
LOG_ERR("[B]GattcSearchCmplUnknownIf[%u][%u]",
|
||||
BTC_GATT_GET_GATT_IF(p_data->search_cmpl.conn_id), gattc_if);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
@@ -1278,8 +1277,8 @@ static void gattc_app_cb(tBTA_GATTC_EVT event, tBTA_GATTC *p_data)
|
||||
p_data->read.status, p_data->read.conn_id, p_data->read.handle);
|
||||
|
||||
if (BTC_GATT_GET_GATT_IF(p_data->read.conn_id) != gattc_if) {
|
||||
LOG_ERR("[B]GattcReadCharUnknownIf[%u]",
|
||||
BTC_GATT_GET_GATT_IF(p_data->read.conn_id));
|
||||
LOG_ERR("[B]GattcReadCharUnknownIf[%u][%u]",
|
||||
BTC_GATT_GET_GATT_IF(p_data->read.conn_id), gattc_if);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1307,8 +1306,8 @@ static void gattc_app_cb(tBTA_GATTC_EVT event, tBTA_GATTC *p_data)
|
||||
p_data->write.handle, p_data->write.offset);
|
||||
|
||||
if (BTC_GATT_GET_GATT_IF(p_data->write.conn_id) != gattc_if) {
|
||||
LOG_ERR("[B]GattcWriteCharUnknownIf[%u]",
|
||||
BTC_GATT_GET_GATT_IF(p_data->write.conn_id));
|
||||
LOG_ERR("[B]GattcWriteCharUnknownIf[%u][%u]",
|
||||
BTC_GATT_GET_GATT_IF(p_data->write.conn_id), gattc_if);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1340,8 +1339,8 @@ static void gattc_app_cb(tBTA_GATTC_EVT event, tBTA_GATTC *p_data)
|
||||
p_data->notify.bda[4], p_data->notify.bda[5]);
|
||||
|
||||
if (BTC_GATT_GET_GATT_IF(p_data->notify.conn_id) != gattc_if) {
|
||||
LOG_ERR("[B]GattcNotifUnknownIf[%u]",
|
||||
BTC_GATT_GET_GATT_IF(p_data->notify.conn_id));
|
||||
LOG_ERR("[B]GattcNotifUnknownIf[%u][%u]",
|
||||
BTC_GATT_GET_GATT_IF(p_data->notify.conn_id), gattc_if);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1405,9 +1404,9 @@ static void post_acl_connect_app_event(struct gatt_conn *gatt_conn)
|
||||
qev->acl_connect.status = gatt_conn->status;
|
||||
|
||||
/* Populate identity unconditionally: connect_event_handler fills these
|
||||
* fields before OPEN_EVT arrives, so failure events still carry valid
|
||||
* conn_handle / role / dst to the application — matches NimBLE's
|
||||
* BLE_GAP_EVENT_CONNECT, which always exposes the full descriptor. */
|
||||
* fields before OPEN_EVT arrives, so a failure that got that far still
|
||||
* carries valid conn_handle / role / dst to the application. One that
|
||||
* did not is reported by post_acl_connect_fail_app_event() instead. */
|
||||
qev->acl_connect.conn_handle = gatt_conn->conn_handle;
|
||||
qev->acl_connect.role = gatt_conn->role;
|
||||
qev->acl_connect.dst.type = gatt_conn->peer.type;
|
||||
@@ -1415,7 +1414,29 @@ static void post_acl_connect_app_event(struct gatt_conn *gatt_conn)
|
||||
|
||||
err = bt_le_iso_task_post(ISO_QUEUE_ITEM_TYPE_GAP_EVENT, qev, sizeof(*qev));
|
||||
if (err) {
|
||||
LOG_ERR("[B]AclConnPostFail[%d]", err);
|
||||
ISO_POST_FAIL_LOG(err, "[B]AclConnPostFail[%d]", err);
|
||||
free(qev);
|
||||
}
|
||||
}
|
||||
|
||||
/* A connect that never reached the link layer gets no CONNECT_EVT, so nothing
|
||||
* would tell the application its request died. Identity stays zero, matching
|
||||
* NimBLE, which fills the descriptor only on status 0. */
|
||||
static void post_acl_connect_fail_app_event(uint8_t status)
|
||||
{
|
||||
struct bt_le_gap_app_param *qev;
|
||||
int err;
|
||||
|
||||
qev = bt_le_ext_calloc(1, sizeof(*qev));
|
||||
BT_LE_ASSERT(qev);
|
||||
|
||||
qev->type = BT_LE_GAP_APP_PARAM_ACL_CONNECT;
|
||||
|
||||
qev->acl_connect.status = status;
|
||||
|
||||
err = bt_le_iso_task_post(ISO_QUEUE_ITEM_TYPE_GAP_EVENT, qev, sizeof(*qev));
|
||||
if (err) {
|
||||
ISO_POST_FAIL_LOG(err, "[B]AclConnFailPostFail[%d]", err);
|
||||
free(qev);
|
||||
}
|
||||
}
|
||||
@@ -1435,7 +1456,7 @@ static void post_acl_disconnect_app_event(uint16_t conn_handle, uint8_t reason)
|
||||
|
||||
err = bt_le_iso_task_post(ISO_QUEUE_ITEM_TYPE_GAP_EVENT, qev, sizeof(*qev));
|
||||
if (err) {
|
||||
LOG_ERR("[B]AclDiscPostFail[%d]", err);
|
||||
ISO_POST_FAIL_LOG(err, "[B]AclDiscPostFail[%d]", err);
|
||||
free(qev);
|
||||
}
|
||||
}
|
||||
@@ -1542,7 +1563,16 @@ static void handle_gattc_open_event(struct bt_le_gattc_open_event *event)
|
||||
|
||||
gatt_conn = find_gatt_conn_by_conn_id_or_index(event->conn_id);
|
||||
if (gatt_conn == NULL) {
|
||||
LOG_ERR("[B]GattcOpenUnknownDev");
|
||||
/* A non-zero status here is the application's own connect request failing
|
||||
* before any link existed; a success without a slot is a real
|
||||
* inconsistency and stays an error. */
|
||||
if (event->status) {
|
||||
LOG_WRN("[B]GattcOpenFailed[%u][0x%04x]", event->status, event->conn_id);
|
||||
post_acl_connect_fail_app_event(event->status);
|
||||
} else {
|
||||
LOG_ERR("[B]GattcOpenUnknownDev[0x%04x]", event->conn_id);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1928,7 +1958,7 @@ static void handle_gatts_disconnect_event(struct bt_le_gatts_disconnect_event *e
|
||||
|
||||
gatt_conn = find_gatt_conn_with_conn_id(event->conn_id);
|
||||
if (gatt_conn == NULL) {
|
||||
LOG_WRN("[B]GattsDisconnUnknownDev");
|
||||
LOG_DBG("[B]GattsDisconnUnknownDev");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -3500,7 +3530,8 @@ int bt_le_bluedroid_gatt_init(void)
|
||||
|
||||
void bt_le_bluedroid_gatt_deinit(void)
|
||||
{
|
||||
/* Block late REG_EVT gives before tearing down the sems. */
|
||||
/* Must precede the deregisters: it is what makes the event callbacks drop
|
||||
* everything BTA still delivers for this registration. */
|
||||
gatt_shutting_down = true;
|
||||
|
||||
if (gattc_if != 0) {
|
||||
|
||||
@@ -111,6 +111,7 @@ static void direct_hci_complete_cb(BT_HDR *response, void *context)
|
||||
/* deinit may have set the shutdown flag and be about to delete the
|
||||
* sem. Skip the give to avoid asserting on a NULL handle. */
|
||||
if (direct_hci_shutting_down) {
|
||||
LOG_INF("[B]DirectHciDropCmpl[0x%04x]", opcode);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -400,7 +400,7 @@ static int hci_cmd_create_cis(struct net_buf *buf, struct net_buf **rsp)
|
||||
BT_LE_ASSERT(cis_params);
|
||||
|
||||
for (size_t i = 0; i < cis_count; i++) {
|
||||
cis_params[i].cis_hdl = sys_get_le16(buf->data + 4 + i * sizeof(struct ble_hci_cis_hdls));
|
||||
cis_params[i].cis_hdl = sys_get_le16(buf->data + 4 + i * sizeof(struct ble_hci_cis_hdls));
|
||||
cis_params[i].acl_hdl = sys_get_le16(buf->data + 6 + i * sizeof(struct ble_hci_cis_hdls));
|
||||
}
|
||||
|
||||
@@ -1089,7 +1089,9 @@ static void iso_evt_handler(tBTM_BLE_ISO_EVENT event, tBTM_BLE_ISO_CB_PARAMS *pa
|
||||
|
||||
err = bt_le_iso_task_post(q_type, qdata, qdata_len);
|
||||
if (err) {
|
||||
LOG_ERR("[B]IsoPostEvtFail[%d][%02x]", err, event);
|
||||
if (q_type == ISO_QUEUE_ITEM_TYPE_ISO_HCI_EVENT) {
|
||||
ISO_POST_FAIL_LOG(err, "[B]IsoPostEvtFail[%d][%02x]", err, event);
|
||||
}
|
||||
free(qdata);
|
||||
}
|
||||
}
|
||||
@@ -1330,8 +1332,12 @@ int bt_le_bluedroid_iso_init(void)
|
||||
void bt_le_bluedroid_iso_deinit(void)
|
||||
{
|
||||
#if CONFIG_BT_ISO_UNICAST
|
||||
/* Mirror bt_le_iso_init() which enables bit 32 only on unicast build. */
|
||||
iso_disable_cis();
|
||||
/* Core 6.0 Vol 4 Part E 7.8.115: LE Set Host Feature is Command Disallowed
|
||||
* while any connection exists, and the ACL outlives ISO deinit. Skip - a
|
||||
* stale host-support bit is harmless and the next init sets it again. */
|
||||
if (bt_le_acl_conn_count() == 0) {
|
||||
iso_disable_cis();
|
||||
}
|
||||
#endif /* CONFIG_BT_ISO_UNICAST */
|
||||
|
||||
#if CONFIG_BT_ISO_RX
|
||||
|
||||
@@ -262,12 +262,8 @@ void bt_le_nimble_gap_post_event(void *param)
|
||||
|
||||
err = bt_le_iso_task_post(q_type, qev, sizeof(*qev));
|
||||
if (err) {
|
||||
/* Floodable reports drop by design when the queue is full; only a
|
||||
* failure on the reliable (normal-queue) path is a real error. */
|
||||
if (q_type == ISO_QUEUE_ITEM_TYPE_GAP_EVENT) {
|
||||
LOG_ERR("[N]GapPostEvtFail[%d][%u]", err, qev->type);
|
||||
} else {
|
||||
LOG_DBG("[N]GapRptDrop[%u]", qev->type);
|
||||
ISO_POST_FAIL_LOG(err, "[N]GapPostEvtFail[%d][%u]", err, qev->type);
|
||||
}
|
||||
goto free;
|
||||
}
|
||||
@@ -275,24 +271,7 @@ void bt_le_nimble_gap_post_event(void *param)
|
||||
return;
|
||||
|
||||
free:
|
||||
switch (qev->type) {
|
||||
case BT_LE_GAP_APP_PARAM_EXT_SCAN_RECV:
|
||||
if (qev->ext_scan_recv.data) {
|
||||
free(qev->ext_scan_recv.data);
|
||||
qev->ext_scan_recv.data = NULL;
|
||||
}
|
||||
break;
|
||||
case BT_LE_GAP_APP_PARAM_PA_SYNC_RECV:
|
||||
if (qev->pa_sync_recv.data) {
|
||||
free(qev->pa_sync_recv.data);
|
||||
qev->pa_sync_recv.data = NULL;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
free(qev);
|
||||
bt_le_gap_event_free(qev);
|
||||
}
|
||||
|
||||
int bt_le_nimble_scan_start(const struct bt_le_scan_param *param, ble_gap_event_fn *cb)
|
||||
|
||||
@@ -102,15 +102,8 @@ void bt_le_nimble_gatt_post_event(void *param)
|
||||
|
||||
err = bt_le_iso_task_post(ISO_QUEUE_ITEM_TYPE_GATT_EVENT, qev, sizeof(*qev));
|
||||
if (err) {
|
||||
LOG_ERR("[N]GattPostEvtFail[%d][%u]", err, qev->type);
|
||||
|
||||
if (qev->type == BT_LE_GATTC_NOTIFY_RX_EVENT &&
|
||||
qev->gattc_notify_rx.value) {
|
||||
free(qev->gattc_notify_rx.value);
|
||||
qev->gattc_notify_rx.value = NULL;
|
||||
}
|
||||
|
||||
free(qev);
|
||||
ISO_POST_FAIL_LOG(err, "[N]GattPostEvtFail[%d][%u]", err, qev->type);
|
||||
bt_le_gatt_event_free(qev);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -140,7 +133,7 @@ int bt_le_nimble_gatt_post_disc_event(uint16_t conn_handle, ble_uuid16_t *uuid,
|
||||
|
||||
err = bt_le_iso_task_post(ISO_QUEUE_ITEM_TYPE_GATT_EVENT, qev, sizeof(*qev));
|
||||
if (err) {
|
||||
LOG_ERR("[N]GattPostDiscEvtFail[%d][%u]", err, type);
|
||||
ISO_POST_FAIL_LOG(err, "[N]GattPostDiscEvtFail[%d][%u]", err, type);
|
||||
free(qev);
|
||||
return err;
|
||||
}
|
||||
@@ -165,7 +158,7 @@ int bt_le_nimble_gatt_post_disc_cmpl_event(uint16_t conn_handle, uint8_t status)
|
||||
|
||||
err = bt_le_iso_task_post(ISO_QUEUE_ITEM_TYPE_GATT_EVENT, qev, sizeof(*qev));
|
||||
if (err) {
|
||||
LOG_ERR("[N]GattPostDiscCmplEvtFail[%d][%u][%02x]", err, conn_handle, status);
|
||||
ISO_POST_FAIL_LOG(err, "[N]GattPostDiscCmplEvtFail[%d][%u][%02x]", err, conn_handle, status);
|
||||
free(qev);
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -808,7 +808,9 @@ static void iso_evt_rx(uint8_t event, const void *data,
|
||||
|
||||
err = bt_le_iso_task_post(q_type, qdata, qdata_len);
|
||||
if (err) {
|
||||
LOG_ERR("[N]IsoPostEvtFail[%d][%02x]", err, event);
|
||||
if (q_type == ISO_QUEUE_ITEM_TYPE_ISO_HCI_EVENT) {
|
||||
ISO_POST_FAIL_LOG(err, "[N]IsoPostEvtFail[%d][%02x]", err, event);
|
||||
}
|
||||
free(qdata);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,6 +70,17 @@ struct bt_le_ext_adv *bt_le_ext_adv_find(uint8_t adv_handle)
|
||||
}
|
||||
|
||||
_IDF_ONLY
|
||||
void bt_le_ext_adv_state_reset(void)
|
||||
{
|
||||
for (size_t i = 0; i < ARRAY_SIZE(ext_adv_pool); i++) {
|
||||
if (atomic_test_bit(ext_adv_pool[i].flags, BT_PER_ADV_PARAMS_SET)) {
|
||||
LOG_WRN("DeinitDropExtAdv[%u]", ext_adv_pool[i].handle);
|
||||
}
|
||||
}
|
||||
|
||||
memset(ext_adv_pool, 0, sizeof(ext_adv_pool));
|
||||
}
|
||||
|
||||
int bt_le_ext_adv_new_safe(uint8_t adv_handle, uint8_t addr_type,
|
||||
const uint8_t *addr, uint8_t sid)
|
||||
{
|
||||
|
||||
@@ -527,6 +527,32 @@ static void handle_bond_delete_event_safe(struct bt_le_gap_app_param *param)
|
||||
bt_le_gap_app_cb_evt(&event);
|
||||
}
|
||||
|
||||
void bt_le_gap_event_free(void *data)
|
||||
{
|
||||
struct bt_le_gap_app_param *qev = data;
|
||||
|
||||
if (qev == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (qev->type) {
|
||||
case BT_LE_GAP_APP_PARAM_EXT_SCAN_RECV:
|
||||
if (qev->ext_scan_recv.data) {
|
||||
free(qev->ext_scan_recv.data);
|
||||
}
|
||||
break;
|
||||
case BT_LE_GAP_APP_PARAM_PA_SYNC_RECV:
|
||||
if (qev->pa_sync_recv.data) {
|
||||
free(qev->pa_sync_recv.data);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
free(qev);
|
||||
}
|
||||
|
||||
void bt_le_gap_handle_event(uint8_t *data, size_t data_len)
|
||||
{
|
||||
struct bt_le_gap_app_param *param;
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <zephyr/kernel.h>
|
||||
@@ -46,6 +47,31 @@ void bt_conn_get_acl_conns(struct bt_conn **conns, uint8_t *count)
|
||||
*count = ARRAY_SIZE(acl_conns);
|
||||
}
|
||||
|
||||
size_t bt_le_acl_conn_count(void)
|
||||
{
|
||||
size_t count = 0;
|
||||
|
||||
for (size_t i = 0; i < ARRAY_SIZE(acl_conns); i++) {
|
||||
if (acl_conns[i].state != BT_CONN_DISCONNECTED) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
void bt_le_conn_reset(void)
|
||||
{
|
||||
LOG_DBG("ConnReset");
|
||||
|
||||
memset(acl_conns, 0, sizeof(acl_conns));
|
||||
memset(conn_ltk, 0, sizeof(conn_ltk));
|
||||
memset(iso_conns, 0, sizeof(struct bt_conn) * CONFIG_BT_ISO_MAX_CHAN);
|
||||
|
||||
sys_slist_init(&conn_cbs);
|
||||
sys_slist_init(&auth_info_cbs);
|
||||
}
|
||||
|
||||
_IDF_ONLY
|
||||
bool bt_conn_is_peer_addr_le(const struct bt_conn *conn, uint8_t id,
|
||||
const bt_addr_le_t *peer)
|
||||
@@ -382,6 +408,27 @@ int bt_le_acl_conn_new(uint16_t conn_handle,
|
||||
return (conn ? 0 : -ENOMEM);
|
||||
}
|
||||
|
||||
_IDF_ONLY
|
||||
int bt_le_acl_conn_new_safe(uint16_t conn_handle, uint8_t role, uint8_t addr_type,
|
||||
const uint8_t *addr, uint8_t sec_level)
|
||||
{
|
||||
bt_addr_le_t dst;
|
||||
int err;
|
||||
|
||||
if (addr == NULL) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
dst.type = addr_type;
|
||||
bt_addr_copy(&dst.a, (const bt_addr_t *)addr);
|
||||
|
||||
bt_le_host_lock();
|
||||
err = bt_le_acl_conn_new(conn_handle, role, &dst, sec_level);
|
||||
bt_le_host_unlock();
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Point conn->le.keys at this ACL connection's LTK slot, filled with the bonded
|
||||
* LTK the adapter captured. Used as key K by the lib's CSIS SIRK encryption. */
|
||||
_IDF_ONLY
|
||||
|
||||
@@ -1641,3 +1641,34 @@ void bt_le_gatt_handle_event(uint8_t *data, size_t data_len)
|
||||
bt_le_nimble_gatt_handle_event(data, data_len);
|
||||
#endif
|
||||
}
|
||||
|
||||
void bt_le_gatt_event_free(void *data)
|
||||
{
|
||||
struct bt_le_gatt_event_param *qev = data;
|
||||
|
||||
if (qev == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (qev->type) {
|
||||
case BT_LE_GATTC_NOTIFY_RX_EVENT:
|
||||
if (qev->gattc_notify_rx.value) {
|
||||
free(qev->gattc_notify_rx.value);
|
||||
}
|
||||
break;
|
||||
case BT_LE_GATTC_READ_CHRC_EVENT:
|
||||
if (qev->gattc_read_chrc.value) {
|
||||
free(qev->gattc_read_chrc.value);
|
||||
}
|
||||
break;
|
||||
case BT_LE_GATTS_WRITE_EVENT:
|
||||
if (qev->gatts_write.value) {
|
||||
free(qev->gatts_write.value);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
free(qev);
|
||||
}
|
||||
|
||||
@@ -13,7 +13,12 @@
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
|
||||
#include <zephyr/bluetooth/iso.h>
|
||||
|
||||
#include <../host/conn_internal.h>
|
||||
|
||||
#include "common/host.h"
|
||||
#include "common/conn.h"
|
||||
#include "common/app/gap.h"
|
||||
#include "common/app/gatt.h"
|
||||
|
||||
@@ -28,6 +33,8 @@ LOG_MODULE_REGISTER(ISO_HOST, CONFIG_BT_ISO_LOG_LEVEL);
|
||||
|
||||
static BT_ISO_CTRL_BSS_ATTR struct k_mutex host_mutex;
|
||||
|
||||
extern struct bt_conn iso_conns[CONFIG_BT_ISO_MAX_CHAN];
|
||||
|
||||
#if HOST_LOCK_DEBUG
|
||||
void bt_le_host_lock_debug(const char *func, int line)
|
||||
#else /* HOST_LOCK_DEBUG */
|
||||
@@ -69,6 +76,32 @@ void bt_le_host_unlock(void)
|
||||
k_mutex_unlock(&host_mutex);
|
||||
}
|
||||
|
||||
int bt_le_host_check_idle(void)
|
||||
{
|
||||
struct bt_iso_chan *chan;
|
||||
size_t busy = 0;
|
||||
|
||||
/* Only what ISO created; the application's adv sets, sync and ACL are
|
||||
* dropped by bt_le_host_deinit() rather than blocked on. Counts every
|
||||
* offender so one attempt tells the caller the whole list. */
|
||||
|
||||
bt_le_host_lock();
|
||||
|
||||
for (size_t i = 0; i < ARRAY_SIZE(iso_conns); i++) {
|
||||
chan = iso_conns[i].iso.chan;
|
||||
if (chan && chan->state != BT_ISO_STATE_DISCONNECTED) {
|
||||
LOG_ERR("DeinitBusyIsoChan[%u][state=%u]", i, chan->state);
|
||||
busy++;
|
||||
}
|
||||
}
|
||||
|
||||
busy += bt_le_iso_report_busy();
|
||||
|
||||
bt_le_host_unlock();
|
||||
|
||||
return busy ? -EBUSY : 0;
|
||||
}
|
||||
|
||||
int bt_le_host_init(void)
|
||||
{
|
||||
int err;
|
||||
@@ -77,6 +110,10 @@ int bt_le_host_init(void)
|
||||
|
||||
k_mutex_create(&host_mutex);
|
||||
|
||||
bt_le_conn_reset();
|
||||
|
||||
bt_le_iso_state_reset();
|
||||
|
||||
err = bt_le_scan_init();
|
||||
if (err) {
|
||||
goto delete_mutex;
|
||||
@@ -125,13 +162,23 @@ delete_mutex:
|
||||
return err;
|
||||
}
|
||||
|
||||
void bt_le_host_deinit(void)
|
||||
int bt_le_host_deinit(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
LOG_DBG("HostDeinit");
|
||||
|
||||
bt_le_iso_task_deinit();
|
||||
/* Everything below frees state the task dispatches into, so bail out while
|
||||
* it is still alive rather than free underneath it. */
|
||||
err = bt_le_iso_task_deinit();
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
bt_le_iso_deinit();
|
||||
#if CONFIG_BT_BLUEDROID_ENABLED
|
||||
/* No gap_deinit: BTM_BleGapRegisterCallback refuses NULL, so the callback
|
||||
* stays. Harmless - task_post rejects once the task is gone. */
|
||||
bt_le_bluedroid_gatt_deinit();
|
||||
#else
|
||||
bt_le_nimble_gattc_db_deinit();
|
||||
@@ -139,5 +186,9 @@ void bt_le_host_deinit(void)
|
||||
#endif /* CONFIG_BT_BLUEDROID_ENABLED */
|
||||
bt_le_scan_deinit();
|
||||
|
||||
/* Last: iso_task has exited and the callbacks above are gone, so nothing
|
||||
* can take the mutex any more. */
|
||||
k_mutex_delete(&host_mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -22,6 +22,8 @@ int bt_le_ext_adv_new_safe(uint8_t adv_handle, uint8_t addr_type,
|
||||
|
||||
int bt_le_ext_adv_delete_safe(uint8_t adv_handle);
|
||||
|
||||
void bt_le_ext_adv_state_reset(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -303,6 +303,8 @@ void bt_le_gap_app_biginfo_event(uint8_t *param);
|
||||
|
||||
void bt_le_gap_handle_event(uint8_t *data, size_t data_len);
|
||||
|
||||
void bt_le_gap_event_free(void *data);
|
||||
|
||||
void bt_le_gap_app_post_event(uint16_t type, void *param);
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
@@ -30,6 +30,10 @@ extern "C" {
|
||||
|
||||
void bt_conn_get_acl_conns(struct bt_conn **conns, uint8_t *count);
|
||||
|
||||
size_t bt_le_acl_conn_count(void);
|
||||
|
||||
void bt_le_conn_reset(void);
|
||||
|
||||
struct bt_conn *bt_le_acl_conn_find(uint16_t conn_handle);
|
||||
|
||||
int bt_le_acl_conn_new(uint16_t conn_handle,
|
||||
@@ -37,6 +41,9 @@ int bt_le_acl_conn_new(uint16_t conn_handle,
|
||||
bt_addr_le_t *dst,
|
||||
uint8_t sec_level);
|
||||
|
||||
int bt_le_acl_conn_new_safe(uint16_t conn_handle, uint8_t role, uint8_t addr_type,
|
||||
const uint8_t *addr, uint8_t sec_level);
|
||||
|
||||
int bt_le_acl_conn_delete(uint16_t conn_handle);
|
||||
|
||||
void bt_conn_le_set_ltk(struct bt_conn *conn, const uint8_t *ltk);
|
||||
|
||||
@@ -297,6 +297,8 @@ void bt_le_acl_conn_bond_deleted_gatt_listener(uint8_t id, const bt_addr_le_t *p
|
||||
|
||||
void bt_le_gatt_handle_event(uint8_t *data, size_t data_len);
|
||||
|
||||
void bt_le_gatt_event_free(void *data);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -44,7 +44,9 @@ void bt_le_host_unlock(void);
|
||||
|
||||
int bt_le_host_init(void);
|
||||
|
||||
void bt_le_host_deinit(void);
|
||||
int bt_le_host_check_idle(void);
|
||||
|
||||
int bt_le_host_deinit(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@@ -75,6 +75,10 @@ int bt_le_iso_rx(const uint8_t *data, uint16_t len, void *arg);
|
||||
|
||||
int bt_le_iso_disconnect(uint16_t conn_handle, uint8_t reason);
|
||||
|
||||
void bt_le_iso_state_reset(void);
|
||||
|
||||
size_t bt_le_iso_report_busy(void);
|
||||
|
||||
int bt_le_iso_init(void);
|
||||
|
||||
void bt_le_iso_deinit(void);
|
||||
|
||||
@@ -48,6 +48,15 @@ int bt_le_per_adv_sync_new(uint16_t sync_handle,
|
||||
uint16_t conn_handle,
|
||||
struct bt_le_per_adv_sync **out_sync);
|
||||
|
||||
int bt_le_per_adv_sync_new_safe(uint16_t sync_handle,
|
||||
uint8_t sid,
|
||||
uint8_t phy,
|
||||
uint16_t interval,
|
||||
uint8_t addr_type,
|
||||
const uint8_t addr[6],
|
||||
uint16_t conn_handle,
|
||||
struct bt_le_per_adv_sync **out_sync);
|
||||
|
||||
int bt_le_per_adv_sync_delete(uint16_t sync_handle);
|
||||
|
||||
int bt_le_per_adv_sync_establish_listener(uint16_t sync_handle);
|
||||
@@ -62,6 +71,8 @@ int bt_le_scan_init(void);
|
||||
|
||||
void bt_le_scan_deinit(void);
|
||||
|
||||
void bt_le_per_adv_sync_state_reset(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -116,10 +116,15 @@ struct iso_queue_item {
|
||||
#define ISO_CRITICAL_QUEUE_LEN 32
|
||||
#define ISO_NORMAL_QUEUE_LEN 64
|
||||
#define ISO_FLOODABLE_QUEUE_LEN 32
|
||||
/* The set must be able to hold one token per item across all three queues. */
|
||||
/* Not a tier. One-deep and written only by deinit, so the stop signal can never
|
||||
* hit a full queue - which is what lets the task block on the set indefinitely
|
||||
* instead of polling for iso_task_stopping. */
|
||||
#define ISO_CTRL_QUEUE_LEN 1
|
||||
/* The set must be able to hold one token per item across all queues. */
|
||||
#define ISO_QUEUE_SET_LEN (ISO_CRITICAL_QUEUE_LEN + \
|
||||
ISO_NORMAL_QUEUE_LEN + \
|
||||
ISO_FLOODABLE_QUEUE_LEN)
|
||||
ISO_FLOODABLE_QUEUE_LEN + \
|
||||
ISO_CTRL_QUEUE_LEN)
|
||||
#define ISO_QUEUE_ITEM_SIZE sizeof(struct iso_queue_item)
|
||||
|
||||
#if CONFIG_BT_ISO_DISPATCH_MONITOR
|
||||
@@ -132,9 +137,21 @@ void bt_le_iso_dispatch_stats_dump(void);
|
||||
int bt_le_iso_task_post(enum iso_queue_item_type type,
|
||||
void *data, size_t data_len);
|
||||
|
||||
/* -ESHUTDOWN is expected for as long as an ACL, scan or PA sync outlives the
|
||||
* deinit that stopped it; anything else is a real drop. A macro so each file
|
||||
* logs under its own LOG_MODULE_REGISTER. */
|
||||
#define ISO_POST_FAIL_LOG(_err, ...) \
|
||||
do { \
|
||||
if ((_err) == -ESHUTDOWN) { \
|
||||
LOG_DBG(__VA_ARGS__); \
|
||||
} else { \
|
||||
LOG_ERR(__VA_ARGS__); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
int bt_le_iso_task_init(void);
|
||||
|
||||
void bt_le_iso_task_deinit(void);
|
||||
int bt_le_iso_task_deinit(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@@ -170,6 +170,24 @@ int bt_le_per_adv_sync_cb_register(struct bt_le_per_adv_sync_cb *cb)
|
||||
return 0;
|
||||
}
|
||||
|
||||
_LIB_ONLY
|
||||
int bt_le_per_adv_sync_cb_unregister(struct bt_le_per_adv_sync_cb *cb)
|
||||
{
|
||||
LOG_DBG("PaSyncCbUnreg");
|
||||
|
||||
if (cb == NULL) {
|
||||
LOG_ERR("PaSyncCbNull");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!sys_slist_find_and_remove(&pa_sync_cbs, &cb->node)) {
|
||||
LOG_ERR("PaSyncCbNotReg[%p]", cb);
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
_LIB_ONLY
|
||||
int bt_le_per_adv_sync_get_info(struct bt_le_per_adv_sync *per_adv_sync,
|
||||
struct bt_le_per_adv_sync_info *info)
|
||||
@@ -322,6 +340,23 @@ int bt_le_per_adv_sync_new(uint16_t sync_handle,
|
||||
}
|
||||
|
||||
_IDF_ONLY
|
||||
int bt_le_per_adv_sync_new_safe(uint16_t sync_handle,
|
||||
uint8_t sid,
|
||||
uint8_t phy,
|
||||
uint16_t interval,
|
||||
uint8_t addr_type,
|
||||
const uint8_t addr[6],
|
||||
uint16_t conn_handle,
|
||||
struct bt_le_per_adv_sync **out_sync)
|
||||
{
|
||||
int err;
|
||||
bt_le_host_lock();
|
||||
err = bt_le_per_adv_sync_new(sync_handle, sid, phy, interval, addr_type,
|
||||
addr, conn_handle, out_sync);
|
||||
bt_le_host_unlock();
|
||||
return err;
|
||||
}
|
||||
|
||||
int bt_le_per_adv_sync_delete(uint16_t sync_handle)
|
||||
{
|
||||
struct bt_le_per_adv_sync *per_adv_sync = NULL;
|
||||
@@ -551,6 +586,17 @@ static void past_features_unset(void)
|
||||
}
|
||||
|
||||
_IDF_ONLY
|
||||
void bt_le_per_adv_sync_state_reset(void)
|
||||
{
|
||||
for (size_t i = 0; i < ARRAY_SIZE(per_adv_sync_pool); i++) {
|
||||
if (atomic_test_bit(per_adv_sync_pool[i].flags, BT_PER_ADV_SYNC_SYNCED)) {
|
||||
LOG_WRN("DeinitDropPaSync[%u][%04x]", i, per_adv_sync_pool[i].handle);
|
||||
}
|
||||
}
|
||||
|
||||
memset(per_adv_sync_pool, 0, sizeof(per_adv_sync_pool));
|
||||
}
|
||||
|
||||
int bt_le_scan_init(void)
|
||||
{
|
||||
LOG_DBG("ScanInit");
|
||||
|
||||
@@ -20,11 +20,25 @@
|
||||
|
||||
#include "common/host.h"
|
||||
#include "common/iso.h"
|
||||
#include "common/gatt.h"
|
||||
#include "common/app/gap.h"
|
||||
#include "common/app/gatt.h"
|
||||
|
||||
LOG_MODULE_REGISTER(ISO_TASK, CONFIG_BT_ISO_LOG_LEVEL);
|
||||
|
||||
/* Nothing to poll for - iso_ctrl_queue wakes the task for deinit. The dispatch
|
||||
* monitor is the exception: its periodic dump is driven from this loop, so it
|
||||
* needs a wakeup even while no event arrives. */
|
||||
#if CONFIG_BT_ISO_DISPATCH_MONITOR
|
||||
#define ISO_TASK_WAIT (ISO_STATS_DUMP_PERIOD_US / 1000 / portTICK_PERIOD_MS)
|
||||
#else /* CONFIG_BT_ISO_DISPATCH_MONITOR */
|
||||
#define ISO_TASK_WAIT portMAX_DELAY
|
||||
#endif /* CONFIG_BT_ISO_DISPATCH_MONITOR */
|
||||
|
||||
/* Generous: expiry means a dispatch handler is wedged, which is a bug
|
||||
* elsewhere. Deinit reports it upward rather than freeing under a live task. */
|
||||
#define ISO_TASK_STOP_TIMEOUT (2000 / portTICK_PERIOD_MS)
|
||||
|
||||
/* Three priority tiers share one task via a queue set. The task drains
|
||||
* critical before normal before floodable, so a flood of GAP reports cannot
|
||||
* delay the latency-critical ISO data path. See common/task.h for the mapping.
|
||||
@@ -32,10 +46,17 @@ LOG_MODULE_REGISTER(ISO_TASK, CONFIG_BT_ISO_LOG_LEVEL);
|
||||
static BT_ISO_CTRL_BSS_ATTR QueueHandle_t iso_critical_queue;
|
||||
static BT_ISO_CTRL_BSS_ATTR QueueHandle_t iso_normal_queue;
|
||||
static BT_ISO_CTRL_BSS_ATTR QueueHandle_t iso_floodable_queue;
|
||||
/* Not a tier: deinit-only wakeup, see ISO_CTRL_QUEUE_LEN. */
|
||||
static BT_ISO_CTRL_BSS_ATTR QueueHandle_t iso_ctrl_queue;
|
||||
static BT_ISO_CTRL_BSS_ATTR QueueSetHandle_t iso_queue_set;
|
||||
|
||||
static BT_ISO_CTRL_BSS_ATTR TaskHandle_t iso_task_handle;
|
||||
|
||||
/* Gate + handshake for deinit. iso_task_stopping also rejects new posts, so a
|
||||
* producer cannot strand a payload on a queue nobody will drain. */
|
||||
static BT_ISO_CTRL_BSS_ATTR volatile bool iso_task_stopping;
|
||||
static BT_ISO_CTRL_BSS_ATTR SemaphoreHandle_t iso_task_stopped;
|
||||
|
||||
extern void bt_le_timer_handle_event(void *arg, size_t gen);
|
||||
|
||||
#if CONFIG_BT_OTS || CONFIG_BT_OTS_CLIENT
|
||||
@@ -43,6 +64,7 @@ extern void bt_le_timer_handle_event(void *arg, size_t gen);
|
||||
* OTS, so the shim lives there. Declared instead of included to keep esp_ble_iso
|
||||
* free of audio headers; both live in the bt component, so the link resolves. */
|
||||
extern void bt_le_l2cap_handle_event(void *data, size_t data_len);
|
||||
extern void bt_le_l2cap_event_free(void *data);
|
||||
#endif
|
||||
|
||||
#if CONFIG_BT_ISO_DISPATCH_MONITOR
|
||||
@@ -90,6 +112,52 @@ void bt_le_iso_dispatch_stats_dump(void)
|
||||
}
|
||||
#endif /* CONFIG_BT_ISO_DISPATCH_MONITOR */
|
||||
|
||||
static void iso_item_release(const struct iso_queue_item *item)
|
||||
{
|
||||
switch (item->type) {
|
||||
case ISO_QUEUE_ITEM_TYPE_TIMER_EVENT:
|
||||
/* data is the k_work, data_len its generation counter - not a block. */
|
||||
break;
|
||||
case ISO_QUEUE_ITEM_TYPE_GATT_EVENT:
|
||||
bt_le_gatt_event_free(item->data);
|
||||
break;
|
||||
case ISO_QUEUE_ITEM_TYPE_GAP_EVENT:
|
||||
case ISO_QUEUE_ITEM_TYPE_EXT_ADV_REPORT:
|
||||
case ISO_QUEUE_ITEM_TYPE_PER_ADV_REPORT:
|
||||
bt_le_gap_event_free(item->data);
|
||||
break;
|
||||
#if CONFIG_BT_OTS || CONFIG_BT_OTS_CLIENT
|
||||
case ISO_QUEUE_ITEM_TYPE_L2CAP_EVENT:
|
||||
bt_le_l2cap_event_free(item->data);
|
||||
break;
|
||||
#endif /* CONFIG_BT_OTS || CONFIG_BT_OTS_CLIENT */
|
||||
default:
|
||||
if (item->data) {
|
||||
free(item->data);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Runs on iso_task after the loop exits, so no producer can be mid-dispatch and
|
||||
* the queues are provably empty when iso_queues_destroy() deletes them. */
|
||||
static void iso_queues_drain(void)
|
||||
{
|
||||
struct iso_queue_item item = {0};
|
||||
|
||||
while (xQueueReceive(iso_critical_queue, &item, 0) == pdTRUE) {
|
||||
iso_item_release(&item);
|
||||
}
|
||||
|
||||
while (xQueueReceive(iso_normal_queue, &item, 0) == pdTRUE) {
|
||||
iso_item_release(&item);
|
||||
}
|
||||
|
||||
while (xQueueReceive(iso_floodable_queue, &item, 0) == pdTRUE) {
|
||||
iso_item_release(&item);
|
||||
}
|
||||
}
|
||||
|
||||
static void iso_dispatch_item(const struct iso_queue_item *item)
|
||||
{
|
||||
#if CONFIG_BT_ISO_DISPATCH_MONITOR
|
||||
@@ -127,9 +195,7 @@ static void iso_dispatch_item(const struct iso_queue_item *item)
|
||||
bt_le_iso_handle_rx_data(item->data, item->data_len);
|
||||
break;
|
||||
default:
|
||||
if (item->data) {
|
||||
free(item->data);
|
||||
}
|
||||
iso_item_release(item);
|
||||
BT_LE_ASSERT(0);
|
||||
break;
|
||||
}
|
||||
@@ -147,13 +213,11 @@ static void iso_task(void *p)
|
||||
#endif /* CONFIG_BT_ISO_DISPATCH_MONITOR */
|
||||
struct iso_queue_item item = {0};
|
||||
|
||||
while (1) {
|
||||
/* Block until any tier has data. The returned member handle is ignored:
|
||||
* we always service by strict priority below (critical > normal >
|
||||
* floodable), processing one item per wakeup and re-checking critical
|
||||
* first on the next loop. A pdFALSE receive is tolerated as a benign
|
||||
* side effect of servicing queues outside xQueueSelectFromSet. */
|
||||
(void)xQueueSelectFromSet(iso_queue_set, portMAX_DELAY);
|
||||
while (!iso_task_stopping) {
|
||||
/* The returned handle is ignored: service by strict priority instead
|
||||
* (critical > normal > floodable), one item per wakeup. A pdFALSE
|
||||
* receive is benign - that is what a deinit wakeup looks like. */
|
||||
(void)xQueueSelectFromSet(iso_queue_set, ISO_TASK_WAIT);
|
||||
|
||||
if (xQueueReceive(iso_critical_queue, &item, 0) == pdTRUE) {
|
||||
iso_dispatch_item(&item);
|
||||
@@ -170,6 +234,14 @@ static void iso_task(void *p)
|
||||
}
|
||||
#endif /* CONFIG_BT_ISO_DISPATCH_MONITOR */
|
||||
}
|
||||
|
||||
/* Draining here rather than in the deinit caller keeps payload ownership on
|
||||
* a single task: no producer is mid-post and no consumer is mid-dispatch. */
|
||||
iso_queues_drain();
|
||||
|
||||
xSemaphoreGive(iso_task_stopped);
|
||||
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
int bt_le_iso_task_post(enum iso_queue_item_type type,
|
||||
@@ -180,6 +252,13 @@ int bt_le_iso_task_post(enum iso_queue_item_type type,
|
||||
TickType_t wait;
|
||||
int ret;
|
||||
|
||||
/* No consumer before init or after deinit began, so accepting would strand
|
||||
* the payload (callers free on failure). Distinct from the -1 below: this
|
||||
* one persists, a full queue is transient. */
|
||||
if (iso_task_handle == NULL || iso_task_stopping) {
|
||||
return -ESHUTDOWN;
|
||||
}
|
||||
|
||||
item.type = type;
|
||||
item.data = data;
|
||||
item.data_len = data_len;
|
||||
@@ -249,6 +328,7 @@ static void iso_queues_destroy(void)
|
||||
iso_queue_destroy_one(&iso_critical_queue);
|
||||
iso_queue_destroy_one(&iso_normal_queue);
|
||||
iso_queue_destroy_one(&iso_floodable_queue);
|
||||
iso_queue_destroy_one(&iso_ctrl_queue);
|
||||
|
||||
if (iso_queue_set) {
|
||||
vQueueDelete(iso_queue_set);
|
||||
@@ -262,19 +342,31 @@ int bt_le_iso_task_init(void)
|
||||
|
||||
LOG_DBG("IsoTaskInit");
|
||||
|
||||
/* Reset here, not at definition, so a deinit/re-init cycle starts clean. */
|
||||
iso_task_stopping = false;
|
||||
|
||||
iso_task_stopped = xSemaphoreCreateBinary();
|
||||
if (iso_task_stopped == NULL) {
|
||||
LOG_ERR("IsoTaskSemCreateFail");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
iso_critical_queue = xQueueCreate(ISO_CRITICAL_QUEUE_LEN, ISO_QUEUE_ITEM_SIZE);
|
||||
iso_normal_queue = xQueueCreate(ISO_NORMAL_QUEUE_LEN, ISO_QUEUE_ITEM_SIZE);
|
||||
iso_floodable_queue = xQueueCreate(ISO_FLOODABLE_QUEUE_LEN, ISO_QUEUE_ITEM_SIZE);
|
||||
iso_ctrl_queue = xQueueCreate(ISO_CTRL_QUEUE_LEN, ISO_QUEUE_ITEM_SIZE);
|
||||
iso_queue_set = xQueueCreateSet(ISO_QUEUE_SET_LEN);
|
||||
if (iso_critical_queue == NULL || iso_normal_queue == NULL ||
|
||||
iso_floodable_queue == NULL || iso_queue_set == NULL) {
|
||||
iso_floodable_queue == NULL || iso_ctrl_queue == NULL ||
|
||||
iso_queue_set == NULL) {
|
||||
LOG_ERR("IsoQCreateFail");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (xQueueAddToSet(iso_critical_queue, iso_queue_set) != pdPASS ||
|
||||
xQueueAddToSet(iso_normal_queue, iso_queue_set) != pdPASS ||
|
||||
xQueueAddToSet(iso_floodable_queue, iso_queue_set) != pdPASS) {
|
||||
xQueueAddToSet(iso_floodable_queue, iso_queue_set) != pdPASS ||
|
||||
xQueueAddToSet(iso_ctrl_queue, iso_queue_set) != pdPASS) {
|
||||
LOG_ERR("IsoQSetAddFail");
|
||||
goto fail;
|
||||
}
|
||||
@@ -295,22 +387,60 @@ int bt_le_iso_task_init(void)
|
||||
|
||||
fail:
|
||||
iso_queues_destroy();
|
||||
vSemaphoreDelete(iso_task_stopped);
|
||||
iso_task_stopped = NULL;
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
void bt_le_iso_task_deinit(void)
|
||||
int bt_le_iso_task_deinit(void)
|
||||
{
|
||||
struct iso_queue_item item = {0};
|
||||
|
||||
LOG_DBG("IsoTaskDeinit");
|
||||
|
||||
if (iso_task_handle) {
|
||||
vTaskDelete(iso_task_handle);
|
||||
iso_task_handle = NULL;
|
||||
if (iso_task_handle == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* This blocks on the task's own exit, so calling it from iso_task would
|
||||
* wait for itself forever. */
|
||||
if (xTaskGetCurrentTaskHandle() == iso_task_handle) {
|
||||
LOG_ERR("IsoTaskDeinitFromSelf");
|
||||
return -EDEADLK;
|
||||
}
|
||||
|
||||
/* Stops new posts as well, so the queues can only shrink from here. */
|
||||
iso_task_stopping = true;
|
||||
|
||||
/* Setting the flag cannot wake a task blocked on the set. Posted after it so
|
||||
* whichever select consumes this token re-checks the flag as true; deinit is
|
||||
* the sole producer of a one-deep queue, so the send cannot fail. */
|
||||
(void)xQueueSend(iso_ctrl_queue, &item, 0);
|
||||
|
||||
if (xSemaphoreTake(iso_task_stopped, ISO_TASK_STOP_TIMEOUT) != pdTRUE) {
|
||||
/* A dispatch handler is wedged. Deleting the queues now would pull them
|
||||
* out from under a live task, so leave everything in place and let the
|
||||
* caller abort the teardown instead. */
|
||||
LOG_ERR("IsoTaskStopTimeout");
|
||||
iso_task_stopping = false;
|
||||
/* Take the wakeup back: nothing reads this queue, so leaving it there
|
||||
* would make the next attempt's send fail and never wake the task. */
|
||||
(void)xQueueReceive(iso_ctrl_queue, &item, 0);
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
iso_task_handle = NULL;
|
||||
|
||||
#if CONFIG_BT_ISO_DISPATCH_MONITOR
|
||||
/* Task is gone: no concurrent writer, safe to read the stats. */
|
||||
bt_le_iso_dispatch_stats_dump();
|
||||
#endif /* CONFIG_BT_ISO_DISPATCH_MONITOR */
|
||||
|
||||
/* Drained by the task before it exited, so these are empty. */
|
||||
iso_queues_destroy();
|
||||
|
||||
vSemaphoreDelete(iso_task_stopped);
|
||||
iso_task_stopped = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -2445,6 +2445,23 @@ int bt_iso_big_register_cb(struct bt_iso_big_cb *cb)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bt_iso_big_unregister_cb(struct bt_iso_big_cb *cb)
|
||||
{
|
||||
CHECKIF(cb == NULL) {
|
||||
LOG_ERR("BigCbNull");
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!sys_slist_find_and_remove(&iso_big_cbs, &cb->_node)) {
|
||||
LOG_ERR("BigCbNotReg[%p]", cb);
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_BT_ISO_BROADCASTER)
|
||||
static int hci_le_create_big(struct bt_le_ext_adv *padv, struct bt_iso_big *big,
|
||||
struct bt_iso_big_create_param *param)
|
||||
@@ -3553,3 +3570,54 @@ void bt_iso_reset_safe(void)
|
||||
bt_iso_reset();
|
||||
bt_le_host_unlock();
|
||||
}
|
||||
|
||||
void bt_le_iso_state_reset(void)
|
||||
{
|
||||
/* Clear the CIG/BIG pools, the registered server and the BIG callback list.
|
||||
* Not bt_iso_reset(), which is the HCI-reset path and tears down live
|
||||
* channels. Call from init, before anything registers. */
|
||||
|
||||
LOG_DBG("IsoStateReset");
|
||||
|
||||
#if CONFIG_BT_ISO_CENTRAL
|
||||
memset(cigs, 0, sizeof(cigs));
|
||||
#endif /* CONFIG_BT_ISO_CENTRAL */
|
||||
|
||||
#if CONFIG_BT_ISO_PERIPHERAL
|
||||
iso_server = NULL;
|
||||
#endif /* CONFIG_BT_ISO_PERIPHERAL */
|
||||
|
||||
#if CONFIG_BT_ISO_BROADCAST
|
||||
memset(bigs, 0, sizeof(bigs));
|
||||
sys_slist_init(&iso_big_cbs);
|
||||
#endif /* CONFIG_BT_ISO_BROADCAST */
|
||||
}
|
||||
|
||||
size_t bt_le_iso_report_busy(void)
|
||||
{
|
||||
size_t busy = 0;
|
||||
|
||||
/* Number of CIG/BIG slots still allocated; each is logged at ERROR. */
|
||||
|
||||
#if CONFIG_BT_ISO_CENTRAL
|
||||
/* A CIS disconnect only moves the CIG to INACTIVE; nothing but
|
||||
* bt_iso_cig_terminate() frees the slot and the controller's CIG. */
|
||||
for (size_t i = 0; i < ARRAY_SIZE(cigs); i++) {
|
||||
if (cigs[i].state != BT_ISO_CIG_STATE_IDLE) {
|
||||
LOG_ERR("DeinitBusyCig[%u][state=%u]", i, cigs[i].state);
|
||||
busy++;
|
||||
}
|
||||
}
|
||||
#endif /* CONFIG_BT_ISO_CENTRAL */
|
||||
|
||||
#if CONFIG_BT_ISO_BROADCAST
|
||||
for (size_t i = 0; i < ARRAY_SIZE(bigs); i++) {
|
||||
if (atomic_test_bit(bigs[i].flags, BT_BIG_INITIALIZED)) {
|
||||
LOG_ERR("DeinitBusyBig[%u]", i);
|
||||
busy++;
|
||||
}
|
||||
}
|
||||
#endif /* CONFIG_BT_ISO_BROADCAST */
|
||||
|
||||
return busy;
|
||||
}
|
||||
|
||||
@@ -20,11 +20,11 @@ void bt_le_assert(const char *tag, size_t info,
|
||||
/* Use instead of assert(): CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_DISABLE sets
|
||||
* -DNDEBUG, which turns every assert() into a no-op and lets execution fall
|
||||
* through onto the very pointer it was guarding. This one always checks. */
|
||||
#define BT_LE_ASSERT(_cond) \
|
||||
do { \
|
||||
if (!(_cond)) { \
|
||||
bt_le_assert(#_cond, 0, __FILE__, __LINE__, __func__); \
|
||||
} \
|
||||
#define BT_LE_ASSERT(_cond) \
|
||||
do { \
|
||||
if (!(_cond)) { \
|
||||
bt_le_assert(#_cond, 0, __FILE_NAME__, __LINE__, __func__); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
@@ -34,7 +34,7 @@ static void iso_timer_cb(void *arg)
|
||||
|
||||
err = bt_le_iso_task_post(ISO_QUEUE_ITEM_TYPE_TIMER_EVENT, work, work->gen);
|
||||
if (err) {
|
||||
LOG_ERR("TimerCbPostFail[%d]", err);
|
||||
ISO_POST_FAIL_LOG(err, "TimerCbPostFail[%d]", err);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -422,6 +422,19 @@ struct bt_le_per_adv_sync *bt_le_per_adv_sync_lookup_addr(const bt_addr_le_t *ad
|
||||
*/
|
||||
int bt_le_per_adv_sync_cb_register(struct bt_le_per_adv_sync_cb *cb);
|
||||
|
||||
/**
|
||||
* @brief Unregister periodic advertising sync callbacks.
|
||||
*
|
||||
* Counterpart of @ref bt_le_per_adv_sync_cb_register(). The list is boot-scoped,
|
||||
* so a module that stops listening must remove itself or keep being invoked.
|
||||
*
|
||||
* @param cb Callback struct previously registered.
|
||||
*
|
||||
* @retval 0 Success.
|
||||
* @retval -ENOENT if @p cb was not registered.
|
||||
*/
|
||||
int bt_le_per_adv_sync_cb_unregister(struct bt_le_per_adv_sync_cb *cb);
|
||||
|
||||
/** LE scan parameters */
|
||||
struct bt_le_scan_param {
|
||||
/** Scan type. @ref BT_LE_SCAN_TYPE_ACTIVE or @ref BT_LE_SCAN_TYPE_PASSIVE. */
|
||||
|
||||
Reference in New Issue
Block a user