From 475f26e5d1846eff8943e4b40101f4f0e589f8ae Mon Sep 17 00:00:00 2001 From: Liu Linyan Date: Fri, 7 Aug 2026 11:10:24 +0800 Subject: [PATCH] feat(ble_audio): Support BLE ISO & BLE Audio deinitialization --- .../esp_ble_audio/api/esp_ble_audio_bap_api.c | 12 + .../api/esp_ble_audio_common_api.c | 50 +++ .../api/esp_ble_audio_csip_api.c | 4 + .../api/esp_ble_audio_pacs_api.c | 4 + .../esp_ble_audio/api/esp_ble_audio_tbs_api.c | 10 + .../api/include/esp_ble_audio_common_api.h | 69 +++- .../bluedroid/include/bluedroid/init.h | 2 + .../include/bluedroid/profiles/ascs.h | 2 + .../include/bluedroid/profiles/bass.h | 2 + .../include/bluedroid/profiles/csis.h | 4 + .../include/bluedroid/profiles/mcs.h | 2 + .../include/bluedroid/profiles/mics.h | 2 + .../include/bluedroid/profiles/pacs.h | 2 + .../include/bluedroid/profiles/tbs.h | 4 + .../include/bluedroid/profiles/vcs.h | 2 + .../bluedroid/include/bluedroid/server.h | 4 + .../host/adapter/bluedroid/init.c | 23 ++ .../host/adapter/bluedroid/profiles/ascs.c | 15 + .../host/adapter/bluedroid/profiles/bass.c | 15 + .../host/adapter/bluedroid/profiles/csis.c | 17 + .../host/adapter/bluedroid/profiles/mcs.c | 17 + .../host/adapter/bluedroid/profiles/mics.c | 5 + .../host/adapter/bluedroid/profiles/pacs.c | 15 + .../host/adapter/bluedroid/profiles/tbs.c | 38 ++ .../host/adapter/bluedroid/profiles/vcs.c | 6 + .../host/adapter/bluedroid/server.c | 117 +++++- .../host/adapter/nimble/include/nimble/init.h | 2 + .../nimble/include/nimble/profiles/ascs.h | 2 + .../nimble/include/nimble/profiles/bass.h | 2 + .../nimble/include/nimble/profiles/csis.h | 4 + .../nimble/include/nimble/profiles/mcs.h | 2 + .../nimble/include/nimble/profiles/mics.h | 2 + .../nimble/include/nimble/profiles/pacs.h | 2 + .../nimble/include/nimble/profiles/tbs.h | 4 + .../nimble/include/nimble/profiles/vcs.h | 2 + .../esp_ble_audio/host/adapter/nimble/init.c | 46 ++- .../host/adapter/nimble/profiles/ascs.c | 6 + .../host/adapter/nimble/profiles/bass.c | 6 + .../host/adapter/nimble/profiles/csis.c | 44 ++- .../host/adapter/nimble/profiles/mcs.c | 7 + .../host/adapter/nimble/profiles/mics.c | 5 + .../host/adapter/nimble/profiles/pacs.c | 6 + .../host/adapter/nimble/profiles/tbs.c | 12 + .../host/adapter/nimble/profiles/vcs.c | 6 + .../host/common/include/common/init.h | 12 + .../bt/esp_ble_audio/host/common/init.c | 367 +++++++++++++++++- .../host/services/ots/adapter/l2cap.c | 36 +- .../host/services/ots/adapter/l2cap.h | 2 + .../bt/esp_ble_audio/host/services/ots/ots.c | 38 ++ .../host/services/ots/ots_client.c | 9 + .../host/services/ots/ots_dir_list.c | 10 + .../host/services/ots/ots_dir_list_internal.h | 1 + .../host/services/ots/ots_obj_manager.c | 16 + .../services/ots/ots_obj_manager_internal.h | 2 + .../bt/esp_ble_audio/lib/include/audio.h | 42 ++ components/bt/esp_ble_audio/lib/lib | 2 +- .../esp_ble_iso/api/esp_ble_iso_common_api.c | 47 +++ .../api/include/esp_ble_iso_common_api.h | 63 ++- .../esp_ble_iso/host/adapter/bluedroid/gap.c | 29 +- .../host/adapter/bluedroid/gatt/gatt.c | 171 ++++---- .../esp_ble_iso/host/adapter/bluedroid/hci.c | 1 + .../esp_ble_iso/host/adapter/bluedroid/iso.c | 14 +- .../bt/esp_ble_iso/host/adapter/nimble/gap.c | 25 +- .../host/adapter/nimble/gatt/gatt.c | 15 +- .../bt/esp_ble_iso/host/adapter/nimble/iso.c | 4 +- components/bt/esp_ble_iso/host/common/adv.c | 11 + .../bt/esp_ble_iso/host/common/app/gap.c | 26 ++ components/bt/esp_ble_iso/host/common/conn.c | 47 +++ components/bt/esp_ble_iso/host/common/gatt.c | 31 ++ components/bt/esp_ble_iso/host/common/host.c | 55 ++- .../host/common/include/common/adv.h | 2 + .../host/common/include/common/app/gap.h | 2 + .../host/common/include/common/conn.h | 7 + .../host/common/include/common/gatt.h | 2 + .../host/common/include/common/host.h | 4 +- .../host/common/include/common/iso.h | 4 + .../host/common/include/common/scan.h | 11 + .../host/common/include/common/task.h | 23 +- components/bt/esp_ble_iso/host/common/scan.c | 46 +++ components/bt/esp_ble_iso/host/common/task.c | 162 +++++++- components/bt/esp_ble_iso/host/iso/iso.c | 68 ++++ .../host/utils/include/utils/assert.h | 10 +- components/bt/esp_ble_iso/host/utils/timer.c | 2 +- .../include/zephyr/bluetooth/bluetooth.h | 13 + 84 files changed, 1825 insertions(+), 210 deletions(-) diff --git a/components/bt/esp_ble_audio/api/esp_ble_audio_bap_api.c b/components/bt/esp_ble_audio/api/esp_ble_audio_bap_api.c index 3e458ea8ad5..c8653f3e2d7 100644 --- a/components/bt/esp_ble_audio/api/esp_ble_audio_bap_api.c +++ b/components/bt/esp_ble_audio/api/esp_ble_audio_bap_api.c @@ -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; } diff --git a/components/bt/esp_ble_audio/api/esp_ble_audio_common_api.c b/components/bt/esp_ble_audio/api/esp_ble_audio_common_api.c index 536a43c0e2a..5551a89b798 100644 --- a/components/bt/esp_ble_audio/api/esp_ble_audio_common_api.c +++ b/components/bt/esp_ble_audio/api/esp_ble_audio_common_api.c @@ -11,8 +11,12 @@ #include #include +#include + #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; diff --git a/components/bt/esp_ble_audio/api/esp_ble_audio_csip_api.c b/components/bt/esp_ble_audio/api/esp_ble_audio_csip_api.c index 3550b4d4ea7..5113de49269 100644 --- a/components/bt/esp_ble_audio/api/esp_ble_audio_csip_api.c +++ b/components/bt/esp_ble_audio/api/esp_ble_audio_csip_api.c @@ -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; diff --git a/components/bt/esp_ble_audio/api/esp_ble_audio_pacs_api.c b/components/bt/esp_ble_audio/api/esp_ble_audio_pacs_api.c index 5050ccc73df..83efc238e96 100644 --- a/components/bt/esp_ble_audio/api/esp_ble_audio_pacs_api.c +++ b/components/bt/esp_ble_audio/api/esp_ble_audio_pacs_api.c @@ -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; } diff --git a/components/bt/esp_ble_audio/api/esp_ble_audio_tbs_api.c b/components/bt/esp_ble_audio/api/esp_ble_audio_tbs_api.c index 5dcc3a58a9a..94ef680d2c7 100644 --- a/components/bt/esp_ble_audio/api/esp_ble_audio_tbs_api.c +++ b/components/bt/esp_ble_audio/api/esp_ble_audio_tbs_api.c @@ -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 */ diff --git a/components/bt/esp_ble_audio/api/include/esp_ble_audio_common_api.h b/components/bt/esp_ble_audio/api/include/esp_ble_audio_common_api.h index 0c2a20d0a55..c751cee4780 100644 --- a/components/bt/esp_ble_audio/api/include/esp_ble_audio_common_api.h +++ b/components/bt/esp_ble_audio/api/include/esp_ble_audio_common_api.h @@ -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. * diff --git a/components/bt/esp_ble_audio/host/adapter/bluedroid/include/bluedroid/init.h b/components/bt/esp_ble_audio/host/adapter/bluedroid/include/bluedroid/init.h index 4923388a935..2c1819c9641 100644 --- a/components/bt/esp_ble_audio/host/adapter/bluedroid/include/bluedroid/init.h +++ b/components/bt/esp_ble_audio/host/adapter/bluedroid/include/bluedroid/init.h @@ -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); diff --git a/components/bt/esp_ble_audio/host/adapter/bluedroid/include/bluedroid/profiles/ascs.h b/components/bt/esp_ble_audio/host/adapter/bluedroid/include/bluedroid/profiles/ascs.h index 97f0996a21d..b7812aa7788 100644 --- a/components/bt/esp_ble_audio/host/adapter/bluedroid/include/bluedroid/profiles/ascs.h +++ b/components/bt/esp_ble_audio/host/adapter/bluedroid/include/bluedroid/profiles/ascs.h @@ -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 diff --git a/components/bt/esp_ble_audio/host/adapter/bluedroid/include/bluedroid/profiles/bass.h b/components/bt/esp_ble_audio/host/adapter/bluedroid/include/bluedroid/profiles/bass.h index c4073352610..9f56d4ba29f 100644 --- a/components/bt/esp_ble_audio/host/adapter/bluedroid/include/bluedroid/profiles/bass.h +++ b/components/bt/esp_ble_audio/host/adapter/bluedroid/include/bluedroid/profiles/bass.h @@ -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 diff --git a/components/bt/esp_ble_audio/host/adapter/bluedroid/include/bluedroid/profiles/csis.h b/components/bt/esp_ble_audio/host/adapter/bluedroid/include/bluedroid/profiles/csis.h index 8038bd8a4d3..b0ed3268d96 100644 --- a/components/bt/esp_ble_audio/host/adapter/bluedroid/include/bluedroid/profiles/csis.h +++ b/components/bt/esp_ble_audio/host/adapter/bluedroid/include/bluedroid/profiles/csis.h @@ -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 diff --git a/components/bt/esp_ble_audio/host/adapter/bluedroid/include/bluedroid/profiles/mcs.h b/components/bt/esp_ble_audio/host/adapter/bluedroid/include/bluedroid/profiles/mcs.h index f9d9b289c09..ca28dcc40ad 100644 --- a/components/bt/esp_ble_audio/host/adapter/bluedroid/include/bluedroid/profiles/mcs.h +++ b/components/bt/esp_ble_audio/host/adapter/bluedroid/include/bluedroid/profiles/mcs.h @@ -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); diff --git a/components/bt/esp_ble_audio/host/adapter/bluedroid/include/bluedroid/profiles/mics.h b/components/bt/esp_ble_audio/host/adapter/bluedroid/include/bluedroid/profiles/mics.h index f5bcca9ce65..14dee7ce075 100644 --- a/components/bt/esp_ble_audio/host/adapter/bluedroid/include/bluedroid/profiles/mics.h +++ b/components/bt/esp_ble_audio/host/adapter/bluedroid/include/bluedroid/profiles/mics.h @@ -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); diff --git a/components/bt/esp_ble_audio/host/adapter/bluedroid/include/bluedroid/profiles/pacs.h b/components/bt/esp_ble_audio/host/adapter/bluedroid/include/bluedroid/profiles/pacs.h index f36e8e76e80..0198b1dacc2 100644 --- a/components/bt/esp_ble_audio/host/adapter/bluedroid/include/bluedroid/profiles/pacs.h +++ b/components/bt/esp_ble_audio/host/adapter/bluedroid/include/bluedroid/profiles/pacs.h @@ -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 diff --git a/components/bt/esp_ble_audio/host/adapter/bluedroid/include/bluedroid/profiles/tbs.h b/components/bt/esp_ble_audio/host/adapter/bluedroid/include/bluedroid/profiles/tbs.h index b866089ccae..be218c7537b 100644 --- a/components/bt/esp_ble_audio/host/adapter/bluedroid/include/bluedroid/profiles/tbs.h +++ b/components/bt/esp_ble_audio/host/adapter/bluedroid/include/bluedroid/profiles/tbs.h @@ -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 diff --git a/components/bt/esp_ble_audio/host/adapter/bluedroid/include/bluedroid/profiles/vcs.h b/components/bt/esp_ble_audio/host/adapter/bluedroid/include/bluedroid/profiles/vcs.h index a7c797dd6e2..ee3ed3b4da6 100644 --- a/components/bt/esp_ble_audio/host/adapter/bluedroid/include/bluedroid/profiles/vcs.h +++ b/components/bt/esp_ble_audio/host/adapter/bluedroid/include/bluedroid/profiles/vcs.h @@ -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); diff --git a/components/bt/esp_ble_audio/host/adapter/bluedroid/include/bluedroid/server.h b/components/bt/esp_ble_audio/host/adapter/bluedroid/include/bluedroid/server.h index ab5b69aa73b..833d8d2778c 100644 --- a/components/bt/esp_ble_audio/host/adapter/bluedroid/include/bluedroid/server.h +++ b/components/bt/esp_ble_audio/host/adapter/bluedroid/include/bluedroid/server.h @@ -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 diff --git a/components/bt/esp_ble_audio/host/adapter/bluedroid/init.c b/components/bt/esp_ble_audio/host/adapter/bluedroid/init.c index 5383acb9abf..c2a0cafe95f 100644 --- a/components/bt/esp_ble_audio/host/adapter/bluedroid/init.c +++ b/components/bt/esp_ble_audio/host/adapter/bluedroid/init.c @@ -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) diff --git a/components/bt/esp_ble_audio/host/adapter/bluedroid/profiles/ascs.c b/components/bt/esp_ble_audio/host/adapter/bluedroid/profiles/ascs.c index 646ae4aeb9e..b8eb0201b99 100644 --- a/components/bt/esp_ble_audio/host/adapter/bluedroid/profiles/ascs.c +++ b/components/bt/esp_ble_audio/host/adapter/bluedroid/profiles/ascs.c @@ -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; diff --git a/components/bt/esp_ble_audio/host/adapter/bluedroid/profiles/bass.c b/components/bt/esp_ble_audio/host/adapter/bluedroid/profiles/bass.c index 36726ddb237..5d99cde34ad 100644 --- a/components/bt/esp_ble_audio/host/adapter/bluedroid/profiles/bass.c +++ b/components/bt/esp_ble_audio/host/adapter/bluedroid/profiles/bass.c @@ -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; diff --git a/components/bt/esp_ble_audio/host/adapter/bluedroid/profiles/csis.c b/components/bt/esp_ble_audio/host/adapter/bluedroid/profiles/csis.c index 02068fbfb07..5d9d9924a0d 100644 --- a/components/bt/esp_ble_audio/host/adapter/bluedroid/profiles/csis.c +++ b/components/bt/esp_ble_audio/host/adapter/bluedroid/profiles/csis.c @@ -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; diff --git a/components/bt/esp_ble_audio/host/adapter/bluedroid/profiles/mcs.c b/components/bt/esp_ble_audio/host/adapter/bluedroid/profiles/mcs.c index 5f0d815f581..0cce7e2cf82 100644 --- a/components/bt/esp_ble_audio/host/adapter/bluedroid/profiles/mcs.c +++ b/components/bt/esp_ble_audio/host/adapter/bluedroid/profiles/mcs.c @@ -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; diff --git a/components/bt/esp_ble_audio/host/adapter/bluedroid/profiles/mics.c b/components/bt/esp_ble_audio/host/adapter/bluedroid/profiles/mics.c index d1e79e02ed8..5546da0707f 100644 --- a/components/bt/esp_ble_audio/host/adapter/bluedroid/profiles/mics.c +++ b/components/bt/esp_ble_audio/host/adapter/bluedroid/profiles/mics.c @@ -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; diff --git a/components/bt/esp_ble_audio/host/adapter/bluedroid/profiles/pacs.c b/components/bt/esp_ble_audio/host/adapter/bluedroid/profiles/pacs.c index 8dc151dbf67..a2e2280f632 100644 --- a/components/bt/esp_ble_audio/host/adapter/bluedroid/profiles/pacs.c +++ b/components/bt/esp_ble_audio/host/adapter/bluedroid/profiles/pacs.c @@ -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; diff --git a/components/bt/esp_ble_audio/host/adapter/bluedroid/profiles/tbs.c b/components/bt/esp_ble_audio/host/adapter/bluedroid/profiles/tbs.c index 2055bb2e066..5f04e72c7fc 100644 --- a/components/bt/esp_ble_audio/host/adapter/bluedroid/profiles/tbs.c +++ b/components/bt/esp_ble_audio/host/adapter/bluedroid/profiles/tbs.c @@ -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; diff --git a/components/bt/esp_ble_audio/host/adapter/bluedroid/profiles/vcs.c b/components/bt/esp_ble_audio/host/adapter/bluedroid/profiles/vcs.c index bc848dbd276..6d4d904643b 100644 --- a/components/bt/esp_ble_audio/host/adapter/bluedroid/profiles/vcs.c +++ b/components/bt/esp_ble_audio/host/adapter/bluedroid/profiles/vcs.c @@ -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; diff --git a/components/bt/esp_ble_audio/host/adapter/bluedroid/server.c b/components/bt/esp_ble_audio/host/adapter/bluedroid/server.c index aafa82e44ff..364bb3bae58 100644 --- a/components/bt/esp_ble_audio/host/adapter/bluedroid/server.c +++ b/components/bt/esp_ble_audio/host/adapter/bluedroid/server.c @@ -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; } diff --git a/components/bt/esp_ble_audio/host/adapter/nimble/include/nimble/init.h b/components/bt/esp_ble_audio/host/adapter/nimble/include/nimble/init.h index ce08a0b0db4..46c5b4f556e 100644 --- a/components/bt/esp_ble_audio/host/adapter/nimble/include/nimble/init.h +++ b/components/bt/esp_ble_audio/host/adapter/nimble/include/nimble/init.h @@ -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); diff --git a/components/bt/esp_ble_audio/host/adapter/nimble/include/nimble/profiles/ascs.h b/components/bt/esp_ble_audio/host/adapter/nimble/include/nimble/profiles/ascs.h index 20ed621c08f..122c57d2946 100644 --- a/components/bt/esp_ble_audio/host/adapter/nimble/include/nimble/profiles/ascs.h +++ b/components/bt/esp_ble_audio/host/adapter/nimble/include/nimble/profiles/ascs.h @@ -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 diff --git a/components/bt/esp_ble_audio/host/adapter/nimble/include/nimble/profiles/bass.h b/components/bt/esp_ble_audio/host/adapter/nimble/include/nimble/profiles/bass.h index 69ddc64c292..ed154da387f 100644 --- a/components/bt/esp_ble_audio/host/adapter/nimble/include/nimble/profiles/bass.h +++ b/components/bt/esp_ble_audio/host/adapter/nimble/include/nimble/profiles/bass.h @@ -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 diff --git a/components/bt/esp_ble_audio/host/adapter/nimble/include/nimble/profiles/csis.h b/components/bt/esp_ble_audio/host/adapter/nimble/include/nimble/profiles/csis.h index e2912395e21..ad382bd3d1f 100644 --- a/components/bt/esp_ble_audio/host/adapter/nimble/include/nimble/profiles/csis.h +++ b/components/bt/esp_ble_audio/host/adapter/nimble/include/nimble/profiles/csis.h @@ -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 diff --git a/components/bt/esp_ble_audio/host/adapter/nimble/include/nimble/profiles/mcs.h b/components/bt/esp_ble_audio/host/adapter/nimble/include/nimble/profiles/mcs.h index d6f0db35290..6d25d87e4f2 100644 --- a/components/bt/esp_ble_audio/host/adapter/nimble/include/nimble/profiles/mcs.h +++ b/components/bt/esp_ble_audio/host/adapter/nimble/include/nimble/profiles/mcs.h @@ -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); diff --git a/components/bt/esp_ble_audio/host/adapter/nimble/include/nimble/profiles/mics.h b/components/bt/esp_ble_audio/host/adapter/nimble/include/nimble/profiles/mics.h index 73177597de7..1c5c41e0c10 100644 --- a/components/bt/esp_ble_audio/host/adapter/nimble/include/nimble/profiles/mics.h +++ b/components/bt/esp_ble_audio/host/adapter/nimble/include/nimble/profiles/mics.h @@ -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 diff --git a/components/bt/esp_ble_audio/host/adapter/nimble/include/nimble/profiles/pacs.h b/components/bt/esp_ble_audio/host/adapter/nimble/include/nimble/profiles/pacs.h index e7d470fcb95..60185a5bb02 100644 --- a/components/bt/esp_ble_audio/host/adapter/nimble/include/nimble/profiles/pacs.h +++ b/components/bt/esp_ble_audio/host/adapter/nimble/include/nimble/profiles/pacs.h @@ -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 diff --git a/components/bt/esp_ble_audio/host/adapter/nimble/include/nimble/profiles/tbs.h b/components/bt/esp_ble_audio/host/adapter/nimble/include/nimble/profiles/tbs.h index 9c14929e03b..e1690641325 100644 --- a/components/bt/esp_ble_audio/host/adapter/nimble/include/nimble/profiles/tbs.h +++ b/components/bt/esp_ble_audio/host/adapter/nimble/include/nimble/profiles/tbs.h @@ -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 diff --git a/components/bt/esp_ble_audio/host/adapter/nimble/include/nimble/profiles/vcs.h b/components/bt/esp_ble_audio/host/adapter/nimble/include/nimble/profiles/vcs.h index db72dc6221e..6bdc0393a59 100644 --- a/components/bt/esp_ble_audio/host/adapter/nimble/include/nimble/profiles/vcs.h +++ b/components/bt/esp_ble_audio/host/adapter/nimble/include/nimble/profiles/vcs.h @@ -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 diff --git a/components/bt/esp_ble_audio/host/adapter/nimble/init.c b/components/bt/esp_ble_audio/host/adapter/nimble/init.c index ce338459170..f299d1c9567 100644 --- a/components/bt/esp_ble_audio/host/adapter/nimble/init.c +++ b/components/bt/esp_ble_audio/host/adapter/nimble/init.c @@ -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(); diff --git a/components/bt/esp_ble_audio/host/adapter/nimble/profiles/ascs.c b/components/bt/esp_ble_audio/host/adapter/nimble/profiles/ascs.c index 007c63c640f..52ed88764c4 100644 --- a/components/bt/esp_ble_audio/host/adapter/nimble/profiles/ascs.c +++ b/components/bt/esp_ble_audio/host/adapter/nimble/profiles/ascs.c @@ -254,3 +254,9 @@ free: } return rc; } + +int bt_le_nimble_ascs_deinit(void) +{ + LOG_DBG("[N]AscsDeinit"); + return 0; +} diff --git a/components/bt/esp_ble_audio/host/adapter/nimble/profiles/bass.c b/components/bt/esp_ble_audio/host/adapter/nimble/profiles/bass.c index 792ba8fa38d..f881c2aa05c 100644 --- a/components/bt/esp_ble_audio/host/adapter/nimble/profiles/bass.c +++ b/components/bt/esp_ble_audio/host/adapter/nimble/profiles/bass.c @@ -218,3 +218,9 @@ free: } return rc; } + +int bt_le_nimble_bass_deinit(void) +{ + LOG_DBG("[N]BassDeinit"); + return 0; +} diff --git a/components/bt/esp_ble_audio/host/adapter/nimble/profiles/csis.c b/components/bt/esp_ble_audio/host/adapter/nimble/profiles/csis.c index 12081f18de8..4d015430eb0 100644 --- a/components/bt/esp_ble_audio/host/adapter/nimble/profiles/csis.c +++ b/components/bt/esp_ble_audio/host/adapter/nimble/profiles/csis.c @@ -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; +} diff --git a/components/bt/esp_ble_audio/host/adapter/nimble/profiles/mcs.c b/components/bt/esp_ble_audio/host/adapter/nimble/profiles/mcs.c index 07ec835e88c..895b2488ee5 100644 --- a/components/bt/esp_ble_audio/host/adapter/nimble/profiles/mcs.c +++ b/components/bt/esp_ble_audio/host/adapter/nimble/profiles/mcs.c @@ -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 diff --git a/components/bt/esp_ble_audio/host/adapter/nimble/profiles/mics.c b/components/bt/esp_ble_audio/host/adapter/nimble/profiles/mics.c index f54ca70ef56..760c30252a3 100644 --- a/components/bt/esp_ble_audio/host/adapter/nimble/profiles/mics.c +++ b/components/bt/esp_ble_audio/host/adapter/nimble/profiles/mics.c @@ -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; diff --git a/components/bt/esp_ble_audio/host/adapter/nimble/profiles/pacs.c b/components/bt/esp_ble_audio/host/adapter/nimble/profiles/pacs.c index a9060cd7ced..3fd530ecb9d 100644 --- a/components/bt/esp_ble_audio/host/adapter/nimble/profiles/pacs.c +++ b/components/bt/esp_ble_audio/host/adapter/nimble/profiles/pacs.c @@ -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; +} diff --git a/components/bt/esp_ble_audio/host/adapter/nimble/profiles/tbs.c b/components/bt/esp_ble_audio/host/adapter/nimble/profiles/tbs.c index 9ea3ffb3a3c..b742797c2fc 100644 --- a/components/bt/esp_ble_audio/host/adapter/nimble/profiles/tbs.c +++ b/components/bt/esp_ble_audio/host/adapter/nimble/profiles/tbs.c @@ -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; +} diff --git a/components/bt/esp_ble_audio/host/adapter/nimble/profiles/vcs.c b/components/bt/esp_ble_audio/host/adapter/nimble/profiles/vcs.c index d0b496f093f..4de10b13bae 100644 --- a/components/bt/esp_ble_audio/host/adapter/nimble/profiles/vcs.c +++ b/components/bt/esp_ble_audio/host/adapter/nimble/profiles/vcs.c @@ -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; diff --git a/components/bt/esp_ble_audio/host/common/include/common/init.h b/components/bt/esp_ble_audio/host/common/include/common/init.h index 221f0f2cbb1..4b572bad5e3 100644 --- a/components/bt/esp_ble_audio/host/common/include/common/init.h +++ b/components/bt/esp_ble_audio/host/common/include/common/init.h @@ -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); diff --git a/components/bt/esp_ble_audio/host/common/init.c b/components/bt/esp_ble_audio/host/common/init.c index 110feee524b..d54d3d9d907 100644 --- a/components/bt/esp_ble_audio/host/common/init.c +++ b/components/bt/esp_ble_audio/host/common/init.c @@ -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"); diff --git a/components/bt/esp_ble_audio/host/services/ots/adapter/l2cap.c b/components/bt/esp_ble_audio/host/services/ots/adapter/l2cap.c index d33bdd98f59..8f8016216de 100644 --- a/components/bt/esp_ble_audio/host/services/ots/adapter/l2cap.c +++ b/components/bt/esp_ble_audio/host/services/ots/adapter/l2cap.c @@ -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 diff --git a/components/bt/esp_ble_audio/host/services/ots/adapter/l2cap.h b/components/bt/esp_ble_audio/host/services/ots/adapter/l2cap.h index ce8bbdf8e88..4f93eb515e7 100644 --- a/components/bt/esp_ble_audio/host/services/ots/adapter/l2cap.h +++ b/components/bt/esp_ble_audio/host/services/ots/adapter/l2cap.h @@ -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); diff --git a/components/bt/esp_ble_audio/host/services/ots/ots.c b/components/bt/esp_ble_audio/host/services/ots/ots.c index 1c5c5e1311e..e1b2f93c916 100644 --- a/components/bt/esp_ble_audio/host/services/ots/ots.c +++ b/components/bt/esp_ble_audio/host/services/ots/ots.c @@ -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; diff --git a/components/bt/esp_ble_audio/host/services/ots/ots_client.c b/components/bt/esp_ble_audio/host/services/ots/ots_client.c index 0d7157bf07a..4adf1e0cd77 100644 --- a/components/bt/esp_ble_audio/host/services/ots/ots_client.c +++ b/components/bt/esp_ble_audio/host/services/ots/ots_client.c @@ -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) diff --git a/components/bt/esp_ble_audio/host/services/ots/ots_dir_list.c b/components/bt/esp_ble_audio/host/services/ots/ots_dir_list.c index fa165d984d0..4c5cd579bcd 100644 --- a/components/bt/esp_ble_audio/host/services/ots/ots_dir_list.c +++ b/components/bt/esp_ble_audio/host/services/ots/ots_dir_list.c @@ -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) { diff --git a/components/bt/esp_ble_audio/host/services/ots/ots_dir_list_internal.h b/components/bt/esp_ble_audio/host/services/ots/ots_dir_list_internal.h index 9615438a2e3..1124b55816d 100644 --- a/components/bt/esp_ble_audio/host/services/ots/ots_dir_list_internal.h +++ b/components/bt/esp_ble_audio/host/services/ots/ots_dir_list_internal.h @@ -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); diff --git a/components/bt/esp_ble_audio/host/services/ots/ots_obj_manager.c b/components/bt/esp_ble_audio/host/services/ots/ots_obj_manager.c index 294e28a5730..2b723b74cfc 100644 --- a/components/bt/esp_ble_audio/host/services/ots/ots_obj_manager.c +++ b/components/bt/esp_ble_audio/host/services/ots/ots_obj_manager.c @@ -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; +} diff --git a/components/bt/esp_ble_audio/host/services/ots/ots_obj_manager_internal.h b/components/bt/esp_ble_audio/host/services/ots/ots_obj_manager_internal.h index 019fdef11b9..d4627db6737 100644 --- a/components/bt/esp_ble_audio/host/services/ots/ots_obj_manager_internal.h +++ b/components/bt/esp_ble_audio/host/services/ots/ots_obj_manager_internal.h @@ -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 diff --git a/components/bt/esp_ble_audio/lib/include/audio.h b/components/bt/esp_ble_audio/lib/include/audio.h index 37533c83d52..00500ca1085 100644 --- a/components/bt/esp_ble_audio/lib/include/audio.h +++ b/components/bt/esp_ble_audio/lib/include/audio.h @@ -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; diff --git a/components/bt/esp_ble_audio/lib/lib b/components/bt/esp_ble_audio/lib/lib index 4cfb5e656a2..3565467bac4 160000 --- a/components/bt/esp_ble_audio/lib/lib +++ b/components/bt/esp_ble_audio/lib/lib @@ -1 +1 @@ -Subproject commit 4cfb5e656a20914faaabd413382a2a9a02ea380e +Subproject commit 3565467bac4cbf22ec53695b8b51922ffd4433e8 diff --git a/components/bt/esp_ble_iso/api/esp_ble_iso_common_api.c b/components/bt/esp_ble_iso/api/esp_ble_iso_common_api.c index bb83fb5ac2f..a260efe385c 100644 --- a/components/bt/esp_ble_iso/api/esp_ble_iso_common_api.c +++ b/components/bt/esp_ble_iso/api/esp_ble_iso_common_api.c @@ -6,8 +6,14 @@ * SPDX-License-Identifier: Apache-2.0 */ +#include + +#include + #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) { diff --git a/components/bt/esp_ble_iso/api/include/esp_ble_iso_common_api.h b/components/bt/esp_ble_iso/api/include/esp_ble_iso_common_api.h index 8f06e42566e..06752094fd0 100644 --- a/components/bt/esp_ble_iso/api/include/esp_ble_iso_common_api.h +++ b/components/bt/esp_ble_iso/api/include/esp_ble_iso_common_api.h @@ -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). diff --git a/components/bt/esp_ble_iso/host/adapter/bluedroid/gap.c b/components/bt/esp_ble_iso/host/adapter/bluedroid/gap.c index dda8a19ed38..33bb6251c35 100644 --- a/components/bt/esp_ble_iso/host/adapter/bluedroid/gap.c +++ b/components/bt/esp_ble_iso/host/adapter/bluedroid/gap.c @@ -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) diff --git a/components/bt/esp_ble_iso/host/adapter/bluedroid/gatt/gatt.c b/components/bt/esp_ble_iso/host/adapter/bluedroid/gatt/gatt.c index af55064cd75..ca1ef04f1f5 100644 --- a/components/bt/esp_ble_iso/host/adapter/bluedroid/gatt/gatt.c +++ b/components/bt/esp_ble_iso/host/adapter/bluedroid/gatt/gatt.c @@ -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) { diff --git a/components/bt/esp_ble_iso/host/adapter/bluedroid/hci.c b/components/bt/esp_ble_iso/host/adapter/bluedroid/hci.c index 5cbb48f3efa..82ec349c905 100644 --- a/components/bt/esp_ble_iso/host/adapter/bluedroid/hci.c +++ b/components/bt/esp_ble_iso/host/adapter/bluedroid/hci.c @@ -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; } diff --git a/components/bt/esp_ble_iso/host/adapter/bluedroid/iso.c b/components/bt/esp_ble_iso/host/adapter/bluedroid/iso.c index 8f34b4728ca..2caf350613c 100644 --- a/components/bt/esp_ble_iso/host/adapter/bluedroid/iso.c +++ b/components/bt/esp_ble_iso/host/adapter/bluedroid/iso.c @@ -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 diff --git a/components/bt/esp_ble_iso/host/adapter/nimble/gap.c b/components/bt/esp_ble_iso/host/adapter/nimble/gap.c index f7cd8c42c8d..d804df9fc16 100644 --- a/components/bt/esp_ble_iso/host/adapter/nimble/gap.c +++ b/components/bt/esp_ble_iso/host/adapter/nimble/gap.c @@ -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) diff --git a/components/bt/esp_ble_iso/host/adapter/nimble/gatt/gatt.c b/components/bt/esp_ble_iso/host/adapter/nimble/gatt/gatt.c index 116d832bec7..2571265d9cc 100644 --- a/components/bt/esp_ble_iso/host/adapter/nimble/gatt/gatt.c +++ b/components/bt/esp_ble_iso/host/adapter/nimble/gatt/gatt.c @@ -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; } diff --git a/components/bt/esp_ble_iso/host/adapter/nimble/iso.c b/components/bt/esp_ble_iso/host/adapter/nimble/iso.c index 0fe7a73f302..9d28ba32d75 100644 --- a/components/bt/esp_ble_iso/host/adapter/nimble/iso.c +++ b/components/bt/esp_ble_iso/host/adapter/nimble/iso.c @@ -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); } } diff --git a/components/bt/esp_ble_iso/host/common/adv.c b/components/bt/esp_ble_iso/host/common/adv.c index 2318ff3a322..7f2af049a57 100644 --- a/components/bt/esp_ble_iso/host/common/adv.c +++ b/components/bt/esp_ble_iso/host/common/adv.c @@ -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) { diff --git a/components/bt/esp_ble_iso/host/common/app/gap.c b/components/bt/esp_ble_iso/host/common/app/gap.c index 3a5fa3c0e5a..c4bdbb82dfe 100644 --- a/components/bt/esp_ble_iso/host/common/app/gap.c +++ b/components/bt/esp_ble_iso/host/common/app/gap.c @@ -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; diff --git a/components/bt/esp_ble_iso/host/common/conn.c b/components/bt/esp_ble_iso/host/common/conn.c index 41419465364..b3796a20964 100644 --- a/components/bt/esp_ble_iso/host/common/conn.c +++ b/components/bt/esp_ble_iso/host/common/conn.c @@ -8,6 +8,7 @@ #include #include +#include #include #include @@ -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 diff --git a/components/bt/esp_ble_iso/host/common/gatt.c b/components/bt/esp_ble_iso/host/common/gatt.c index 30cc4c4564e..742d70c4562 100644 --- a/components/bt/esp_ble_iso/host/common/gatt.c +++ b/components/bt/esp_ble_iso/host/common/gatt.c @@ -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); +} diff --git a/components/bt/esp_ble_iso/host/common/host.c b/components/bt/esp_ble_iso/host/common/host.c index 4d2f72e1074..6b3185eb8e3 100644 --- a/components/bt/esp_ble_iso/host/common/host.c +++ b/components/bt/esp_ble_iso/host/common/host.c @@ -13,7 +13,12 @@ #include #include +#include + +#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; } diff --git a/components/bt/esp_ble_iso/host/common/include/common/adv.h b/components/bt/esp_ble_iso/host/common/include/common/adv.h index fdaf9d66ae7..b519e58a1e0 100644 --- a/components/bt/esp_ble_iso/host/common/include/common/adv.h +++ b/components/bt/esp_ble_iso/host/common/include/common/adv.h @@ -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 diff --git a/components/bt/esp_ble_iso/host/common/include/common/app/gap.h b/components/bt/esp_ble_iso/host/common/include/common/app/gap.h index f2391214f72..c64822f5cf7 100644 --- a/components/bt/esp_ble_iso/host/common/include/common/app/gap.h +++ b/components/bt/esp_ble_iso/host/common/include/common/app/gap.h @@ -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 diff --git a/components/bt/esp_ble_iso/host/common/include/common/conn.h b/components/bt/esp_ble_iso/host/common/include/common/conn.h index 41d22eacf83..5659a8b7ecc 100644 --- a/components/bt/esp_ble_iso/host/common/include/common/conn.h +++ b/components/bt/esp_ble_iso/host/common/include/common/conn.h @@ -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); diff --git a/components/bt/esp_ble_iso/host/common/include/common/gatt.h b/components/bt/esp_ble_iso/host/common/include/common/gatt.h index 3a286273e14..ad33cfdd2e2 100644 --- a/components/bt/esp_ble_iso/host/common/include/common/gatt.h +++ b/components/bt/esp_ble_iso/host/common/include/common/gatt.h @@ -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 diff --git a/components/bt/esp_ble_iso/host/common/include/common/host.h b/components/bt/esp_ble_iso/host/common/include/common/host.h index b79165cd3be..bf83a07e1c2 100644 --- a/components/bt/esp_ble_iso/host/common/include/common/host.h +++ b/components/bt/esp_ble_iso/host/common/include/common/host.h @@ -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 } diff --git a/components/bt/esp_ble_iso/host/common/include/common/iso.h b/components/bt/esp_ble_iso/host/common/include/common/iso.h index 8c79528dfdc..61a8fe5d041 100644 --- a/components/bt/esp_ble_iso/host/common/include/common/iso.h +++ b/components/bt/esp_ble_iso/host/common/include/common/iso.h @@ -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); diff --git a/components/bt/esp_ble_iso/host/common/include/common/scan.h b/components/bt/esp_ble_iso/host/common/include/common/scan.h index 894e22794a4..e58b2e8ea97 100644 --- a/components/bt/esp_ble_iso/host/common/include/common/scan.h +++ b/components/bt/esp_ble_iso/host/common/include/common/scan.h @@ -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 diff --git a/components/bt/esp_ble_iso/host/common/include/common/task.h b/components/bt/esp_ble_iso/host/common/include/common/task.h index 8ecf4c60a9b..7cd25f04c33 100644 --- a/components/bt/esp_ble_iso/host/common/include/common/task.h +++ b/components/bt/esp_ble_iso/host/common/include/common/task.h @@ -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 } diff --git a/components/bt/esp_ble_iso/host/common/scan.c b/components/bt/esp_ble_iso/host/common/scan.c index 36e49c2a6c0..cd04b1cc644 100644 --- a/components/bt/esp_ble_iso/host/common/scan.c +++ b/components/bt/esp_ble_iso/host/common/scan.c @@ -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"); diff --git a/components/bt/esp_ble_iso/host/common/task.c b/components/bt/esp_ble_iso/host/common/task.c index a7ae764af21..9e3dc1f44b5 100644 --- a/components/bt/esp_ble_iso/host/common/task.c +++ b/components/bt/esp_ble_iso/host/common/task.c @@ -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; } diff --git a/components/bt/esp_ble_iso/host/iso/iso.c b/components/bt/esp_ble_iso/host/iso/iso.c index eb896b9cc2a..024351633f2 100644 --- a/components/bt/esp_ble_iso/host/iso/iso.c +++ b/components/bt/esp_ble_iso/host/iso/iso.c @@ -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; +} diff --git a/components/bt/esp_ble_iso/host/utils/include/utils/assert.h b/components/bt/esp_ble_iso/host/utils/include/utils/assert.h index 9fc2b3891d0..4fe3f0957ab 100644 --- a/components/bt/esp_ble_iso/host/utils/include/utils/assert.h +++ b/components/bt/esp_ble_iso/host/utils/include/utils/assert.h @@ -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 diff --git a/components/bt/esp_ble_iso/host/utils/timer.c b/components/bt/esp_ble_iso/host/utils/timer.c index 4bae756a0c3..61d4dfee826 100644 --- a/components/bt/esp_ble_iso/host/utils/timer.c +++ b/components/bt/esp_ble_iso/host/utils/timer.c @@ -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); } } diff --git a/components/bt/esp_ble_iso/include/zephyr/bluetooth/bluetooth.h b/components/bt/esp_ble_iso/include/zephyr/bluetooth/bluetooth.h index 8a73206c08d..74b2191c597 100644 --- a/components/bt/esp_ble_iso/include/zephyr/bluetooth/bluetooth.h +++ b/components/bt/esp_ble_iso/include/zephyr/bluetooth/bluetooth.h @@ -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. */