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

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