feat(ble_audio): Support BLE ISO & BLE Audio deinitialization

This commit is contained in:
Liu Linyan
2026-08-25 11:15:15 +08:00
parent b532be2707
commit 475f26e5d1
84 changed files with 1825 additions and 210 deletions
@@ -54,6 +54,12 @@ esp_err_t esp_ble_audio_bap_unicast_server_unregister(void)
return ESP_FAIL;
}
#if BLE_AUDIO_SVC_DEFERRED_ADD
if (bt_le_ascs_deinit()) {
return ESP_FAIL;
}
#endif /* BLE_AUDIO_SVC_DEFERRED_ADD */
return ESP_OK;
}
@@ -772,6 +778,12 @@ esp_err_t esp_ble_audio_bap_scan_delegator_unregister(void)
return ESP_FAIL;
}
#if BLE_AUDIO_SVC_DEFERRED_ADD
if (bt_le_bass_deinit()) {
return ESP_FAIL;
}
#endif /* BLE_AUDIO_SVC_DEFERRED_ADD */
return ESP_OK;
}
@@ -11,8 +11,12 @@
#include <stdbool.h>
#include <errno.h>
#include <zephyr/logging/log.h>
#include "esp_ble_audio_common_api.h"
LOG_MODULE_REGISTER(LEA_API, CONFIG_BT_ISO_LOG_LEVEL);
esp_err_t esp_ble_audio_data_parse(const uint8_t ltv[], size_t size,
bool (*func)(uint8_t type,
const uint8_t *data,
@@ -127,6 +131,52 @@ unregister_gap:
return ESP_FAIL;
}
esp_err_t esp_ble_audio_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_iso_task_deinit();
if (err) {
return ESP_ERR_TIMEOUT;
}
bt_le_audio_deinit();
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_gatt_app_cb_unregister();
bt_le_gap_app_cb_unregister();
return ESP_OK;
}
esp_err_t esp_ble_audio_common_start(esp_ble_audio_start_info_t *info)
{
int err;
@@ -59,6 +59,10 @@ esp_err_t esp_ble_audio_csip_set_member_unregister(esp_ble_audio_csip_set_member
return ESP_ERR_INVALID_ARG;
}
if (bt_le_csis_deinit(svc_inst)) {
return ESP_FAIL;
}
err = bt_csip_set_member_unregister_safe(svc_inst);
if (err) {
return ESP_FAIL;
@@ -34,6 +34,10 @@ esp_err_t esp_ble_audio_pacs_unregister(void)
return ESP_FAIL;
}
if (bt_le_pacs_deinit()) {
return ESP_FAIL;
}
return ESP_OK;
}
@@ -376,6 +376,16 @@ esp_err_t esp_ble_audio_tbs_unregister_bearer(uint8_t bearer_index)
return ESP_FAIL;
}
#if BLE_AUDIO_SVC_DEFERRED_ADD
if (bearer_index == ESP_BLE_AUDIO_TBS_GTBS_INDEX) {
if (bt_le_gtbs_deinit()) {
return ESP_FAIL;
}
} else if (bt_le_tbs_deinit(bearer_index)) {
return ESP_FAIL;
}
#endif /* BLE_AUDIO_SVC_DEFERRED_ADD */
return ESP_OK;
}
#endif /* CONFIG_BT_TBS */
@@ -121,10 +121,30 @@ typedef struct {
/**
* @brief Post an application-layer GAP event for audio internal usage.
*
* @note This function is only needed while using NimBLE Host.
* @note NimBLE-only: NimBLE has no global GAP callback, so the layers below
* see only what the application forwards here. An event left out is
* dropped silently. Bluedroid's adapter 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 an unused one is harmless; omitting one leaves the
* matching 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
*
* GATT events go to esp_ble_audio_gatt_app_post_event() instead.
*
* @note Shares a sink with esp_ble_iso_gap_app_post_event(): forward a given
* event through exactly one of them or it is delivered twice.
*
* @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_audio_gap_app_post_event(uint16_t type, void *param);
@@ -132,13 +152,20 @@ void esp_ble_audio_gap_app_post_event(uint16_t type, void *param);
/**
* @brief Post an application-layer GATT event for audio internal usage.
*
* @note NimBLE-only. Bluedroid dispatches GATT events directly inside the
* adapter (BTA callbacks), so no app-level post is needed. This
* declaration is hidden from Bluedroid builds to make misuse a
* compile-time error.
* @note NimBLE-only: Bluedroid dispatches GATT events inside the adapter, so
* no app-level post is needed. Hidden from Bluedroid builds to make
* misuse a compile-time error.
*
* @param type Event type.
* @param param Event parameters.
* @note Forward all four below here rather than through
* esp_ble_audio_gap_app_post_event() - GATT has no ISO counterpart.
* Omitting one is silent; ASCS and PACS simply never react.
* - BLE_GAP_EVENT_MTU ATT MTU exchanged
* - BLE_GAP_EVENT_SUBSCRIBE peer wrote a CCC descriptor
* - BLE_GAP_EVENT_NOTIFY_RX notification or indication received
* - BLE_GAP_EVENT_NOTIFY_TX notification or indication sent
*
* @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_audio_gatt_app_post_event(uint8_t type, void *param);
#endif /* !CONFIG_BT_BLUEDROID_ENABLED */
@@ -163,6 +190,30 @@ typedef struct {
uint8_t dummy; /*!< Dummy field to avoid empty struct */
} esp_ble_audio_start_info_t;
/**
* @brief Deinitialize BLE Audio.
*
* The only precondition is that the application has stopped its streams: a live
* stream always has a CIS under it, so one still up makes this return
* ESP_ERR_INVALID_STATE with each offending item logged at ERROR level.
*
* @note Registered profiles need no prior unregister - this releases them
* itself, while the host lock and GATT application are still alive.
* The per-profile esp_ble_audio_*_unregister() calls are for dropping
* one profile while audio stays up.
*
* @note Also releases the ISO layer, so esp_ble_iso_common_deinit() must not
* be called as well. @p info has the same meaning as there.
*
* @param info Which ISO-layer records to clear, or NULL to clear all.
*
* @return ESP_OK on success,
* ESP_ERR_INVALID_STATE if something 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_audio_common_deinit(const esp_ble_iso_deinit_info_t *info);
/**
* @brief Start BLE Audio services.
*
@@ -29,6 +29,8 @@ extern "C" {
int bt_le_bluedroid_audio_init(void);
void bt_le_bluedroid_audio_deinit(void);
int bt_le_bluedroid_media_proxy_pl_init(void);
int bt_le_bluedroid_vcp_vol_rend_init(void);
@@ -16,6 +16,8 @@ extern "C" {
int bt_le_bluedroid_ascs_init(void);
int bt_le_bluedroid_ascs_deinit(void);
int bt_le_bluedroid_ascs_start(void);
#ifdef __cplusplus
@@ -16,6 +16,8 @@ extern "C" {
int bt_le_bluedroid_bass_init(void);
int bt_le_bluedroid_bass_deinit(void);
int bt_le_bluedroid_bass_start(void);
#ifdef __cplusplus
@@ -14,8 +14,12 @@
extern "C" {
#endif
void bt_le_bluedroid_csis_state_reset(void);
int bt_le_bluedroid_csis_init(void *csis_svc, uint8_t count);
int bt_le_bluedroid_csis_deinit(void *csis_svc);
int bt_le_bluedroid_csis_start(void);
#ifdef __cplusplus
@@ -14,6 +14,8 @@
extern "C" {
#endif
void bt_le_bluedroid_mcs_state_reset(void);
int bt_le_bluedroid_gmcs_init(void);
int bt_le_bluedroid_gmcs_start(void);
@@ -14,6 +14,8 @@
extern "C" {
#endif
void bt_le_bluedroid_mics_state_reset(void);
int bt_le_bluedroid_mics_init(void *micp_inc);
int bt_le_bluedroid_mics_start(void);
@@ -16,6 +16,8 @@ extern "C" {
int bt_le_bluedroid_pacs_init(void);
int bt_le_bluedroid_pacs_deinit(void);
int bt_le_bluedroid_pacs_start(void);
#ifdef __cplusplus
@@ -16,10 +16,14 @@ extern "C" {
int bt_le_bluedroid_gtbs_init(void);
int bt_le_bluedroid_gtbs_deinit(void);
int bt_le_bluedroid_gtbs_start(void);
int bt_le_bluedroid_tbs_init(void);
int bt_le_bluedroid_tbs_deinit(uint8_t bearer_index);
int bt_le_bluedroid_tbs_start(void);
#ifdef __cplusplus
@@ -14,6 +14,8 @@
extern "C" {
#endif
void bt_le_bluedroid_vcs_state_reset(void);
int bt_le_bluedroid_vcs_init(void *vcp_inc);
int bt_le_bluedroid_vcs_start(void);
@@ -162,10 +162,14 @@ enum {
void bt_le_bluedroid_audio_gatts_init(void);
void bt_le_bluedroid_audio_gatts_deinit(void);
int bt_le_bluedroid_set_svc_in_progress(uint8_t value);
int bt_le_bluedroid_svc_init(struct bt_gatt_service *svc);
int bt_le_bluedroid_svc_deinit(struct bt_gatt_service *svc);
int bt_le_bluedroid_svc_start(struct bt_gatt_service *svc);
#ifdef __cplusplus
@@ -71,6 +71,19 @@ int bt_le_bluedroid_audio_init(void)
bt_le_bluedroid_audio_gatts_init();
#if CONFIG_BT_CSIP_SET_MEMBER
bt_le_bluedroid_csis_state_reset();
#endif /* CONFIG_BT_CSIP_SET_MEMBER */
#if CONFIG_BT_VCP_VOL_REND
bt_le_bluedroid_vcs_state_reset();
#endif /* CONFIG_BT_VCP_VOL_REND */
#if CONFIG_BT_MICP_MIC_DEV
bt_le_bluedroid_mics_state_reset();
#endif /* CONFIG_BT_MICP_MIC_DEV */
#if CONFIG_BT_MCS
bt_le_bluedroid_mcs_state_reset();
#endif /* CONFIG_BT_MCS */
#if CONFIG_BT_PACS
err = bt_le_bluedroid_pacs_init();
if (err) {
@@ -123,6 +136,16 @@ int bt_le_bluedroid_audio_init(void)
return err;
}
void bt_le_bluedroid_audio_deinit(void)
{
LOG_DBG("[B]AudioDeinit");
/* The services are removed by the BTA_GATTS_AppDeregister() in the ISO
* layer's gatt deinit - GATT_Deregister() stops every service the app
* owns - so only this layer's own hook is dropped here. */
bt_le_bluedroid_audio_gatts_deinit();
}
#if CONFIG_BT_CSIP_SET_MEMBER
static int bluedroid_gatt_csis_init(struct bt_le_audio_start_info *info,
struct bt_gatt_service **inc_csis_svc)
@@ -38,6 +38,21 @@ int bt_le_bluedroid_ascs_init(void)
return bt_le_bluedroid_svc_init(ascs_svc);
}
int bt_le_bluedroid_ascs_deinit(void)
{
struct bt_gatt_service *ascs_svc;
LOG_DBG("[B]AscsDeinit");
ascs_svc = lib_ascs_svc_get();
if (!ascs_svc) {
LOG_ERR("[B]AscsSvcGetFail");
return -ENODEV;
}
return bt_le_bluedroid_svc_deinit(ascs_svc);
}
int bt_le_bluedroid_ascs_start(void)
{
struct bt_gatt_service *ascs_svc;
@@ -38,6 +38,21 @@ int bt_le_bluedroid_bass_init(void)
return bt_le_bluedroid_svc_init(bass_svc);
}
int bt_le_bluedroid_bass_deinit(void)
{
struct bt_gatt_service *bass_svc;
LOG_DBG("[B]BassDeinit");
bass_svc = lib_bap_bass_svc_get();
if (!bass_svc) {
LOG_ERR("[B]BassSvcGetFail");
return -ENODEV;
}
return bt_le_bluedroid_svc_deinit(bass_svc);
}
int bt_le_bluedroid_bass_start(void)
{
struct bt_gatt_service *bass_svc;
@@ -30,6 +30,11 @@ static BT_AUDIO_EXT_RAM_BSS_ATTR struct csis_inst {
static BT_AUDIO_EXT_RAM_BSS_ATTR uint8_t csis_svc_count;
void bt_le_bluedroid_csis_state_reset(void)
{
csis_svc_count = 0;
}
int bt_le_bluedroid_csis_init(void *svc, uint8_t count)
{
int err;
@@ -58,6 +63,18 @@ int bt_le_bluedroid_csis_init(void *svc, uint8_t count)
return 0;
}
int bt_le_bluedroid_csis_deinit(void *csis_svc)
{
LOG_DBG("[B]CsisDeinit");
if (csis_svc == NULL) {
LOG_ERR("[B]CsisSvcNull");
return -EINVAL;
}
return bt_le_bluedroid_svc_deinit(csis_svc);
}
int bt_le_bluedroid_csis_start(void)
{
int err;
@@ -62,12 +62,28 @@ static int bluedroid_gmcs_ots_init(void)
static int bluedroid_gmcs_ots_start(void)
{
/* svc_p comes from bluedroid_gmcs_ots_init(), which only runs once the app
* calls media_proxy_pl_init(); NULL means MCS was never registered. The
* count-guarded loops in vcs.c / mics.c / csis.c skip for the same reason. */
if (inc_ots_inst.svc_p == NULL) {
LOG_DBG("[B]GmcsOtsNotReg");
return 0;
}
bt_le_bluedroid_set_svc_in_progress(OTS_IN_PROGRESS);
return bt_le_bluedroid_svc_start(inc_ots_inst.svc_p);
}
#endif /* CONFIG_BT_OTS */
void bt_le_bluedroid_mcs_state_reset(void)
{
#if CONFIG_BT_OTS
inc_ots_inst.svc_p = NULL;
inc_ots_inst.included = false;
#endif /* CONFIG_BT_OTS */
}
int bt_le_bluedroid_gmcs_init(void)
{
struct bt_gatt_service *gmcs_svc;
@@ -151,6 +167,7 @@ int bt_le_bluedroid_mcs_init(void)
* (created during gmcs_init). Reset the flag so it re-resolves per instance. */
inc_ots_inst.included = false;
#endif /* CONFIG_BT_OTS */
err = bt_le_bluedroid_svc_init(&list[i]);
if (err) {
return err;
@@ -41,6 +41,11 @@ struct inc_svc_inst *mics_not_included_inst(void)
return NULL;
}
void bt_le_bluedroid_mics_state_reset(void)
{
inc_aics_svc_count = 0;
}
int bt_le_bluedroid_mics_init(void *micp_inc)
{
struct bt_micp_included *micp_included;
@@ -38,6 +38,21 @@ int bt_le_bluedroid_pacs_init(void)
return bt_le_bluedroid_svc_init(pacs_svc);
}
int bt_le_bluedroid_pacs_deinit(void)
{
struct bt_gatt_service *pacs_svc;
LOG_DBG("[B]PacsDeinit");
pacs_svc = lib_pacs_svc_get();
if (!pacs_svc) {
LOG_ERR("[B]PacsSvcGetFail");
return -ENODEV;
}
return bt_le_bluedroid_svc_deinit(pacs_svc);
}
int bt_le_bluedroid_pacs_start(void)
{
struct bt_gatt_service *pacs_svc;
@@ -36,6 +36,21 @@ int bt_le_bluedroid_gtbs_init(void)
return bt_le_bluedroid_svc_init(gtbs_svc);
}
int bt_le_bluedroid_gtbs_deinit(void)
{
struct bt_gatt_service *gtbs_svc;
LOG_DBG("[B]GtbsDeinit");
gtbs_svc = lib_gtbs_svc_get();
if (!gtbs_svc) {
LOG_ERR("[B]GtbsSvcGetFail");
return -ENODEV;
}
return bt_le_bluedroid_svc_deinit(gtbs_svc);
}
int bt_le_bluedroid_gtbs_start(void)
{
struct bt_gatt_service *gtbs_svc;
@@ -83,6 +98,29 @@ int bt_le_bluedroid_tbs_init(void)
return 0;
}
int bt_le_bluedroid_tbs_deinit(uint8_t bearer_index)
{
#if CONFIG_BT_TBS_BEARER_COUNT > 0
struct bt_gatt_service *tbs_list;
#endif /* CONFIG_BT_TBS_BEARER_COUNT > 0 */
LOG_DBG("[B]TbsDeinit[%u]", bearer_index);
#if CONFIG_BT_TBS_BEARER_COUNT > 0
if (bearer_index < CONFIG_BT_TBS_BEARER_COUNT) {
tbs_list = lib_tbs_server_list_get();
if (!tbs_list) {
return 0;
}
return bt_le_bluedroid_svc_deinit(&tbs_list[bearer_index]);
}
#endif /* CONFIG_BT_TBS_BEARER_COUNT > 0 */
LOG_ERR("[B]TbsInvBearerIdx[%u]", bearer_index);
return -EINVAL;
}
int bt_le_bluedroid_tbs_start(void)
{
struct bt_gatt_service *tbs_list;
@@ -49,6 +49,12 @@ struct inc_svc_inst *vcs_not_included_inst(void)
return NULL;
}
void bt_le_bluedroid_vcs_state_reset(void)
{
inc_vocs_svc_count = 0;
inc_aics_svc_count = 0;
}
int bt_le_bluedroid_vcs_init(void *vcp_inc)
{
struct bt_vcp_included *vcp_included;
@@ -33,6 +33,39 @@ static BT_AUDIO_EXT_RAM_BSS_ATTR uint16_t inc_svc_handle;
static BT_AUDIO_EXT_RAM_BSS_ATTR uint16_t svc_handle;
static BT_AUDIO_EXT_RAM_BSS_ATTR uint16_t chrc_handle;
static BT_AUDIO_EXT_RAM_BSS_ATTR uint8_t aics_count;
static BT_AUDIO_EXT_RAM_BSS_ATTR uint8_t csis_count;
static BT_AUDIO_EXT_RAM_BSS_ATTR uint8_t vocs_count;
static BT_AUDIO_EXT_RAM_BSS_ATTR uint8_t mcs_count;
static BT_AUDIO_EXT_RAM_BSS_ATTR uint8_t ots_count;
static BT_AUDIO_EXT_RAM_BSS_ATTR uint8_t tbs_count;
/* Every service svc_init() created, so the deinit can delete each one:
* BTA_GATTS_AppDeregister() clears BTA's srvc_cb[] but leaves gatt_cb.sr_reg[],
* which is the table GATTS_StartService() checks on the next init. */
static BT_AUDIO_EXT_RAM_BSS_ATTR struct bt_gatt_service *created_svcs[CONFIG_BT_GATT_MAX_SR_PROFILES];
static BT_AUDIO_EXT_RAM_BSS_ATTR uint8_t created_svc_count;
static void svc_created_record(struct bt_gatt_service *svc)
{
for (uint8_t i = 0; i < created_svc_count; i++) {
if (created_svcs[i] == svc) {
/* Re-created in place: gtbs_init has no "already added" guard, and
* the tbs/mcs loops re-enter for each newly registered instance. */
return;
}
}
if (created_svc_count == ARRAY_SIZE(created_svcs)) {
/* Same ceiling as the stack's, so reaching it means the static_assert on
* TOTAL_SERVICE_COUNT is out of date rather than that we over-created. */
LOG_ERR("[B]SvcRecordFull[%u]", svc_in_progress);
return;
}
created_svcs[created_svc_count++] = svc;
}
static bool is_primary_svc(void)
{
if (svc_in_progress == ASCS_IN_PROGRESS ||
@@ -237,9 +270,61 @@ static struct gatts_svc_cb svc_cb = {
void bt_le_bluedroid_audio_gatts_init(void)
{
svc_in_progress = 0;
inc_svc_handle = 0;
svc_handle = 0;
chrc_handle = 0;
created_svc_count = 0;
aics_count = 0;
csis_count = 0;
vocs_count = 0;
mcs_count = 0;
ots_count = 0;
tbs_count = 0;
bt_le_bluedroid_gatts_svc_cb_register(&svc_cb);
}
int bt_le_bluedroid_svc_deinit(struct bt_gatt_service *svc)
{
if (svc == NULL || svc->attrs == NULL) {
LOG_ERR("[B]SvcDeinitNoSvc");
return -EINVAL;
}
if (svc->attrs[0].handle == 0) {
LOG_DBG("[B]SvcDeinitNotCreated");
return 0;
}
LOG_DBG("[B]SvcDeinit[0x%04x]", svc->attrs[0].handle);
BTA_GATTS_DeleteService(svc->attrs[0].handle);
svc->attrs[0].handle = 0;
return 0;
}
void bt_le_bluedroid_audio_gatts_deinit(void)
{
/* Reverse creation order, so an included secondary outlives the primary that
* includes it. Each delete logs upstream's "Active Service Found" at ERROR;
* that is its successful lookup, not a failure. */
while (created_svc_count > 0) {
struct bt_gatt_service *svc = created_svcs[--created_svc_count];
created_svcs[created_svc_count] = NULL;
/* Already-unregistered entries fall out as a no-op inside. */
(void)bt_le_bluedroid_svc_deinit(svc);
}
bt_le_bluedroid_gatts_svc_cb_register(NULL);
}
int bt_le_bluedroid_set_svc_in_progress(uint8_t value)
{
if (value >= MAX_IN_PROGRESS) {
@@ -414,17 +499,6 @@ static struct inc_svc_inst *get_not_included_inst(void)
static uint8_t get_svc_inst_id(uint16_t svc_uuid)
{
/* Note:
* For LE Audio, some service could have multiple instances.
*/
static BT_AUDIO_EXT_RAM_BSS_ATTR uint8_t aics_count;
static BT_AUDIO_EXT_RAM_BSS_ATTR uint8_t csis_count;
static BT_AUDIO_EXT_RAM_BSS_ATTR uint8_t vocs_count;
static BT_AUDIO_EXT_RAM_BSS_ATTR uint8_t mcs_count;
static BT_AUDIO_EXT_RAM_BSS_ATTR uint8_t ots_count;
static BT_AUDIO_EXT_RAM_BSS_ATTR uint8_t tbs_count;
switch (svc_uuid) {
case BT_UUID_AICS_VAL:
return aics_count++;
@@ -611,6 +685,12 @@ int bt_le_bluedroid_svc_init(struct bt_gatt_service *svc)
}
}
/* Created, so the deinit has to delete it. attr_count 0 is a service the
* application never registered; svc_init walked nothing. */
if (svc->attr_count > 0 && svc->attrs[0].handle != 0) {
svc_created_record(svc);
}
return 0;
}
@@ -618,11 +698,16 @@ int bt_le_bluedroid_svc_start(struct bt_gatt_service *svc)
{
BT_LE_ASSERT(svc);
/* App may not register this svc; skip rather than fail audio_start.
* Unused CAP Acceptor capabilities stop at handle 0, GMAS at NULL
* attrs - only bt_gmap_register() allocates them. */
if (svc->attrs == NULL) {
LOG_DBG("[B]SvcNotReg[%u]", svc_in_progress);
return 0;
}
svc_handle = svc->attrs[0].handle;
/* App may not register this svc (e.g. CAP Acceptor single mode keeps
* unused capability built). Skip rather than fail audio_start.
*/
if (svc_handle == 0) {
LOG_DBG("[B]SvcNotInit[%u]", svc_in_progress);
return 0;
@@ -632,7 +717,9 @@ int bt_le_bluedroid_svc_start(struct bt_gatt_service *svc)
BTA_GATTS_StartService(svc_handle, BTA_GATT_TRANSPORT_LE);
if (bt_le_bluedroid_gatts_sem_take()) {
LOG_ERR("[B]SvcStartFail");
/* Which service and which handle: BTA only says "already started" or
* "not created", neither of which names the caller. */
LOG_ERR("[B]SvcStartFail[%u][0x%04x]", svc_in_progress, svc_handle);
return -1;
}
@@ -28,6 +28,8 @@ extern "C" {
int bt_le_nimble_audio_init(void);
void bt_le_nimble_audio_deinit(void);
int bt_le_nimble_media_proxy_pl_init(void);
int bt_le_nimble_vcp_vol_rend_init(void);
@@ -18,6 +18,8 @@ int bt_le_nimble_ascs_attr_handle_set(void);
int bt_le_nimble_ascs_init(void);
int bt_le_nimble_ascs_deinit(void);
#ifdef __cplusplus
}
#endif
@@ -18,6 +18,8 @@ int bt_le_nimble_bass_attr_handle_set(void);
int bt_le_nimble_bass_init(void);
int bt_le_nimble_bass_deinit(void);
#ifdef __cplusplus
}
#endif
@@ -19,8 +19,12 @@ extern "C" {
int bt_le_nimble_csis_attr_handle_set(void);
void bt_le_nimble_csis_state_reset(void);
int bt_le_nimble_csis_init(void *csis_svc, uint8_t count);
int bt_le_nimble_csis_deinit(void *csis_svc);
#ifdef __cplusplus
}
#endif
@@ -19,6 +19,8 @@ extern "C" {
int bt_le_nimble_gmcs_attr_handle_set(void);
void bt_le_nimble_mcs_state_reset(void);
int bt_le_nimble_gmcs_init(bool ots_included);
int bt_le_nimble_mcs_attr_handle_set(void);
@@ -19,6 +19,8 @@ extern "C" {
int bt_le_nimble_mics_attr_handle_set(void);
void bt_le_nimble_mics_state_reset(void);
int bt_le_nimble_mics_init(void *micp_inc);
#ifdef __cplusplus
@@ -21,6 +21,8 @@ int bt_le_nimble_pacs_attr_handle_set(void);
int bt_le_nimble_pacs_init(void);
int bt_le_nimble_pacs_deinit(void);
#ifdef __cplusplus
}
#endif
@@ -18,10 +18,14 @@ int bt_le_nimble_gtbs_attr_handle_set(void);
int bt_le_nimble_gtbs_init(void);
int bt_le_nimble_gtbs_deinit(void);
int bt_le_nimble_tbs_attr_handle_set(void);
int bt_le_nimble_tbs_init(void);
int bt_le_nimble_tbs_deinit(uint8_t bearer_index);
#ifdef __cplusplus
}
#endif
@@ -16,6 +16,8 @@ extern "C" {
int bt_le_nimble_vcs_attr_handle_set(void);
void bt_le_nimble_vcs_state_reset(void);
int bt_le_nimble_vcs_init(void *vcp_inc);
#ifdef __cplusplus
@@ -20,6 +20,7 @@
#include "nimble/init.h"
#include "common/init.h"
#include "common/audio_attr.h"
#include "../../../lib/include/audio.h"
@@ -159,6 +160,9 @@ LOG_MODULE_REGISTER(LEA_NINIT, CONFIG_BT_ISO_LOG_LEVEL);
_Static_assert(TOTAL_CCCDS_COUNT <= CONFIG_BT_NIMBLE_MAX_CCCDS, "Too small BT_NIMBLE_MAX_CCCDS");
static BT_AUDIO_EXT_RAM_BSS_ATTR bool nimble_svcs_registered;
static BT_AUDIO_EXT_RAM_BSS_ATTR bool nimble_gatts_started;
int bt_le_nimble_audio_init(void)
{
int err = 0;
@@ -169,6 +173,23 @@ int bt_le_nimble_audio_init(void)
return err;
}
#if CONFIG_BT_CSIP_SET_MEMBER
bt_le_nimble_csis_state_reset();
#endif /* CONFIG_BT_CSIP_SET_MEMBER */
#if CONFIG_BT_VCP_VOL_REND
bt_le_nimble_vcs_state_reset();
#endif /* CONFIG_BT_VCP_VOL_REND */
#if CONFIG_BT_MICP_MIC_DEV
bt_le_nimble_mics_state_reset();
#endif /* CONFIG_BT_MICP_MIC_DEV */
#if CONFIG_BT_MCS
bt_le_nimble_mcs_state_reset();
#endif /* CONFIG_BT_MCS */
if (nimble_svcs_registered) {
return 0;
}
ble_svc_gap_init();
ble_svc_gatt_init();
@@ -222,6 +243,8 @@ int bt_le_nimble_audio_init(void)
#endif /* CONFIG_BT_HAS */
#endif /* (BLE_AUDIO_SVC_DEFERRED_ADD == 0) */
nimble_svcs_registered = true;
return err;
}
@@ -442,6 +465,14 @@ int bt_le_nimble_micp_mic_dev_init(void)
}
#endif /* CONFIG_BT_MICP_MIC_DEV */
void bt_le_nimble_audio_deinit(void)
{
LOG_DBG("[N]AudioDeinit");
/* Nothing to release: the services added here are boot-scoped and the defs
* NimBLE holds reference no lib memory, so resources_deinit() is safe. */
}
int bt_le_nimble_audio_start(void *info)
{
struct bt_gatt_service *inc_csis_svc = NULL;
@@ -499,10 +530,17 @@ int bt_le_nimble_audio_start(void *info)
}
#endif /* CONFIG_BT_GMAP */
err = ble_gatts_start();
if (err) {
LOG_ERR("[N]GattsStartFail[%d]", err);
return err;
/* Once per boot: ble_gatts_start() resets the whole ATT database and only
* re-registers defs added since the last start, so calling it again drops
* other owners' services. attr_handle_set() still runs every cycle. */
if (!nimble_gatts_started) {
err = ble_gatts_start();
if (err) {
LOG_ERR("[N]GattsStartFail[%d]", err);
return err;
}
nimble_gatts_started = true;
}
err = nimble_gatt_attr_handle_set();
@@ -254,3 +254,9 @@ free:
}
return rc;
}
int bt_le_nimble_ascs_deinit(void)
{
LOG_DBG("[N]AscsDeinit");
return 0;
}
@@ -218,3 +218,9 @@ free:
}
return rc;
}
int bt_le_nimble_bass_deinit(void)
{
LOG_DBG("[N]BassDeinit");
return 0;
}
@@ -87,6 +87,9 @@ static BT_AUDIO_EXT_RAM_BSS_ATTR struct csis_inst {
static BT_AUDIO_EXT_RAM_BSS_ATTR uint8_t csis_svc_count;
static BT_AUDIO_EXT_RAM_BSS_ATTR uint8_t csis_reg_count;
static BT_AUDIO_EXT_RAM_BSS_ATTR bool csis_registered;
/* Extra one for terminating the CSIS service array */
static BT_AUDIO_EXT_RAM_BSS_ATTR struct ble_gatt_svc_def gatt_svc_csis[CSIS_SVC_COUNT + 1];
@@ -162,7 +165,14 @@ int bt_le_nimble_csis_attr_handle_set(void)
struct bt_gatt_service *zsvc = csis_insts[i].svc_p;
BT_LE_ASSERT(zsvc);
BT_LE_ASSERT(csis_insts[i].sirk_handle >= 2);
/* Zero means no registration round ever covered this instance, so it is
* absent from the ATT database and there is no range to anchor. Happens
* when CSIS is first registered after the boot's ble_gatts_start(). */
if (csis_insts[i].sirk_handle < 2) {
LOG_ERR("[N]CsisNoAttrHdl[%u]", i);
return -1;
}
/* SIRK is always the first characteristic, so its value handle anchors the range. */
start_handle = csis_insts[i].sirk_handle - 2; /* server attr handle & char def handle */
@@ -295,6 +305,11 @@ static void csis_svc_init(struct csis_inst *inst,
BT_LE_ASSERT(chr_cnt < CSIS_CHR_COUNT);
}
void bt_le_nimble_csis_state_reset(void)
{
csis_svc_count = 0;
}
int bt_le_nimble_csis_init(void *svc, uint8_t count)
{
bool csis_added = false;
@@ -307,11 +322,27 @@ int bt_le_nimble_csis_init(void *svc, uint8_t count)
return -1;
}
/* The attribute table is built by the boot's single ble_gatts_start(), so a
* different instance count now needs a table this boot cannot produce. Fail
* here, not later on a handle NimBLE never assigned. */
if (csis_registered && count != csis_reg_count) {
LOG_ERR("[N]CsisCountChanged[%u][%u]", csis_reg_count, count);
return -1;
}
csis_svc_count = count;
/* Refreshed every cycle: the lib frees and reallocates its service objects,
* while the NimBLE defs below are built once and stay in the database. */
for (size_t i = 0; i < csis_svc_count; i++) {
csis_insts[i].svc_p = ((struct bt_gatt_service **)svc)[i];
}
if (csis_registered) {
return 0;
}
for (size_t i = 0; i < csis_svc_count; i++) {
csis_svc_init(&csis_insts[i], &gatt_svc_csis[i], csis_insts[i].svc_p);
}
@@ -333,6 +364,9 @@ int bt_le_nimble_csis_init(void *svc, uint8_t count)
goto free;
}
csis_reg_count = count;
csis_registered = true;
return 0;
free:
@@ -348,3 +382,11 @@ free:
csis_svc_count = 0;
return rc;
}
int bt_le_nimble_csis_deinit(void *csis_svc)
{
ARG_UNUSED(csis_svc);
LOG_DBG("[N]CsisDeinit");
return 0;
}
@@ -630,6 +630,13 @@ static int inc_ots_svc_init(void)
}
#endif /* CONFIG_BT_OTS */
void bt_le_nimble_mcs_state_reset(void)
{
#if CONFIG_BT_OTS
inc_ots_svc_count = 0;
#endif /* CONFIG_BT_OTS */
}
int bt_le_nimble_gmcs_init(bool ots_included)
{
#if CONFIG_BT_OTS
@@ -346,6 +346,11 @@ static void inc_aics_svc_init(struct inc_aics_inst *inst,
INC_AICS_CHR_FLAGS_DESCRIPTION);
}
void bt_le_nimble_mics_state_reset(void)
{
inc_aics_svc_count = 0;
}
int bt_le_nimble_mics_init(void *micp_inc)
{
struct bt_micp_included *micp_included;
@@ -275,3 +275,9 @@ int bt_le_nimble_pacs_init(void)
return 0;
}
int bt_le_nimble_pacs_deinit(void)
{
LOG_DBG("[N]PacsDeinit");
return 0;
}
@@ -372,3 +372,15 @@ int bt_le_nimble_tbs_init(void)
return 0;
}
int bt_le_nimble_gtbs_deinit(void)
{
LOG_DBG("[N]GtbsDeinit");
return 0;
}
int bt_le_nimble_tbs_deinit(uint8_t bearer_index)
{
LOG_DBG("[N]TbsDeinit[%u]", bearer_index);
return 0;
}
@@ -542,6 +542,12 @@ static void inc_aics_svc_init(struct inc_aics_inst *inst,
INC_AICS_CHR_FLAGS_DESCRIPTION);
}
void bt_le_nimble_vcs_state_reset(void)
{
inc_vocs_svc_count = 0;
inc_aics_svc_count = 0;
}
int bt_le_nimble_vcs_init(void *vcp_inc)
{
struct bt_vcp_included *vcp_included;
@@ -49,14 +49,26 @@ void bt_le_audio_deinit(void);
int bt_le_ascs_init(void);
int bt_le_ascs_deinit(void);
int bt_le_pacs_deinit(void);
int bt_le_csis_deinit(const void *svc_inst);
int bt_le_bass_init(void);
int bt_le_bass_deinit(void);
int bt_le_tmas_init(void);
int bt_le_gtbs_init(void);
int bt_le_gtbs_deinit(void);
int bt_le_tbs_init(void);
int bt_le_tbs_deinit(uint8_t bearer_index);
int bt_le_has_init(void);
int bt_le_media_proxy_pl_init(void);
+360 -7
View File
@@ -185,7 +185,7 @@ static const uint16_t ext_structs[] = {
sizeof(struct bt_bond_info),
};
#define LEA_VERSION (0x20260820)
#define LEA_VERSION (0x20260824)
struct lib_ext_cfgs {
/* BLE */
@@ -1085,16 +1085,20 @@ struct lib_ext_funcs {
/* Scan */
int (*_scan_cb_register)(struct bt_le_scan_cb *cb);
void (*_scan_cb_unregister)(struct bt_le_scan_cb *cb);
int (*_scan_start)(const struct bt_le_scan_param *param, void *cb);
int (*_scan_stop)(void);
int (*_pa_sync_cb_register)(struct bt_le_per_adv_sync_cb *cb);
int (*_pa_sync_cb_unregister)(struct bt_le_per_adv_sync_cb *cb);
int (*_pa_sync_get_info)(struct bt_le_per_adv_sync *per_adv_sync,
struct bt_le_per_adv_sync_info *info);
struct bt_le_per_adv_sync *(*_pa_sync_lookup_addr)(const bt_addr_le_t *adv_addr, uint8_t sid);
/* Connection */
int (*_conn_cb_register)(struct bt_conn_cb *cb);
int (*_conn_cb_unregister)(struct bt_conn_cb *cb);
int (*_conn_auth_info_cb_register)(struct bt_conn_auth_info_cb *cb);
int (*_conn_auth_info_cb_unregister)(struct bt_conn_auth_info_cb *cb);
void (*_conn_foreach)(enum bt_conn_type type,
void (*func)(struct bt_conn *conn, void *data),
void *data);
@@ -1295,14 +1299,18 @@ static const struct lib_ext_funcs ext_funcs = {
._bt_foreach_bond = (void *)bt_foreach_bond,
._scan_cb_register = (void *)bt_le_scan_cb_register,
._scan_cb_unregister = (void *)bt_le_scan_cb_unregister,
._scan_start = (void *)bt_le_scan_start,
._scan_stop = (void *)bt_le_scan_stop,
._pa_sync_cb_register = (void *)bt_le_per_adv_sync_cb_register,
._pa_sync_cb_unregister = (void *)bt_le_per_adv_sync_cb_unregister,
._pa_sync_get_info = (void *)bt_le_per_adv_sync_get_info,
._pa_sync_lookup_addr = (void *)bt_le_per_adv_sync_lookup_addr,
._conn_cb_register = (void *)bt_conn_cb_register,
._conn_cb_unregister = (void *)bt_conn_cb_unregister,
._conn_auth_info_cb_register = (void *)bt_conn_auth_info_cb_register,
._conn_auth_info_cb_unregister = (void *)bt_conn_auth_info_cb_unregister,
._conn_foreach = (void *)bt_conn_foreach,
._conn_get_info = (void *)bt_conn_get_info,
._conn_index = (void *)bt_conn_index,
@@ -2075,6 +2083,260 @@ static int lib_audio_resources_init(void)
return err;
}
/* Reverse order of lib_audio_resources_init(): a module may reference an earlier
* one. Failures log and continue - stopping half way leaves more behind. Keep
* every lib_*_init() listed here; a missing counterpart leaks silently. */
static void lib_audio_resources_deinit(void)
{
#if CONFIG_BT_GMAP
/* No init counterpart above: bt_gmap_register() allocates GMAS on demand,
* so only the deinit side is listed. */
if (lib_gmap_server_deinit()) {
LOG_ERR("LibGmapSrvDeinitFail");
}
if (lib_gmap_client_deinit()) {
LOG_ERR("LibGmapCliDeinitFail");
}
#endif /* CONFIG_BT_GMAP */
#if CONFIG_BT_HAS_CLIENT
if (lib_has_client_deinit()) {
LOG_ERR("LibHasCliDeinitFail");
}
#endif /* CONFIG_BT_HAS_CLIENT */
#if CONFIG_BT_HAS
if (lib_has_deinit()) {
LOG_ERR("LibHasDeinitFail");
}
#endif /* CONFIG_BT_HAS */
#if CONFIG_BT_VOCS_CLIENT
if (lib_vocs_client_deinit()) {
LOG_ERR("LibVocsClientDeinitFail");
}
#endif /* CONFIG_BT_VOCS_CLIENT */
#if CONFIG_BT_VOCS || CONFIG_BT_VOCS_CLIENT
if (lib_vocs_deinit()) {
LOG_ERR("LibVocsDeinitFail");
}
#endif /* CONFIG_BT_VOCS || CONFIG_BT_VOCS_CLIENT */
#if CONFIG_BT_VCP_VOL_REND
if (lib_vcp_vol_rend_deinit()) {
LOG_ERR("LibVcpVolRendDeinitFail");
}
#endif /* CONFIG_BT_VCP_VOL_REND */
#if CONFIG_BT_VCP_VOL_CTLR
if (lib_vcp_vol_ctlr_deinit()) {
LOG_ERR("LibVcpVolCtlrDeinitFail");
}
#endif /* CONFIG_BT_VCP_VOL_CTLR */
#if CONFIG_BT_TMAP
if (lib_tmap_deinit()) {
LOG_ERR("LibTmapDeinitFail");
}
#endif /* CONFIG_BT_TMAP */
#if CONFIG_BT_TBS
if (lib_tbs_deinit()) {
LOG_ERR("LibTbsDeinitFail");
}
#endif /* CONFIG_BT_TBS */
#if CONFIG_BT_TBS_CLIENT
if (lib_tbs_client_deinit()) {
LOG_ERR("LibTbsCliDeinitFail");
}
#endif /* CONFIG_BT_TBS_CLIENT */
#if CONFIG_BT_PBP
if (lib_pbp_deinit()) {
LOG_ERR("LibPbpDeinitFail");
}
#endif /* CONFIG_BT_PBP */
#if CONFIG_BT_MPL
if (lib_mpl_deinit()) {
LOG_ERR("LibMplDeinitFail");
}
#endif /* CONFIG_BT_MPL */
#if CONFIG_BT_MICP_MIC_DEV
if (lib_micp_mic_dev_deinit()) {
LOG_ERR("LibMicpMicDevDeinitFail");
}
#endif /* CONFIG_BT_MICP_MIC_DEV */
#if CONFIG_BT_MICP_MIC_CTLR
if (lib_micp_mic_ctlr_deinit()) {
LOG_ERR("LibMicpMicCtlrDeinitFail");
}
#endif /* CONFIG_BT_MICP_MIC_CTLR */
#if CONFIG_BT_MCTL
if (lib_media_proxy_deinit()) {
LOG_ERR("LibMediaProxyDeinitFail");
}
#endif /* CONFIG_BT_MCTL */
#if CONFIG_BT_MCS
if (lib_mcs_deinit()) {
LOG_ERR("LibMcsDeinitFail");
}
#endif /* CONFIG_BT_MCS */
#if CONFIG_BT_MCC
if (lib_mcc_deinit()) {
LOG_ERR("LibMccDeinitFail");
}
#endif /* CONFIG_BT_MCC */
#if CONFIG_BT_CSIP_SET_COORDINATOR
if (lib_csip_set_coordinator_deinit()) {
LOG_ERR("LibCsipSetCoordinatorDeinitFail");
}
#endif /* CONFIG_BT_CSIP_SET_COORDINATOR */
#if CONFIG_BT_CSIP_SET_MEMBER
if (lib_csip_set_member_deinit()) {
LOG_ERR("LibCsipSetMemberDeinitFail");
}
#endif /* CONFIG_BT_CSIP_SET_MEMBER */
#if CONFIG_BT_BAP_STREAM
if (lib_codec_deinit()) {
LOG_ERR("LibCodecDeinitFail");
}
#endif /* CONFIG_BT_BAP_STREAM */
#if CONFIG_BT_CCP_CALL_CONTROL_SERVER
if (lib_ccp_call_control_server_deinit()) {
LOG_ERR("LibCcpCallControlServerDeinitFail");
}
#endif /* CONFIG_BT_CCP_CALL_CONTROL_SERVER */
#if CONFIG_BT_CCP_CALL_CONTROL_CLIENT
if (lib_ccp_call_control_client_deinit()) {
LOG_ERR("LibCcpCallControlClientDeinitFail");
}
#endif /* CONFIG_BT_CCP_CALL_CONTROL_CLIENT */
if (lib_ccid_deinit()) {
LOG_ERR("LibCcidDeinitFail");
}
#if CONFIG_BT_CAP_COMMANDER
if (lib_cap_commander_deinit()) {
LOG_ERR("LibCapComDeinitFail");
}
#endif /* CONFIG_BT_CAP_COMMANDER */
#if CONFIG_BT_CAP_ACCEPTOR
if (lib_cap_acceptor_deinit()) {
LOG_ERR("LibCapAccDeinitFail");
}
#endif /* CONFIG_BT_CAP_ACCEPTOR */
#if CONFIG_BT_CAP_INITIATOR
if (lib_cap_initiator_deinit()) {
LOG_ERR("LibCapIniDeinitFail");
}
#endif /* CONFIG_BT_CAP_INITIATOR */
#if CONFIG_BT_CAP
if (lib_cap_stream_deinit()) {
LOG_ERR("LibCapStreamDeinitFail");
}
#endif /* CONFIG_BT_CAP */
#if CONFIG_BT_CAP_INITIATOR_UNICAST || CONFIG_BT_CAP_COMMANDER
if (lib_cap_common_deinit()) {
LOG_ERR("LibCapCommonDeinitFail");
}
#endif /* CONFIG_BT_CAP_INITIATOR_UNICAST || CONFIG_BT_CAP_COMMANDER */
#if CONFIG_BT_BAP_UNICAST_SERVER
if (lib_bap_unicast_server_deinit()) {
LOG_ERR("LibBapUniSrvDeinitFail");
}
#endif /* CONFIG_BT_BAP_UNICAST_SERVER */
#if CONFIG_BT_BAP_UNICAST_CLIENT
if (lib_bap_unicast_client_deinit()) {
LOG_ERR("LibBapUniCliDeinitFail");
}
#endif /* CONFIG_BT_BAP_UNICAST_CLIENT */
#if CONFIG_BT_BAP_BROADCAST_ASSISTANT
if (lib_bap_broadcast_assistant_deinit()) {
LOG_ERR("LibBapBaDeinitFail");
}
#endif /* CONFIG_BT_BAP_BROADCAST_ASSISTANT */
#if CONFIG_BT_BAP_SCAN_DELEGATOR
if (lib_bap_scan_delegator_deinit()) {
LOG_ERR("LibBapSdeDeinitFail");
}
#endif /* CONFIG_BT_BAP_SCAN_DELEGATOR */
#if CONFIG_BT_BAP_BROADCAST_SOURCE
if (lib_bap_broadcast_source_deinit()) {
LOG_ERR("LibBapBsrcDeinitFail");
}
#endif /* CONFIG_BT_BAP_BROADCAST_SOURCE */
#if CONFIG_BT_BAP_BROADCAST_SINK
if (lib_bap_broadcast_sink_deinit()) {
LOG_ERR("LibBapBsnkDeinitFail");
}
#endif /* CONFIG_BT_BAP_BROADCAST_SINK */
#if CONFIG_BT_PACS
if (lib_pacs_deinit()) {
LOG_ERR("LibPacsDeinitFail");
}
#endif /* CONFIG_BT_PACS */
#if CONFIG_BT_ASCS
if (lib_ascs_deinit()) {
LOG_ERR("LibAscsDeinitFail");
}
#endif /* CONFIG_BT_ASCS */
#if CONFIG_BT_BAP_STREAM
if (lib_bap_stream_deinit()) {
LOG_ERR("LibBapStreamDeinitFail");
}
if (lib_bap_iso_deinit()) {
LOG_ERR("LibBapIsoDeinitFail");
}
#endif /* CONFIG_BT_BAP_STREAM */
#if CONFIG_BT_BAP_BASE
if (lib_bap_base_deinit()) {
LOG_ERR("LibBapBaseDeinitFail");
}
#endif /* CONFIG_BT_BAP_BASE */
#if CONFIG_BT_AICS_CLIENT
if (lib_aics_client_deinit()) {
LOG_ERR("LibAicsClientDeinitFail");
}
#endif /* CONFIG_BT_AICS_CLIENT */
#if CONFIG_BT_AICS || CONFIG_BT_AICS_CLIENT
if (lib_aics_deinit()) {
LOG_ERR("LibAicsDeinitFail");
}
#endif /* CONFIG_BT_AICS || CONFIG_BT_AICS_CLIENT */
}
int bt_le_audio_init(void)
{
int err;
@@ -2112,13 +2374,13 @@ int bt_le_audio_init(void)
err = lib_audio_resources_init();
if (err) {
return err;
goto deinit_lib;
}
#if CONFIG_BT_OTS || CONFIG_BT_OTS_CLIENT
err = bt_le_l2cap_ots_init();
if (err) {
return err;
goto deinit_lib;
}
#endif
@@ -2128,20 +2390,33 @@ int bt_le_audio_init(void)
err = bt_le_nimble_audio_init();
#endif
if (err) {
#if CONFIG_BT_OTS || CONFIG_BT_OTS_CLIENT
bt_le_l2cap_ots_deinit();
#endif
return err;
goto deinit_ots;
}
return 0;
deinit_ots:
#if CONFIG_BT_OTS || CONFIG_BT_OTS_CLIENT
bt_le_l2cap_ots_deinit();
#endif
deinit_lib:
lib_audio_resources_deinit();
return err;
}
void bt_le_audio_deinit(void)
{
#if CONFIG_BT_BLUEDROID_ENABLED
bt_le_bluedroid_audio_deinit();
#else
bt_le_nimble_audio_deinit();
#endif
#if CONFIG_BT_OTS || CONFIG_BT_OTS_CLIENT
bt_le_l2cap_ots_deinit();
#endif
lib_audio_resources_deinit();
}
#if BLE_AUDIO_SVC_DEFERRED_ADD
@@ -2156,6 +2431,17 @@ int bt_le_ascs_init(void)
return bt_le_nimble_ascs_init();
#endif
}
int bt_le_ascs_deinit(void)
{
LOG_DBG("AscsDeinit");
#if CONFIG_BT_BLUEDROID_ENABLED
return bt_le_bluedroid_ascs_deinit();
#else
return bt_le_nimble_ascs_deinit();
#endif
}
#endif /* CONFIG_BT_ASCS */
#if CONFIG_BT_BAP_SCAN_DELEGATOR
@@ -2169,6 +2455,17 @@ int bt_le_bass_init(void)
return bt_le_nimble_bass_init();
#endif
}
int bt_le_bass_deinit(void)
{
LOG_DBG("BassDeinit");
#if CONFIG_BT_BLUEDROID_ENABLED
return bt_le_bluedroid_bass_deinit();
#else
return bt_le_nimble_bass_deinit();
#endif
}
#endif /* CONFIG_BT_BAP_SCAN_DELEGATOR */
#if CONFIG_BT_TMAP
@@ -2196,6 +2493,17 @@ int bt_le_gtbs_init(void)
#endif
}
int bt_le_gtbs_deinit(void)
{
LOG_DBG("GtbsDeinit");
#if CONFIG_BT_BLUEDROID_ENABLED
return bt_le_bluedroid_gtbs_deinit();
#else
return bt_le_nimble_gtbs_deinit();
#endif
}
int bt_le_tbs_init(void)
{
LOG_DBG("TbsInit");
@@ -2206,6 +2514,17 @@ int bt_le_tbs_init(void)
return bt_le_nimble_tbs_init();
#endif
}
int bt_le_tbs_deinit(uint8_t bearer_index)
{
LOG_DBG("TbsDeinit[%u]", bearer_index);
#if CONFIG_BT_BLUEDROID_ENABLED
return bt_le_bluedroid_tbs_deinit(bearer_index);
#else
return bt_le_nimble_tbs_deinit(bearer_index);
#endif
}
#endif /* CONFIG_BT_TBS */
#if CONFIG_BT_HAS
@@ -2261,6 +2580,40 @@ int bt_le_micp_mic_dev_init(void)
#endif /* CONFIG_BT_MICP_MIC_DEV */
#endif /* BLE_AUDIO_SVC_DEFERRED_ADD */
#if CONFIG_BT_PACS
int bt_le_pacs_deinit(void)
{
LOG_DBG("PacsDeinit");
#if CONFIG_BT_BLUEDROID_ENABLED
return bt_le_bluedroid_pacs_deinit();
#else
return bt_le_nimble_pacs_deinit();
#endif
}
#endif /* CONFIG_BT_PACS */
#if CONFIG_BT_CSIP_SET_MEMBER
int bt_le_csis_deinit(const void *svc_inst)
{
struct bt_gatt_service *csis_svc;
LOG_DBG("CsisDeinit");
csis_svc = lib_csip_set_member_svc_get(svc_inst);
if (!csis_svc) {
LOG_ERR("CsisSvcGetFail");
return -ENODEV;
}
#if CONFIG_BT_BLUEDROID_ENABLED
return bt_le_bluedroid_csis_deinit(csis_svc);
#else
return bt_le_nimble_csis_deinit(csis_svc);
#endif
}
#endif /* CONFIG_BT_CSIP_SET_MEMBER */
int bt_le_audio_start(void *info)
{
LOG_DBG("AudioStart");
@@ -44,10 +44,12 @@ static BT_AUDIO_EXT_RAM_BSS_ATTR sys_slist_t l2cap_servers;
extern int bt_gatt_ots_conn_cb_register(void);
extern void bt_gatt_ots_conn_cb_unregister(void);
extern int bt_gatt_ots_instances_prepare(void);
extern void bt_ots_instances_rewind(void);
#endif /* CONFIG_BT_OTS */
#if CONFIG_BT_OTS_CLIENT
extern int bt_gatt_ots_client_conn_cb_register(void);
extern void bt_gatt_ots_client_conn_cb_unregister(void);
extern void bt_ots_client_unregister_all(void);
#endif /* CONFIG_BT_OTS_CLIENT */
extern int bt_gatt_ots_l2cap_init(void);
@@ -361,17 +363,31 @@ static void l2cap_sent(uint16_t conn_handle, uint16_t psm)
}
}
_IDF_ONLY
void bt_le_l2cap_event_free(void *data)
{
struct bt_le_l2cap_event *qev = data;
if (qev == NULL) {
return;
}
/* Only RECEIVED carries a buffer of its own; the rest are header-only. */
if (qev->type == BT_LE_L2CAP_EVENT_RECEIVED) {
free(qev->received.data);
}
free(qev);
}
static int l2cap_post_event(struct bt_le_l2cap_event *qev)
{
int err;
err = bt_le_iso_task_post(ISO_QUEUE_ITEM_TYPE_L2CAP_EVENT, qev, sizeof(*qev));
if (err) {
LOG_ERR("L2capPostFail[%d][%u]", err, qev->type);
if (qev->type == BT_LE_L2CAP_EVENT_RECEIVED) {
free(qev->received.data);
}
free(qev);
ISO_POST_FAIL_LOG(err, "L2capPostFail[%d][%u]", err, qev->type);
bt_le_l2cap_event_free(qev);
}
return err;
@@ -811,14 +827,20 @@ void bt_le_l2cap_ots_deinit(void)
{
LOG_DBG("L2capOtsDeinit");
/* TODO(deinit): unassign OTS obj managers / clear instance->obj_manager. */
/* Symmetric with the registers in _init: conn_cbs is never reset elsewhere. */
#if CONFIG_BT_OTS
bt_gatt_ots_conn_cb_unregister();
/* Give the instances handed out by bt_ots_free_instance_get() back to the
* pool; the profiles that took them are released right after this. */
bt_ots_instances_rewind();
#endif /* CONFIG_BT_OTS */
#if CONFIG_BT_OTS_CLIENT
bt_gatt_ots_client_conn_cb_unregister();
/* Before lib_audio_resources_deinit() frees the bt_ots_client instances
* the table points at - this runs first in bt_le_audio_deinit(). */
bt_ots_client_unregister_all();
#endif /* CONFIG_BT_OTS_CLIENT */
#if CONFIG_BT_BLUEDROID_ENABLED
@@ -106,6 +106,8 @@ int bt_le_l2cap_post_sent(uint16_t conn_handle, uint16_t psm);
void bt_le_l2cap_handle_event(void *data, size_t data_len);
void bt_le_l2cap_event_free(void *data);
int bt_le_l2cap_ots_init(void);
void bt_le_l2cap_ots_deinit(void);
@@ -563,6 +563,9 @@ int bt_ots_init(struct bt_ots *ots,
/* Check OACP supported features against Kconfig. */
if (ots_init->features.oacp & (~((uint32_t) OACP_FEAT))) {
/* cb is what bt_ots_instances_rewind() reads to decide there is an init
* to reverse, so every bail after the assignment above has to undo it. */
ots->cb = NULL;
return -ENOTSUP;
}
@@ -575,6 +578,7 @@ int bt_ots_init(struct bt_ots *ots,
/* Check OLCP supported features against Kconfig. */
if (ots_init->features.olcp & (~((uint32_t) OLCP_FEAT))) {
ots->cb = NULL;
return -ENOTSUP;
}
ots->features.olcp = ots_init->features.olcp;
@@ -583,6 +587,7 @@ int bt_ots_init(struct bt_ots *ots,
/* Register L2CAP context. */
err = bt_gatt_ots_l2cap_register(&ots->l2cap);
if (err) {
ots->cb = NULL;
return err;
}
@@ -594,6 +599,7 @@ int bt_ots_init(struct bt_ots *ots,
LOG_ERR("OtsL2capUnregFail[%d]", unreg_err);
}
ots->cb = NULL;
return err;
}
@@ -768,6 +774,38 @@ struct bt_ots *bt_ots_free_instance_get(void)
return &BT_GATT_OTS_INSTANCE_LIST_START[instance_cnt++];
}
/* Both pools only count up, so without this a second init finds them exhausted
* (McsNoFreeOtsInst / OtsObjMgrInstUnavail). Safe here: the profile that took an
* instance has dropped its reference by now. */
void bt_ots_instances_rewind(void)
{
for (struct bt_ots *instance = BT_GATT_OTS_INSTANCE_LIST_START;
instance != BT_GATT_OTS_INSTANCE_LIST_END; instance++) {
/* cb is the marker for "bt_ots_init() ran here": it is set on entry and
* cleared again on every failure path, so it is exactly the set of
* instances with something to reverse. Reverse order of that function. */
if (instance->cb != NULL) {
k_work_deinit_delayable(&instance->olcp_ind.work);
k_work_deinit_delayable(&instance->oacp_ind.work);
if (IS_ENABLED(CONFIG_BT_OTS_DIR_LIST_OBJ)) {
bt_ots_dir_list_deinit(&instance->dir_list);
}
(void)bt_gatt_service_unregister(instance->service);
(void)bt_gatt_ots_l2cap_unregister(&instance->l2cap);
instance->cb = NULL;
}
if (instance->obj_manager != NULL) {
bt_gatt_ots_obj_manager_release(instance->obj_manager);
instance->obj_manager = NULL;
}
}
instance_cnt = 0;
}
int bt_gatt_ots_instances_prepare(void)
{
uint32_t index;
@@ -637,6 +637,15 @@ int bt_ots_client_unregister(uint8_t index)
return 0;
}
void bt_ots_client_unregister_all(void)
{
for (uint8_t i = 0; i < ARRAY_SIZE(otc_insts); i++) {
if (otc_insts[i].otc_inst != NULL) {
(void)bt_ots_client_unregister(i);
}
}
}
__attribute__((unused))
int bt_ots_client_read_feature(struct bt_ots_client *otc_inst,
struct bt_conn *conn)
@@ -320,6 +320,16 @@ void bt_ots_dir_list_init(struct bt_ots_dir_list **dir_list, void *obj_manager)
dir_list_update_size(*dir_list, obj_manager);
}
void bt_ots_dir_list_deinit(struct bt_ots_dir_list **dir_list)
{
if (*dir_list == NULL) {
return;
}
memset(*dir_list, 0, sizeof(**dir_list));
*dir_list = NULL;
}
ssize_t bt_ots_dir_list_content_get(struct bt_ots_dir_list *dir_list, void *obj_manager,
void **data, size_t len, off_t offset)
{
@@ -161,6 +161,7 @@ void bt_ots_dir_list_selected(struct bt_ots_dir_list *dir_list, void *obj_manage
struct bt_gatt_ots_object *cur_obj);
void bt_ots_dir_list_content_changed(struct bt_ots_dir_list *dir_list, void *obj_manager);
void bt_ots_dir_list_init(struct bt_ots_dir_list **dir_list, void *obj_manager);
void bt_ots_dir_list_deinit(struct bt_ots_dir_list **dir_list);
ssize_t bt_ots_dir_list_content_get(struct bt_ots_dir_list *dir_list, void *obj_manager,
void **data, size_t len, off_t offset);
bool bt_ots_dir_list_is_idle(const struct bt_ots_dir_list *dir_list);
@@ -256,3 +256,19 @@ void *bt_gatt_ots_obj_manager_assign(void)
return cur_manager;
}
void bt_gatt_ots_obj_manager_release(void *obj_manager)
{
struct bt_gatt_ots_obj_manager *manager = obj_manager;
if (manager == NULL) {
return;
}
for (size_t i = 0; i < CONFIG_BT_OTS_MAX_OBJ_CNT; i++) {
manager->pool[i].is_allocated = false;
}
sys_dlist_init(&manager->list);
manager->is_assigned = false;
}
@@ -43,6 +43,8 @@ bool bt_gatt_ots_obj_manager_obj_contains(void *obj_manager, struct bt_gatt_ots_
void *bt_gatt_ots_obj_manager_assign(void);
void bt_gatt_ots_obj_manager_release(void *obj_manager);
#ifdef __cplusplus
}
#endif
@@ -32,86 +32,128 @@ struct bt_gatt_service;
extern struct bt_aics *lib_aics_insts_get(void);
extern void *lib_aics_svc_get(struct bt_aics *aics);
extern int lib_aics_init(void);
extern int lib_aics_deinit(void);
extern int lib_aics_client_init(void);
extern int lib_aics_client_deinit(void);
extern int lib_bap_base_init(void);
extern int lib_bap_base_deinit(void);
extern int lib_bap_iso_init(void);
extern int lib_bap_iso_deinit(void);
extern int lib_bap_stream_init(void);
extern int lib_bap_stream_deinit(void);
extern struct bt_gatt_service *lib_ascs_svc_get(void);
extern int lib_ascs_init(void);
extern int lib_ascs_deinit(void);
extern struct bt_gatt_service *lib_bap_bass_svc_get(void);
extern int lib_bap_broadcast_assistant_init(void);
extern int lib_bap_broadcast_assistant_deinit(void);
extern int lib_bap_broadcast_sink_init(void);
extern int lib_bap_broadcast_sink_deinit(void);
extern int lib_bap_broadcast_source_init(void);
extern int lib_bap_broadcast_source_deinit(void);
extern int lib_bap_scan_delegator_init(void);
extern int lib_bap_scan_delegator_deinit(void);
extern int lib_bap_unicast_client_init(void);
extern int lib_bap_unicast_client_deinit(void);
extern int lib_bap_unicast_server_init(void);
extern int lib_bap_unicast_server_deinit(void);
extern struct bt_gatt_service *lib_pacs_svc_get(void);
extern int lib_pacs_init(void);
extern int lib_pacs_deinit(void);
extern struct bt_gatt_service *lib_cas_svc_get(void);
extern int lib_cap_common_init(void);
extern int lib_cap_common_deinit(void);
extern int lib_cap_stream_init(void);
extern int lib_cap_stream_deinit(void);
extern int lib_cap_initiator_init(void);
extern int lib_cap_initiator_deinit(void);
extern int lib_cap_acceptor_init(void);
extern int lib_cap_acceptor_deinit(void);
extern int lib_cap_commander_init(void);
extern int lib_cap_commander_deinit(void);
extern int lib_ccid_init(void);
extern int lib_ccid_deinit(void);
extern int lib_ccp_call_control_client_init(void);
extern int lib_ccp_call_control_client_deinit(void);
extern int lib_ccp_call_control_server_init(void);
extern int lib_ccp_call_control_server_deinit(void);
extern int lib_codec_init(void);
extern int lib_codec_deinit(void);
extern int lib_csip_set_coordinator_init(void);
extern int lib_csip_set_coordinator_deinit(void);
extern void *lib_csip_set_member_svc_get(const void *svc_inst);
extern int lib_csip_set_member_init(void);
extern int lib_csip_set_member_deinit(void);
extern int lib_gmap_client_init(void);
extern int lib_gmap_client_deinit(void);
extern struct bt_gatt_service *lib_gmas_svc_get(void);
extern int lib_gmap_server_deinit(void);
extern struct bt_gatt_service *lib_has_svc_get(void);
extern int lib_has_init(void);
extern int lib_has_deinit(void);
extern int lib_has_client_init(void);
extern int lib_has_client_deinit(void);
extern int lib_mcc_init(void);
extern int lib_mcc_deinit(void);
extern struct bt_gatt_service *lib_gmcs_svc_get(void);
extern struct bt_gatt_service *lib_mcs_server_list_get(void);
extern int lib_mcs_init(void);
extern int lib_mcs_deinit(void);
extern int lib_media_proxy_init(void);
extern int lib_media_proxy_deinit(void);
extern int lib_micp_mic_ctlr_init(void);
extern int lib_micp_mic_ctlr_deinit(void);
extern struct bt_gatt_service *lib_mics_svc_get(void);
extern int lib_micp_mic_dev_init(void);
extern int lib_micp_mic_dev_deinit(void);
extern int lib_mpl_init(void);
extern int lib_mpl_deinit(void);
extern int lib_pbp_init(void);
extern int lib_pbp_deinit(void);
extern int lib_tbs_client_init(void);
extern int lib_tbs_client_deinit(void);
extern struct bt_gatt_service *lib_gtbs_svc_get(void);
extern struct bt_gatt_service *lib_tbs_server_list_get(void);
extern int lib_tbs_init(void);
extern int lib_tbs_deinit(void);
extern struct bt_gatt_service *lib_tmas_svc_get(void);
extern int lib_tmap_init(void);
extern int lib_tmap_deinit(void);
extern int lib_vcp_vol_ctlr_init(void);
extern int lib_vcp_vol_ctlr_deinit(void);
extern struct bt_gatt_service *lib_vcs_svc_get(void);
extern int lib_vcp_vol_rend_init(void);
extern int lib_vcp_vol_rend_deinit(void);
struct bt_vocs;
struct bt_vocs_server;
extern int lib_vocs_client_init(void);
extern int lib_vocs_client_deinit(void);
extern struct bt_vocs_server *lib_vocs_insts_get(void);
extern void *lib_vocs_svc_get(struct bt_vocs *vocs);
extern int lib_vocs_init(void);
extern int lib_vocs_deinit(void);
struct bt_conn;
struct bt_gatt_attr;
@@ -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);
}
+53 -2
View File
@@ -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");
+146 -16
View File
@@ -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;
}
+68
View File
@@ -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
+1 -1
View File
@@ -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. */