feat(ble_audio): Support running ISO & LE Audio with Bluedroid Host

This commit is contained in:
Liu Linyan
2026-05-29 17:24:54 +08:00
parent 6cd0e56ae2
commit 8dbef3bad4
223 changed files with 14714 additions and 2347 deletions
+27 -14
View File
@@ -29,13 +29,24 @@ endif()
list(APPEND ble_audio_include_dirs
"${CMAKE_CURRENT_LIST_DIR}/api/include"
"${CMAKE_CURRENT_LIST_DIR}/host/common/include"
"${CMAKE_CURRENT_LIST_DIR}/host/adapter/nimble/include"
"${CMAKE_CURRENT_LIST_DIR}/include"
)
if(CONFIG_BT_NIMBLE_ENABLED)
set(audio_adapter "host/adapter/nimble")
elseif(CONFIG_BT_BLUEDROID_ENABLED)
set(audio_adapter "host/adapter/bluedroid")
endif()
list(APPEND ble_audio_include_dirs
"${CMAKE_CURRENT_LIST_DIR}/${audio_adapter}/include"
)
list(APPEND ble_audio_srcs
"${CMAKE_CURRENT_LIST_DIR}/${audio_adapter}/server.c"
"${CMAKE_CURRENT_LIST_DIR}/${audio_adapter}/init.c"
)
list(APPEND ble_audio_srcs
"${CMAKE_CURRENT_LIST_DIR}/host/adapter/nimble/profiles/server.c"
"${CMAKE_CURRENT_LIST_DIR}/host/adapter/nimble/init.c"
"${CMAKE_CURRENT_LIST_DIR}/host/common/init.c"
"${CMAKE_CURRENT_LIST_DIR}/api/esp_ble_audio_aics_api.c"
"${CMAKE_CURRENT_LIST_DIR}/api/esp_ble_audio_bap_api.c"
@@ -57,69 +68,71 @@ list(APPEND ble_audio_srcs
"${CMAKE_CURRENT_LIST_DIR}/api/esp_ble_audio_vocs_api.c"
)
# Profile glue files live under the host adapter; same set of profile sources
# for either host, dispatched via ${audio_adapter}.
if(CONFIG_BT_ASCS)
list(APPEND ble_audio_srcs
"${CMAKE_CURRENT_LIST_DIR}/host/adapter/nimble/profiles/ascs.c"
"${CMAKE_CURRENT_LIST_DIR}/${audio_adapter}/profiles/ascs.c"
)
endif()
if(CONFIG_BT_PACS)
list(APPEND ble_audio_srcs
"${CMAKE_CURRENT_LIST_DIR}/host/adapter/nimble/profiles/pacs.c"
"${CMAKE_CURRENT_LIST_DIR}/${audio_adapter}/profiles/pacs.c"
)
endif()
if(CONFIG_BT_BAP_SCAN_DELEGATOR)
list(APPEND ble_audio_srcs
"${CMAKE_CURRENT_LIST_DIR}/host/adapter/nimble/profiles/bass.c"
"${CMAKE_CURRENT_LIST_DIR}/${audio_adapter}/profiles/bass.c"
)
endif()
if(CONFIG_BT_CAP_ACCEPTOR)
list(APPEND ble_audio_srcs
"${CMAKE_CURRENT_LIST_DIR}/host/adapter/nimble/profiles/cas.c"
"${CMAKE_CURRENT_LIST_DIR}/${audio_adapter}/profiles/cas.c"
)
endif()
if(CONFIG_BT_CSIP_SET_MEMBER)
list(APPEND ble_audio_srcs
"${CMAKE_CURRENT_LIST_DIR}/host/adapter/nimble/profiles/csis.c"
"${CMAKE_CURRENT_LIST_DIR}/${audio_adapter}/profiles/csis.c"
)
endif()
if(CONFIG_BT_TBS)
list(APPEND ble_audio_srcs
"${CMAKE_CURRENT_LIST_DIR}/host/adapter/nimble/profiles/tbs.c"
"${CMAKE_CURRENT_LIST_DIR}/${audio_adapter}/profiles/tbs.c"
)
endif()
if(CONFIG_BT_TMAP)
list(APPEND ble_audio_srcs
"${CMAKE_CURRENT_LIST_DIR}/host/adapter/nimble/profiles/tmas.c"
"${CMAKE_CURRENT_LIST_DIR}/${audio_adapter}/profiles/tmas.c"
)
endif()
if(CONFIG_BT_VCP_VOL_REND)
list(APPEND ble_audio_srcs
"${CMAKE_CURRENT_LIST_DIR}/host/adapter/nimble/profiles/vcs.c"
"${CMAKE_CURRENT_LIST_DIR}/${audio_adapter}/profiles/vcs.c"
)
endif()
if(CONFIG_BT_MICP_MIC_DEV)
list(APPEND ble_audio_srcs
"${CMAKE_CURRENT_LIST_DIR}/host/adapter/nimble/profiles/mics.c"
"${CMAKE_CURRENT_LIST_DIR}/${audio_adapter}/profiles/mics.c"
)
endif()
if(CONFIG_BT_MCS)
list(APPEND ble_audio_srcs
"${CMAKE_CURRENT_LIST_DIR}/host/adapter/nimble/profiles/mcs.c"
"${CMAKE_CURRENT_LIST_DIR}/${audio_adapter}/profiles/mcs.c"
)
endif()
if(CONFIG_BT_HAS)
list(APPEND ble_audio_srcs
"${CMAKE_CURRENT_LIST_DIR}/host/adapter/nimble/profiles/has.c"
"${CMAKE_CURRENT_LIST_DIR}/${audio_adapter}/profiles/has.c"
)
endif()
@@ -61,8 +61,8 @@ if BT_MCC
it.
config BT_MCC_OTS
bool "Media Control Client support for Object Transfer Service (depends on OTS Client)"
depends on BT_OTS_CLIENT
bool "Media Control Client support for Object Transfer Service"
select BT_OTS_CLIENT
help
Use this option to configure support in the Media Control Client for
an included Object Transfer Service in the Media Control Service.
@@ -71,9 +71,10 @@ if BT_MPL
config BT_MPL_OBJECTS
bool "Support for media player objects"
depends on BT_OTS
select BT_OTS
# TODO: Temporarily depends also on BT_MCS, to avoid issues with the
# bt_mcs_get_ots() call
# bt_mcs_get_ots() call. BT_MCS stays as `depends on` because it has
# non-leaf prerequisites (BT_MCTL_LOCAL_PLAYER_REMOTE_CONTROL ...).
depends on BT_MCS
# OTS shall be instantiated as a Secondary Service and shall be
# included in MCS or GMCS.
@@ -70,10 +70,12 @@ void esp_ble_audio_gap_app_post_event(uint8_t type, void *param)
bt_le_gap_app_post_event(type, param);
}
#if !CONFIG_BT_BLUEDROID_ENABLED
void esp_ble_audio_gatt_app_post_event(uint8_t type, void *param)
{
bt_le_gatt_app_post_event(type, param);
}
#endif /* !CONFIG_BT_BLUEDROID_ENABLED */
esp_err_t esp_ble_audio_common_init(esp_ble_audio_init_info_t *info)
{
@@ -156,3 +158,10 @@ esp_err_t esp_ble_audio_common_start(esp_ble_audio_start_info_t *info)
return ESP_OK;
}
#if CONFIG_BT_BLUEDROID_ENABLED
uint8_t esp_ble_audio_bluedroid_get_gattc_if(void)
{
return bt_le_bluedroid_gattc_get_if();
}
#endif /* CONFIG_BT_BLUEDROID_ENABLED */
@@ -128,15 +128,20 @@ typedef struct {
*/
void esp_ble_audio_gap_app_post_event(uint8_t type, void *param);
#if !CONFIG_BT_BLUEDROID_ENABLED
/**
* @brief Post an application-layer GATT event for audio internal usage.
*
* @note This function is only needed while using NimBLE Host.
* @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.
*
* @param type Event type.
* @param param Event parameters.
*/
void esp_ble_audio_gatt_app_post_event(uint8_t type, void *param);
#endif /* !CONFIG_BT_BLUEDROID_ENABLED */
/**
* @brief Initialize BLE Audio common functionality.
@@ -167,6 +172,21 @@ typedef struct {
*/
esp_err_t esp_ble_audio_common_start(esp_ble_audio_start_info_t *info);
#if CONFIG_BT_BLUEDROID_ENABLED
/**
* @brief Get the engine's internal GATTC interface handle (Bluedroid only).
*
* Pass this to esp_ble_gattc_aux_open() / esp_ble_gattc_open() so the
* resulting ACL events route back to the engine, avoiding the need for the
* application to register a second BTA GATTC app for connection initiation.
*
* @return Engine's gattc_if (ABI-compatible with esp_gatt_if_t), or
* ESP_GATT_IF_NONE (0xFF) if GATTC registration has not completed —
* callers must bail rather than pass it to aux_open.
*/
uint8_t esp_ble_audio_bluedroid_get_gattc_if(void);
#endif /* CONFIG_BT_BLUEDROID_ENABLED */
#ifdef __cplusplus
}
#endif
@@ -0,0 +1,43 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef HOST_BLUEDROID_INIT_H_
#define HOST_BLUEDROID_INIT_H_
#include <stdint.h>
#include "bluedroid/server.h"
#include "bluedroid/profiles/ascs.h"
#include "bluedroid/profiles/bass.h"
#include "bluedroid/profiles/cas.h"
#include "bluedroid/profiles/csis.h"
#include "bluedroid/profiles/mcs.h"
#include "bluedroid/profiles/mics.h"
#include "bluedroid/profiles/pacs.h"
#include "bluedroid/profiles/tbs.h"
#include "bluedroid/profiles/tmas.h"
#include "bluedroid/profiles/vcs.h"
#include "bluedroid/profiles/has.h"
#ifdef __cplusplus
extern "C" {
#endif
int bt_le_bluedroid_audio_init(void);
int bt_le_bluedroid_media_proxy_pl_init(void);
int bt_le_bluedroid_vcp_vol_rend_init(void);
int bt_le_bluedroid_micp_mic_dev_init(void);
int bt_le_bluedroid_audio_start(void *info);
#ifdef __cplusplus
}
#endif
#endif /* HOST_BLUEDROID_INIT_H_ */
@@ -0,0 +1,25 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef HOST_BLUEDROID_PROFILE_ASCS_H_
#define HOST_BLUEDROID_PROFILE_ASCS_H_
#include <stdint.h>
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
int bt_le_bluedroid_ascs_init(void);
int bt_le_bluedroid_ascs_start(void);
#ifdef __cplusplus
}
#endif
#endif /* HOST_BLUEDROID_PROFILE_ASCS_H_ */
@@ -0,0 +1,25 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef HOST_BLUEDROID_PROFILE_BASS_H_
#define HOST_BLUEDROID_PROFILE_BASS_H_
#include <stdint.h>
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
int bt_le_bluedroid_bass_init(void);
int bt_le_bluedroid_bass_start(void);
#ifdef __cplusplus
}
#endif
#endif /* HOST_BLUEDROID_PROFILE_BASS_H_ */
@@ -0,0 +1,25 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef HOST_BLUEDROID_PROFILE_CAS_H_
#define HOST_BLUEDROID_PROFILE_CAS_H_
#include <stdint.h>
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
int bt_le_bluedroid_cas_init(void *csis_svc_p);
int bt_le_bluedroid_cas_start(void);
#ifdef __cplusplus
}
#endif
#endif /* HOST_BLUEDROID_PROFILE_CAS_H_ */
@@ -0,0 +1,25 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef HOST_BLUEDROID_PROFILE_CSIS_H_
#define HOST_BLUEDROID_PROFILE_CSIS_H_
#include <stdint.h>
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
int bt_le_bluedroid_csis_init(void *csis_svc, uint8_t count);
int bt_le_bluedroid_csis_start(void);
#ifdef __cplusplus
}
#endif
#endif /* HOST_BLUEDROID_PROFILE_CSIS_H_ */
@@ -0,0 +1,25 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef HOST_BLUEDROID_PROFILE_HAS_H_
#define HOST_BLUEDROID_PROFILE_HAS_H_
#include <stdint.h>
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
int bt_le_bluedroid_has_init(void);
int bt_le_bluedroid_has_start(void);
#ifdef __cplusplus
}
#endif
#endif /* HOST_BLUEDROID_PROFILE_HAS_H_ */
@@ -0,0 +1,25 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef HOST_BLUEDROID_PROFILE_MCS_H_
#define HOST_BLUEDROID_PROFILE_MCS_H_
#include <stdint.h>
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
int bt_le_bluedroid_gmcs_init(void);
int bt_le_bluedroid_gmcs_start(void);
#ifdef __cplusplus
}
#endif
#endif /* HOST_BLUEDROID_PROFILE_MCS_H_ */
@@ -0,0 +1,25 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef HOST_BLUEDROID_PROFILE_MICS_H_
#define HOST_BLUEDROID_PROFILE_MICS_H_
#include <stdint.h>
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
int bt_le_bluedroid_mics_init(void *micp_inc);
int bt_le_bluedroid_mics_start(void);
#ifdef __cplusplus
}
#endif
#endif /* HOST_BLUEDROID_PROFILE_MICS_H_ */
@@ -0,0 +1,25 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef HOST_BLUEDROID_PROFILE_PACS_H_
#define HOST_BLUEDROID_PROFILE_PACS_H_
#include <stdint.h>
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
int bt_le_bluedroid_pacs_init(void);
int bt_le_bluedroid_pacs_start(void);
#ifdef __cplusplus
}
#endif
#endif /* HOST_BLUEDROID_PROFILE_PACS_H_ */
@@ -0,0 +1,25 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef HOST_BLUEDROID_PROFILE_TBS_H_
#define HOST_BLUEDROID_PROFILE_TBS_H_
#include <stdint.h>
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
int bt_le_bluedroid_gtbs_init(void);
int bt_le_bluedroid_gtbs_start(void);
#ifdef __cplusplus
}
#endif
#endif /* HOST_BLUEDROID_PROFILE_TBS_H_ */
@@ -0,0 +1,25 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef HOST_BLUEDROID_PROFILE_TMAS_H_
#define HOST_BLUEDROID_PROFILE_TMAS_H_
#include <stdint.h>
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
int bt_le_bluedroid_tmas_init(void);
int bt_le_bluedroid_tmas_start(void);
#ifdef __cplusplus
}
#endif
#endif /* HOST_BLUEDROID_PROFILE_TMAS_H_ */
@@ -0,0 +1,25 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef HOST_BLUEDROID_PROFILE_VCS_H_
#define HOST_BLUEDROID_PROFILE_VCS_H_
#include <stdint.h>
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
int bt_le_bluedroid_vcs_init(void *vcp_inc);
int bt_le_bluedroid_vcs_start(void);
#ifdef __cplusplus
}
#endif
#endif /* HOST_BLUEDROID_PROFILE_VCS_H_ */
@@ -0,0 +1,172 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef HOST_BLUEDROID_SERVER_H_
#define HOST_BLUEDROID_SERVER_H_
#include <stdint.h>
#include <stdbool.h>
#include <zephyr/bluetooth/gatt.h>
#include <zephyr/bluetooth/uuid.h>
#ifdef __cplusplus
extern "C" {
#endif
static inline char *audio_svc_uuid_to_str(uint16_t uuid)
{
switch (uuid) {
case BT_UUID_AICS_VAL: return "AICS";
case BT_UUID_CAS_VAL: return "CAS";
case BT_UUID_VCS_VAL: return "VCS";
case BT_UUID_VOCS_VAL: return "VOCS";
case BT_UUID_CSIS_VAL: return "CSIS";
case BT_UUID_MCS_VAL: return "MCS";
case BT_UUID_GMCS_VAL: return "GMCS";
case BT_UUID_TBS_VAL: return "TBS";
case BT_UUID_GTBS_VAL: return "GTBS";
case BT_UUID_MICS_VAL: return "MICS";
case BT_UUID_ASCS_VAL: return "ASCS";
case BT_UUID_BASS_VAL: return "BASS";
case BT_UUID_PACS_VAL: return "PACS";
case BT_UUID_BASIC_AUDIO_VAL: return "BASIC_AUDIO";
case BT_UUID_HAS_VAL: return "HAS";
case BT_UUID_TMAS_VAL: return "TMAS";
case BT_UUID_PBA_VAL: return "PBA";
case BT_UUID_GMAS_VAL: return "GMAS";
case BT_UUID_OTS_VAL: return "OTS";
default: return "UnknownSvc";
}
}
static inline char *audio_chrc_uuid_to_str(uint16_t uuid)
{
switch (uuid) {
case BT_UUID_OTS_FEATURE_VAL: return "OTS_FEATURE";
case BT_UUID_OTS_NAME_VAL: return "OTS_NAME";
case BT_UUID_OTS_TYPE_VAL: return "OTS_TYPE";
case BT_UUID_OTS_SIZE_VAL: return "OTS_SIZE";
case BT_UUID_OTS_FIRST_CREATED_VAL: return "OTS_FIRST_CREATED";
case BT_UUID_OTS_LAST_MODIFIED_VAL: return "OTS_LAST_MODIFIED";
case BT_UUID_OTS_ID_VAL: return "OTS_ID";
case BT_UUID_OTS_PROPERTIES_VAL: return "OTS_PROPERTIES";
case BT_UUID_OTS_ACTION_CP_VAL: return "OTS_ACTION_CP";
case BT_UUID_OTS_LIST_CP_VAL: return "OTS_LIST_CP";
case BT_UUID_OTS_LIST_FILTER_VAL: return "OTS_LIST_FILTER";
case BT_UUID_OTS_CHANGED_VAL: return "OTS_CHANGED";
case BT_UUID_GATT_TMAPR_VAL: return "TMAP_ROLE";
case BT_UUID_AICS_STATE_VAL: return "AICS_STATE";
case BT_UUID_AICS_GAIN_SETTINGS_VAL: return "AICS_GAIN_SETTINGS";
case BT_UUID_AICS_INPUT_TYPE_VAL: return "AICS_INPUT_TYPE";
case BT_UUID_AICS_INPUT_STATUS_VAL: return "AICS_INPUT_STATUS";
case BT_UUID_AICS_CONTROL_VAL: return "AICS_CONTROL";
case BT_UUID_AICS_DESCRIPTION_VAL: return "AICS_DESCRIPTION";
case BT_UUID_VCS_STATE_VAL: return "VCS_STATE";
case BT_UUID_VCS_CONTROL_VAL: return "VCS_CONTROL";
case BT_UUID_VCS_FLAGS_VAL: return "VCS_FLAGS";
case BT_UUID_VOCS_STATE_VAL: return "VOCS_STATE";
case BT_UUID_VOCS_LOCATION_VAL: return "VOCS_LOCATION";
case BT_UUID_VOCS_CONTROL_VAL: return "VOCS_CONTROL";
case BT_UUID_VOCS_DESCRIPTION_VAL: return "VOCS_DESCRIPTION";
case BT_UUID_CSIS_SIRK_VAL: return "CSIS_SIRK";
case BT_UUID_CSIS_SET_SIZE_VAL: return "CSIS_SET_SIZE";
case BT_UUID_CSIS_SET_LOCK_VAL: return "CSIS_SET_LOCK";
case BT_UUID_CSIS_RANK_VAL: return "CSIS_RANK";
case BT_UUID_MCS_PLAYER_NAME_VAL: return "MCS_PLAYER_NAME";
case BT_UUID_MCS_ICON_OBJ_ID_VAL: return "MCS_ICON_OBJ_ID";
case BT_UUID_MCS_ICON_URL_VAL: return "MCS_ICON_URL";
case BT_UUID_MCS_TRACK_CHANGED_VAL: return "MCS_TRACK_CHANGED";
case BT_UUID_MCS_TRACK_TITLE_VAL: return "MCS_TRACK_TITLE";
case BT_UUID_MCS_TRACK_DURATION_VAL: return "MCS_TRACK_DURATION";
case BT_UUID_MCS_TRACK_POSITION_VAL: return "MCS_TRACK_POSITION";
case BT_UUID_MCS_PLAYBACK_SPEED_VAL: return "MCS_PLAYBACK_SPEED";
case BT_UUID_MCS_SEEKING_SPEED_VAL: return "MCS_SEEKING_SPEED";
case BT_UUID_MCS_TRACK_SEGMENTS_OBJ_ID_VAL: return "MCS_TRACK_SEGMENTS_OBJ_ID";
case BT_UUID_MCS_CURRENT_TRACK_OBJ_ID_VAL: return "MCS_CURRENT_TRACK_OBJ_ID";
case BT_UUID_MCS_NEXT_TRACK_OBJ_ID_VAL: return "MCS_NEXT_TRACK_OBJ_ID";
case BT_UUID_MCS_PARENT_GROUP_OBJ_ID_VAL: return "MCS_PARENT_GROUP_OBJ_ID";
case BT_UUID_MCS_CURRENT_GROUP_OBJ_ID_VAL: return "MCS_CURRENT_GROUP_OBJ_ID";
case BT_UUID_MCS_PLAYING_ORDER_VAL: return "MCS_PLAYING_ORDER";
case BT_UUID_MCS_PLAYING_ORDERS_VAL: return "MCS_PLAYING_ORDERS";
case BT_UUID_MCS_MEDIA_STATE_VAL: return "MCS_MEDIA_STATE";
case BT_UUID_MCS_MEDIA_CONTROL_POINT_VAL: return "MCS_MEDIA_CONTROL_POINT";
case BT_UUID_MCS_MEDIA_CONTROL_OPCODES_VAL: return "MCS_MEDIA_CONTROL_OPCODES";
case BT_UUID_MCS_SEARCH_RESULTS_OBJ_ID_VAL: return "MCS_SEARCH_RESULTS_OBJ_ID";
case BT_UUID_MCS_SEARCH_CONTROL_POINT_VAL: return "MCS_SEARCH_CONTROL_POINT";
case BT_UUID_TBS_PROVIDER_NAME_VAL: return "TBS_PROVIDER_NAME";
case BT_UUID_TBS_UCI_VAL: return "TBS_UCI";
case BT_UUID_TBS_TECHNOLOGY_VAL: return "TBS_TECHNOLOGY";
case BT_UUID_TBS_URI_LIST_VAL: return "TBS_URI_LIST";
case BT_UUID_TBS_SIGNAL_STRENGTH_VAL: return "TBS_SIGNAL_STRENGTH";
case BT_UUID_TBS_SIGNAL_INTERVAL_VAL: return "TBS_SIGNAL_INTERVAL";
case BT_UUID_TBS_LIST_CURRENT_CALLS_VAL: return "TBS_LIST_CURRENT_CALLS";
case BT_UUID_CCID_VAL: return "CCID";
case BT_UUID_TBS_STATUS_FLAGS_VAL: return "TBS_STATUS_FLAGS";
case BT_UUID_TBS_INCOMING_URI_VAL: return "TBS_INCOMING_URI";
case BT_UUID_TBS_CALL_STATE_VAL: return "TBS_CALL_STATE";
case BT_UUID_TBS_CALL_CONTROL_POINT_VAL: return "TBS_CALL_CONTROL_POINT";
case BT_UUID_TBS_OPTIONAL_OPCODES_VAL: return "TBS_OPTIONAL_OPCODES";
case BT_UUID_TBS_TERMINATE_REASON_VAL: return "TBS_TERMINATE_REASON";
case BT_UUID_TBS_INCOMING_CALL_VAL: return "TBS_INCOMING_CALL";
case BT_UUID_TBS_FRIENDLY_NAME_VAL: return "TBS_FRIENDLY_NAME";
case BT_UUID_MICS_MUTE_VAL: return "MICS_MUTE";
case BT_UUID_ASCS_ASE_SNK_VAL: return "ASCS_ASE_SNK";
case BT_UUID_ASCS_ASE_SRC_VAL: return "ASCS_ASE_SRC";
case BT_UUID_ASCS_ASE_CP_VAL: return "ASCS_ASE_CP";
case BT_UUID_BASS_CONTROL_POINT_VAL: return "BASS_CP";
case BT_UUID_BASS_RECV_STATE_VAL: return "BASS_RECV_STATE";
case BT_UUID_PACS_SNK_VAL: return "PACS_SNK";
case BT_UUID_PACS_SNK_LOC_VAL: return "PACS_SNK_LOC";
case BT_UUID_PACS_SRC_VAL: return "PACS_SRC";
case BT_UUID_PACS_SRC_LOC_VAL: return "PACS_SRC_LOC";
case BT_UUID_PACS_AVAILABLE_CONTEXT_VAL: return "PACS_AVAILABLE_CONTEXT";
case BT_UUID_PACS_SUPPORTED_CONTEXT_VAL: return "PACS_SUPPORTED_CONTEXT";
case BT_UUID_HAS_HEARING_AID_FEATURES_VAL: return "HAS_HEARING_AID_FEATURES";
case BT_UUID_HAS_PRESET_CONTROL_POINT_VAL: return "HAS_PRESET_CONTROL_POINT";
case BT_UUID_HAS_ACTIVE_PRESET_INDEX_VAL: return "HAS_ACTIVE_PRESET_INDEX";
case BT_UUID_GMAP_ROLE_VAL: return "GMAP_ROLE";
case BT_UUID_GMAP_UGG_FEAT_VAL: return "GMAP_UGG_FEAT";
case BT_UUID_GMAP_UGT_FEAT_VAL: return "GMAP_UGT_FEAT";
case BT_UUID_GMAP_BGS_FEAT_VAL: return "GMAP_BGS_FEAT";
case BT_UUID_GMAP_BGR_FEAT_VAL: return "GMAP_BGR_FEAT";
default: return "UnknownChrc";
}
}
enum {
AICS_IN_PROGRESS,
ASCS_IN_PROGRESS,
BASS_IN_PROGRESS,
CAS_IN_PROGRESS,
CSIS_IN_PROGRESS,
HAS_IN_PROGRESS,
GMCS_IN_PROGRESS,
MCS_IN_PROGRESS,
MICS_IN_PROGRESS,
PACS_IN_PROGRESS,
GTBS_IN_PROGRESS,
TBS_IN_PROGRESS,
TMAS_IN_PROGRESS,
VCS_IN_PROGRESS,
VOCS_IN_PROGRESS,
MAX_IN_PROGRESS,
};
void bt_le_bluedroid_audio_gatts_init(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_start(struct bt_gatt_service *svc);
#ifdef __cplusplus
}
#endif
#endif /* HOST_BLUEDROID_SERVER_H_ */
@@ -0,0 +1,342 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdint.h>
#include <errno.h>
#include <assert.h>
#include "sdkconfig.h"
#include "bta/bta_gatt_common.h"
#include <zephyr/sys/util.h>
#include <zephyr/logging/log.h>
#include <zephyr/bluetooth/audio/micp.h>
#include <zephyr/bluetooth/audio/vcp.h>
#include "bluedroid/init.h"
#include "common/init.h"
#include "../../../lib/include/audio.h"
LOG_MODULE_REGISTER(LEA_BINIT, CONFIG_BT_ISO_LOG_LEVEL);
#if CONFIG_BT_CSIP_SET_MEMBER
#define CSIS_SVC_COUNT CONFIG_BT_CSIP_SET_MEMBER_MAX_INSTANCE_COUNT
#else /* CONFIG_BT_CSIP_SET_MEMBER */
#define CSIS_SVC_COUNT 0
#endif /* CONFIG_BT_CSIP_SET_MEMBER */
/* 3 is reserved for other GATT services */
#define TOTAL_SERVICE_COUNT (3 + \
(IS_ENABLED(CONFIG_BT_ASCS) ? 1 : 0) + \
(IS_ENABLED(CONFIG_BT_PACS) ? 1 : 0) + \
(IS_ENABLED(CONFIG_BT_BAP_SCAN_DELEGATOR) ? 1 : 0) + \
(IS_ENABLED(CONFIG_BT_TMAP) ? 1 : 0) + \
(IS_ENABLED(CONFIG_BT_MCS) ? 1 : 0) + \
(IS_ENABLED(CONFIG_BT_CSIP_SET_MEMBER) ? CSIS_SVC_COUNT : 0) + \
(IS_ENABLED(CONFIG_BT_CAP_ACCEPTOR) ? 1 : 0) + \
(IS_ENABLED(CONFIG_BT_VCP_VOL_REND) ? 1 : 0) + \
(IS_ENABLED(CONFIG_BT_MICP_MIC_DEV) ? 1 : 0) + \
CONFIG_BT_VOCS_MAX_INSTANCE_COUNT + \
CONFIG_BT_AICS_MAX_INSTANCE_COUNT + \
(IS_ENABLED(CONFIG_BT_TBS) ? 1 : 0) + \
(IS_ENABLED(CONFIG_BT_HAS) ? 1 : 0))
_Static_assert(TOTAL_SERVICE_COUNT <= CONFIG_BT_GATT_MAX_SR_PROFILES, "Too small BT_GATT_MAX_SR_PROFILES");
int bt_le_bluedroid_audio_init(void)
{
int err = 0;
BTA_GATT_SetLocalMTU(BLE_AUDIO_ATT_MTU_MIN);
bt_le_bluedroid_audio_gatts_init();
#if CONFIG_BT_PACS
err = bt_le_bluedroid_pacs_init();
if (err) {
return err;
}
#endif /* CONFIG_BT_PACS */
#if (BLE_AUDIO_SVC_DEFERRED_ADD == 0)
#if CONFIG_BT_ASCS
err = bt_le_bluedroid_ascs_init();
if (err) {
return err;
}
#endif /* CONFIG_BT_ASCS */
#if CONFIG_BT_BAP_SCAN_DELEGATOR
err = bt_le_bluedroid_bass_init();
if (err) {
return err;
}
#endif /* CONFIG_BT_BAP_SCAN_DELEGATOR */
#if CONFIG_BT_TMAP
err = bt_le_bluedroid_tmas_init();
if (err) {
return err;
}
#endif /* CONFIG_BT_TMAP */
#if CONFIG_BT_TBS
err = bt_le_bluedroid_gtbs_init();
if (err) {
return err;
}
#endif /* CONFIG_BT_TBS */
#if CONFIG_BT_HAS
err = bt_le_bluedroid_has_init();
if (err) {
return err;
}
#endif /* CONFIG_BT_HAS */
#endif /* (BLE_AUDIO_SVC_DEFERRED_ADD == 0) */
return err;
}
#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)
{
struct bt_gatt_service *csis_svc[CONFIG_BT_CSIP_SET_MEMBER_MAX_INSTANCE_COUNT];
uint8_t count;
int err;
if (info == NULL) {
return 0;
}
*inc_csis_svc = NULL;
count = 0;
for (size_t i = 0; i < ARRAY_SIZE(info->csis_insts); i++) {
if (info->csis_insts[i].svc_inst == NULL) {
continue;
}
csis_svc[count] = lib_csip_set_member_svc_get(info->csis_insts[i].svc_inst);
if (!csis_svc[count]) {
LOG_ERR("[B]CsisSvcGetFail[%u]", i);
return -ENODEV;
}
if (info->csis_insts[i].included_by_cas) {
if (*inc_csis_svc == NULL) {
*inc_csis_svc = csis_svc[count];
} else {
/* CAS may include at most one CSIS — caller misconfigured. */
LOG_ERR("[B]CsisMultiIncByCas");
return -EINVAL;
}
}
count++;
}
err = bt_le_bluedroid_csis_init(csis_svc, count);
if (err) {
return err;
}
return 0;
}
#endif /* CONFIG_BT_CSIP_SET_MEMBER */
#if CONFIG_BT_MCS
int bt_le_bluedroid_media_proxy_pl_init(void)
{
int err;
#if CONFIG_BT_MPL_OBJECTS
#endif /* CONFIG_BT_MPL_OBJECTS */
err = bt_le_bluedroid_gmcs_init();
if (err) {
return err;
}
return 0;
}
#endif /* CONFIG_BT_MCS */
#if CONFIG_BT_VCP_VOL_REND
int bt_le_bluedroid_vcp_vol_rend_init(void)
{
struct bt_vcp_included vcp_included;
int err;
memset(&vcp_included, 0, sizeof(vcp_included));
err = bt_vcp_vol_rend_included_get_safe(&vcp_included);
if (err) {
return err;
}
err = bt_le_bluedroid_vcs_init(&vcp_included);
if (err) {
return err;
}
return 0;
}
#endif /* CONFIG_BT_VCP_VOL_REND */
#if CONFIG_BT_MICP_MIC_DEV
int bt_le_bluedroid_micp_mic_dev_init(void)
{
struct bt_micp_included micp_included;
int err;
memset(&micp_included, 0, sizeof(micp_included));
err = bt_micp_mic_dev_included_get_safe(&micp_included);
if (err) {
return err;
}
err = bt_le_bluedroid_mics_init(&micp_included);
if (err) {
return err;
}
return 0;
}
#endif /* CONFIG_BT_MICP_MIC_DEV */
int bt_le_bluedroid_audio_start(void *info)
{
struct bt_gatt_service *inc_csis_svc = NULL;
int err = 0;
#if CONFIG_BT_CSIP_SET_MEMBER
/* Note:
* Should add CSIS before CAS in case one CSIS instance
* is included by CAS.
*/
err = bluedroid_gatt_csis_init(info, &inc_csis_svc);
if (err) {
return err;
}
#endif /* CONFIG_BT_CSIP_SET_MEMBER */
#if CONFIG_BT_CAP_ACCEPTOR
err = bt_le_bluedroid_cas_init(inc_csis_svc);
if (err) {
return err;
}
#else /* CONFIG_BT_CAP_ACCEPTOR */
ARG_UNUSED(inc_csis_svc);
#endif /* CONFIG_BT_CAP_ACCEPTOR */
#if (BLE_AUDIO_SVC_DEFERRED_ADD == 0)
#if CONFIG_BT_MCS
err = bt_le_bluedroid_media_proxy_pl_init();
if (err) {
return err;
}
#endif /* CONFIG_BT_MCS */
#if CONFIG_BT_VCP_VOL_REND
err = bt_le_bluedroid_vcp_vol_rend_init();
if (err) {
return err;
}
#endif /* CONFIG_BT_VCP_VOL_REND */
#if CONFIG_BT_MICP_MIC_DEV
err = bt_le_bluedroid_micp_mic_dev_init();
if (err) {
return err;
}
#endif /* CONFIG_BT_MICP_MIC_DEV */
#endif /* (BLE_AUDIO_SVC_DEFERRED_ADD == 0) */
#if CONFIG_BT_ASCS
err = bt_le_bluedroid_ascs_start();
if (err) {
return err;
}
#endif /* CONFIG_BT_ASCS */
#if CONFIG_BT_PACS
err = bt_le_bluedroid_pacs_start();
if (err) {
return err;
}
#endif /* CONFIG_BT_PACS */
#if CONFIG_BT_BAP_SCAN_DELEGATOR
err = bt_le_bluedroid_bass_start();
if (err) {
return err;
}
#endif /* CONFIG_BT_BAP_SCAN_DELEGATOR */
#if CONFIG_BT_TMAP
err = bt_le_bluedroid_tmas_start();
if (err) {
return err;
}
#endif /* CONFIG_BT_TMAP */
#if CONFIG_BT_CSIP_SET_MEMBER
err = bt_le_bluedroid_csis_start();
if (err) {
return err;
}
#endif /* CONFIG_BT_CSIP_SET_MEMBER */
#if CONFIG_BT_CAP_ACCEPTOR
err = bt_le_bluedroid_cas_start();
if (err) {
return err;
}
#endif /* CONFIG_BT_CAP_ACCEPTOR */
#if CONFIG_BT_VCP_VOL_REND
err = bt_le_bluedroid_vcs_start();
if (err) {
return err;
}
#endif /* CONFIG_BT_VCP_VOL_REND */
#if CONFIG_BT_MICP_MIC_DEV
err = bt_le_bluedroid_mics_start();
if (err) {
return err;
}
#endif /* CONFIG_BT_MICP_MIC_DEV */
#if CONFIG_BT_MCS
err = bt_le_bluedroid_gmcs_start();
if (err) {
return err;
}
#endif /* CONFIG_BT_MCS */
#if CONFIG_BT_TBS
err = bt_le_bluedroid_gtbs_start();
if (err) {
return err;
}
#endif /* CONFIG_BT_TBS */
#if CONFIG_BT_HAS
err = bt_le_bluedroid_has_start();
if (err) {
return err;
}
#endif /* CONFIG_BT_HAS */
return err;
}
@@ -0,0 +1,56 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdint.h>
#include <stdbool.h>
#include <errno.h>
#include <zephyr/autoconf.h>
#include <zephyr/logging/log.h>
#include <zephyr/bluetooth/uuid.h>
#include <zephyr/bluetooth/gatt.h>
#include "bluedroid/server.h"
#include "common/host.h"
#include "../../../lib/include/audio.h"
LOG_MODULE_REGISTER(LEA_ASCS, CONFIG_BT_ISO_LOG_LEVEL);
int bt_le_bluedroid_ascs_init(void)
{
struct bt_gatt_service *ascs_svc;
LOG_DBG("[B]AscsInit");
ascs_svc = lib_ascs_svc_get();
if (!ascs_svc) {
LOG_ERR("[B]AscsSvcGetFail");
return -ENODEV;
}
bt_le_bluedroid_set_svc_in_progress(ASCS_IN_PROGRESS);
return bt_le_bluedroid_svc_init(ascs_svc);
}
int bt_le_bluedroid_ascs_start(void)
{
struct bt_gatt_service *ascs_svc;
LOG_DBG("[B]AscsStart");
ascs_svc = lib_ascs_svc_get();
if (!ascs_svc) {
LOG_ERR("[B]AscsSvcGetFail");
return -ENODEV;
}
bt_le_bluedroid_set_svc_in_progress(ASCS_IN_PROGRESS);
return bt_le_bluedroid_svc_start(ascs_svc);
}
@@ -0,0 +1,56 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdint.h>
#include <stdbool.h>
#include <errno.h>
#include <zephyr/autoconf.h>
#include <zephyr/logging/log.h>
#include <zephyr/bluetooth/uuid.h>
#include <zephyr/bluetooth/gatt.h>
#include "bluedroid/server.h"
#include "common/host.h"
#include "../../../lib/include/audio.h"
LOG_MODULE_REGISTER(LEA_BASS, CONFIG_BT_ISO_LOG_LEVEL);
int bt_le_bluedroid_bass_init(void)
{
struct bt_gatt_service *bass_svc;
LOG_DBG("[B]BassInit");
bass_svc = lib_bap_bass_svc_get();
if (!bass_svc) {
LOG_ERR("[B]BassSvcGetFail");
return -ENODEV;
}
bt_le_bluedroid_set_svc_in_progress(BASS_IN_PROGRESS);
return bt_le_bluedroid_svc_init(bass_svc);
}
int bt_le_bluedroid_bass_start(void)
{
struct bt_gatt_service *bass_svc;
LOG_DBG("[B]BassStart");
bass_svc = lib_bap_bass_svc_get();
if (!bass_svc) {
LOG_ERR("[B]BassSvcGetFail");
return -ENODEV;
}
bt_le_bluedroid_set_svc_in_progress(BASS_IN_PROGRESS);
return bt_le_bluedroid_svc_start(bass_svc);
}
@@ -0,0 +1,105 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdint.h>
#include <stdbool.h>
#include <errno.h>
#include <zephyr/autoconf.h>
#include <zephyr/logging/log.h>
#include <zephyr/bluetooth/uuid.h>
#include <zephyr/bluetooth/gatt.h>
#include "bluedroid/server.h"
#include "common/host.h"
#include "../../../lib/include/audio.h"
LOG_MODULE_REGISTER(LEA_CAS, CONFIG_BT_ISO_LOG_LEVEL);
#if CONFIG_BT_CAP_ACCEPTOR_SET_MEMBER
static struct inc_svc_inst inc_csis_inst;
struct inc_svc_inst *cas_not_included_inst(void)
{
if (inc_csis_inst.included == false) {
return &inc_csis_inst;
}
return NULL;
}
#endif /* CONFIG_BT_CAP_ACCEPTOR_SET_MEMBER */
int bt_le_bluedroid_cas_init(void *csis_svc_p)
{
struct bt_gatt_service *cas_svc;
int err = 0;
LOG_DBG("[B]CasInit");
#if CONFIG_BT_CAP_ACCEPTOR_SET_MEMBER
/* csis_svc_p is non-NULL in practice (SET_MEMBER depends on CSIP_SET_MEMBER,
* which populates it), but guard for parity with the nimble adapter. With
* no CSIS, mark included=true so cas_not_included_inst() skips it and
* svc_init never feeds a NULL svc_p into BTA_GATTS_AddIncludeService. */
if (csis_svc_p) {
/* The instance of CSIS is included by CAS */
inc_csis_inst.svc_p = csis_svc_p;
/* Reset included before svc_init; see mics.c for rationale. */
inc_csis_inst.included = false;
} else {
inc_csis_inst.included = true;
}
#else /* CONFIG_BT_CAP_ACCEPTOR_SET_MEMBER */
ARG_UNUSED(csis_svc_p);
#endif /* CONFIG_BT_CAP_ACCEPTOR_SET_MEMBER */
cas_svc = lib_cas_svc_get();
if (!cas_svc) {
LOG_ERR("[B]CasSvcGetFail");
return -ENODEV;
}
bt_le_bluedroid_set_svc_in_progress(CAS_IN_PROGRESS);
err = bt_le_bluedroid_svc_init(cas_svc);
if (err) {
return err;
}
#if CONFIG_BT_CAP_ACCEPTOR_SET_MEMBER
if (cas_not_included_inst()) {
LOG_ERR("[B]CasCsisNotInc");
return -EIO;
}
#else /* CONFIG_BT_CAP_ACCEPTOR_SET_MEMBER */
/* Insert CAS to the GATT db list */
err = bt_gatt_service_register_safe(cas_svc);
if (err) {
LOG_ERR("[B]CasSvcRegFail[%d]", err);
}
#endif /* CONFIG_BT_CAP_ACCEPTOR_SET_MEMBER */
return err;
}
int bt_le_bluedroid_cas_start(void)
{
struct bt_gatt_service *cas_svc;
LOG_DBG("[B]CasStart");
cas_svc = lib_cas_svc_get();
if (!cas_svc) {
LOG_ERR("[B]CasSvcGetFail");
return -ENODEV;
}
bt_le_bluedroid_set_svc_in_progress(CAS_IN_PROGRESS);
return bt_le_bluedroid_svc_start(cas_svc);
}
@@ -0,0 +1,76 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdint.h>
#include <stdbool.h>
#include <errno.h>
#include <zephyr/autoconf.h>
#include <zephyr/logging/log.h>
#include <zephyr/bluetooth/uuid.h>
#include <zephyr/bluetooth/gatt.h>
#include "bluedroid/server.h"
#include "common/host.h"
#include "../../../lib/include/audio.h"
LOG_MODULE_REGISTER(LEA_CSIS, CONFIG_BT_ISO_LOG_LEVEL);
#define CSIS_SVC_COUNT CONFIG_BT_CSIP_SET_MEMBER_MAX_INSTANCE_COUNT
static struct csis_inst {
struct bt_gatt_service *svc_p;
} csis_insts[CSIS_SVC_COUNT];
static uint8_t csis_svc_count;
int bt_le_bluedroid_csis_init(void *svc, uint8_t count)
{
int err;
LOG_DBG("[B]CsisInit");
if (count > CSIS_SVC_COUNT) {
return -1;
}
bt_le_bluedroid_set_svc_in_progress(CSIS_IN_PROGRESS);
for (size_t i = 0; i < count; i++) {
csis_insts[i].svc_p = ((struct bt_gatt_service **)svc)[i];
err = bt_le_bluedroid_svc_init(csis_insts[i].svc_p);
if (err) {
return err;
}
}
/* Commit the count only after the loop succeeds, so a mid-loop failure
* doesn't leave csis_start() iterating uninitialized entries. */
csis_svc_count = count;
return 0;
}
int bt_le_bluedroid_csis_start(void)
{
int err;
LOG_DBG("[B]CsisStart");
bt_le_bluedroid_set_svc_in_progress(CSIS_IN_PROGRESS);
for (size_t i = 0; i < csis_svc_count; i++) {
err = bt_le_bluedroid_svc_start(csis_insts[i].svc_p);
if (err) {
return err;
}
}
return 0;
}
@@ -0,0 +1,56 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdint.h>
#include <stdbool.h>
#include <errno.h>
#include <zephyr/autoconf.h>
#include <zephyr/logging/log.h>
#include <zephyr/bluetooth/uuid.h>
#include <zephyr/bluetooth/gatt.h>
#include "bluedroid/server.h"
#include "common/host.h"
#include "../../../lib/include/audio.h"
LOG_MODULE_REGISTER(LEA_HAS, CONFIG_BT_ISO_LOG_LEVEL);
int bt_le_bluedroid_has_init(void)
{
struct bt_gatt_service *has_svc;
LOG_DBG("[B]HasInit");
has_svc = lib_has_svc_get();
if (!has_svc) {
LOG_ERR("[B]HasSvcGetFail");
return -ENODEV;
}
bt_le_bluedroid_set_svc_in_progress(HAS_IN_PROGRESS);
return bt_le_bluedroid_svc_init(has_svc);
}
int bt_le_bluedroid_has_start(void)
{
struct bt_gatt_service *has_svc;
LOG_DBG("[B]HasStart");
has_svc = lib_has_svc_get();
if (!has_svc) {
LOG_ERR("[B]HasSvcGetFail");
return -ENODEV;
}
bt_le_bluedroid_set_svc_in_progress(HAS_IN_PROGRESS);
return bt_le_bluedroid_svc_start(has_svc);
}
@@ -0,0 +1,52 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdint.h>
#include <stdbool.h>
#include <errno.h>
#include <zephyr/autoconf.h>
#include <zephyr/logging/log.h>
#include <zephyr/bluetooth/uuid.h>
#include <zephyr/bluetooth/gatt.h>
#include "bluedroid/server.h"
#include "common/host.h"
#include "../../../lib/include/audio.h"
LOG_MODULE_REGISTER(LEA_MCS, CONFIG_BT_ISO_LOG_LEVEL);
int bt_le_bluedroid_gmcs_init(void)
{
struct bt_gatt_service *gmcs_svc;
gmcs_svc = lib_mcs_svc_get();
if (!gmcs_svc) {
LOG_ERR("[B]GmcsSvcGetFail");
return -ENODEV;
}
bt_le_bluedroid_set_svc_in_progress(GMCS_IN_PROGRESS);
return bt_le_bluedroid_svc_init(gmcs_svc);
}
int bt_le_bluedroid_gmcs_start(void)
{
struct bt_gatt_service *gmcs_svc;
gmcs_svc = lib_mcs_svc_get();
if (!gmcs_svc) {
LOG_ERR("[B]GmcsSvcGetFail");
return -ENODEV;
}
bt_le_bluedroid_set_svc_in_progress(GMCS_IN_PROGRESS);
return bt_le_bluedroid_svc_start(gmcs_svc);
}
@@ -0,0 +1,136 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdint.h>
#include <stdbool.h>
#include <errno.h>
#include <zephyr/autoconf.h>
#include <zephyr/logging/log.h>
#include <zephyr/bluetooth/uuid.h>
#include <zephyr/bluetooth/gatt.h>
#include <zephyr/bluetooth/audio/micp.h>
#include "bluedroid/server.h"
#include "common/host.h"
#include "../../../lib/include/audio.h"
LOG_MODULE_REGISTER(LEA_MICS, CONFIG_BT_ISO_LOG_LEVEL);
#define AICS_INST_COUNT CONFIG_BT_MICP_MIC_DEV_AICS_INSTANCE_COUNT
static uint8_t inc_aics_svc_count;
static struct inc_svc_inst inc_aics_insts[AICS_INST_COUNT];
struct inc_svc_inst *mics_not_included_inst(void)
{
for (size_t i = 0; i < inc_aics_svc_count; i++) {
if (inc_aics_insts[i].included == false) {
return &inc_aics_insts[i];
}
}
return NULL;
}
int bt_le_bluedroid_mics_init(void *micp_inc)
{
struct bt_micp_included *micp_included;
struct bt_gatt_service *mics_svc;
int err;
LOG_DBG("[B]MicsInit");
mics_svc = lib_mics_svc_get();
if (!mics_svc) {
LOG_ERR("[B]MicsSvcGetFail");
return -ENODEV;
}
/* Reset before the NULL check so a re-init with micp_included == NULL
* doesn't iterate over stale entries from a previous non-NULL call. */
inc_aics_svc_count = 0;
micp_included = micp_inc;
if (micp_included) {
if (micp_included->aics_cnt > AICS_INST_COUNT) {
return -EINVAL;
}
bt_le_bluedroid_set_svc_in_progress(AICS_IN_PROGRESS);
/* MICS may include zero or more instances of AICS */
for (size_t i = 0; i < micp_included->aics_cnt; i++) {
inc_aics_insts[i].svc_p = lib_aics_svc_get(micp_included->aics[i]);
if (!inc_aics_insts[i].svc_p) {
LOG_ERR("[B]AicsSvcGetFail[%u]", i);
return -ENODEV;
}
/* Reset included before svc_init: server.c sets it to true when
* the include attribute is processed. Without this reset, a
* re-init where the include attribute is missing would leave
* the stale true from a previous init and mics_not_included_inst()
* would falsely report all instances as included. */
inc_aics_insts[i].included = false;
err = bt_le_bluedroid_svc_init(inc_aics_insts[i].svc_p);
if (err) {
return err;
}
}
/* Commit the count only after every instance initialized — a mid-loop
* failure above leaves it at 0 (reset on entry) so mics_start() never
* iterates partially-initialized entries. */
inc_aics_svc_count = micp_included->aics_cnt;
}
bt_le_bluedroid_set_svc_in_progress(MICS_IN_PROGRESS);
err = bt_le_bluedroid_svc_init(mics_svc);
if (err) {
return err;
}
if (mics_not_included_inst()) {
LOG_ERR("[B]MicsAicsNotAllInc");
return -EIO;
}
return 0;
}
int bt_le_bluedroid_mics_start(void)
{
struct bt_gatt_service *mics_svc;
int err;
LOG_DBG("[B]MicsStart");
mics_svc = lib_mics_svc_get();
if (!mics_svc) {
LOG_ERR("[B]MicsSvcGetFail");
return -ENODEV;
}
bt_le_bluedroid_set_svc_in_progress(AICS_IN_PROGRESS);
for (size_t i = 0; i < inc_aics_svc_count; i++) {
err = bt_le_bluedroid_svc_start(inc_aics_insts[i].svc_p);
if (err) {
return err;
}
}
bt_le_bluedroid_set_svc_in_progress(MICS_IN_PROGRESS);
return bt_le_bluedroid_svc_start(mics_svc);
}
@@ -0,0 +1,56 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdint.h>
#include <stdbool.h>
#include <errno.h>
#include <zephyr/autoconf.h>
#include <zephyr/logging/log.h>
#include <zephyr/bluetooth/uuid.h>
#include <zephyr/bluetooth/gatt.h>
#include "bluedroid/server.h"
#include "common/host.h"
#include "../../../lib/include/audio.h"
LOG_MODULE_REGISTER(LEA_PACS, CONFIG_BT_ISO_LOG_LEVEL);
int bt_le_bluedroid_pacs_init(void)
{
struct bt_gatt_service *pacs_svc;
LOG_DBG("[B]PacsInit");
pacs_svc = lib_pacs_svc_get();
if (!pacs_svc) {
LOG_ERR("[B]PacsSvcGetFail");
return -ENODEV;
}
bt_le_bluedroid_set_svc_in_progress(PACS_IN_PROGRESS);
return bt_le_bluedroid_svc_init(pacs_svc);
}
int bt_le_bluedroid_pacs_start(void)
{
struct bt_gatt_service *pacs_svc;
LOG_DBG("[B]PacsStart");
pacs_svc = lib_pacs_svc_get();
if (!pacs_svc) {
LOG_ERR("[B]PacsSvcGetFail");
return -ENODEV;
}
bt_le_bluedroid_set_svc_in_progress(PACS_IN_PROGRESS);
return bt_le_bluedroid_svc_start(pacs_svc);
}
@@ -0,0 +1,52 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdint.h>
#include <stdbool.h>
#include <errno.h>
#include <zephyr/autoconf.h>
#include <zephyr/logging/log.h>
#include <zephyr/bluetooth/uuid.h>
#include <zephyr/bluetooth/gatt.h>
#include "bluedroid/server.h"
#include "common/host.h"
#include "../../../lib/include/audio.h"
LOG_MODULE_REGISTER(LEA_TBS, CONFIG_BT_ISO_LOG_LEVEL);
int bt_le_bluedroid_gtbs_init(void)
{
struct bt_gatt_service *gtbs_svc;
gtbs_svc = lib_gtbs_svc_get();
if (!gtbs_svc) {
LOG_ERR("[B]GtbsSvcGetFail");
return -ENODEV;
}
bt_le_bluedroid_set_svc_in_progress(GTBS_IN_PROGRESS);
return bt_le_bluedroid_svc_init(gtbs_svc);
}
int bt_le_bluedroid_gtbs_start(void)
{
struct bt_gatt_service *gtbs_svc;
gtbs_svc = lib_gtbs_svc_get();
if (!gtbs_svc) {
LOG_ERR("[B]GtbsSvcGetFail");
return -ENODEV;
}
bt_le_bluedroid_set_svc_in_progress(GTBS_IN_PROGRESS);
return bt_le_bluedroid_svc_start(gtbs_svc);
}
@@ -0,0 +1,56 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdint.h>
#include <stdbool.h>
#include <errno.h>
#include <zephyr/autoconf.h>
#include <zephyr/logging/log.h>
#include <zephyr/bluetooth/uuid.h>
#include <zephyr/bluetooth/gatt.h>
#include "bluedroid/server.h"
#include "common/host.h"
#include "../../../lib/include/audio.h"
LOG_MODULE_REGISTER(LEA_TMAS, CONFIG_BT_ISO_LOG_LEVEL);
int bt_le_bluedroid_tmas_init(void)
{
struct bt_gatt_service *tmas_svc;
LOG_DBG("[B]TmasInit");
tmas_svc = lib_tmas_svc_get();
if (!tmas_svc) {
LOG_ERR("[B]TmasSvcGetFail");
return -ENODEV;
}
bt_le_bluedroid_set_svc_in_progress(TMAS_IN_PROGRESS);
return bt_le_bluedroid_svc_init(tmas_svc);
}
int bt_le_bluedroid_tmas_start(void)
{
struct bt_gatt_service *tmas_svc;
LOG_DBG("[B]TmasStart");
tmas_svc = lib_tmas_svc_get();
if (!tmas_svc) {
LOG_ERR("[B]TmasSvcGetFail");
return -ENODEV;
}
bt_le_bluedroid_set_svc_in_progress(TMAS_IN_PROGRESS);
return bt_le_bluedroid_svc_start(tmas_svc);
}
@@ -0,0 +1,169 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdint.h>
#include <stdbool.h>
#include <errno.h>
#include <zephyr/autoconf.h>
#include <zephyr/logging/log.h>
#include <zephyr/bluetooth/uuid.h>
#include <zephyr/bluetooth/gatt.h>
#include <zephyr/bluetooth/audio/vcp.h>
#include "bluedroid/server.h"
#include "common/host.h"
#include "../../../lib/include/audio.h"
LOG_MODULE_REGISTER(LEA_VCS, CONFIG_BT_ISO_LOG_LEVEL);
#define VOCS_INST_COUNT CONFIG_BT_VCP_VOL_REND_VOCS_INSTANCE_COUNT
#define AICS_INST_COUNT CONFIG_BT_VCP_VOL_REND_AICS_INSTANCE_COUNT
static uint8_t inc_vocs_svc_count;
static uint8_t inc_aics_svc_count;
static struct inc_svc_inst inc_vocs_insts[VOCS_INST_COUNT];
static struct inc_svc_inst inc_aics_insts[AICS_INST_COUNT];
struct inc_svc_inst *vcs_not_included_inst(void)
{
for (size_t i = 0; i < inc_vocs_svc_count; i++) {
if (inc_vocs_insts[i].included == false) {
return &inc_vocs_insts[i];
}
}
for (size_t i = 0; i < inc_aics_svc_count; i++) {
if (inc_aics_insts[i].included == false) {
return &inc_aics_insts[i];
}
}
return NULL;
}
int bt_le_bluedroid_vcs_init(void *vcp_inc)
{
struct bt_vcp_included *vcp_included;
struct bt_gatt_service *vcs_svc;
int err;
LOG_DBG("[B]VcsInit");
vcs_svc = lib_vcs_svc_get();
if (!vcs_svc) {
LOG_ERR("[B]VcsSvcGetFail");
return -ENODEV;
}
/* Reset before the NULL check so a re-init with vcp_included == NULL
* doesn't iterate over stale entries from a previous non-NULL call. */
inc_vocs_svc_count = 0;
inc_aics_svc_count = 0;
vcp_included = vcp_inc;
if (vcp_included) {
if (vcp_included->vocs_cnt > VOCS_INST_COUNT ||
vcp_included->aics_cnt > AICS_INST_COUNT) {
return -EINVAL;
}
inc_vocs_svc_count = vcp_included->vocs_cnt;
inc_aics_svc_count = vcp_included->aics_cnt;
bt_le_bluedroid_set_svc_in_progress(VOCS_IN_PROGRESS);
/* VCS may include zero or more instances of VOCS */
for (size_t i = 0; i < inc_vocs_svc_count; i++) {
inc_vocs_insts[i].svc_p = lib_vocs_svc_get(vcp_included->vocs[i]);
if (!inc_vocs_insts[i].svc_p) {
LOG_ERR("[B]VocsSvcGetFail[%u]", i);
return -ENODEV;
}
/* Reset included before svc_init; see mics.c for rationale. */
inc_vocs_insts[i].included = false;
err = bt_le_bluedroid_svc_init(inc_vocs_insts[i].svc_p);
if (err) {
return err;
}
}
bt_le_bluedroid_set_svc_in_progress(AICS_IN_PROGRESS);
/* VCS may include zero or more instances of AICS */
for (size_t i = 0; i < inc_aics_svc_count; i++) {
inc_aics_insts[i].svc_p = lib_aics_svc_get(vcp_included->aics[i]);
if (!inc_aics_insts[i].svc_p) {
LOG_ERR("[B]AicsSvcGetFail[%u]", i);
return -ENODEV;
}
/* Reset included before svc_init; see mics.c for rationale. */
inc_aics_insts[i].included = false;
err = bt_le_bluedroid_svc_init(inc_aics_insts[i].svc_p);
if (err) {
return err;
}
}
}
bt_le_bluedroid_set_svc_in_progress(VCS_IN_PROGRESS);
err = bt_le_bluedroid_svc_init(vcs_svc);
if (err) {
return err;
}
if (vcs_not_included_inst()) {
LOG_ERR("[B]VcsVocsAicsNotAllInc");
return -EIO;
}
return 0;
}
int bt_le_bluedroid_vcs_start(void)
{
struct bt_gatt_service *vcs_svc;
int err;
LOG_DBG("[B]VcsStart");
vcs_svc = lib_vcs_svc_get();
if (!vcs_svc) {
LOG_ERR("[B]VcsSvcGetFail");
return -ENODEV;
}
bt_le_bluedroid_set_svc_in_progress(VOCS_IN_PROGRESS);
for (size_t i = 0; i < inc_vocs_svc_count; i++) {
err = bt_le_bluedroid_svc_start(inc_vocs_insts[i].svc_p);
if (err) {
return err;
}
}
bt_le_bluedroid_set_svc_in_progress(AICS_IN_PROGRESS);
for (size_t i = 0; i < inc_aics_svc_count; i++) {
err = bt_le_bluedroid_svc_start(inc_aics_insts[i].svc_p);
if (err) {
return err;
}
}
bt_le_bluedroid_set_svc_in_progress(VCS_IN_PROGRESS);
return bt_le_bluedroid_svc_start(vcs_svc);
}
@@ -0,0 +1,596 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdint.h>
#include <stdbool.h>
#include <errno.h>
#include <zephyr/autoconf.h>
#include <zephyr/logging/log.h>
#include <zephyr/bluetooth/uuid.h>
#include <zephyr/bluetooth/gatt.h>
#include <zephyr/bluetooth/audio/micp.h>
#include <zephyr/bluetooth/audio/vcp.h>
#include <../host/conn_internal.h>
#include "bta/bta_gatt_api.h"
#include "bluedroid/gatt.h"
#include "bluedroid/server.h"
#include "common/host.h"
LOG_MODULE_REGISTER(LEA_GSRV, CONFIG_BT_ISO_LOG_LEVEL);
static uint8_t svc_in_progress;
static uint16_t inc_svc_handle;
static uint16_t svc_handle;
static uint16_t chrc_handle;
static bool is_primary_svc(void)
{
if (svc_in_progress == ASCS_IN_PROGRESS ||
svc_in_progress == BASS_IN_PROGRESS ||
svc_in_progress == CAS_IN_PROGRESS ||
svc_in_progress == CSIS_IN_PROGRESS ||
svc_in_progress == HAS_IN_PROGRESS ||
svc_in_progress == GMCS_IN_PROGRESS ||
svc_in_progress == MCS_IN_PROGRESS ||
svc_in_progress == MICS_IN_PROGRESS ||
svc_in_progress == PACS_IN_PROGRESS ||
svc_in_progress == GTBS_IN_PROGRESS ||
svc_in_progress == TBS_IN_PROGRESS ||
svc_in_progress == TMAS_IN_PROGRESS ||
svc_in_progress == VCS_IN_PROGRESS) {
return true;
}
return false;
}
static bool is_secondary_svc(void)
{
if (svc_in_progress == AICS_IN_PROGRESS ||
svc_in_progress == VOCS_IN_PROGRESS) {
return true;
}
return false;
}
static bool any_included_svc(void)
{
if (svc_in_progress == CAS_IN_PROGRESS ||
svc_in_progress == MICS_IN_PROGRESS ||
svc_in_progress == VCS_IN_PROGRESS) {
return true;
}
return false;
}
static bool is_svc_uuid_valid(uint16_t uuid)
{
switch (svc_in_progress) {
case AICS_IN_PROGRESS:
return (uuid == BT_UUID_AICS_VAL);
case ASCS_IN_PROGRESS:
return (uuid == BT_UUID_ASCS_VAL);
case BASS_IN_PROGRESS:
return (uuid == BT_UUID_BASS_VAL);
case CAS_IN_PROGRESS:
return (uuid == BT_UUID_CAS_VAL);
case CSIS_IN_PROGRESS:
return (uuid == BT_UUID_CSIS_VAL);
case HAS_IN_PROGRESS:
return (uuid == BT_UUID_HAS_VAL);
case GMCS_IN_PROGRESS:
return (uuid == BT_UUID_GMCS_VAL);
case MCS_IN_PROGRESS:
return (uuid == BT_UUID_MCS_VAL);
case MICS_IN_PROGRESS:
return (uuid == BT_UUID_MICS_VAL);
case PACS_IN_PROGRESS:
return (uuid == BT_UUID_PACS_VAL);
case GTBS_IN_PROGRESS:
return (uuid == BT_UUID_GTBS_VAL);
case TBS_IN_PROGRESS:
return (uuid == BT_UUID_TBS_VAL);
case TMAS_IN_PROGRESS:
return (uuid == BT_UUID_TMAS_VAL);
case VCS_IN_PROGRESS:
return (uuid == BT_UUID_VCS_VAL);
case VOCS_IN_PROGRESS:
return (uuid == BT_UUID_VOCS_VAL);
default:
return false;
}
}
static void svc_create_cb(uint16_t service_id, uint16_t svc_instance,
bool is_primary, uint8_t status, void *uuid)
{
int result = status;
if ((is_primary_svc() && is_primary == false) ||
(is_secondary_svc() && is_primary == true) ||
((tBT_UUID *)uuid)->len != 2 ||
is_svc_uuid_valid(((tBT_UUID *)uuid)->uu.uuid16) == false) {
/* uuid16 is only meaningful when len == 2; for 32/128-bit UUIDs the
* union access would log the first 2 bytes of a wider value. */
if (((tBT_UUID *)uuid)->len == 2) {
LOG_ERR("[B]SvcCreateMismatch[%u][%u][2][0x%04x]",
svc_in_progress, is_primary,
((tBT_UUID *)uuid)->uu.uuid16);
} else {
LOG_ERR("[B]SvcCreateMismatch[%u][%u][%u][nonU16]",
svc_in_progress, is_primary,
((tBT_UUID *)uuid)->len);
}
result = -1;
goto end;
}
svc_handle = service_id;
end:
bt_le_bluedroid_gatts_sem_give(result);
}
static void inc_svc_add_cb(uint16_t service_id, uint16_t attr_id, uint8_t status)
{
int result = status;
if (service_id != svc_handle) {
LOG_ERR("[B]IncSvcAddMismatch[%u][%u][%u]",
svc_in_progress, service_id, svc_handle);
result = -1;
goto end;
}
inc_svc_handle = attr_id;
end:
bt_le_bluedroid_gatts_sem_give(result);
}
static void chrc_add_cb(uint16_t service_id, uint16_t attr_id,
uint8_t status, void *uuid)
{
int result = status;
if (service_id != svc_handle || ((tBT_UUID *)uuid)->len != 2) {
/* uuid16 is only meaningful when len == 2; for 32/128-bit UUIDs the
* union access would log the first 2 bytes of a wider value. */
if (((tBT_UUID *)uuid)->len == 2) {
LOG_ERR("[B]ChrcAddMismatch[%u][%u][%u][2][0x%04x]",
svc_in_progress, service_id, svc_handle,
((tBT_UUID *)uuid)->uu.uuid16);
} else {
LOG_ERR("[B]ChrcAddMismatch[%u][%u][%u][%u][nonU16]",
svc_in_progress, service_id, svc_handle,
((tBT_UUID *)uuid)->len);
}
result = -1;
goto end;
}
chrc_handle = attr_id;
end:
bt_le_bluedroid_gatts_sem_give(result);
}
static void svc_start_cb(uint16_t service_id, uint8_t status)
{
int result = status;
if (service_id != svc_handle) {
LOG_ERR("[B]SvcStartMismatch[%u][%u]", service_id, svc_handle);
result = -1;
}
bt_le_bluedroid_gatts_sem_give(result);
}
static struct gatts_svc_cb svc_cb = {
.svc_create_cb = svc_create_cb,
.inc_svc_add_cb = inc_svc_add_cb,
.chrc_add_cb = chrc_add_cb,
.svc_start_cb = svc_start_cb,
};
void bt_le_bluedroid_audio_gatts_init(void)
{
bt_le_bluedroid_gatts_svc_cb_register(&svc_cb);
}
int bt_le_bluedroid_set_svc_in_progress(uint8_t value)
{
if (value >= MAX_IN_PROGRESS) {
LOG_ERR("[B]SvcInProgressInv[%u]", value);
return -1;
}
svc_in_progress = value;
return 0;
}
static bool is_svc_attr_uuid_valid(uint16_t attr_uuid)
{
switch (svc_in_progress) {
case AICS_IN_PROGRESS:
return (attr_uuid == BT_UUID_AICS_STATE_VAL ||
attr_uuid == BT_UUID_AICS_GAIN_SETTINGS_VAL ||
attr_uuid == BT_UUID_AICS_INPUT_TYPE_VAL ||
attr_uuid == BT_UUID_AICS_INPUT_STATUS_VAL ||
attr_uuid == BT_UUID_AICS_CONTROL_VAL ||
attr_uuid == BT_UUID_AICS_DESCRIPTION_VAL);
case ASCS_IN_PROGRESS:
return (attr_uuid == BT_UUID_ASCS_ASE_SNK_VAL ||
attr_uuid == BT_UUID_ASCS_ASE_SRC_VAL ||
attr_uuid == BT_UUID_ASCS_ASE_CP_VAL);
case BASS_IN_PROGRESS:
return (attr_uuid == BT_UUID_BASS_CONTROL_POINT_VAL ||
attr_uuid == BT_UUID_BASS_RECV_STATE_VAL);
case CAS_IN_PROGRESS:
/* No characteristic in CAS */
return false;
case CSIS_IN_PROGRESS:
return (attr_uuid == BT_UUID_CSIS_SIRK_VAL ||
attr_uuid == BT_UUID_CSIS_SET_SIZE_VAL ||
attr_uuid == BT_UUID_CSIS_SET_LOCK_VAL ||
attr_uuid == BT_UUID_CSIS_RANK_VAL);
case HAS_IN_PROGRESS:
return (attr_uuid == BT_UUID_HAS_HEARING_AID_FEATURES_VAL ||
attr_uuid == BT_UUID_HAS_PRESET_CONTROL_POINT_VAL ||
attr_uuid == BT_UUID_HAS_ACTIVE_PRESET_INDEX_VAL);
case GMCS_IN_PROGRESS:
case MCS_IN_PROGRESS:
return (attr_uuid == BT_UUID_MCS_PLAYER_NAME_VAL ||
attr_uuid == BT_UUID_MCS_ICON_OBJ_ID_VAL ||
attr_uuid == BT_UUID_MCS_ICON_URL_VAL ||
attr_uuid == BT_UUID_MCS_TRACK_CHANGED_VAL ||
attr_uuid == BT_UUID_MCS_TRACK_TITLE_VAL ||
attr_uuid == BT_UUID_MCS_TRACK_DURATION_VAL ||
attr_uuid == BT_UUID_MCS_TRACK_POSITION_VAL ||
attr_uuid == BT_UUID_MCS_PLAYBACK_SPEED_VAL ||
attr_uuid == BT_UUID_MCS_SEEKING_SPEED_VAL ||
attr_uuid == BT_UUID_MCS_TRACK_SEGMENTS_OBJ_ID_VAL ||
attr_uuid == BT_UUID_MCS_CURRENT_TRACK_OBJ_ID_VAL ||
attr_uuid == BT_UUID_MCS_NEXT_TRACK_OBJ_ID_VAL ||
attr_uuid == BT_UUID_MCS_PARENT_GROUP_OBJ_ID_VAL ||
attr_uuid == BT_UUID_MCS_CURRENT_GROUP_OBJ_ID_VAL ||
attr_uuid == BT_UUID_MCS_PLAYING_ORDER_VAL ||
attr_uuid == BT_UUID_MCS_PLAYING_ORDERS_VAL ||
attr_uuid == BT_UUID_MCS_MEDIA_STATE_VAL ||
attr_uuid == BT_UUID_MCS_MEDIA_CONTROL_POINT_VAL ||
attr_uuid == BT_UUID_MCS_MEDIA_CONTROL_OPCODES_VAL ||
attr_uuid == BT_UUID_MCS_SEARCH_RESULTS_OBJ_ID_VAL ||
attr_uuid == BT_UUID_MCS_SEARCH_CONTROL_POINT_VAL ||
attr_uuid == BT_UUID_CCID_VAL);
case MICS_IN_PROGRESS:
return (attr_uuid == BT_UUID_MICS_MUTE_VAL);
case PACS_IN_PROGRESS:
return (attr_uuid == BT_UUID_PACS_SNK_VAL ||
attr_uuid == BT_UUID_PACS_SNK_LOC_VAL ||
attr_uuid == BT_UUID_PACS_SRC_VAL ||
attr_uuid == BT_UUID_PACS_SRC_LOC_VAL ||
attr_uuid == BT_UUID_PACS_AVAILABLE_CONTEXT_VAL ||
attr_uuid == BT_UUID_PACS_SUPPORTED_CONTEXT_VAL);
case GTBS_IN_PROGRESS:
case TBS_IN_PROGRESS:
return (attr_uuid == BT_UUID_TBS_PROVIDER_NAME_VAL ||
attr_uuid == BT_UUID_TBS_UCI_VAL ||
attr_uuid == BT_UUID_TBS_TECHNOLOGY_VAL ||
attr_uuid == BT_UUID_TBS_URI_LIST_VAL ||
attr_uuid == BT_UUID_TBS_SIGNAL_STRENGTH_VAL ||
attr_uuid == BT_UUID_TBS_SIGNAL_INTERVAL_VAL ||
attr_uuid == BT_UUID_TBS_LIST_CURRENT_CALLS_VAL ||
attr_uuid == BT_UUID_CCID_VAL ||
attr_uuid == BT_UUID_TBS_STATUS_FLAGS_VAL ||
attr_uuid == BT_UUID_TBS_INCOMING_URI_VAL ||
attr_uuid == BT_UUID_TBS_CALL_STATE_VAL ||
attr_uuid == BT_UUID_TBS_CALL_CONTROL_POINT_VAL ||
attr_uuid == BT_UUID_TBS_OPTIONAL_OPCODES_VAL ||
attr_uuid == BT_UUID_TBS_TERMINATE_REASON_VAL ||
attr_uuid == BT_UUID_TBS_INCOMING_CALL_VAL ||
attr_uuid == BT_UUID_TBS_FRIENDLY_NAME_VAL);
case TMAS_IN_PROGRESS:
return (attr_uuid == BT_UUID_GATT_TMAPR_VAL);
case VCS_IN_PROGRESS:
return (attr_uuid == BT_UUID_VCS_STATE_VAL ||
attr_uuid == BT_UUID_VCS_CONTROL_VAL ||
attr_uuid == BT_UUID_VCS_FLAGS_VAL);
case VOCS_IN_PROGRESS:
return (attr_uuid == BT_UUID_VOCS_STATE_VAL ||
attr_uuid == BT_UUID_VOCS_LOCATION_VAL ||
attr_uuid == BT_UUID_VOCS_CONTROL_VAL ||
attr_uuid == BT_UUID_VOCS_DESCRIPTION_VAL);
default:
return false;
}
}
static struct inc_svc_inst *get_not_included_inst(void)
{
switch (svc_in_progress) {
#if CONFIG_BT_CAP_ACCEPTOR_SET_MEMBER
case CAS_IN_PROGRESS:
extern struct inc_svc_inst *cas_not_included_inst(void);
return cas_not_included_inst();
#endif /* CONFIG_BT_CAP_ACCEPTOR_SET_MEMBER */
#if CONFIG_BT_MICP_MIC_DEV
case MICS_IN_PROGRESS:
extern struct inc_svc_inst *mics_not_included_inst(void);
return mics_not_included_inst();
#endif /* CONFIG_BT_MICP_MIC_DEV */
#if CONFIG_BT_VCP_VOL_REND
case VCS_IN_PROGRESS:
extern struct inc_svc_inst *vcs_not_included_inst(void);
return vcs_not_included_inst();
#endif /* CONFIG_BT_VCP_VOL_REND */
default:
return NULL;
}
}
static uint8_t get_svc_inst_id(uint16_t svc_uuid)
{
/* Note:
* For LE Audio, some service could have multiple instances.
*/
static uint8_t aics_count;
static uint8_t csis_count;
static uint8_t vocs_count;
static uint8_t mcs_count;
static uint8_t ots_count;
static uint8_t tbs_count;
switch (svc_uuid) {
case BT_UUID_AICS_VAL:
return aics_count++;
case BT_UUID_CSIS_VAL:
return csis_count++;
case BT_UUID_VOCS_VAL:
return vocs_count++;
case BT_UUID_MCS_VAL:
return mcs_count++;
case BT_UUID_OTS_VAL:
return ots_count++;
case BT_UUID_TBS_VAL:
return tbs_count++;
default:
return 0;
}
}
int bt_le_bluedroid_svc_init(struct bt_gatt_service *svc)
{
struct bt_gatt_attr *curr_attr;
struct bt_gatt_attr *next_attr;
struct inc_svc_inst *inc_inst;
struct bt_gatt_chrc *chrc;
tBTA_GATT_PERM perm;
uint16_t attr_uuid;
uint8_t inst_id;
tBT_UUID uuid;
assert(svc);
for (size_t i = 0; i < svc->attr_count; i++) {
curr_attr = &svc->attrs[i];
attr_uuid = BT_UUID_16(curr_attr->uuid)->val;
switch (attr_uuid) {
case BT_UUID_GATT_PRIMARY_VAL:
if (is_primary_svc() == false) {
LOG_ERR("[B]NotCreatingPrimary");
return -1;
}
assert(curr_attr->user_data);
bt_le_bluedroid_gatt_uuid_convert(curr_attr->user_data, &uuid);
inst_id = get_svc_inst_id(uuid.uu.uuid16);
bt_le_bluedroid_gatts_sem_reset();
BTA_GATTS_CreateService(bt_le_bluedroid_gatts_get_if(), &uuid, inst_id, svc->attr_count, true);
if (bt_le_bluedroid_gatts_sem_take()) {
LOG_ERR("[B]PrimaryCreateFail");
return -1;
}
curr_attr->handle = svc_handle;
LOG_INF("[B]PrimarySvc[%s][0x%04x][%u][0x%04x][%d]",
audio_svc_uuid_to_str(uuid.uu.uuid16), uuid.uu.uuid16,
inst_id, curr_attr->perm, svc_handle);
break;
case BT_UUID_GATT_SECONDARY_VAL:
if (is_secondary_svc() == false) {
LOG_ERR("[B]NotCreatingSecondary");
return -1;
}
assert(curr_attr->user_data);
bt_le_bluedroid_gatt_uuid_convert(curr_attr->user_data, &uuid);
inst_id = get_svc_inst_id(uuid.uu.uuid16);
bt_le_bluedroid_gatts_sem_reset();
BTA_GATTS_CreateService(bt_le_bluedroid_gatts_get_if(), &uuid, inst_id, svc->attr_count, false);
if (bt_le_bluedroid_gatts_sem_take()) {
LOG_ERR("[B]SecondaryCreateFail");
return -1;
}
curr_attr->handle = svc_handle;
LOG_INF("[B]SecondarySvc[%s][0x%04x][%u][0x%04x][%d]",
audio_svc_uuid_to_str(uuid.uu.uuid16), uuid.uu.uuid16,
inst_id, curr_attr->perm, svc_handle);
break;
case BT_UUID_GATT_INCLUDE_VAL:
if (any_included_svc() == false) {
LOG_ERR("[B]NoSvcToInc[%u]", svc_in_progress);
return -1;
}
inc_inst = get_not_included_inst();
if (inc_inst == NULL) {
LOG_ERR("[B]IncSvcNotFound[%u]", svc_in_progress);
return -1;
}
bt_le_bluedroid_gatts_sem_reset();
BTA_GATTS_AddIncludeService(svc_handle, inc_inst->svc_p->attrs[0].handle);
if (bt_le_bluedroid_gatts_sem_take()) {
LOG_ERR("[B]IncSvcAddFail");
return -1;
}
curr_attr->handle = inc_svc_handle;
inc_inst->included = true; /* Mark the svc as included */
LOG_INF("[B]IncSvc[%s][0x%04x][0x%04x][%d]",
audio_svc_uuid_to_str(BT_UUID_16(inc_inst->svc_p->attrs[0].user_data)->val),
BT_UUID_16(inc_inst->svc_p->attrs[0].user_data)->val,
curr_attr->perm, inc_svc_handle);
break;
case BT_UUID_GATT_CHRC_VAL:
/* Chrc declaration must be followed by its value attribute.
* Standard BT_GATT_CHARACTERISTIC pairs them, but guard against
* a malformed table where it is the last entry. */
if (i + 1 >= svc->attr_count) {
LOG_ERR("[B]ChrcDeclLastAttr[%u]", svc_in_progress);
return -1;
}
next_attr = &svc->attrs[i + 1];
perm = bt_le_bluedroid_gatt_perm_convert(next_attr->perm);
assert(curr_attr->user_data);
chrc = curr_attr->user_data;
bt_le_bluedroid_gatt_uuid_convert(chrc->uuid, &uuid);
bt_le_bluedroid_gatts_sem_reset();
BTA_GATTS_AddCharacteristic(svc_handle, &uuid, perm, chrc->properties, NULL, NULL);
if (bt_le_bluedroid_gatts_sem_take()) {
LOG_ERR("[B]ChrcAddFail");
return -1;
}
/* Characteristic declaration handle and Characteristic value handle */
curr_attr->handle = chrc_handle - 1;
next_attr->handle = chrc_handle;
LOG_INF("[B]Chrc[%s][0x%04x][%u][%u][0x%04x][0x%02x]",
audio_chrc_uuid_to_str(uuid.uu.uuid16), uuid.uu.uuid16,
chrc_handle - 1, chrc_handle, next_attr->perm, chrc->properties);
break;
case BT_UUID_GATT_CCC_VAL:
perm = bt_le_bluedroid_gatt_perm_convert(curr_attr->perm);
bt_le_bluedroid_gatt_uuid_convert(curr_attr->uuid, &uuid);
bt_le_bluedroid_gatts_sem_reset();
BTA_GATTS_AddCharDescriptor(svc_handle, perm, &uuid, NULL, NULL);
if (bt_le_bluedroid_gatts_sem_take()) {
LOG_ERR("[B]ChrcCccdAddFail");
return -1;
}
curr_attr->handle = chrc_handle;
LOG_INF("[B]ChrcCccd[%u][0x%04x]", chrc_handle, curr_attr->perm);
break;
default:
if (is_svc_attr_uuid_valid(attr_uuid) == false) {
LOG_ERR("[B]InvSvcAttrUuid[0x%04x][%u]", attr_uuid, svc_in_progress);
return -1;
}
break;
}
}
return 0;
}
int bt_le_bluedroid_svc_start(struct bt_gatt_service *svc)
{
assert(svc);
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;
}
bt_le_bluedroid_gatts_sem_reset();
BTA_GATTS_StartService(svc_handle, BTA_GATT_TRANSPORT_LE);
if (bt_le_bluedroid_gatts_sem_take()) {
LOG_ERR("[B]SvcStartFail");
return -1;
}
return 0;
}
@@ -313,13 +313,18 @@ static int nimble_gatt_csis_init(struct bt_le_audio_start_info *info,
}
csis_svc[count] = lib_csip_set_member_svc_get(info->csis_insts[i].svc_inst);
assert(csis_svc[count]);
if (!csis_svc[count]) {
LOG_ERR("[N]CsisSvcGetFail[%u]", i);
return -ENODEV;
}
if (info->csis_insts[i].included_by_cas) {
if (*inc_csis_svc == NULL) {
*inc_csis_svc = csis_svc[count];
} else {
assert(0);
/* CAS may include at most one CSIS — caller misconfigured. */
LOG_ERR("[N]CsisMultiIncByCas");
return -EINVAL;
}
}
@@ -23,7 +23,7 @@
#include "host/ble_gatt.h"
#include "host/ble_hs_mbuf.h"
#include "nimble/profiles/server.h"
#include "nimble/server.h"
#include "common/host.h"
@@ -122,7 +122,10 @@ int bt_le_nimble_ascs_attr_handle_set(void)
}
ascs_svc = lib_ascs_svc_get();
assert(ascs_svc);
if (!ascs_svc) {
LOG_ERR("[N]AscsSvcGetFail");
return -ENODEV;
}
assert(ase_control_point_handle >= 2);
start_handle = ase_control_point_handle - 2; /* server attr handle & char def handle */
@@ -164,7 +167,10 @@ static int ascs_svc_check(void)
*/
ascs_svc = lib_ascs_svc_get();
assert(ascs_svc);
if (!ascs_svc) {
LOG_ERR("[N]AscsSvcGetFail");
return -ENODEV;
}
LOG_DBG("[N]AscsSvcCheck");
@@ -23,7 +23,7 @@
#include "host/ble_gatt.h"
#include "host/ble_hs_mbuf.h"
#include "nimble/profiles/server.h"
#include "nimble/server.h"
#include "common/host.h"
@@ -98,7 +98,10 @@ int bt_le_nimble_bass_attr_handle_set(void)
}
bass_svc = lib_bap_bass_svc_get();
assert(bass_svc);
if (!bass_svc) {
LOG_ERR("[N]BassSvcGetFail");
return -ENODEV;
}
assert(bass_control_point_handle >= 2);
start_handle = bass_control_point_handle - 2; /* server attr handle & char def handle */
@@ -134,7 +137,10 @@ static int bass_svc_check(void)
*/
bass_svc = lib_bap_bass_svc_get();
assert(bass_svc);
if (!bass_svc) {
LOG_ERR("[N]BassSvcGetFail");
return -ENODEV;
}
LOG_DBG("[N]BassSvcCheck");
@@ -60,7 +60,10 @@ int bt_le_nimble_cas_attr_handle_set(void)
}
cas_svc = lib_cas_svc_get();
assert(cas_svc);
if (!cas_svc) {
LOG_ERR("[N]CasSvcGetFail");
return -ENODEV;
}
LOG_DBG("[N]CasAttrHdlSet[%u][%u]", handle, cas_svc->attr_count);
@@ -113,7 +116,10 @@ int bt_le_nimble_cas_init(void *csis_svc_p)
struct bt_gatt_service *cas_svc;
cas_svc = lib_cas_svc_get();
assert(cas_svc);
if (!cas_svc) {
LOG_ERR("[N]CasSvcGetFail");
return -ENODEV;
}
/* Insert CAS to the GATT db list */
rc = bt_gatt_service_register_safe(cas_svc);
@@ -25,7 +25,7 @@
#include "host/ble_gatt.h"
#include "host/ble_hs_mbuf.h"
#include "nimble/profiles/server.h"
#include "nimble/server.h"
#include "common/host.h"
@@ -23,7 +23,7 @@
#include "host/ble_gatt.h"
#include "host/ble_hs_mbuf.h"
#include "nimble/profiles/server.h"
#include "nimble/server.h"
#include "common/host.h"
@@ -120,7 +120,10 @@ int bt_le_nimble_has_attr_handle_set(void)
}
has_svc = lib_has_svc_get();
assert(has_svc);
if (!has_svc) {
LOG_ERR("[N]HasSvcGetFail");
return -ENODEV;
}
assert(has_svc->attr_count > 0);
end_handle = start_handle + has_svc->attr_count - 1;
@@ -136,7 +139,7 @@ int bt_le_nimble_has_attr_handle_set(void)
attr = has_svc->attrs + has_svc->attr_count - 1;
if (attr->handle != end_handle) {
LOG_ERR("[N]HasMismatchAttrHdl (%u %u %u %u)",
LOG_ERR("[N]HasMismatchAttrHdl[%u][%u][%u][%u]",
start_handle, end_handle, attr->handle, has_svc->attr_count);
return -1;
}
@@ -155,7 +158,10 @@ static int has_svc_check(void)
*/
has_svc = lib_has_svc_get();
assert(has_svc);
if (!has_svc) {
LOG_ERR("[N]HasSvcGetFail");
return -ENODEV;
}
LOG_DBG("[N]HasSvcCheck");
@@ -28,7 +28,7 @@
#include "host/ble_gatt.h"
#include "host/ble_hs_mbuf.h"
#include "nimble/profiles/server.h"
#include "nimble/server.h"
#include "common/host.h"
@@ -403,7 +403,10 @@ int bt_le_nimble_gmcs_attr_handle_set(void)
}
gmcs_svc = lib_mcs_svc_get();
assert(gmcs_svc);
if (!gmcs_svc) {
LOG_ERR("[N]GmcsSvcGetFail");
return -ENODEV;
}
end_handle = start_handle + gmcs_svc->attr_count - 1;
@@ -446,7 +449,10 @@ static int gmcs_svc_check(void)
*/
gmcs_svc = lib_mcs_svc_get();
assert(gmcs_svc);
if (!gmcs_svc) {
LOG_ERR("[N]GmcsSvcGetFail");
return -ENODEV;
}
LOG_DBG("[N]GmcsSvcCheck");
@@ -23,7 +23,7 @@
#include "host/ble_gatt.h"
#include "host/ble_hs_mbuf.h"
#include "nimble/profiles/server.h"
#include "nimble/server.h"
#include "common/host.h"
@@ -159,7 +159,10 @@ static int mics_svc_check(void)
bool chr_found;
mics_svc = lib_mics_svc_get();
assert(mics_svc);
if (!mics_svc) {
LOG_ERR("[N]MicsSvcGetFail");
return -ENODEV;
}
LOG_DBG("[N]MicsSvcCheck");
@@ -244,7 +247,10 @@ int bt_le_nimble_mics_attr_handle_set(void)
}
mics_svc = lib_mics_svc_get();
assert(mics_svc);
if (!mics_svc) {
LOG_ERR("[N]MicsSvcGetFail");
return -ENODEV;
}
end_handle = start_handle + mics_svc->attr_count - 1;
@@ -379,6 +385,11 @@ int bt_le_nimble_mics_init(void *micp_inc)
inc_aics_svc_init(&inc_aics_insts[i], &gatt_svc_inc_aics[i]);
inc_aics_insts[i].svc_p = lib_aics_svc_get(micp_included->aics[i]);
if (!inc_aics_insts[i].svc_p) {
LOG_ERR("[N]AicsSvcGetFail[%u]", i);
rc = -ENODEV;
goto free;
}
mics_inc_svcs[i] = &gatt_svc_inc_aics[i];
}
@@ -21,7 +21,7 @@
#include "host/ble_gatt.h"
#include "host/ble_hs_mbuf.h"
#include "nimble/profiles/server.h"
#include "nimble/server.h"
#include "common/host.h"
@@ -166,7 +166,10 @@ int bt_le_nimble_pacs_attr_handle_set(void)
uint16_t end_handle = 0;
pacs_svc = lib_pacs_svc_get();
assert(pacs_svc);
if (!pacs_svc) {
LOG_ERR("[N]PacsSvcGetFail");
return -ENODEV;
}
#if CONFIG_BT_PAC_SNK
assert(pacs_snk_handle >= 2);
@@ -214,7 +217,10 @@ static int pacs_svc_check(void)
*/
pacs_svc = lib_pacs_svc_get();
assert(pacs_svc);
if (!pacs_svc) {
LOG_ERR("[N]PacsSvcGetFail");
return -ENODEV;
}
LOG_DBG("[N]PacsSvcCheck");
@@ -21,7 +21,7 @@
#include "host/ble_gatt.h"
#include "host/ble_hs_mbuf.h"
#include "nimble/profiles/server.h"
#include "nimble/server.h"
#include "common/host.h"
@@ -178,7 +178,10 @@ int bt_le_nimble_gtbs_attr_handle_set(void)
}
gtbs_svc = lib_gtbs_svc_get();
assert(gtbs_svc);
if (!gtbs_svc) {
LOG_ERR("[N]GtbsSvcGetFail");
return -ENODEV;
}
LOG_DBG("[N]GtbsAttrHdlSet[%u][%u]", handle, gtbs_svc->attr_count);
@@ -200,7 +203,10 @@ static int gtbs_svc_check(void)
*/
gtbs_svc = lib_gtbs_svc_get();
assert(gtbs_svc);
if (!gtbs_svc) {
LOG_ERR("[N]GtbsSvcGetFail");
return -ENODEV;
}
LOG_DBG("[N]GtbsSvcCheck");
@@ -21,7 +21,7 @@
#include "host/ble_gatt.h"
#include "host/ble_hs_mbuf.h"
#include "nimble/profiles/server.h"
#include "nimble/server.h"
#include "common/host.h"
@@ -71,7 +71,10 @@ int bt_le_nimble_tmas_attr_handle_set(void)
}
tmas_svc = lib_tmas_svc_get();
assert(tmas_svc);
if (!tmas_svc) {
LOG_ERR("[N]TmasSvcGetFail");
return -ENODEV;
}
LOG_DBG("[N]TmasAttrHdlSet[%u][%u]", handle, tmas_svc->attr_count);
@@ -93,7 +96,10 @@ static int tmas_svc_check(void)
*/
tmas_svc = lib_tmas_svc_get();
assert(tmas_svc);
if (!tmas_svc) {
LOG_ERR("[N]TmasSvcGetFail");
return -ENODEV;
}
LOG_DBG("[N]TmasSvcCheck");
@@ -23,7 +23,7 @@
#include "host/ble_gatt.h"
#include "host/ble_hs_mbuf.h"
#include "nimble/profiles/server.h"
#include "nimble/server.h"
#include "common/host.h"
@@ -261,7 +261,10 @@ static int vcs_svc_check(void)
*/
vcs_svc = lib_vcs_svc_get();
assert(vcs_svc);
if (!vcs_svc) {
LOG_ERR("[N]VcsSvcGetFail");
return -ENODEV;
}
LOG_DBG("[N]VCSSvcCheck");
@@ -380,7 +383,10 @@ int bt_le_nimble_vcs_attr_handle_set(void)
}
vcs_svc = lib_vcs_svc_get();
assert(vcs_svc);
if (!vcs_svc) {
LOG_ERR("[N]VcsSvcGetFail");
return -ENODEV;
}
end_handle = start_handle + vcs_svc->attr_count - 1;
@@ -576,6 +582,11 @@ int bt_le_nimble_vcs_init(void *vcp_inc)
inc_vocs_svc_init(&inc_vocs_insts[i], &gatt_svc_inc_vocs[i]);
inc_vocs_insts[i].svc_p = lib_vocs_svc_get(vcp_included->vocs[i]);
if (!inc_vocs_insts[i].svc_p) {
LOG_ERR("[N]VocsSvcGetFail[%u]", i);
rc = -ENODEV;
goto free;
}
vcs_inc_svcs[i] = &gatt_svc_inc_vocs[i];
}
@@ -608,6 +619,11 @@ int bt_le_nimble_vcs_init(void *vcp_inc)
inc_aics_svc_init(&inc_aics_insts[i], &gatt_svc_inc_aics[i]);
inc_aics_insts[i].svc_p = lib_aics_svc_get(vcp_included->aics[i]);
if (!inc_aics_insts[i].svc_p) {
LOG_ERR("[N]AicsSvcGetFail[%u]", i);
rc = -ENODEV;
goto free;
}
vcs_inc_svcs[inc_vocs_svc_count + i] = &gatt_svc_inc_aics[i];
}
@@ -670,13 +686,17 @@ free:
}
if (inc_aics_svc_count) {
for (size_t i = 0; i < inc_aics_svc_count; i++) {
free((void *)gatt_svc_inc_aics[i].characteristics);
gatt_svc_inc_aics[i].characteristics = NULL;
}
/* A VOCS-phase failure reaches here with the count already set
* but gatt_svc_inc_aics not yet allocated (still NULL). */
if (gatt_svc_inc_aics) {
for (size_t i = 0; i < inc_aics_svc_count; i++) {
free((void *)gatt_svc_inc_aics[i].characteristics);
gatt_svc_inc_aics[i].characteristics = NULL;
}
free(gatt_svc_inc_aics);
gatt_svc_inc_aics = NULL;
free(gatt_svc_inc_aics);
gatt_svc_inc_aics = NULL;
}
inc_aics_svc_count = 0;
}
@@ -44,7 +44,11 @@
#include <../host/conn_internal.h>
#include <../host/hci_core.h>
#if CONFIG_BT_BLUEDROID_ENABLED
#include "bluedroid/init.h"
#else
#include "nimble/init.h"
#endif
#include "../../../lib/include/audio.h"
@@ -2091,7 +2095,11 @@ int bt_le_audio_init(void)
return err;
}
#if CONFIG_BT_BLUEDROID_ENABLED
return bt_le_bluedroid_audio_init();
#else
return bt_le_nimble_audio_init();
#endif
}
#if BLE_AUDIO_SVC_DEFERRED_ADD
@@ -2100,7 +2108,11 @@ int bt_le_ascs_init(void)
{
LOG_DBG("AscsInit");
#if CONFIG_BT_BLUEDROID_ENABLED
return bt_le_bluedroid_ascs_init();
#else
return bt_le_nimble_ascs_init();
#endif
}
#endif /* CONFIG_BT_ASCS */
@@ -2109,7 +2121,11 @@ int bt_le_bass_init(void)
{
LOG_DBG("BassInit");
#if CONFIG_BT_BLUEDROID_ENABLED
return bt_le_bluedroid_bass_init();
#else
return bt_le_nimble_bass_init();
#endif
}
#endif /* CONFIG_BT_BAP_SCAN_DELEGATOR */
@@ -2118,7 +2134,11 @@ int bt_le_tmas_init(void)
{
LOG_DBG("TmasInit");
#if CONFIG_BT_BLUEDROID_ENABLED
return bt_le_bluedroid_tmas_init();
#else
return bt_le_nimble_tmas_init();
#endif
}
#endif /* CONFIG_BT_TMAP */
@@ -2127,7 +2147,11 @@ int bt_le_gtbs_init(void)
{
LOG_DBG("GtbsInit");
#if CONFIG_BT_BLUEDROID_ENABLED
return bt_le_bluedroid_gtbs_init();
#else
return bt_le_nimble_gtbs_init();
#endif
}
#endif /* CONFIG_BT_TBS */
@@ -2136,7 +2160,11 @@ int bt_le_has_init(void)
{
LOG_DBG("HasInit");
#if CONFIG_BT_BLUEDROID_ENABLED
return bt_le_bluedroid_has_init();
#else
return bt_le_nimble_has_init();
#endif
}
#endif /* CONFIG_BT_HAS */
@@ -2145,7 +2173,11 @@ int bt_le_media_proxy_pl_init(void)
{
LOG_DBG("MprxPlInit");
#if CONFIG_BT_BLUEDROID_ENABLED
return bt_le_bluedroid_media_proxy_pl_init();
#else
return bt_le_nimble_media_proxy_pl_init();
#endif
}
#endif /* CONFIG_BT_MCS */
@@ -2154,7 +2186,11 @@ int bt_le_vcp_vol_rend_init(void)
{
LOG_DBG("VcpVolRendInit");
#if CONFIG_BT_BLUEDROID_ENABLED
return bt_le_bluedroid_vcp_vol_rend_init();
#else
return bt_le_nimble_vcp_vol_rend_init();
#endif
}
#endif /* CONFIG_BT_VCP_VOL_REND */
@@ -2163,7 +2199,11 @@ int bt_le_micp_mic_dev_init(void)
{
LOG_DBG("MicpMicDevInit");
#if CONFIG_BT_BLUEDROID_ENABLED
return bt_le_bluedroid_micp_mic_dev_init();
#else
return bt_le_nimble_micp_mic_dev_init();
#endif
}
#endif /* CONFIG_BT_MICP_MIC_DEV */
#endif /* BLE_AUDIO_SVC_DEFERRED_ADD */
@@ -2172,7 +2212,11 @@ int bt_le_audio_start(void *info)
{
LOG_DBG("AudioStart");
#if CONFIG_BT_BLUEDROID_ENABLED
return bt_le_bluedroid_audio_start(info);
#else
return bt_le_nimble_audio_start(info);
#endif
}
void ble_audio_lib_compressed_out(uint8_t log_level, uint32_t log_index, size_t arg_cnt, ...)
+35 -9
View File
@@ -15,12 +15,24 @@ if(CONFIG_BT_CONTROLLER_ONLY OR NOT CONFIG_BT_ISO)
return()
endif()
if(CONFIG_BT_NIMBLE_ENABLED)
list(APPEND ble_iso_srcs
"${CMAKE_CURRENT_LIST_DIR}/host/adapter/nimble/gatt/gatt.c"
"${CMAKE_CURRENT_LIST_DIR}/host/adapter/nimble/gatt/gatt.db.c"
"${CMAKE_CURRENT_LIST_DIR}/host/adapter/nimble/gatt/gatt.nrp.c"
"${CMAKE_CURRENT_LIST_DIR}/host/adapter/nimble/gap.c"
"${CMAKE_CURRENT_LIST_DIR}/host/adapter/nimble/iso.c"
)
elseif(CONFIG_BT_BLUEDROID_ENABLED)
list(APPEND ble_iso_srcs
"${CMAKE_CURRENT_LIST_DIR}/host/adapter/bluedroid/hci.c"
"${CMAKE_CURRENT_LIST_DIR}/host/adapter/bluedroid/iso.c"
"${CMAKE_CURRENT_LIST_DIR}/host/adapter/bluedroid/gap.c"
"${CMAKE_CURRENT_LIST_DIR}/host/adapter/bluedroid/gatt/gatt.c"
)
endif()
list(APPEND ble_iso_srcs
"${CMAKE_CURRENT_LIST_DIR}/host/adapter/nimble/gatt/gatt.c"
"${CMAKE_CURRENT_LIST_DIR}/host/adapter/nimble/gatt/gatt.db.c"
"${CMAKE_CURRENT_LIST_DIR}/host/adapter/nimble/gatt/gatt.nrp.c"
"${CMAKE_CURRENT_LIST_DIR}/host/adapter/nimble/gap.c"
"${CMAKE_CURRENT_LIST_DIR}/host/adapter/nimble/iso.c"
"${CMAKE_CURRENT_LIST_DIR}/host/common/adv.c"
"${CMAKE_CURRENT_LIST_DIR}/host/common/conn.c"
"${CMAKE_CURRENT_LIST_DIR}/host/common/gatt.c"
@@ -45,23 +57,37 @@ list(APPEND ble_iso_srcs
)
# L2CAP host shim wiring is currently only consumed by the OTS service
# (which lives in esp_ble_audio). Keep the same gate so build output
# matches the pre-split behavior; the .c files belong to the host shim.
# (which lives in esp_ble_audio). common/l2cap.c has both host branches
# (Bluedroid returns -ENOTSUP), so compile it under either host whenever
# OTS is enabled; the NimBLE-only adapter glue stays NimBLE-gated.
if(CONFIG_BT_OTS OR CONFIG_BT_OTS_CLIENT)
list(APPEND ble_iso_srcs
"${CMAKE_CURRENT_LIST_DIR}/host/common/l2cap.c"
"${CMAKE_CURRENT_LIST_DIR}/host/adapter/nimble/l2cap.c"
)
if(CONFIG_BT_NIMBLE_ENABLED)
list(APPEND ble_iso_srcs
"${CMAKE_CURRENT_LIST_DIR}/host/adapter/nimble/l2cap.c"
)
endif()
endif()
list(APPEND ble_iso_include_dirs
"${CMAKE_CURRENT_LIST_DIR}/api/include"
"${CMAKE_CURRENT_LIST_DIR}/host/common/include"
"${CMAKE_CURRENT_LIST_DIR}/host/adapter/nimble/include"
"${CMAKE_CURRENT_LIST_DIR}/include/subsys/bluetooth"
"${CMAKE_CURRENT_LIST_DIR}/include/subsys/bluetooth/host"
"${CMAKE_CURRENT_LIST_DIR}/include"
)
if(CONFIG_BT_NIMBLE_ENABLED)
list(APPEND ble_iso_include_dirs
"${CMAKE_CURRENT_LIST_DIR}/host/adapter/nimble/include"
)
elseif(CONFIG_BT_BLUEDROID_ENABLED)
list(APPEND ble_iso_include_dirs
"${CMAKE_CURRENT_LIST_DIR}/host/adapter/bluedroid/include"
)
endif()
set(ble_iso_srcs "${ble_iso_srcs}" PARENT_SCOPE)
set(ble_iso_include_dirs "${ble_iso_include_dirs}" PARENT_SCOPE)
+10 -3
View File
@@ -6,6 +6,7 @@
config BT_ISO
bool
select BT_NIMBLE_ISO if BT_NIMBLE_ENABLED
select BT_BLE_FEAT_ISO_EN if BT_BLUEDROID_ENABLED
config BT_ISO_TX
bool
@@ -44,7 +45,6 @@ config BT_ISO_BROADCASTER
bool "Bluetooth Isochronous Broadcaster Support"
select BT_ISO_BROADCAST
select BT_ISO_TX
select BT_BROADCASTER
help
This option enables support for the Bluetooth Isochronous Broadcaster.
@@ -68,6 +68,7 @@ if BT_ISO
config BT_ISO_TEST_PARAMS
bool "ISO test parameters support"
default y if BT_BLUEDROID_ENABLED
default BT_NIMBLE_ISO_TEST if BT_NIMBLE_ENABLED
help
Enabling advanced ISO parameters will allow the use of the ISO test
@@ -79,7 +80,10 @@ if BT_ISO
config BT_ISO_MAX_CIG
int "Maximum number of Connected Isochronous Groups (CIGs) to support"
range 1 BT_NIMBLE_ISO_CIG
range 1 1 if BT_BLUEDROID_ENABLED
range 1 BT_NIMBLE_ISO_CIG if BT_NIMBLE_ENABLED
default 1 if BT_BLUEDROID_ENABLED
default BT_NIMBLE_ISO_CIG if BT_NIMBLE_ENABLED
help
Maximum number of CIGs that are supported by the host. A CIG can be
used for either transmitting or receiving.
@@ -90,7 +94,10 @@ if BT_ISO
config BT_ISO_MAX_BIG
int "Maximum number of Broadcast Isochronous Groups (BIGs) to support"
range 1 BT_NIMBLE_ISO_BIG
range 1 1 if BT_BLUEDROID_ENABLED
range 1 BT_NIMBLE_ISO_BIG if BT_NIMBLE_ENABLED
default 1 if BT_BLUEDROID_ENABLED
default BT_NIMBLE_ISO_BIG if BT_NIMBLE_ENABLED
help
Maximum number of BIGs that are supported by the host. A BIG can be
used for either transmitting or receiving, but not at the same time.
@@ -430,3 +430,10 @@ unregister_gap:
}
return ESP_FAIL;
}
#if CONFIG_BT_BLUEDROID_ENABLED
uint8_t esp_ble_iso_bluedroid_get_gattc_if(void)
{
return bt_le_bluedroid_gattc_get_if();
}
#endif /* CONFIG_BT_BLUEDROID_ENABLED */
@@ -591,6 +591,21 @@ void esp_ble_iso_gap_app_post_event(uint8_t type, void *param);
*/
esp_err_t esp_ble_iso_common_init(esp_ble_iso_init_info_t *info);
#if CONFIG_BT_BLUEDROID_ENABLED
/**
* @brief Get the engine's internal GATTC interface handle (Bluedroid only).
*
* Pass this to esp_ble_gattc_aux_open() / esp_ble_gattc_open() so the
* resulting ACL events route back to the engine, avoiding the need for the
* application to register a second BTA GATTC app for connection initiation.
*
* @return Engine's gattc_if (ABI-compatible with esp_gatt_if_t), or
* ESP_GATT_IF_NONE (0xFF) if GATTC registration has not completed —
* callers must bail rather than pass it to aux_open.
*/
uint8_t esp_ble_iso_bluedroid_get_gattc_if(void);
#endif /* CONFIG_BT_BLUEDROID_ENABLED */
#ifdef __cplusplus
}
#endif
@@ -0,0 +1,506 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <errno.h>
#include <zephyr/kernel.h>
#include <zephyr/logging/log.h>
#include <zephyr/bluetooth/buf.h>
#include <zephyr/bluetooth/hci.h>
#include <../host/hci_core.h>
#include <../host/iso_internal.h>
#include "bta/bta_api.h"
#include "stack/btm_ble_api.h"
#include "stack/hcidefs.h"
#include "esp_gap_ble_api.h"
#include "esp_gattc_api.h"
#include "esp_gatts_api.h"
#include "bluedroid/btm_error.h"
#include "bluedroid/hci.h"
#include "common/host.h"
#include "common/app/gap.h"
LOG_MODULE_REGISTER(ISO_BGAP, CONFIG_BT_ISO_LOG_LEVEL);
extern void btc_ble_5_gap_callback(tBTA_DM_BLE_5_GAP_EVENT event,
tBTA_DM_BLE_5_GAP_CB_PARAMS *params);
#if (BLE_50_EXTEND_SYNC_EN == TRUE)
/* Active PA sync handle tracker. Used to synthesize a PA_SYNC_LOST event
* to the iso task when the application calls
* esp_ble_gap_periodic_adv_sync_terminate(): Bluedroid's
* BTM_BLE_5_GAP_PERIODIC_ADV_SYNC_TERMINATE_COMPLETE_EVT only carries a
* status byte and lacks sync_handle, so we recover it from here. This
* mirrors NimBLE host behavior, where ble_gap_periodic_adv_sync_terminate
* internally enqueues a sync_lost event.
*
* The tracker is populated by both PA_SYNC_ESTAB (direct scan-and-sync)
* and PA_SYNC_TRANS_RECV (PAST-delivered sync) on success, and cleared
* by real PA_SYNC_LOST or by the synthesized terminate path below.
*
* Single-slot — assumes one active PA sync (and at most one in-flight
* terminate) at a time. The audio examples never hold multiple PA syncs
* concurrently. Multi-sync acceptors / Broadcast Assistants will need an
* N-slot tracker plus per-terminate intent capture.
*
* TODO(bluedroid): drop this once
* BTM_BLE_5_GAP_PERIODIC_ADV_SYNC_TERMINATE_COMPLETE_EVT also surfaces
* sync_handle (BTM_BlePeriodicAdvSyncTerm in btm_ble_5_gap.c knows the
* handle, it just isn't propagated into cb_params). Ideally Bluedroid
* would directly synthesize a SYNC_LOST event on successful terminate,
* matching the NimBLE host contract; then both this tracker and the
* synthesis below become dead code. */
#define ISO_PA_SYNC_HANDLE_NONE 0xFFFF
static uint16_t active_pa_sync_handle = ISO_PA_SYNC_HANDLE_NONE;
#endif /* BLE_50_EXTEND_SYNC_EN == TRUE */
/* Fast-path BTA → iso-queue post.
*
* BTU calls gap_app_cb directly; this function bypasses the BTC dispatch
* hop for the audio-critical events so they reach the iso task in the same
* order BTU received them. BIGINFO_ADV_REPORT already posts directly from
* iso_evt_handler; routing PA_SYNC_ESTAB / PA_SYNC_LOST / EXT_ADV_REPORT /
* PAST_RECV the same way keeps both event streams ordered. */
static void bt_le_bluedroid_gap_post_event_bta(tBTA_DM_BLE_5_GAP_EVENT event,
tBTA_DM_BLE_5_GAP_CB_PARAMS *params);
static void gap_app_cb(tBTA_DM_BLE_5_GAP_EVENT event, tBTA_DM_BLE_5_GAP_CB_PARAMS *params)
{
/* Audio-critical data events: post directly to iso task here to keep
* ordering with BIGINFO_ADV_REPORT (which iso_evt_handler also posts
* directly). The BTC dispatch below still runs for non-audio recipients
* of esp_ble_gap_register_callback. */
switch (event) {
#if (BLE_50_EXTEND_SCAN_EN == TRUE)
case BTM_BLE_5_GAP_EXT_ADV_REPORT_EVT:
#endif
#if (BLE_50_EXTEND_SYNC_EN == TRUE)
case BTM_BLE_5_GAP_PERIODIC_ADV_SYNC_ESTAB_EVT:
case BTM_BLE_5_GAP_PERIODIC_ADV_SYNC_LOST_EVT:
case BTM_BLE_5_GAP_PERIODIC_ADV_SYNC_TERMINATE_COMPLETE_EVT:
case BTM_BLE_5_GAP_PERIODIC_ADV_REPORT_EVT:
#endif
#if (BLE_FEAT_PERIODIC_ADV_SYNC_TRANSFER == TRUE)
case BTM_BLE_GAP_PERIODIC_ADV_SYNC_TRANS_RECV_EVT:
#endif
bt_le_bluedroid_gap_post_event_bta(event, params);
break;
default:
break;
}
/* Forward to BTC so esp_ble_gap_register_callback() recipients see it. */
btc_ble_5_gap_callback(event, params);
}
void bt_le_bluedroid_gap_post_event(uint16_t event, void *param)
{
const esp_ble_gap_cb_param_t *p = param;
struct bt_le_gap_app_param *qev = NULL;
int err;
qev = calloc(1, sizeof(*qev));
assert(qev);
/* Only AUTH_CMPL reaches here from the application's
* esp_ble_gap_register_callback path. EXT_ADV_REPORT / PA_SYNC_ESTAB /
* PA_SYNC_LOST / PAST_RECV are posted directly by gap_app_cb on the BTM
* BLE 5 channel; SMP events have no such channel so AUTH_CMPL is the
* only one the application must forward. */
switch (event) {
case ESP_GAP_BLE_AUTH_CMPL_EVT: {
const esp_ble_auth_cmpl_t *a = &p->ble_security.auth_cmpl;
struct gatt_conn *gatt_conn;
uint8_t sec_level;
gatt_conn = bt_le_bluedroid_find_gatt_conn_with_addr(a->addr_type, a->bd_addr, false);
if (gatt_conn == NULL) {
LOG_ERR("[B]UnknownDevForEnc");
free(qev);
return;
}
qev->type = BT_LE_GAP_APP_PARAM_SECURITY_CHANGE;
qev->security_change.status = (a->success ? 0x00 : 0xFF);
/* Populate connection identity unconditionally so failure events
* carry valid conn_handle / role / dst to the application. */
qev->security_change.conn_handle = gatt_conn->conn_handle;
qev->security_change.role = gatt_conn->role;
qev->security_change.dst.type = gatt_conn->peer.type;
memcpy(qev->security_change.dst.val, gatt_conn->peer.val, BT_ADDR_SIZE);
if (qev->security_change.status == 0) {
/* Derive level from auth_mode bits (mirrors NimBLE's sec_state
* mapping). esp_ble_auth_cmpl_t exposes neither encrypted/
* authenticated flags nor a key_size, so MITM presence is the
* only authenticated-vs-Just-Works signal we have. */
if (!(a->auth_mode & ESP_LE_AUTH_REQ_MITM)) {
sec_level = BT_SECURITY_L2;
} else if (!(a->auth_mode & ESP_LE_AUTH_REQ_SC_ONLY)) {
sec_level = BT_SECURITY_L3;
} else {
sec_level = BT_SECURITY_L4;
}
qev->security_change.sec_level = sec_level;
qev->security_change.bonded = (a->auth_mode & ESP_LE_AUTH_BOND) ? 1 : 0;
}
break;
}
default:
LOG_WRN("[B]GapPostEvtUnexp[%u]", event);
free(qev);
return;
}
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);
free(qev);
}
}
static void bt_le_bluedroid_gap_post_event_bta(tBTA_DM_BLE_5_GAP_EVENT event,
tBTA_DM_BLE_5_GAP_CB_PARAMS *params)
{
struct bt_le_gap_app_param *qev = NULL;
int err;
qev = calloc(1, sizeof(*qev));
assert(qev);
switch (event) {
#if (BLE_50_EXTEND_SCAN_EN == TRUE)
case BTM_BLE_5_GAP_EXT_ADV_REPORT_EVT: {
const tBTM_BLE_EXT_ADV_REPORT *r = &params->ext_adv_report;
qev->type = BT_LE_GAP_APP_PARAM_EXT_SCAN_RECV;
qev->ext_scan_recv.event_type = (r->data_status << 5) | r->event_type;
qev->ext_scan_recv.addr.type = r->addr_type;
memcpy(qev->ext_scan_recv.addr.val, r->addr, BT_ADDR_SIZE);
/* BTM declares rssi/tx_power as UINT8; the bytes are spec-defined as
* signed (RSSI typically negative). Cast preserves the bit pattern. */
qev->ext_scan_recv.rssi = (int8_t)r->rssi;
qev->ext_scan_recv.tx_power = (int8_t)r->tx_power;
qev->ext_scan_recv.sid = r->sid;
qev->ext_scan_recv.pri_phy = r->primary_phy;
qev->ext_scan_recv.sec_phy = r->secondry_phy;
qev->ext_scan_recv.per_adv_itvl = r->per_adv_interval;
qev->ext_scan_recv.data_len = r->adv_data_len;
if (qev->ext_scan_recv.data_len) {
qev->ext_scan_recv.data = calloc(1, qev->ext_scan_recv.data_len);
assert(qev->ext_scan_recv.data);
memcpy(qev->ext_scan_recv.data, r->adv_data, qev->ext_scan_recv.data_len);
}
break;
}
#endif /* BLE_50_EXTEND_SCAN_EN == TRUE */
#if (BLE_50_EXTEND_SYNC_EN == TRUE)
case BTM_BLE_5_GAP_PERIODIC_ADV_SYNC_ESTAB_EVT: {
const tBTM_BLE_PERIOD_ADV_SYNC_ESTAB *e = &params->sync_estab;
qev->type = BT_LE_GAP_APP_PARAM_PA_SYNC;
qev->pa_sync.status = e->status;
qev->pa_sync.sync_handle = e->sync_handle;
qev->pa_sync.addr.type = e->adv_addr_type;
memcpy(qev->pa_sync.addr.val, e->adv_addr, BT_ADDR_SIZE);
qev->pa_sync.sid = e->sid;
qev->pa_sync.adv_phy = e->adv_phy;
qev->pa_sync.per_adv_itvl = e->period_adv_interval;
qev->pa_sync.adv_ca = e->adv_clk_accuracy;
if (e->status == 0) {
if (active_pa_sync_handle != ISO_PA_SYNC_HANDLE_NONE) {
/* Concurrent PA sync: single-slot tracker overwrites the
* existing handle, so a later terminate on the first sync
* would synthesize SYNC_LOST against the wrong one.
* See ISO_PA_SYNC_HANDLE_NONE doc for the multi-sync TODO. */
LOG_WRN("[B]PaSyncEstabOverwrite[%04x->%04x]",
active_pa_sync_handle, e->sync_handle);
} else {
LOG_INF("[B]PaSyncEstab[%04x]", e->sync_handle);
}
active_pa_sync_handle = e->sync_handle;
}
break;
}
case BTM_BLE_5_GAP_PERIODIC_ADV_SYNC_LOST_EVT:
qev->type = BT_LE_GAP_APP_PARAM_PA_SYNC_LOST;
/* Public API exposes only sync_handle; reason isn't surfaced. */
qev->pa_sync_lost.sync_handle = params->sync_lost.sync_handle;
qev->pa_sync_lost.reason = 0;
if (active_pa_sync_handle == qev->pa_sync_lost.sync_handle) {
LOG_INF("[B]PaSyncLost[%04x]", qev->pa_sync_lost.sync_handle);
active_pa_sync_handle = ISO_PA_SYNC_HANDLE_NONE;
}
break;
case BTM_BLE_5_GAP_PERIODIC_ADV_SYNC_TERMINATE_COMPLETE_EVT:
/* Host-commanded terminate: synthesize PA_SYNC_LOST so the iso
* task sees a unified sync-end event regardless of whether the
* sync was lost on-air or torn down via
* esp_ble_gap_periodic_adv_sync_terminate(). The complete event
* only carries status; recover sync_handle from the tracker.
* See ISO_PA_SYNC_HANDLE_NONE definition for limitations and
* the Bluedroid TODO. */
if (params->per_adv_sync_term.status != BTM_SUCCESS ||
active_pa_sync_handle == ISO_PA_SYNC_HANDLE_NONE) {
LOG_WRN("[B]PaSyncTermNoSynth[%02x][%04x]",
params->per_adv_sync_term.status, active_pa_sync_handle);
free(qev);
return;
}
qev->type = BT_LE_GAP_APP_PARAM_PA_SYNC_LOST;
qev->pa_sync_lost.sync_handle = active_pa_sync_handle;
qev->pa_sync_lost.reason = 0;
LOG_INF("[B]PaSyncTermSynth[%04x]", active_pa_sync_handle);
active_pa_sync_handle = ISO_PA_SYNC_HANDLE_NONE;
break;
case BTM_BLE_5_GAP_PERIODIC_ADV_REPORT_EVT: {
const tBTM_PERIOD_ADV_REPORT *r = &params->period_adv_report;
qev->type = BT_LE_GAP_APP_PARAM_PA_SYNC_RECV;
qev->pa_sync_recv.sync_handle = r->sync_handle;
qev->pa_sync_recv.tx_power = r->tx_power;
qev->pa_sync_recv.rssi = r->rssi;
qev->pa_sync_recv.data_status = r->data_status;
qev->pa_sync_recv.data_len = r->data_length;
if (qev->pa_sync_recv.data_len) {
qev->pa_sync_recv.data = calloc(1, qev->pa_sync_recv.data_len);
assert(qev->pa_sync_recv.data);
memcpy(qev->pa_sync_recv.data, r->data, qev->pa_sync_recv.data_len);
}
break;
}
#endif /* BLE_50_EXTEND_SYNC_EN == TRUE */
#if (BLE_FEAT_PERIODIC_ADV_SYNC_TRANSFER == TRUE)
case BTM_BLE_GAP_PERIODIC_ADV_SYNC_TRANS_RECV_EVT: {
const tBTM_BLE_PERIOD_ADV_SYNC_TRANS_RECV *r = &params->past_recv;
struct gatt_conn *gatt_conn;
/* Look up the PAST-delivering ACL handle by peer BDA — past_recv
* doesn't carry addr type. */
gatt_conn = bt_le_bluedroid_find_gatt_conn_with_addr(0, r->addr, true);
if (gatt_conn == NULL) {
LOG_ERR("[B]UnknownPastSrc");
free(qev);
return;
}
qev->type = BT_LE_GAP_APP_PARAM_PA_SYNC_PAST;
qev->pa_sync_past.status = r->status;
qev->pa_sync_past.sync_handle = r->sync_handle;
qev->pa_sync_past.addr.type = r->adv_addr_type;
memcpy(qev->pa_sync_past.addr.val, r->adv_addr, BT_ADDR_SIZE);
qev->pa_sync_past.sid = r->adv_sid;
qev->pa_sync_past.adv_phy = r->adv_phy;
qev->pa_sync_past.per_adv_itvl = r->adv_interval;
qev->pa_sync_past.adv_ca = r->adv_clk_accuracy;
qev->pa_sync_past.conn_handle = gatt_conn->conn_handle;
#if (BLE_50_EXTEND_SYNC_EN == TRUE)
/* PAST-established syncs need tracker too: a later app-initiated
* terminate hits TERMINATE_COMPLETE_EVT regardless of how the sync
* was originally created. Gated by BLE_50_EXTEND_SYNC_EN since
* that's where the tracker decl + LOST/TERMINATE consumers live. */
if (r->status == 0) {
if (active_pa_sync_handle != ISO_PA_SYNC_HANDLE_NONE) {
LOG_WRN("[B]PaSyncTransOverwrite[%04x->%04x]",
active_pa_sync_handle, r->sync_handle);
} else {
LOG_INF("[B]PaSyncTrans[%04x]", r->sync_handle);
}
active_pa_sync_handle = r->sync_handle;
}
#endif /* BLE_50_EXTEND_SYNC_EN == TRUE */
break;
}
#endif /* BLE_FEAT_PERIODIC_ADV_SYNC_TRANSFER == TRUE */
default:
free(qev);
return;
}
err = bt_le_iso_task_post(ISO_QUEUE_ITEM_TYPE_GAP_EVENT, qev, sizeof(*qev));
if (err) {
LOG_ERR("[B]GapPostEvtBtaFail[%d][%u]", err, qev->type);
goto free;
}
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);
}
int bt_le_bluedroid_scan_start(const struct bt_le_scan_param *param)
{
tBTM_STATUS status;
LOG_DBG("[B]ScanStart[%u][%u][%u]", param->type, param->interval, param->window);
#if USE_DIRECT_HCI
{
/* HCI LE Set Extended Scan Parameters (uncoded only):
* own_addr_type(1) | scan_filter_policy(1) | scanning_phys(1)
* | per-phy { scan_type(1) | scan_interval(2) | scan_window(2) } */
uint8_t cmd_params[8];
cmd_params[0] = BLE_ADDR_PUBLIC;
cmd_params[1] = 0; /* filter_policy: accept all */
cmd_params[2] = 0x01; /* scanning_phys: LE 1M only */
cmd_params[3] = param->type;
sys_put_le16(param->interval, cmd_params + 4);
sys_put_le16(param->window, cmd_params + 6);
status = bt_le_bluedroid_hci_send_sync(HCI_BLE_SET_EXT_SCAN_PARAMS,
cmd_params, sizeof(cmd_params),
NULL, 0);
}
#else /* USE_DIRECT_HCI */
{
tBTM_BLE_EXT_SCAN_PARAMS scan_params = {0};
scan_params.own_addr_type = BLE_ADDR_PUBLIC;
scan_params.filter_policy = 0;
scan_params.scan_duplicate = 0;
scan_params.cfg_mask = BTM_BLE_GAP_EXT_SCAN_UNCODE_MASK;
scan_params.uncoded_cfg.scan_type = param->type;
scan_params.uncoded_cfg.scan_interval = param->interval;
scan_params.uncoded_cfg.scan_window = param->window;
bt_le_host_lock();
status = BTM_BleSetExtendedScanParams(&scan_params);
bt_le_host_unlock();
}
#endif /* USE_DIRECT_HCI */
if (status != BTM_SUCCESS) {
LOG_ERR("[B]SetScanParamsFail[%02x]", status);
return bluedroid_err_to_errno(status);
}
#if USE_DIRECT_HCI
{
/* HCI LE Set Extended Scan Enable:
* enable(1) | filter_duplicates(1) | duration(2) | period(2)
* duration=period=0 → continuous scan. */
uint8_t cmd_params[6] = { 1, 0, 0, 0, 0, 0 };
status = bt_le_bluedroid_hci_send_sync(HCI_BLE_SET_EXT_SCAN_ENABLE,
cmd_params, sizeof(cmd_params),
NULL, 0);
}
#else /* USE_DIRECT_HCI */
bt_le_host_lock();
status = BTM_BleExtendedScan(true, 0, 0);
bt_le_host_unlock();
#endif /* USE_DIRECT_HCI */
if (status != BTM_SUCCESS) {
LOG_ERR("[B]ScanStartFail[%02x]", status);
}
return bluedroid_err_to_errno(status);
}
int bt_le_bluedroid_scan_stop(void)
{
tBTM_STATUS status;
LOG_DBG("[B]ScanStop");
#if USE_DIRECT_HCI
{
/* HCI LE Set Extended Scan Enable with enable=0; other fields
* are ignored by the controller per spec but must be present. */
uint8_t cmd_params[6] = { 0, 0, 0, 0, 0, 0 };
status = bt_le_bluedroid_hci_send_sync(HCI_BLE_SET_EXT_SCAN_ENABLE,
cmd_params, sizeof(cmd_params),
NULL, 0);
}
#else /* USE_DIRECT_HCI */
bt_le_host_lock();
status = BTM_BleExtendedScan(false, 0, 0);
bt_le_host_unlock();
#endif /* USE_DIRECT_HCI */
if (status != BTM_SUCCESS) {
LOG_ERR("[B]ScanStopFail[%02x]", status);
}
return bluedroid_err_to_errno(status);
}
int bt_le_bluedroid_iso_disconnect(uint16_t conn_handle, uint8_t reason)
{
tBTM_STATUS status;
LOG_DBG("[B]IsoDisconn[0x%03x][%02x]", conn_handle, reason);
/* No direct_hci variant: HCI Disconnect returns Command_Status;
* outcome arrives via BTM_BLE_ISO_CIS_DISCONNECTED_EVT. */
status = BTM_BleDisconCis(conn_handle, reason);
if (status != BTM_SUCCESS) {
LOG_ERR("[B]IsoDisconnFail[0x%03x][%02x]", conn_handle, status);
}
return bluedroid_err_to_errno(status);
}
int bt_le_bluedroid_gap_init(void)
{
BTM_BleGapRegisterCallback(gap_app_cb);
return 0;
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,198 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdint.h>
#include <string.h>
#include <zephyr/kernel.h>
#include <zephyr/logging/log.h>
#include "bluedroid/hci.h"
#if USE_DIRECT_HCI
/* hci/hci_layer.h pulls in osi/osi.h which defines a 2-arg CONCAT(a, b)
* macro that conflicts with the variadic CONCAT(...) from Zephyr's
* <zephyr/sys/util.h>. hci.c does not use CONCAT itself, so drop the
* Zephyr definition before pulling in osi's. */
#undef CONCAT
#include "hci/hci_layer.h"
#include "stack/hcimsgs.h"
#include "stack/btm_ble_api.h"
#include "bluedroid/btm_error.h"
#include "common/host.h"
LOG_MODULE_REGISTER(ISO_BHCI, CONFIG_BT_ISO_LOG_LEVEL);
/* Adapter-private sync cmd path. Decouples sync HCI cmds from BTU's global
* ble_sync_info entirely: each cmd carries its own command_complete_cb (this
* file's direct_hci_complete_cb) and the caller waits on direct_hci_sem,
* not ble_sync_info.sync_sem. Two-task race on sync_info->opcode disappears.
*
* Caller sets direct_hci_rsp.opcode before transmit; cb verifies the
* response opcode matches before giving the sem (guards against unexpected
* stray Command_Complete dispatches).
*
* direct_hci_complete_cb runs on the hci_layer task — keep it tiny and
* non-blocking, no host_lock, no further dispatch.
*
* Concurrent safety: send_sync is serialized by callers via bt_le_host_lock,
* so the static rsp_buf pointer / opcode latch are single-slot. */
static struct k_sem direct_hci_sem;
/* Set by deinit before deleting the sem. Late-arriving cb's must check
* this before touching the sem. Residual race exists (cb past the check
* but pre-give vs. deinit completing the delete) — accepted in practice
* because the BTU/HCI layer offers no way to cancel an in-flight cmd. */
static volatile bool direct_hci_shutting_down;
static struct {
uint16_t opcode; /* expected — set by caller, verified by cb */
uint8_t status; /* HCI status from Command_Complete */
} direct_hci_rsp;
/* Internal sink for the cmd-specific return payload (bytes after the status
* byte). The cb copies HERE, never into the caller's stack buffer — a
* timed-out caller may already have freed its stack frame by the time a late
* cb runs on the hci_layer task. The caller copies this out only after a
* successful take. Static storage: a late cb always has a valid target, so no
* teardown race. Sized for the largest payload any direct-HCI caller reads
* back (SET_CIG: 2 + 2*cis_count, ISO_READ_TX_SYNC: 11). */
#define DIRECT_HCI_RSP_MAX MAX(2 + 2 * CONFIG_BT_ISO_MAX_CHAN, 11)
static uint8_t direct_hci_rsp_data[DIRECT_HCI_RSP_MAX];
static uint8_t direct_hci_rsp_data_len;
static void direct_hci_complete_cb(BT_HDR *response, void *context)
{
/* Command_Complete event layout after BT_HDR data/offset:
* [0] event code (0x0E) [1] param len [2] num_cmd_packets
* [3..4] opcode (LE) [5] status [6..] cmd-specific params
*/
uint8_t *stream = response->data + response->offset + 3;
uint16_t opcode;
uint8_t status;
uint8_t event_param_len;
ARG_UNUSED(context);
event_param_len = response->data[response->offset + 1];
STREAM_TO_UINT16(opcode, stream);
STREAM_TO_UINT8(status, stream);
if (opcode != direct_hci_rsp.opcode) {
LOG_ERR("[B]DirectHciOpcodeMismatch[exp=0x%04x][got=0x%04x]",
direct_hci_rsp.opcode, opcode);
/* Drop — don't give sem, caller times out. */
osi_free(response);
return;
}
direct_hci_rsp.status = status;
/* Copy cmd-specific return params (everything after the status byte)
* into the caller's buffer. event_param_len counts:
* num_cmd_packets(1) + opcode(2) + status(1) + cmd-specific(N)
* so the payload available to caller is event_param_len - 4. */
if (event_param_len > 4) {
direct_hci_rsp_data_len = MIN(event_param_len - 4, sizeof(direct_hci_rsp_data));
memcpy(direct_hci_rsp_data, stream, direct_hci_rsp_data_len);
}
osi_free(response);
/* 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) {
return;
}
k_sem_give(&direct_hci_sem);
}
tBTM_STATUS bt_le_bluedroid_hci_send_sync(uint16_t opcode,
const uint8_t *cmd_params,
uint8_t cmd_params_len,
uint8_t *rsp_buf,
uint8_t rsp_buf_len)
{
BT_HDR *p;
UINT8 *pp;
hci_cmd_metadata_t *metadata;
p = HCI_GET_CMD_BUF(cmd_params_len);
if (p == NULL) {
return BTM_NO_RESOURCES;
}
pp = p->data;
UINT16_TO_STREAM(pp, opcode);
UINT8_TO_STREAM(pp, cmd_params_len);
if (cmd_params_len > 0 && cmd_params != NULL) {
memcpy(pp, cmd_params, cmd_params_len);
}
metadata = HCI_GET_CMD_METAMSG(p);
metadata->command_complete_cb = direct_hci_complete_cb;
metadata->command_status_cb = NULL;
metadata->opcode = opcode;
metadata->context = NULL;
/* Set expected opcode and rsp sink BEFORE transmit — cb may run
* before transmit returns if response is already queued. */
direct_hci_rsp.opcode = opcode;
direct_hci_rsp.status = 0;
direct_hci_rsp_data_len = 0;
/* Drop any stale give from a previous timed-out cmd whose cb arrived
* late. Must run AFTER opcode update (so any cb arriving after this
* drain hits the mismatch path and doesn't give) and BEFORE transmit
* (so this cmd's own cb-give isn't accidentally consumed here). */
k_sem_reset(&direct_hci_sem);
hci_layer_get_interface()->transmit_command(p, direct_hci_complete_cb,
NULL, NULL);
if (k_sem_take(&direct_hci_sem, K_SEM_SHORT) != 0) {
LOG_ERR("[B]DirectHciTimeout[0x%04x]", opcode);
return BTM_ERR_PROCESSING;
}
/* The cb gives the sem only after its memcpy, so the payload is complete;
* copy it into the caller's (now-confirmed-live) sink. */
if (rsp_buf != NULL && rsp_buf_len > 0 && direct_hci_rsp_data_len > 0) {
memcpy(rsp_buf, direct_hci_rsp_data, MIN(direct_hci_rsp_data_len, rsp_buf_len));
}
if (direct_hci_rsp.status != HCI_SUCCESS) {
LOG_ERR("[B]DirectHciStatus[0x%04x][%02x]",
opcode, direct_hci_rsp.status);
return BTM_HCI_ERROR | direct_hci_rsp.status;
}
return BTM_SUCCESS;
}
int bt_le_bluedroid_hci_init(void)
{
direct_hci_shutting_down = false;
k_sem_create(&direct_hci_sem);
return 0;
}
void bt_le_bluedroid_hci_deinit(void)
{
/* Flip the gate first so any cb past this point skips the give.
* Drain any give that was already posted before the flag flip so
* the sem is clean before deletion. */
direct_hci_shutting_down = true;
k_sem_reset(&direct_hci_sem);
k_sem_delete(&direct_hci_sem);
}
#endif /* USE_DIRECT_HCI */
@@ -0,0 +1,47 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <errno.h>
#include "stack/btm_api.h"
/**
* Translate a Bluedroid BTM status (tBTM_STATUS, see stack/btm_api.h) into
* a standard negative POSIX errno.
*
* Counterpart to nimble_err_to_errno() — both adapters return errno values
* to common/ and lib code so the audio layer can branch on -EAGAIN /
* -EBUSY / -ENOMEM etc. without knowing which host it ran on.
*
* Unknown codes map to -EIO so caller logic doesn't accidentally match a
* known errno.
*/
static inline int bluedroid_err_to_errno(tBTM_STATUS status)
{
switch (status) {
case BTM_SUCCESS: return 0;
case BTM_CMD_STARTED: return 0;
case BTM_CMD_STORED: return 0;
case BTM_SUCCESS_NO_SECURITY: return 0;
case BTM_BUSY: return -EBUSY;
case BTM_NO_RESOURCES: return -ENOMEM;
case BTM_MODE_UNSUPPORTED: return -ENOTSUP;
case BTM_ILLEGAL_VALUE: return -EINVAL;
case BTM_WRONG_MODE: return -EPERM;
case BTM_UNKNOWN_ADDR: return -ENOENT;
case BTM_DEVICE_TIMEOUT: return -ETIMEDOUT;
case BTM_BAD_VALUE_RET: return -EBADMSG;
case BTM_ERR_PROCESSING: return -EIO;
case BTM_NOT_AUTHORIZED: return -EACCES;
case BTM_DEV_RESET: return -ECONNRESET;
case BTM_ILLEGAL_ACTION: return -EPERM;
case BTM_FAILED_ON_SECURITY: return -EPERM;
case BTM_REPEATED_ATTEMPTS: return -EAGAIN;
default: return -EIO;
}
}
@@ -0,0 +1,32 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef HOST_BLUEDROID_APP_GAP_H_
#define HOST_BLUEDROID_APP_GAP_H_
#include <stdint.h>
#include <zephyr/bluetooth/bluetooth.h>
#ifdef __cplusplus
extern "C" {
#endif
void bt_le_bluedroid_gap_post_event(uint16_t event, void *param);
int bt_le_bluedroid_scan_start(const struct bt_le_scan_param *param);
int bt_le_bluedroid_scan_stop(void);
int bt_le_bluedroid_iso_disconnect(uint16_t conn_handle, uint8_t reason);
int bt_le_bluedroid_gap_init(void);
#ifdef __cplusplus
}
#endif
#endif /* HOST_BLUEDROID_APP_GAP_H_ */
@@ -0,0 +1,133 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef HOST_BLUEDROID_GATT_H_
#define HOST_BLUEDROID_GATT_H_
#include <stdint.h>
#include <assert.h>
#include <zephyr/kernel.h>
#include <zephyr/sys/slist.h>
#include <zephyr/bluetooth/uuid.h>
#include <zephyr/bluetooth/conn.h>
#include <zephyr/bluetooth/gatt.h>
#ifdef __cplusplus
extern "C" {
#endif
struct gatt_conn {
uint8_t used : 1;
uint8_t conn_create : 1;
uint8_t gattc_open : 1;
uint8_t cfg_mtu : 1;
uint8_t mtu_posted : 1;
uint8_t status;
uint8_t gatt_if;
uint16_t conn_handle;
uint8_t role;
struct {
uint8_t type;
uint8_t val[6];
} peer;
uint16_t mtu;
/* FIFO of in-flight GATTC read/write ops; mirrors BTA p_cmd_list ordering.
* BTA EVTs deliver attr_handle/status only — we need this to recover the
* caller's params on each cmpl. See struct gattc_list_node in gatt.c. */
sys_slist_t gattc_list;
/* FIFO of pending GATTS indications. GATT spec restricts per-conn to a
* single outstanding indication; this queue absorbs that limit so callers
* never see -EBUSY. See struct gatts_list_node in gatt.c. Mirrors
* NimBLE adapter NRP indicate behavior. */
sys_slist_t gatts_list;
/* FIFO of in-flight GATTS notify markers. BTA fires CONF_EVT for notify
* too (immediately after send) — tBTA_GATTS_REQ carries no notify/indicate
* flag so the handler pops this list first to disambiguate from the
* indication acks tracked in gatts_list. See struct gatts_notify_node. */
sys_slist_t gatts_notify_list;
};
uint8_t bt_le_bluedroid_gattc_get_if(void);
uint8_t bt_le_bluedroid_gatts_get_if(void);
void bt_le_bluedroid_gatts_sem_reset(void);
int bt_le_bluedroid_gatts_sem_take(void);
void bt_le_bluedroid_gatts_sem_give(int result);
struct gatt_conn *bt_le_bluedroid_find_gatt_conn_with_addr(uint8_t addr_type,
const uint8_t addr[6],
bool ignore_type);
struct gatt_conn *bt_le_bluedroid_find_gatt_conn_with_handle(uint16_t conn_handle);
struct gatt_conn *bt_le_bluedroid_find_free_gatt_conn(void);
void bt_le_bluedroid_gatt_handle_event(uint8_t *data, size_t data_len);
void bt_le_bluedroid_gatt_uuid_convert(const struct bt_uuid *uuid_in, void *uuid_out);
uint16_t bt_le_bluedroid_gatt_perm_convert(uint16_t perm_in);
uint16_t bt_le_bluedroid_gatt_get_mtu(struct bt_conn *conn);
ssize_t bt_le_bluedroid_gatts_attr_read(struct bt_conn *conn, const struct bt_gatt_attr *attr,
void *buf, uint16_t buf_len, uint16_t offset,
const void *value, uint16_t value_len);
int bt_le_bluedroid_gatts_notify(struct bt_conn *conn, struct bt_gatt_notify_params *params);
int bt_le_bluedroid_gatts_indicate(struct bt_conn *conn, struct bt_gatt_indicate_params *params);
int bt_le_bluedroid_gattc_disc_start(uint16_t conn_handle);
int bt_le_bluedroid_gattc_discover(struct bt_conn *conn, struct bt_gatt_discover_params *params);
int bt_le_bluedroid_gattc_read(struct bt_conn *conn, struct bt_gatt_read_params *params);
int bt_le_bluedroid_gattc_write(struct bt_conn *conn, struct bt_gatt_write_params *params);
int bt_le_bluedroid_gattc_write_without_rsp(struct bt_conn *conn, uint16_t handle,
const void *data, uint16_t length);
int bt_le_bluedroid_gattc_write_ccc(struct bt_conn *conn, struct bt_gatt_subscribe_params *params);
int bt_le_bluedroid_gatt_init(void);
void bt_le_bluedroid_gatt_deinit(void);
struct inc_svc_inst {
struct bt_gatt_service *svc_p;
bool included;
};
struct gatts_svc_cb {
void (*svc_create_cb)(uint16_t service_id, uint16_t svc_instance,
bool is_primary, uint8_t status, void *uuid);
void (*inc_svc_add_cb)(uint16_t service_id, uint16_t attr_id, uint8_t status);
void (*chrc_add_cb)(uint16_t service_id, uint16_t attr_id,
uint8_t status, void *uuid);
void (*svc_start_cb)(uint16_t service_id, uint8_t status);
};
void bt_le_bluedroid_gatts_svc_cb_register(struct gatts_svc_cb *cb);
#ifdef __cplusplus
}
#endif
#endif /* HOST_BLUEDROID_GATT_H_ */
@@ -0,0 +1,56 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef HOST_BLUEDROID_APP_HCI_H_
#define HOST_BLUEDROID_APP_HCI_H_
#include <stdint.h>
#include "stack/btm_ble_api.h"
#ifdef __cplusplus
extern "C" {
#endif
/* When set to 1, sync HCI commands issued by the iso adapter bypass BTM
* and the global ble_sync_info to avoid the sync_info opcode race with
* concurrent BTC/BTA sync cmds. Experimental. Toggle is shared with
* iso.c and gap.c via this header so the BTM fallback paths in both
* files stay in sync. */
#define USE_DIRECT_HCI 1
int bt_le_bluedroid_hci_init(void);
void bt_le_bluedroid_hci_deinit(void);
/* Send a sync HCI command, bypassing BTM and ble_sync_info. `cmd_params`
* is the cmd parameter payload (without opcode/length preamble).
*
* Returns BTM_SUCCESS, BTM_HCI_ERROR | hci_status on controller failure,
* or BTM_ERR_PROCESSING on host-side timeout.
*
* If `rsp_buf` is non-NULL, up to `rsp_buf_len` bytes of the
* cmd-specific return params (the bytes after the status byte in the
* HCI Command_Complete event) are copied in. If the actual payload is
* shorter, the tail of rsp_buf is left untouched; if longer, the excess
* is silently dropped. Callers parse opcode-specific structure.
*
* Serialization: NOT thread-safe — internally uses single-slot static
* state (sem, opcode latch, rsp buffer pointer). Callers must serialize
* externally; in practice all current callers (iso.c hci_cmd_* and
* gap.c scan_start/stop) run on the iso task, so single-task affinity
* provides serialization without explicit locking. */
tBTM_STATUS bt_le_bluedroid_hci_send_sync(uint16_t opcode,
const uint8_t *cmd_params,
uint8_t cmd_params_len,
uint8_t *rsp_buf,
uint8_t rsp_buf_len);
#ifdef __cplusplus
}
#endif
#endif /* HOST_BLUEDROID_APP_HCI_H_ */
@@ -0,0 +1,30 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef HOST_BLUEDROID_ISO_H_
#define HOST_BLUEDROID_ISO_H_
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
struct net_buf;
int bt_le_bluedroid_hci_iso_cmd_send_sync(uint16_t opcode,
struct net_buf *buf,
struct net_buf **rsp);
int bt_le_bluedroid_iso_init(void);
void bt_le_bluedroid_iso_deinit(void);
#ifdef __cplusplus
}
#endif
#endif /* HOST_BLUEDROID_ISO_H_ */
File diff suppressed because it is too large Load Diff
@@ -217,9 +217,9 @@ void bt_le_nimble_gap_post_event(void *param)
break;
default:
LOG_WRN("[N]GapPostEvtUnexp[%u]", ev->type);
free(qev);
assert(0);
break;
return;
}
err = bt_le_iso_task_post(ISO_QUEUE_ITEM_TYPE_GAP_EVENT, qev, sizeof(*qev));
@@ -253,22 +253,39 @@ free:
int bt_le_nimble_scan_start(const struct bt_le_scan_param *param, ble_gap_event_fn *cb)
{
struct ble_gap_disc_params scan_param = {0};
struct ble_gap_ext_disc_params uncoded = {0};
int rc;
scan_param.itvl = param->interval;
scan_param.window = param->window;
scan_param.filter_policy = 0;
scan_param.limited = 0;
scan_param.passive = !param->type;
scan_param.filter_duplicates = 0;
LOG_DBG("[N]ScanStart[%u][%u][%u]", param->type, param->interval, param->window);
return nimble_err_to_errno(ble_gap_disc(BLE_OWN_ADDR_PUBLIC, BLE_HS_FOREVER,
&scan_param, cb, NULL));
uncoded.itvl = param->interval;
uncoded.window = param->window;
uncoded.passive = !param->type;
/* LE Audio sources broadcast via extended advertising; legacy
* ble_gap_disc would miss them. Uncoded-only mirrors the Bluedroid
* side which sets BTM_BLE_GAP_EXT_SCAN_UNCODE_MASK. */
rc = ble_gap_ext_disc(BLE_OWN_ADDR_PUBLIC, 0, 0, 0, 0, 0,
&uncoded, NULL, cb, NULL);
if (rc) {
LOG_ERR("[N]ScanStartFail[%d]", rc);
}
return nimble_err_to_errno(rc);
}
int bt_le_nimble_scan_stop(void)
{
return nimble_err_to_errno(ble_gap_disc_cancel());
int rc;
LOG_DBG("[N]ScanStop");
rc = ble_gap_disc_cancel();
if (rc) {
LOG_ERR("[N]ScanStopFail[%d]", rc);
}
return nimble_err_to_errno(rc);
}
int bt_le_nimble_iso_disconnect(uint16_t conn_handle, uint8_t reason)
@@ -95,9 +95,9 @@ void bt_le_nimble_gatt_post_event(void *param)
break;
default:
LOG_WRN("[N]GattPostEvtUnexp[%u]", ev->type);
free(qev);
assert(0);
break;
return;
}
err = bt_le_iso_task_post(ISO_QUEUE_ITEM_TYPE_GATT_EVENT, qev, sizeof(*qev));
@@ -209,7 +209,7 @@ static void handle_gattc_notify_rx_event_safe(struct bt_le_gattc_notify_rx_event
conn = bt_le_acl_conn_find(event->conn_handle);
if (conn == NULL || conn->state != BT_CONN_CONNECTED) {
LOG_ERR("[N]NotConn[%d]", __LINE__);
LOG_INF("[N]GattcNtfRxNotConn[%u][%u]", event->conn_handle, BT_CONN_STATE_GET(conn));
goto end;
}
@@ -222,6 +222,12 @@ static void handle_gattc_notify_rx_event_safe(struct bt_le_gattc_notify_rx_event
if (params->ccc_handle != BT_GATT_AUTO_DISCOVER_CCC_HANDLE &&
params->value_handle == event->attr_handle) {
if (params->notify) {
/* Return value intentionally ignored. Zephyr unsubscribes on
* BT_GATT_ITER_STOP, but lib clients return STOP on a single
* malformed/short notify (e.g. unicast_client_cp_notify);
* tearing down a core subscription like the ASCS control point
* over one bad PDU would drop every later notification. Tolerate
* the bad PDU and keep the subscription. */
params->notify(conn, params, event->value, event->len);
}
}
@@ -446,6 +452,16 @@ int bt_le_nimble_gattc_discover(struct bt_conn *conn, struct bt_gatt_discover_pa
LOG_DBG("[N]GattcDisc[%u]", conn->handle);
if (params->uuid) {
/* Downstream gattc_db_disc_*_by_uuid takes ble_uuid16_t — reject
* non-16-bit caller UUIDs explicitly. BT_UUID_16 would otherwise
* reinterpret a 32/128-bit struct and feed garbage to the lookup.
* LE Audio profiles only use SIG-assigned 16-bit UUIDs, so this
* gate stays a compile-time-style guard in practice. */
if (params->uuid->type != BT_UUID_TYPE_16) {
LOG_ERR("[N]DiscNon16BitUuid[%u]", params->uuid->type);
return -ENOTSUP;
}
uuid.u.type = BLE_UUID_TYPE_16;
uuid.value = BT_UUID_16(params->uuid)->val;
}
@@ -529,9 +529,11 @@ static void gattc_db_disc(struct gattc_db *adb, uint8_t status)
* to the upper layer.
*/
if (status == DISC_FAIL) {
uint16_t conn_handle = adb->conn_handle;
gattc_db_del(adb);
bt_le_nimble_gatt_post_disc_cmpl_event(adb->conn_handle, 0x01);
bt_le_nimble_gatt_post_disc_cmpl_event(conn_handle, 0x01);
return;
}
@@ -553,9 +555,11 @@ static void gattc_db_disc(struct gattc_db *adb, uint8_t status)
* we will delete the gatt client database and post the
* discovery completion event.
*/
uint16_t conn_handle = adb->conn_handle;
gattc_db_del(adb);
bt_le_nimble_gatt_post_disc_cmpl_event(adb->conn_handle, 0x01);
bt_le_nimble_gatt_post_disc_cmpl_event(conn_handle, 0x01);
break;
}
@@ -590,8 +594,8 @@ static int gattc_db_disc_all_svcs_cb_safe(uint16_t conn_handle,
case 0:
assert(svc);
LOG_DBG("[N]GattcDbDiscAllSvcs[%s][%u][%u]",
audio_svc_uuid_to_str(svc->uuid.u16.value),
LOG_DBG("[N]GattcDbDiscAllSvcs[0x%04x][%u][%u]",
svc->uuid.u16.value,
svc->start_handle, svc->end_handle);
gattc_db_svc_insert(adb, svc);
@@ -637,7 +641,7 @@ static int gattc_db_find_inc_svcs_cb_safe(uint16_t conn_handle,
adb = gattc_db_find(conn_handle);
if (adb == NULL) {
LOG_ERR("[N]GattcDbNotFound");
LOG_WRN("[N]GattcDbFindIncSvcsCbNotFound");
rc = -ENODEV;
goto end;
}
@@ -646,8 +650,8 @@ static int gattc_db_find_inc_svcs_cb_safe(uint16_t conn_handle,
case 0:
assert(svc);
LOG_DBG("[N]GattcDbFindIncSvcs[%s][%u][%u]",
audio_svc_uuid_to_str(svc->uuid.u16.value),
LOG_DBG("[N]GattcDbFindIncSvcs[0x%04x][%u][%u]",
svc->uuid.u16.value,
svc->start_handle, svc->end_handle);
gattc_db_inc_svc_insert(asvc, svc);
@@ -725,7 +729,7 @@ static int gattc_db_disc_all_inc_chrs_cb_safe(uint16_t conn_handle,
adb = gattc_db_find(conn_handle);
if (adb == NULL) {
LOG_ERR("[N]GattcDbNotFound");
LOG_WRN("[N]GattcDbDiscAllIncChrsCbNotFound");
rc = -ENODEV;
goto end;
}
@@ -783,7 +787,7 @@ static int gattc_db_disc_all_chrs_cb_safe(uint16_t conn_handle,
adb = gattc_db_find(conn_handle);
if (adb == NULL) {
LOG_ERR("[N]GattcDbNotFound");
LOG_WRN("[N]GattcDbDiscAllChrsCbNotFound");
rc = -ENODEV;
goto end;
}
@@ -843,7 +847,7 @@ static int gattc_db_disc_all_inc_dscs_cb_safe(uint16_t conn_handle,
adb = gattc_db_find(conn_handle);
if (adb == NULL) {
LOG_ERR("[N]GattcDbNotFound");
LOG_WRN("[N]GattcDbDiscAllIncDscsCbNotFound");
rc = -ENODEV;
goto end;
}
@@ -906,7 +910,7 @@ static int gattc_db_disc_all_dscs_cb_safe(uint16_t conn_handle,
adb = gattc_db_find(conn_handle);
if (adb == NULL) {
LOG_ERR("[N]GattcDbNotFound");
LOG_WRN("[N]GattcDbDiscAllDscsCbNotFound");
rc = -ENODEV;
goto end;
}
@@ -967,7 +971,7 @@ static int gattc_db_enable_notify_cb_safe(uint16_t conn_handle,
adb = gattc_db_find(conn_handle);
if (adb == NULL) {
LOG_ERR("[N]GattcDbNotFound");
LOG_WRN("[N]GattcDbEnableNotifyCbNotFound");
rc = -ENODEV;
goto end;
}
@@ -987,10 +991,17 @@ static int gattc_db_enable_notify_cb_safe(uint16_t conn_handle,
break;
}
/* Mark the CCCD write as completed */
achrc->cccd_write = 1;
gattc_db_disc(adb, DISC_SUCCESS);
if (rc) {
/* Terminal failure (ENOTCONN/ETIMEOUT) — bail via DISC_FAIL so the
* next CCCD write isn't queued; it would otherwise sit on NimBLE's
* pending list and only resolve after the 30s ATT timeout, by which
* time the adb has been removed. */
gattc_db_disc(adb, DISC_FAIL);
} else {
/* Mark the CCCD write as completed */
achrc->cccd_write = 1;
gattc_db_disc(adb, DISC_SUCCESS);
}
end:
bt_le_host_unlock();
@@ -1014,7 +1025,7 @@ static int handle_gattc_disc_svc_by_uuid(struct bt_conn *conn, ble_uuid16_t *uui
adb = gattc_db_find(conn->handle);
if (adb == NULL) {
LOG_ERR("[N]GattcDbNotFound");
LOG_WRN("[N]GattcDiscSvcByUuidNotFound");
goto end;
}
@@ -1068,7 +1079,7 @@ static int handle_gattc_find_inc_svcs(struct bt_conn *conn,
adb = gattc_db_find(conn->handle);
if (adb == NULL) {
LOG_ERR("[N]GattcDbNotFound");
LOG_WRN("[N]GattcFindIncSvcsNotFound");
goto end;
}
@@ -1087,6 +1098,9 @@ static int handle_gattc_find_inc_svcs(struct bt_conn *conn,
inc_svc.start_handle = ainc_svc->svc.start_handle;
inc_svc.end_handle = ainc_svc->svc.end_handle;
/* Include declaration's own attribute type — same rationale
* as the Bluedroid side: caller may dereference attr->uuid. */
attr.uuid = BT_UUID_GATT_INCLUDE;
attr.user_data = &inc_svc;
/* TODO:
* When CONFIG_BT_NIMBLE_INCL_SVC_DISCOVERY is enabled, use ainc_svc->attr_handle here.
@@ -1158,7 +1172,7 @@ static int handle_gattc_disc_chrs(struct bt_conn *conn, ble_uuid16_t *uuid,
adb = gattc_db_find(conn->handle);
if (adb == NULL) {
LOG_ERR("[N]GattcDbNotFound");
LOG_WRN("[N]GattcDiscChrsNotFound");
goto end;
}
@@ -1234,7 +1248,7 @@ void handle_gattc_db_disc_event_safe(struct bt_le_gattc_discover_event *event)
conn = bt_le_acl_conn_find(event->conn_handle);
if (conn == NULL || conn->state != BT_CONN_CONNECTED) {
LOG_ERR("[N]GattcDbNotConn[%d]", __LINE__);
LOG_INF("[N]GattcDbDiscNotConn[%u][%u]", event->conn_handle, BT_CONN_STATE_GET(conn));
goto end;
}
@@ -1341,7 +1355,7 @@ static int handle_gattc_disc_all_dscs(struct bt_conn *conn,
adb = gattc_db_find(conn->handle);
if (adb == NULL) {
LOG_ERR("[N]GattcDbNotFound");
LOG_WRN("[N]GattcDiscAllDscsNotFound");
rc = -ENODEV;
goto end;
}
@@ -117,7 +117,7 @@ static int gattc_nrp_read_by_uuid_cb_safe(uint16_t conn_handle,
conn = bt_le_acl_conn_find(conn_handle);
if (conn == NULL || conn->state != BT_CONN_CONNECTED) {
LOG_ERR("[N]GattcNrpNotConn[%d]", __LINE__);
LOG_INF("[N]GattcNrpRdByUuidNotConn[%u][%u]", conn_handle, BT_CONN_STATE_GET(conn));
rc = -ENOTCONN;
goto end;
}
@@ -207,7 +207,7 @@ static int gattc_nrp_read_long_cb_safe(uint16_t conn_handle,
conn = bt_le_acl_conn_find(conn_handle);
if (conn == NULL || conn->state != BT_CONN_CONNECTED) {
LOG_ERR("[N]GattcNrpNotConn[%d]", __LINE__);
LOG_INF("[N]GattcNrpRdLongNotConn[%u][%u]", conn_handle, BT_CONN_STATE_GET(conn));
rc = -ENOTCONN;
goto end;
}
@@ -287,7 +287,7 @@ static int gattc_nrp_read_single_cb_safe(uint16_t conn_handle,
conn = bt_le_acl_conn_find(conn_handle);
if (conn == NULL || conn->state != BT_CONN_CONNECTED) {
LOG_ERR("[N]GattcNrpNotConn[%d]", __LINE__);
LOG_INF("[N]GattcNrpRdSingleNotConn[%u][%u]", conn_handle, BT_CONN_STATE_GET(conn));
rc = -ENOTCONN;
goto end;
}
@@ -404,7 +404,7 @@ static int gattc_nrp_write_cb_safe(uint16_t conn_handle,
conn = bt_le_acl_conn_find(conn_handle);
if (conn == NULL || conn->state != BT_CONN_CONNECTED) {
LOG_ERR("[N]GattcNrpNotConn[%d]", __LINE__);
LOG_INF("[N]GattcNrpWrNotConn[%u][%u]", conn_handle, BT_CONN_STATE_GET(conn));
rc = -ENOTCONN;
goto end;
}
@@ -480,7 +480,7 @@ static int gattc_nrp_subscribe_cb_safe(uint16_t conn_handle,
conn = bt_le_acl_conn_find(conn_handle);
if (conn == NULL || conn->state != BT_CONN_CONNECTED) {
LOG_ERR("[N]GattcNrpNotConn[%d]", __LINE__);
LOG_INF("[N]GattcNrpSubNotConn[%u][%u]", conn_handle, BT_CONN_STATE_GET(conn));
rc = -ENOTCONN;
goto end;
}
@@ -560,7 +560,7 @@ void bt_le_nimble_gatts_nrp_indicate_cb(uint16_t conn_handle,
conn = bt_le_acl_conn_find(conn_handle);
if (conn == NULL || conn->state != BT_CONN_CONNECTED) {
LOG_ERR("[N]GattsNrpNotConn[%d]", __LINE__);
LOG_INF("[N]GattsNrpIndNotConn[%u][%u]", conn_handle, BT_CONN_STATE_GET(conn));
return;
}
@@ -902,17 +902,22 @@ int bt_le_nimble_gatt_nrp_remove(struct bt_conn *conn, uint8_t type, void *param
case GATTS_NRP_INDICATE:
assert(nrp_head->indicate.params);
/* params is the deep copy taken at insert time; cb sees the copy's
* address, not the caller's original (which may have been reused).
* On match: err propagates the mapped NimBLE status (0 on EDONE,
* else err). On handle mismatch the cb event doesn't correspond
* to this queue head — we cannot report this head's real outcome,
* so fire with BT_ATT_ERR_UNLIKELY to signal failure rather than
* silently passing the wrong op's status (could be 0/success and
* mislead the lib state machine). */
if (nrp_head->indicate.params->attr->handle !=
((struct bt_gatt_indicate_params *)params)->attr->handle) {
LOG_ERR("[N]GattNrpMismatchIndHdl[%u][%u]",
((struct bt_gatt_indicate_params *)params)->attr->handle,
nrp_head->indicate.params->attr->handle);
assert(0); /* Should not happen */
err = BT_ATT_ERR_UNLIKELY;
}
/* params is the deep copy taken at insert time; cb sees the copy's
* address, not the caller's original (which may have been reused).
* err propagates the mapped NimBLE status (0 on EDONE, else err). */
nrp_head->indicate.params->func(conn, nrp_head->indicate.params, err);
free(nrp_head->indicate.data_copy);
@@ -13,6 +13,8 @@
extern "C" {
#endif
struct net_buf;
int bt_le_nimble_iso_cmd_send_sync(uint16_t opcode,
struct net_buf *buf,
struct net_buf **rsp);
@@ -842,15 +842,20 @@ int bt_le_nimble_iso_init(void)
{
int err;
/* nimble's setters reject NULL and refuse re-registration (EALREADY), and
* expose no unregister path — so deinit cannot clear these. On a host
* re-enable the callback is still our own iso_evt_rx / bt_le_iso_rx, so
* treat EALREADY as success instead of failing the whole init. (evt setter
* returns -BLE_HS_EALREADY, pkt setter returns BLE_HS_EALREADY.) */
err = ble_hs_iso_evt_rx_cb_set(iso_evt_rx);
if (err) {
if (err && err != -BLE_HS_EALREADY) {
LOG_ERR("[N]IsoEvtRxCbSetFail[%d]", err);
return err;
}
#if CONFIG_BT_ISO_RX
err = ble_hs_iso_pkt_rx_cb_set(bt_le_iso_rx);
if (err) {
if (err && err != BLE_HS_EALREADY) {
LOG_ERR("[N]IsoPktRxCbSetFail[%d]", err);
return err;
}
@@ -869,14 +874,11 @@ int bt_le_nimble_iso_init(void)
void bt_le_nimble_iso_deinit(void)
{
LOG_DBG("IsoDeinit");
ble_hs_iso_evt_rx_cb_set(NULL);
#if CONFIG_BT_ISO_RX
ble_hs_iso_pkt_rx_cb_set(NULL);
#endif /* CONFIG_BT_ISO_RX */
LOG_DBG("[N]IsoDeinit");
/* nimble exposes no way to unregister iso_evt_rx / bt_le_iso_rx (the
* setters reject NULL); they harmlessly persist until the next init,
* which tolerates the resulting EALREADY. */
#if CONFIG_BT_ISO_UNICAST
iso_disable_cis();
#endif /* CONFIG_BT_ISO_UNICAST */
@@ -87,7 +87,11 @@ static int ots_l2cap_event_cb(struct ble_l2cap_event *event, void *arg)
ots_chan = event->connect.chan;
if (ble_l2cap_get_chan_info(event->connect.chan, &chan_info)) {
assert(0);
LOG_ERR("[N]CocGetChanInfoFail");
/* Roll back the latch so the next COC_CONNECTED isn't refused
* by the if (ots_chan) guard above. */
ots_chan = NULL;
return -EIO;
}
LOG_DBG("[N]CocConnect[%u][%04x][%04x][%04x][%u][%u][%u][%u]",
@@ -320,7 +324,7 @@ int bt_le_nimble_l2cap_init(void)
void bt_le_nimble_l2cap_deinit(void)
{
LOG_DBG("NimbleL2capDeinit");
LOG_DBG("[N]L2capDeinit");
/* TODO: free the ots_mbuf_pool and ots_mbuf_mempool */
@@ -517,8 +517,11 @@ void bt_le_gap_handle_event(uint8_t *data, size_t data_len)
void bt_le_gap_app_post_event(uint8_t type, void *param)
{
/* Currently type is not used */
#if CONFIG_BT_BLUEDROID_ENABLED
/* For Bluedroid, post the typed event to the ISO task instead. */
bt_le_bluedroid_gap_post_event(type, param);
#else
ARG_UNUSED(type);
bt_le_nimble_gap_post_event(param);
#endif
}
@@ -106,6 +106,7 @@ void bt_le_gatts_app_subscribe_event(struct bt_le_gatts_subscribe_event *param)
bt_le_gatt_app_cb_evt(&event);
}
#if !CONFIG_BT_BLUEDROID_ENABLED
void bt_le_gatt_app_post_event(uint8_t type, void *param)
{
/* Currently type is not used */
@@ -113,3 +114,24 @@ void bt_le_gatt_app_post_event(uint8_t type, void *param)
bt_le_nimble_gatt_post_event(param);
}
#endif /* !CONFIG_BT_BLUEDROID_ENABLED */
#if CONFIG_BT_BLUEDROID_ENABLED
/* Bluedroid-only: GATTC open completion carries the BTA gattc_if, which has
* no NimBLE analogue. MTU and disc-cmpl events reuse the host-agnostic
* helpers above (bt_le_gatt_app_mtu_change_event / bt_le_gattc_app_disc_cmpl_event). */
void bt_le_gattc_app_open_event(struct bt_le_gattc_open_event *param, uint8_t gattc_if)
{
struct bt_le_gatt_app_event event = {
.type = BT_LE_GATT_APP_EVENT_GATTC_OPEN,
};
LOG_DBG("GattcOpenAppEvt[%u][%02x][%u]", param->conn_handle, param->status, gattc_if);
event.gattc_open.status = param->status;
event.gattc_open.conn_handle = param->conn_handle;
event.gattc_open.gattc_if = gattc_if;
bt_le_gatt_app_cb_evt(&event);
}
#endif /* CONFIG_BT_BLUEDROID_ENABLED */
+15 -9
View File
@@ -382,11 +382,12 @@ int bt_le_acl_conn_delete(uint16_t conn_handle)
{
struct bt_conn *conn;
LOG_DBG("AclConnDel[%u]", conn_handle);
LOG_INF("AclConnDel[%u]", conn_handle);
/* disconnected_listener already flipped state to DISCONNECTED. */
conn = bt_le_acl_conn_find(conn_handle);
if (conn == NULL || conn->state != BT_CONN_CONNECTED) {
LOG_ERR("NotConn[%d]", __LINE__);
if (conn == NULL || conn->state != BT_CONN_DISCONNECTED) {
LOG_ERR("AclConnDelNotDisc[%u][%u]", conn_handle, BT_CONN_STATE_GET(conn));
return -ENOTCONN;
}
@@ -406,7 +407,7 @@ int bt_le_acl_conn_update(uint16_t conn_handle,
conn = bt_le_acl_conn_find(conn_handle);
if (conn == NULL || conn->state != BT_CONN_CONNECTED) {
LOG_ERR("NotConn[%d]", __LINE__);
LOG_ERR("AclConnUpdNotConn[%u][%u]", conn_handle, BT_CONN_STATE_GET(conn));
return -ENOTCONN;
}
@@ -543,7 +544,7 @@ int bt_le_acl_conn_connected_listener(uint16_t conn_handle)
conn = bt_le_acl_conn_find(conn_handle);
if (conn == NULL || conn->state != BT_CONN_CONNECTED) {
LOG_ERR("NotConn[%d]", __LINE__);
LOG_ERR("AclConnConnectedListenerNotConn[%u][%u]", conn_handle, BT_CONN_STATE_GET(conn));
return -ENOTCONN;
}
@@ -566,10 +567,15 @@ int bt_le_acl_conn_disconnected_listener(uint16_t conn_handle, uint8_t reason)
conn = bt_le_acl_conn_find(conn_handle);
if (conn == NULL || conn->state != BT_CONN_CONNECTED) {
LOG_ERR("NotConn[%d]", __LINE__);
LOG_ERR("AclConnDisconnectedListenerNotConn[%u][%u]", conn_handle, BT_CONN_STATE_GET(conn));
return -ENOTCONN;
}
/* Flip before dispatch — lib disconnect cbs guard late notifies on
* bt_conn_get_info().state. Otherwise BTA queues the send and the conn
* is gone by the time BTU drains it ("Unknown connection ID"). */
conn->state = BT_CONN_DISCONNECTED;
SYS_SLIST_FOR_EACH_CONTAINER(&conn_cbs, listener, _node) {
if (listener->disconnected) {
listener->disconnected(conn, reason);
@@ -593,7 +599,7 @@ int bt_le_acl_conn_security_changed_listener(uint16_t conn_handle, bt_security_t
conn = bt_le_acl_conn_find(conn_handle);
if (conn == NULL || conn->state != BT_CONN_CONNECTED) {
LOG_ERR("NotConn[%d]", __LINE__);
LOG_ERR("AclConnSecChgListenerNotConn[%u][%u]", conn_handle, BT_CONN_STATE_GET(conn));
return -ENOTCONN;
}
@@ -621,7 +627,7 @@ int bt_le_acl_conn_identity_resolved_listener(uint16_t conn_handle,
conn = bt_le_acl_conn_find(conn_handle);
if (conn == NULL || conn->state != BT_CONN_CONNECTED) {
LOG_ERR("NotConn[%d]", __LINE__);
LOG_ERR("AclConnIdResolvedListenerNotConn[%u][%u]", conn_handle, BT_CONN_STATE_GET(conn));
return -ENOTCONN;
}
@@ -644,7 +650,7 @@ int bt_le_acl_conn_pairing_completed_listener(uint16_t conn_handle, bool bonded)
conn = bt_le_acl_conn_find(conn_handle);
if (conn == NULL || conn->state != BT_CONN_CONNECTED) {
LOG_ERR("NotConn[%d]", __LINE__);
LOG_ERR("AclConnPairingCompletedListenerNotConn[%u][%u]", conn_handle, BT_CONN_STATE_GET(conn));
return -ENOTCONN;
}
+69 -24
View File
@@ -100,7 +100,11 @@ uint16_t bt_gatt_get_mtu(struct bt_conn *conn)
assert(conn);
#if CONFIG_BT_BLUEDROID_ENABLED
mtu = bt_le_bluedroid_gatt_get_mtu(conn);
#else
mtu = bt_le_nimble_gatt_get_mtu(conn);
#endif
LOG_DBG("GattGetMtu[%u]", mtu);
@@ -141,13 +145,12 @@ uint16_t bt_gatt_attr_get_handle(const struct bt_gatt_attr *attr)
{
uint16_t handle = 0;
LOG_DBG("GattAttrGetHdl");
if (attr && attr->handle) {
handle = attr->handle;
}
LOG_DBG("Hdl[%u]", handle);
LOG_DBG("GattAttrGetHdl[%u]", handle);
return handle;
}
@@ -162,32 +165,29 @@ uint16_t bt_gatt_attr_value_handle(const struct bt_gatt_attr *attr)
* Currently this function is only used by TMAP.
*/
LOG_DBG("GattAttrValHdl");
if (attr) {
LOG_DBG("GattAttrUuid[%s]", attr->uuid ? bt_uuid_str(attr->uuid) : "Null");
if (attr->uuid == NULL) {
handle = (attr->handle + 1);
} else {
LOG_DBG("Uuid[%s]", bt_uuid_str(attr->uuid));
if (bt_uuid_cmp(attr->uuid, BT_UUID_GATT_CHRC) == 0) {
struct bt_gatt_chrc *chrc = attr->user_data;
assert(chrc);
handle = chrc->value_handle;
LOG_DBG("ValHdl[%u]", handle);
if (handle == 0) {
/* Fall back to Zephyr value handle policy */
handle = bt_gatt_attr_get_handle(attr) + 1;
LOG_INF("ValHdlNew[%u]", handle);
LOG_INF("GattAttrValHdlNew[%u]", handle);
}
}
}
}
LOG_DBG("Hdl[%u]", handle);
LOG_DBG("GattAttrValHdl[%u]", handle);
return handle;
}
@@ -401,8 +401,13 @@ ssize_t bt_gatt_attr_read(struct bt_conn *conn, const struct bt_gatt_attr *attr,
return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET);
}
#if CONFIG_BT_BLUEDROID_ENABLED
return bt_le_bluedroid_gatts_attr_read(conn, attr, buf, buf_len,
offset, value, value_len);
#else
return bt_le_nimble_gatts_attr_read(conn, attr, buf, buf_len,
offset, value, value_len);
#endif
}
_LIB_ONLY
@@ -415,11 +420,15 @@ int bt_gatt_notify_cb(struct bt_conn *conn, struct bt_gatt_notify_params *params
LOG_DBG("GattNtfCb[%u]", params->len);
if (conn && conn->state != BT_CONN_CONNECTED) {
LOG_ERR("NotConn[%d]", __LINE__);
LOG_ERR("GattNtfNotConn[%u][%u]", conn->handle, conn->state);
return -ENOTCONN;
}
#if CONFIG_BT_BLUEDROID_ENABLED
err = bt_le_bluedroid_gatts_notify(conn, params);
#else
err = bt_le_nimble_gatts_notify(conn, params);
#endif
/* gatts_notify is synchronous (mbuf-copy + dispatch on return); fire the
* caller's completion cb here so state machines like PACS_FLAG_NOTIFY_RDY
@@ -440,7 +449,7 @@ int bt_gatt_indicate(struct bt_conn *conn, struct bt_gatt_indicate_params *param
LOG_DBG("GattInd[%s]", bt_uuid_str(params->attr->uuid));
if (conn && conn->state != BT_CONN_CONNECTED) {
LOG_ERR("NotConn[%d]", __LINE__);
LOG_ERR("GattIndNotConn[%u][%u]", conn->handle, conn->state);
return -ENOTCONN;
}
@@ -454,7 +463,11 @@ int bt_gatt_indicate(struct bt_conn *conn, struct bt_gatt_indicate_params *param
return -ENOTSUP;
}
#if CONFIG_BT_BLUEDROID_ENABLED
return bt_le_bluedroid_gatts_indicate(conn, params);
#else
return bt_le_nimble_gatts_indicate(conn, params);
#endif
}
_LIB_IDF
@@ -774,7 +787,8 @@ bool bt_gatt_is_subscribed(struct bt_conn *conn,
LOG_DBG("GattIsSub[%04x][%s]", ccc_type, bt_uuid_str(attr->uuid));
if (conn->state != BT_CONN_CONNECTED) {
LOG_ERR("NotConn[%d]", __LINE__);
/* Query — disconnected conn is "not subscribed", not an error. */
LOG_INF("GattIsSubNotConn[%u][%u]", conn->handle, conn->state);
return false;
}
@@ -952,7 +966,7 @@ int bt_gatts_sub_changed(uint16_t conn_handle,
conn = bt_le_acl_conn_find(conn_handle);
if (conn == NULL || conn->state != BT_CONN_CONNECTED) {
LOG_ERR("NotConn[%d]", __LINE__);
LOG_ERR("GattsSubChgNotConn[%u][%u]", conn_handle, BT_CONN_STATE_GET(conn));
return -ENOTCONN;
}
@@ -975,7 +989,11 @@ int bt_gattc_disc_start_safe(uint16_t conn_handle)
int err;
LOG_DBG("GattcDiscStart[%u]", conn_handle);
bt_le_host_lock();
#if CONFIG_BT_BLUEDROID_ENABLED
err = bt_le_bluedroid_gattc_disc_start(conn_handle);
#else
err = bt_le_nimble_gattc_disc_start(conn_handle);
#endif
bt_le_host_unlock();
return err;
}
@@ -991,7 +1009,7 @@ int bt_gatt_discover(struct bt_conn *conn, struct bt_gatt_discover_params *param
LOG_DBG("GattDisc[%u][%u]", params->start_handle, params->end_handle);
if (conn->state != BT_CONN_CONNECTED) {
LOG_ERR("NotConn[%d]", __LINE__);
LOG_ERR("GattDiscNotConn[%u][%u]", conn->handle, conn->state);
return -ENOTCONN;
}
@@ -1000,7 +1018,11 @@ int bt_gatt_discover(struct bt_conn *conn, struct bt_gatt_discover_params *param
return -ENOTSUP;
}
#if CONFIG_BT_BLUEDROID_ENABLED
return bt_le_bluedroid_gattc_discover(conn, params);
#else
return bt_le_nimble_gattc_discover(conn, params);
#endif
}
static struct gattc_sub *gattc_sub_find(struct bt_conn *conn)
@@ -1141,7 +1163,11 @@ static int gattc_write_ccc(struct bt_conn *conn, struct bt_gatt_subscribe_params
{
LOG_DBG("GattcWrCcc[%u][%04x]", params->ccc_handle, params->value);
#if CONFIG_BT_BLUEDROID_ENABLED
return bt_le_bluedroid_gattc_write_ccc(conn, params);
#else
return bt_le_nimble_gattc_write_ccc(conn, params);
#endif
}
_LIB_ONLY
@@ -1167,7 +1193,7 @@ int bt_gatt_subscribe(struct bt_conn *conn, struct bt_gatt_subscribe_params *par
* can resubscribe.
*/
if (conn->state != BT_CONN_CONNECTED) {
LOG_ERR("NotConn[%d]", __LINE__);
LOG_ERR("GattSubNotConn[%u][%u]", conn->handle, conn->state);
params->value_handle = 0; /* unlinked: clear retry guard */
return -ENOTCONN;
}
@@ -1251,7 +1277,7 @@ int bt_gatt_unsubscribe(struct bt_conn *conn, struct bt_gatt_subscribe_params *p
params->value, params->value_handle, params->ccc_handle, params->end_handle);
if (conn->state != BT_CONN_CONNECTED) {
LOG_ERR("NotConn[%d]", __LINE__);
LOG_ERR("GattUnsubNotConn[%u][%u]", conn->handle, conn->state);
return -ENOTCONN;
}
@@ -1327,7 +1353,7 @@ int bt_gatt_read(struct bt_conn *conn, struct bt_gatt_read_params *params)
LOG_DBG("GattRd[%u]", params->handle_count);
if (conn->state != BT_CONN_CONNECTED) {
LOG_ERR("NotConn[%d]", __LINE__);
LOG_ERR("GattRdNotConn[%u][%u]", conn->handle, conn->state);
return -ENOTCONN;
}
@@ -1345,7 +1371,11 @@ int bt_gatt_read(struct bt_conn *conn, struct bt_gatt_read_params *params)
}
}
#if CONFIG_BT_BLUEDROID_ENABLED
return bt_le_bluedroid_gattc_read(conn, params);
#else
return bt_le_nimble_gattc_read(conn, params);
#endif
}
_LIB_IDF
@@ -1360,7 +1390,7 @@ int bt_gatt_write(struct bt_conn *conn, struct bt_gatt_write_params *params)
LOG_DBG("GattWr[%u][%u][%u]", params->handle, params->length, params->offset);
if (conn->state != BT_CONN_CONNECTED) {
LOG_ERR("NotConn[%d]", __LINE__);
LOG_ERR("GattWrNotConn[%u][%u]", conn->handle, conn->state);
return -ENOTCONN;
}
@@ -1369,7 +1399,11 @@ int bt_gatt_write(struct bt_conn *conn, struct bt_gatt_write_params *params)
return -ENOTSUP;
}
#if CONFIG_BT_BLUEDROID_ENABLED
return bt_le_bluedroid_gattc_write(conn, params);
#else
return bt_le_nimble_gattc_write(conn, params);
#endif
}
_LIB_ONLY
@@ -1388,7 +1422,7 @@ int bt_gatt_write_without_response_cb(struct bt_conn *conn, uint16_t handle,
LOG_DBG("GattWrCmd[%u][%u][%u]", handle, length, sign);
if (conn->state != BT_CONN_CONNECTED) {
LOG_ERR("NotConn[%d]", __LINE__);
LOG_ERR("GattWrCmdNotConn[%u][%u]", conn->handle, conn->state);
return -ENOTCONN;
}
@@ -1397,7 +1431,11 @@ int bt_gatt_write_without_response_cb(struct bt_conn *conn, uint16_t handle,
return -ENOTSUP;
}
#if CONFIG_BT_BLUEDROID_ENABLED
return bt_le_bluedroid_gattc_write_without_rsp(conn, handle, data, length);
#else
return bt_le_nimble_gattc_write_without_rsp(conn, handle, data, length);
#endif
}
_LIB_ONLY
@@ -1407,6 +1445,7 @@ void bt_le_acl_conn_disconnected_gatt_listener(uint16_t conn_handle)
LOG_DBG("AclConnDisconnectedGattListener[%u]", conn_handle);
#if !CONFIG_BT_BLUEDROID_ENABLED
bt_le_nimble_gatt_nrp_clear(conn_handle);
/* Drop the cached GATT client database so the next connection runs a
@@ -1420,10 +1459,12 @@ void bt_le_acl_conn_disconnected_gatt_listener(uint16_t conn_handle)
* and Service Changed handling end-to-end.
*/
bt_le_nimble_gattc_db_remove(conn_handle);
#endif
/* The cached DB is dropped above, so the app will rediscover and call
* bt_gatt_subscribe() again on reconnect with the BAP library's reused
* `bt_gatt_subscribe_params` pointers. Drop those nodes from the
/* On NimBLE the cached DB was dropped above; on Bluedroid no client-side
* DB cache cleanup is performed here. Either way the app will rediscover
* and call bt_gatt_subscribe() again on reconnect with the BAP library's
* reused `bt_gatt_subscribe_params` pointers. Drop those nodes from the
* tracking list (and zero their value) so the duplicate check in
* bt_gatt_subscribe() doesn't short-circuit with -EALREADY, which
* would otherwise skip the CCCD write and starve notifications.
@@ -1438,5 +1479,9 @@ void bt_le_gatt_handle_event(uint8_t *data, size_t data_len)
{
assert(data && data_len);
#if CONFIG_BT_BLUEDROID_ENABLED
bt_le_bluedroid_gatt_handle_event(data, data_len);
#else
bt_le_nimble_gatt_handle_event(data, data_len);
#endif
}
@@ -70,5 +70,9 @@ int bt_hci_cmd_send_sync(uint16_t opcode,
{
LOG_DBG("HciCmdSendSync[%04x]", opcode);
#if CONFIG_BT_BLUEDROID_ENABLED
return bt_le_bluedroid_hci_iso_cmd_send_sync(opcode, buf, rsp);
#else
return bt_le_nimble_iso_cmd_send_sync(opcode, buf, rsp);
#endif
}
+27 -5
View File
@@ -17,12 +17,15 @@
#include "common/app/gap.h"
#include "common/app/gatt.h"
#if CONFIG_BT_BLUEDROID_ENABLED
#include "bluedroid/gap.h"
#include "bluedroid/gatt.h"
#endif
LOG_MODULE_REGISTER(ISO_HOST, CONFIG_BT_ISO_LOG_LEVEL);
static struct k_mutex host_mutex;
#define TIMEOUT_MS (5000 / portTICK_PERIOD_MS) /* 5s */
#if HOST_LOCK_DEBUG
void bt_le_host_lock_debug(const char *func, int line)
#else /* HOST_LOCK_DEBUG */
@@ -31,9 +34,9 @@ void bt_le_host_lock(void)
{
/* LOG_DBG("%s: %d", func, line); */
int err = k_mutex_lock(&host_mutex, TIMEOUT_MS);
int err = k_mutex_lock(&host_mutex, K_MUTEX_SHORT);
if (err) {
/* 5s wait failed: the host stack is wedged. k_mutex_lock has
/* K_MUTEX_SHORT wait failed: the host stack is wedged. k_mutex_lock has
* already logged self/holder task names. Use libc abort() rather
* than assert(0) — assert is a no-op under NDEBUG, which would
* let the caller enter the critical section without the mutex
@@ -84,11 +87,23 @@ int bt_le_host_init(void)
}
#endif /* CONFIG_BT_OTS || CONFIG_BT_OTS_CLIENT */
err = bt_le_iso_init();
#if CONFIG_BT_BLUEDROID_ENABLED
err = bt_le_bluedroid_gap_init();
if (err) {
goto deinit_l2cap;
}
err = bt_le_bluedroid_gatt_init();
if (err) {
goto deinit_bluedroid_gatt;
}
#endif /* CONFIG_BT_BLUEDROID_ENABLED */
err = bt_le_iso_init();
if (err) {
goto deinit_bluedroid_gatt;
}
err = bt_le_iso_task_init();
if (err) {
goto deinit_iso;
@@ -98,7 +113,11 @@ int bt_le_host_init(void)
deinit_iso:
bt_le_iso_deinit();
deinit_bluedroid_gatt:
#if CONFIG_BT_BLUEDROID_ENABLED
bt_le_bluedroid_gatt_deinit();
deinit_l2cap:
#endif /* CONFIG_BT_BLUEDROID_ENABLED */
#if CONFIG_BT_OTS || CONFIG_BT_OTS_CLIENT
bt_le_l2cap_deinit();
deinit_scan: /* only reachable when OTS path is compiled in */
@@ -116,6 +135,9 @@ void bt_le_host_deinit(void)
bt_le_iso_task_deinit();
bt_le_iso_deinit();
#if CONFIG_BT_BLUEDROID_ENABLED
bt_le_bluedroid_gatt_deinit();
#endif /* CONFIG_BT_BLUEDROID_ENABLED */
#if CONFIG_BT_OTS || CONFIG_BT_OTS_CLIENT
bt_le_l2cap_deinit();
#endif /* CONFIG_BT_OTS || CONFIG_BT_OTS_CLIENT */
@@ -11,7 +11,11 @@
#include "sdkconfig.h"
#if CONFIG_BT_BLUEDROID_ENABLED
#include "bluedroid/gap.h"
#else
#include "nimble/gap.h"
#endif
#ifdef __cplusplus
extern "C" {
@@ -11,7 +11,11 @@
#include "sdkconfig.h"
#if CONFIG_BT_BLUEDROID_ENABLED
/* ref bluedroid/app/gatt.h was an empty header; nothing to include here. */
#else
#include "nimble/gatt.h"
#endif
#ifdef __cplusplus
extern "C" {
@@ -37,6 +41,14 @@ struct bt_le_gatt_app_event_gatts_subscribe {
uint8_t reason;
};
/* Bluedroid-only: surfaces the GATTC open completion (carries the gattc_if
* the BTA dispatcher used, which NimBLE has no analogue of). */
struct bt_le_gatt_app_event_gattc_open {
uint8_t status;
uint16_t conn_handle;
uint8_t gattc_if;
};
struct bt_le_gatt_app_event {
uint8_t type;
@@ -44,6 +56,7 @@ struct bt_le_gatt_app_event {
struct bt_le_gatt_app_event_gatt_mtu_change gatt_mtu_change;
struct bt_le_gatt_app_event_gattc_disc_cmpl gattc_disc_cmpl;
struct bt_le_gatt_app_event_gatts_subscribe gatts_subscribe;
struct bt_le_gatt_app_event_gattc_open gattc_open;
};
};
@@ -51,6 +64,7 @@ enum bt_le_gatt_app_event_type {
BT_LE_GATT_APP_EVENT_GATT_MTU_CHANGE,
BT_LE_GATT_APP_EVENT_GATTC_DISC_CMPL,
BT_LE_GATT_APP_EVENT_GATTS_SUBSCRIBE,
BT_LE_GATT_APP_EVENT_GATTC_OPEN,
BT_LE_GATT_APP_EVENT_MAX,
};
@@ -67,7 +81,18 @@ void bt_le_gattc_app_disc_cmpl_event(struct bt_le_gattc_disc_cmpl_event *param);
void bt_le_gatts_app_subscribe_event(struct bt_le_gatts_subscribe_event *param);
#if CONFIG_BT_BLUEDROID_ENABLED
void bt_le_gattc_app_open_event(struct bt_le_gattc_open_event *param, uint8_t gattc_if);
#endif /* CONFIG_BT_BLUEDROID_ENABLED */
#if !CONFIG_BT_BLUEDROID_ENABLED
/* NimBLE-only: callers post the raw ble_gap_event so the adapter can
* translate it into the host-agnostic bt_le_gatt_app_event. Bluedroid has
* no analogous app-level event stream (BTA dispatches directly to BTC),
* so this is hidden from the API surface to make misuse a compile error.
*/
void bt_le_gatt_app_post_event(uint8_t type, void *param);
#endif /* !CONFIG_BT_BLUEDROID_ENABLED */
#ifdef __cplusplus
}
@@ -13,13 +13,21 @@
#include "sdkconfig.h"
#if CONFIG_BT_BLUEDROID_ENABLED
/* TODO */
#else
#include "nimble/gap.h"
#include "nimble/iso.h"
#endif
#ifdef __cplusplus
extern "C" {
#endif
/* 0xff is out of bt_conn_state_t range — distinguishes NULL conn from any
* real state in logs. */
#define BT_CONN_STATE_GET(_c) ((_c) ? (_c)->state : 0xff)
void bt_conn_get_acl_conns(struct bt_conn **conns, uint8_t *count);
struct bt_conn *bt_le_acl_conn_find(uint16_t conn_handle);
@@ -14,7 +14,11 @@
#include <zephyr/autoconf.h>
#include <zephyr/bluetooth/gatt.h>
#if CONFIG_BT_BLUEDROID_ENABLED
#include "bluedroid/gatt.h"
#else
#include "nimble/gatt.h"
#endif
#ifdef __cplusplus
extern "C" {
@@ -73,16 +77,115 @@ struct bt_le_gatts_notify_tx_event {
uint8_t status;
};
/* Bluedroid-side adapter events. NimBLE produces ACL connect/disconnect via
* the GAP event path (BT_LE_GAP_APP_PARAM_ACL_*), but Bluedroid's BTA
* surfaces these through GATTS/GATTC callbacks. The bluedroid adapter posts
* these typed events into the iso task and translates them into ACL/MTU/
* discovery events for the LE Audio profile dispatcher. */
struct bt_le_addr_simple {
uint8_t type;
uint8_t val[6];
};
struct bt_le_gattc_connect_event {
uint16_t conn_handle;
uint8_t role;
struct bt_le_addr_simple peer;
};
struct bt_le_gattc_disconnect_event {
uint16_t conn_handle;
uint8_t reason;
};
struct bt_le_gattc_open_event {
uint8_t status;
uint16_t conn_handle;
};
struct bt_le_gattc_mtu_event {
uint8_t status;
uint16_t conn_handle;
uint16_t mtu;
};
struct bt_le_gattc_read_chrc_event {
uint8_t status;
uint16_t conn_handle;
uint16_t attr_handle;
uint16_t len;
uint8_t *value;
};
struct bt_le_gattc_write_chrc_event {
uint8_t status;
uint16_t conn_handle;
uint16_t attr_handle;
uint16_t offset;
};
struct bt_le_gatts_connect_event {
uint16_t conn_handle;
uint8_t role;
struct bt_le_addr_simple peer;
};
struct bt_le_gatts_disconnect_event {
uint16_t conn_handle;
uint8_t reason;
};
struct bt_le_gatts_mtu_event {
uint16_t conn_handle;
uint16_t mtu;
};
struct bt_le_gatts_read_event {
uint16_t conn_handle;
uint32_t trans_id;
uint8_t peer[6];
uint16_t attr_handle;
uint16_t offset;
bool is_long;
bool need_rsp;
};
struct bt_le_gatts_write_event {
uint16_t conn_handle;
uint32_t trans_id;
uint8_t peer[6];
uint16_t attr_handle;
uint16_t offset;
bool is_prep;
bool need_rsp;
uint16_t len;
uint8_t *value;
};
struct bt_le_gatt_event_param {
uint8_t type;
union {
struct bt_le_gatt_mtu_change_event gatt_mtu_change;
struct bt_le_gattc_discover_event gattc_discover;
struct bt_le_gattc_disc_cmpl_event gattc_disc_cmpl;
struct bt_le_gatts_subscribe_event gatts_subscribe;
struct bt_le_gattc_notify_rx_event gattc_notify_rx;
struct bt_le_gatts_notify_tx_event gatts_notify_tx;
struct bt_le_gatt_mtu_change_event gatt_mtu_change;
struct bt_le_gattc_discover_event gattc_discover;
struct bt_le_gattc_disc_cmpl_event gattc_disc_cmpl;
struct bt_le_gatts_subscribe_event gatts_subscribe;
struct bt_le_gattc_notify_rx_event gattc_notify_rx;
struct bt_le_gatts_notify_tx_event gatts_notify_tx;
/* Bluedroid-only paths (see comment above). */
struct bt_le_gattc_connect_event gattc_connect;
struct bt_le_gattc_disconnect_event gattc_disconnect;
struct bt_le_gattc_open_event gattc_open;
struct bt_le_gattc_mtu_event gattc_mtu;
struct bt_le_gattc_read_chrc_event gattc_read_chrc;
struct bt_le_gattc_write_chrc_event gattc_write_chrc;
struct bt_le_gatts_connect_event gatts_connect;
struct bt_le_gatts_disconnect_event gatts_disconnect;
struct bt_le_gatts_mtu_event gatts_mtu;
struct bt_le_gatts_read_event gatts_read;
struct bt_le_gatts_write_event gatts_write;
};
};
@@ -94,231 +197,22 @@ enum {
BT_LE_GATTC_NOTIFY_RX_EVENT,
BT_LE_GATTS_NOTIFY_TX_EVENT,
/* Bluedroid-only paths (see comment above). */
BT_LE_GATTC_CONNECT_EVENT,
BT_LE_GATTC_DISCONNECT_EVENT,
BT_LE_GATTC_OPEN_EVENT,
BT_LE_GATTC_MTU_EVENT,
BT_LE_GATTC_READ_CHRC_EVENT,
BT_LE_GATTC_WRITE_CHRC_EVENT,
BT_LE_GATTS_CONNECT_EVENT,
BT_LE_GATTS_DISCONNECT_EVENT,
BT_LE_GATTS_MTU_EVENT,
BT_LE_GATTS_READ_EVENT,
BT_LE_GATTS_WRITE_EVENT,
BT_LE_GATT_EVENT_MAX,
};
static inline char *audio_svc_uuid_to_str(uint16_t uuid)
{
switch (uuid) {
case BT_UUID_GAP_VAL:
return "GAP";
case BT_UUID_GATT_VAL:
return "GATT";
case BT_UUID_AICS_VAL:
return "AICS";
case BT_UUID_CAS_VAL:
return "CAS";
case BT_UUID_VCS_VAL:
return "VCS";
case BT_UUID_VOCS_VAL:
return "VOCS";
case BT_UUID_CSIS_VAL:
return "CSIS";
case BT_UUID_MCS_VAL:
return "MCS";
case BT_UUID_GMCS_VAL:
return "GMCS";
case BT_UUID_TBS_VAL:
return "TBS";
case BT_UUID_GTBS_VAL:
return "GTBS";
case BT_UUID_MICS_VAL:
return "MICS";
case BT_UUID_ASCS_VAL:
return "ASCS";
case BT_UUID_BASS_VAL:
return "BASS";
case BT_UUID_PACS_VAL:
return "PACS";
case BT_UUID_BASIC_AUDIO_VAL:
return "BASIC_AUDIO";
case BT_UUID_HAS_VAL:
return "HAS";
case BT_UUID_TMAS_VAL:
return "TMAS";
case BT_UUID_PBA_VAL:
return "PBA";
case BT_UUID_GMAS_VAL:
return "GMAS";
case BT_UUID_OTS_VAL:
return "OTS";
default:
return "Unknown";
}
}
static inline char *audio_chrc_uuid_to_str(uint16_t uuid)
{
switch (uuid) {
case BT_UUID_OTS_FEATURE_VAL:
return "OTS_FEATURE";
case BT_UUID_OTS_NAME_VAL:
return "OTS_NAME";
case BT_UUID_OTS_TYPE_VAL:
return "OTS_TYPE";
case BT_UUID_OTS_SIZE_VAL:
return "OTS_SIZE";
case BT_UUID_OTS_FIRST_CREATED_VAL:
return "OTS_FIRST_CREATED";
case BT_UUID_OTS_LAST_MODIFIED_VAL:
return "OTS_LAST_MODIFIED";
case BT_UUID_OTS_ID_VAL:
return "OTS_ID";
case BT_UUID_OTS_PROPERTIES_VAL:
return "OTS_PROPERTIES";
case BT_UUID_OTS_ACTION_CP_VAL:
return "OTS_ACTION_CP";
case BT_UUID_OTS_LIST_CP_VAL:
return "OTS_LIST_CP";
case BT_UUID_OTS_LIST_FILTER_VAL:
return "OTS_LIST_FILTER";
case BT_UUID_OTS_CHANGED_VAL:
return "OTS_CHANGED";
case BT_UUID_GATT_TMAPR_VAL:
return "TMAP_ROLE";
case BT_UUID_AICS_STATE_VAL:
return "AICS_STATE";
case BT_UUID_AICS_GAIN_SETTINGS_VAL:
return "AICS_GAIN_SETTINGS";
case BT_UUID_AICS_INPUT_TYPE_VAL:
return "AICS_INPUT_TYPE";
case BT_UUID_AICS_INPUT_STATUS_VAL:
return "AICS_INPUT_STATUS";
case BT_UUID_AICS_CONTROL_VAL:
return "AICS_CONTROL";
case BT_UUID_AICS_DESCRIPTION_VAL:
return "AICS_DESCRIPTION";
case BT_UUID_VCS_STATE_VAL:
return "VCS_STATE";
case BT_UUID_VCS_CONTROL_VAL:
return "VCS_CONTROL";
case BT_UUID_VCS_FLAGS_VAL:
return "VCS_FLAGS";
case BT_UUID_VOCS_STATE_VAL:
return "VOCS_STATE";
case BT_UUID_VOCS_LOCATION_VAL:
return "VOCS_LOCATION";
case BT_UUID_VOCS_CONTROL_VAL:
return "VOCS_CONTROL";
case BT_UUID_VOCS_DESCRIPTION_VAL:
return "VOCS_DESCRIPTION";
case BT_UUID_CSIS_SIRK_VAL:
return "CSIS_SIRK";
case BT_UUID_CSIS_SET_SIZE_VAL:
return "CSIS_SET_SIZE";
case BT_UUID_CSIS_SET_LOCK_VAL:
return "CSIS_SET_LOCK";
case BT_UUID_CSIS_RANK_VAL:
return "CSIS_RANK";
case BT_UUID_MCS_PLAYER_NAME_VAL:
return "MCS_PLAYER_NAME";
case BT_UUID_MCS_ICON_OBJ_ID_VAL:
return "MCS_ICON_OBJ_ID";
case BT_UUID_MCS_ICON_URL_VAL:
return "MCS_ICON_URL";
case BT_UUID_MCS_TRACK_CHANGED_VAL:
return "MCS_TRACK_CHANGED";
case BT_UUID_MCS_TRACK_TITLE_VAL:
return "MCS_TRACK_TITLE";
case BT_UUID_MCS_TRACK_DURATION_VAL:
return "MCS_TRACK_DURATION";
case BT_UUID_MCS_TRACK_POSITION_VAL:
return "MCS_TRACK_POSITION";
case BT_UUID_MCS_PLAYBACK_SPEED_VAL:
return "MCS_PLAYBACK_SPEED";
case BT_UUID_MCS_SEEKING_SPEED_VAL:
return "MCS_SEEKING_SPEED";
case BT_UUID_MCS_TRACK_SEGMENTS_OBJ_ID_VAL:
return "MCS_TRACK_SEGMENTS_OBJ_ID";
case BT_UUID_MCS_CURRENT_TRACK_OBJ_ID_VAL:
return "MCS_CURRENT_TRACK_OBJ_ID";
case BT_UUID_MCS_NEXT_TRACK_OBJ_ID_VAL:
return "MCS_NEXT_TRACK_OBJ_ID";
case BT_UUID_MCS_PARENT_GROUP_OBJ_ID_VAL:
return "MCS_PARENT_GROUP_OBJ_ID";
case BT_UUID_MCS_CURRENT_GROUP_OBJ_ID_VAL:
return "MCS_CURRENT_GROUP_OBJ_ID";
case BT_UUID_MCS_PLAYING_ORDER_VAL:
return "MCS_PLAYING_ORDER";
case BT_UUID_MCS_PLAYING_ORDERS_VAL:
return "MCS_PLAYING_ORDERS";
case BT_UUID_MCS_MEDIA_STATE_VAL:
return "MCS_MEDIA_STATE";
case BT_UUID_MCS_MEDIA_CONTROL_POINT_VAL:
return "MCS_MEDIA_CONTROL_POINT";
case BT_UUID_MCS_MEDIA_CONTROL_OPCODES_VAL:
return "MCS_MEDIA_CONTROL_OPCODES";
case BT_UUID_MCS_SEARCH_RESULTS_OBJ_ID_VAL:
return "MCS_SEARCH_RESULTS_OBJ_ID";
case BT_UUID_MCS_SEARCH_CONTROL_POINT_VAL:
return "MCS_SEARCH_CONTROL_POINT";
case BT_UUID_TBS_PROVIDER_NAME_VAL:
return "TBS_PROVIDER_NAME";
case BT_UUID_TBS_UCI_VAL:
return "TBS_UCI";
case BT_UUID_TBS_TECHNOLOGY_VAL:
return "TBS_TECHNOLOGY";
case BT_UUID_TBS_URI_LIST_VAL:
return "TBS_URI_LIST";
case BT_UUID_TBS_SIGNAL_STRENGTH_VAL:
return "TBS_SIGNAL_STRENGTH";
case BT_UUID_TBS_SIGNAL_INTERVAL_VAL:
return "TBS_SIGNAL_INTERVAL";
case BT_UUID_TBS_LIST_CURRENT_CALLS_VAL:
return "TBS_LIST_CURRENT_CALLS";
case BT_UUID_CCID_VAL:
return "CCID";
case BT_UUID_TBS_STATUS_FLAGS_VAL:
return "TBS_STATUS_FLAGS";
case BT_UUID_TBS_INCOMING_URI_VAL:
return "TBS_INCOMING_URI";
case BT_UUID_TBS_CALL_STATE_VAL:
return "TBS_CALL_STATE";
case BT_UUID_TBS_CALL_CONTROL_POINT_VAL:
return "TBS_CALL_CONTROL_POINT";
case BT_UUID_TBS_OPTIONAL_OPCODES_VAL:
return "TBS_OPTIONAL_OPCODES";
case BT_UUID_TBS_TERMINATE_REASON_VAL:
return "TBS_TERMINATE_REASON";
case BT_UUID_TBS_INCOMING_CALL_VAL:
return "TBS_INCOMING_CALL";
case BT_UUID_TBS_FRIENDLY_NAME_VAL:
return "TBS_FRIENDLY_NAME";
case BT_UUID_MICS_MUTE_VAL:
return "MICS_MUTE";
case BT_UUID_ASCS_ASE_SNK_VAL:
return "ASCS_ASE_SNK";
case BT_UUID_ASCS_ASE_SRC_VAL:
return "ASCS_ASE_SRC";
case BT_UUID_ASCS_ASE_CP_VAL:
return "ASCS_ASE_CP";
case BT_UUID_BASS_CONTROL_POINT_VAL:
return "BASS_CP";
case BT_UUID_BASS_RECV_STATE_VAL:
return "BASS_RECV_STATE";
case BT_UUID_PACS_SNK_VAL:
return "PACS_SNK";
case BT_UUID_PACS_SNK_LOC_VAL:
return "PACS_SNK_LOC";
case BT_UUID_PACS_SRC_VAL:
return "PACS_SRC";
case BT_UUID_PACS_SRC_LOC_VAL:
return "PACS_SRC_LOC";
case BT_UUID_PACS_AVAILABLE_CONTEXT_VAL:
return "PACS_AVAILABLE_CONTEXT";
case BT_UUID_PACS_SUPPORTED_CONTEXT_VAL:
return "PACS_SUPPORTED_CONTEXT";
case BT_UUID_HAS_HEARING_AID_FEATURES_VAL:
return "HAS_HEARING_AID_FEATURES";
case BT_UUID_HAS_PRESET_CONTROL_POINT_VAL:
return "HAS_PRESET_CONTROL_POINT";
case BT_UUID_HAS_ACTIVE_PRESET_INDEX_VAL:
return "HAS_ACTIVE_PRESET_INDEX";
default:
return "Unknown";
}
}
struct gattc_sub {
uint8_t id;
bt_addr_le_t peer;
@@ -13,12 +13,18 @@
#include "sdkconfig.h"
#if CONFIG_BT_BLUEDROID_ENABLED
#include "bluedroid/iso.h"
#else
#include "nimble/iso.h"
#endif
#ifdef __cplusplus
extern "C" {
#endif
struct net_buf;
struct bt_le_iso_cb {
void (*cis_dis)(struct net_buf *buf);
void (*cis_est)(struct net_buf *buf);
@@ -13,7 +13,11 @@
#include "sdkconfig.h"
#if CONFIG_BT_BLUEDROID_ENABLED
/* TODO */
#else
#include "nimble/l2cap.h"
#endif
#ifdef __cplusplus
extern "C" {
@@ -13,7 +13,11 @@
#include "sdkconfig.h"
#if CONFIG_BT_BLUEDROID_ENABLED
#include "bluedroid/gap.h"
#else
#include "nimble/gap.h"
#endif
#ifdef __cplusplus
extern "C" {
@@ -20,18 +20,32 @@
extern "C" {
#endif
#if CONFIG_BT_BLUEDROID_ENABLED
#if CONFIG_BT_BLUEDROID_PINNED_TO_CORE
#define ISO_TASK_CORE CONFIG_BT_BLUEDROID_PINNED_TO_CORE
#else /* CONFIG_BT_BLUEDROID_PINNED_TO_CORE */
#define ISO_TASK_CORE (0)
#endif /* CONFIG_BT_BLUEDROID_PINNED_TO_CORE */
#else /* CONFIG_BT_BLUEDROID_ENABLED */
#if CONFIG_BT_NIMBLE_PINNED_TO_CORE
#define ISO_TASK_CORE CONFIG_BT_NIMBLE_PINNED_TO_CORE
#else /* CONFIG_BT_NIMBLE_PINNED_TO_CORE */
#define ISO_TASK_CORE (0)
#endif /* CONFIG_BT_NIMBLE_PINNED_TO_CORE */
#endif /* CONFIG_BT_BLUEDROID_ENABLED */
#define ISO_TASK_STACK_SIZE 4096
#define ISO_TASK_NAME "iso_task"
/* Ref:
* - Bluedroid BTC task: configMAX_PRIORITIES - 6
* - Bluedroid BTU task: configMAX_PRIORITIES - 5
* - NimBLE Host task: configMAX_PRIORITIES - 4
*/
#if CONFIG_BT_BLUEDROID_ENABLED
#define ISO_TASK_PRIO (configMAX_PRIORITIES - 5)
#else
#define ISO_TASK_PRIO (configMAX_PRIORITIES - 4)
#endif
enum iso_queue_item_type {
ISO_QUEUE_ITEM_TYPE_TIMER_EVENT,
@@ -25,11 +25,19 @@
LOG_MODULE_REGISTER(ISO_SHIM, CONFIG_BT_ISO_LOG_LEVEL);
#if CONFIG_BT_BLUEDROID_ENABLED
#ifdef CONFIG_BT_BLE_ISO_STD_FLOW_CTRL
#define ISO_STD_FLOW_CTRL true
#else /* CONFIG_BT_BLE_ISO_STD_FLOW_CTRL */
#define ISO_STD_FLOW_CTRL false
#endif /* CONFIG_BT_BLE_ISO_STD_FLOW_CTRL */
#else /* CONFIG_BT_BLUEDROID_ENABLED */
#ifdef CONFIG_BT_NIMBLE_ISO_STD_FLOW_CTRL
#define ISO_STD_FLOW_CTRL true
#else /* CONFIG_BT_NIMBLE_ISO_STD_FLOW_CTRL */
#define ISO_STD_FLOW_CTRL false
#endif /* CONFIG_BT_NIMBLE_ISO_STD_FLOW_CTRL */
#endif /* CONFIG_BT_BLUEDROID_ENABLED */
#define ISO_PKT_FIRST_FRAG (0b00)
#define ISO_PKT_CONT_FRAG (0b01)
@@ -757,7 +765,11 @@ void bt_le_iso_handle_rx_data(uint8_t *data, size_t data_len)
int bt_le_iso_disconnect(uint16_t conn_handle, uint8_t reason)
{
#if CONFIG_BT_BLUEDROID_ENABLED
return bt_le_bluedroid_iso_disconnect(conn_handle, reason);
#else
return bt_le_nimble_iso_disconnect(conn_handle, reason);
#endif
}
static void iso_features_set(void)
@@ -819,7 +831,11 @@ int bt_le_iso_init(void)
r_ble_ll_isoal_tx_comp_cb_set(iso_tx_comp_cb);
#endif /* CONFIG_BT_ISO_TX */
#if CONFIG_BT_BLUEDROID_ENABLED
err = bt_le_bluedroid_iso_init();
#else
err = bt_le_nimble_iso_init();
#endif
if (err) {
return err;
}
@@ -839,5 +855,9 @@ void bt_le_iso_deinit(void)
r_ble_ll_isoal_tx_comp_cb_set(NULL);
#endif /* CONFIG_BT_ISO_TX */
#if CONFIG_BT_BLUEDROID_ENABLED
bt_le_bluedroid_iso_deinit();
#else
bt_le_nimble_iso_deinit();
#endif
}
+31 -4
View File
@@ -146,7 +146,7 @@ int bt_le_l2cap_accept(uint16_t conn_handle, uint16_t psm,
conn = bt_le_acl_conn_find(conn_handle);
if (conn == NULL || conn->state != BT_CONN_CONNECTED) {
LOG_ERR("NotConn[%d]", __LINE__);
LOG_INF("L2capAcceptNotConn[%u][%u]", conn_handle, BT_CONN_STATE_GET(conn));
*result = L2CAP_LE_ERR_INVALID_PARAMS;
return -ENOTCONN;
@@ -219,7 +219,7 @@ void bt_le_l2cap_connected(uint16_t conn_handle, uint16_t psm,
conn = bt_le_acl_conn_find(conn_handle);
if (conn == NULL || conn->state != BT_CONN_CONNECTED) {
LOG_ERR("NotConn[%d]", __LINE__);
LOG_INF("L2capConnectedNotConn[%u][%u]", conn_handle, BT_CONN_STATE_GET(conn));
return;
}
@@ -255,7 +255,7 @@ void bt_le_l2cap_disconnected(uint16_t conn_handle, uint16_t psm)
conn = bt_le_acl_conn_find(conn_handle);
if (conn == NULL || conn->state != BT_CONN_CONNECTED) {
LOG_ERR("NotConn[%d]", __LINE__);
LOG_INF("L2capDisconnectedNotConn[%u][%u]", conn_handle, BT_CONN_STATE_GET(conn));
return;
}
@@ -294,7 +294,7 @@ void bt_le_l2cap_received(uint16_t conn_handle, uint16_t psm,
conn = bt_le_acl_conn_find(conn_handle);
if (conn == NULL || conn->state != BT_CONN_CONNECTED) {
LOG_ERR("NotConn[%d]", __LINE__);
LOG_INF("L2capReceivedNotConn[%u][%u]", conn_handle, BT_CONN_STATE_GET(conn));
return;
}
@@ -315,7 +315,9 @@ void bt_le_l2cap_received(uint16_t conn_handle, uint16_t psm,
_IDF_ONLY
int bt_l2cap_chan_connect(struct bt_conn *conn, struct bt_l2cap_chan *chan, uint16_t psm)
{
#if !CONFIG_BT_BLUEDROID_ENABLED
int err;
#endif
LOG_DBG("L2capChanConnect[%04x]", psm);
@@ -334,6 +336,12 @@ int bt_l2cap_chan_connect(struct bt_conn *conn, struct bt_l2cap_chan *chan, uint
return -EINVAL;
}
#if CONFIG_BT_BLUEDROID_ENABLED
/* L2CAP COC is not yet implemented in the Bluedroid adapter (no
* bt_le_bluedroid_l2cap_chan_connect exists). Return early so callers
* like bt_gatt_ots_l2cap_connect see a clean -ENOTSUP. */
return -ENOTSUP;
#else
err = bt_le_nimble_l2cap_chan_connect(conn->handle);
if (err) {
return err;
@@ -342,12 +350,15 @@ int bt_l2cap_chan_connect(struct bt_conn *conn, struct bt_l2cap_chan *chan, uint
l2cap_chan_add(conn, chan, psm);
return 0;
#endif
}
_IDF_ONLY
int bt_l2cap_chan_disconnect(struct bt_l2cap_chan *chan)
{
#if !CONFIG_BT_BLUEDROID_ENABLED
int err;
#endif
LOG_DBG("L2capChanDisconnect");
@@ -361,6 +372,9 @@ int bt_l2cap_chan_disconnect(struct bt_l2cap_chan *chan)
return -ENOTCONN;
}
#if CONFIG_BT_BLUEDROID_ENABLED
return -ENOTSUP;
#else
err = bt_le_nimble_l2cap_chan_disconnect(chan);
if (err) {
/* If the disconnect failed, remove the channel from the connection.
@@ -371,6 +385,7 @@ int bt_l2cap_chan_disconnect(struct bt_l2cap_chan *chan)
}
return err;
#endif
}
_IDF_ONLY
@@ -393,7 +408,11 @@ int bt_l2cap_chan_send(struct bt_l2cap_chan *chan, struct net_buf *buf)
return -EMSGSIZE;
}
#if CONFIG_BT_BLUEDROID_ENABLED
return -ENOTSUP;
#else
return bt_le_nimble_l2cap_chan_send(chan, buf);
#endif
}
_IDF_ONLY
@@ -490,10 +509,18 @@ int bt_le_l2cap_init(void)
}
#endif /* CONFIG_BT_OTS || CONFIG_BT_OTS_CLIENT */
#if CONFIG_BT_BLUEDROID_ENABLED
/* No adapter-level L2CAP init exists (COC not implemented). Don't fail
* host init here — the host-agnostic OTS registrations above are still
* valid; any actual COC connect attempt later returns -ENOTSUP in
* bt_l2cap_chan_connect, so OTS features fail gracefully at use time
* instead of preventing the whole host from coming up. */
#else
err = bt_le_nimble_l2cap_init();
if (err) {
return err;
}
#endif
return 0;
}
@@ -484,7 +484,12 @@ int bt_le_scan_start(const struct bt_le_scan_param *param, void *cb)
int err = 0;
if (atomic_test_bit(bt_dev.flags, BT_DEV_SCANNING) == false) {
#if CONFIG_BT_BLUEDROID_ENABLED
ARG_UNUSED(cb);
err = bt_le_bluedroid_scan_start(param);
#else
err = bt_le_nimble_scan_start(param, cb);
#endif
if (err == 0) {
atomic_set_bit(bt_dev.flags, BT_DEV_SCANNING);
}
@@ -501,7 +506,11 @@ int bt_le_scan_stop(void)
int err = 0;
if (atomic_test_bit(bt_dev.flags, BT_DEV_SCANNING)) {
#if CONFIG_BT_BLUEDROID_ENABLED
err = bt_le_bluedroid_scan_stop();
#else
err = bt_le_nimble_scan_stop();
#endif
if (err == 0) {
atomic_clear_bit(bt_dev.flags, BT_DEV_SCANNING);
}
@@ -13,6 +13,49 @@
#define CONFIG_LITTLE_ENDIAN 1
#define CONFIG_BT_CONN_TX_USER_DATA_SIZE 8
#if CONFIG_BT_BLUEDROID_ENABLED
#define CONFIG_BT_ISO_EXT_ADV CONFIG_BT_BLE_50_EXTEND_ADV_EN
#define CONFIG_BT_ISO_PER_ADV CONFIG_BT_BLE_50_PERIODIC_ADV_EN
/* Bluedroid host does not expose ext-adv/periodic-sync slot counts;
* keep using the controller-side limits as the source of truth. */
#if CONFIG_BT_ISO_EXT_ADV
#define CONFIG_BT_EXT_ADV_MAX_ADV_SET CONFIG_BT_LE_MAX_EXT_ADV_INSTANCES
#else /* CONFIG_BT_ISO_EXT_ADV */
#define CONFIG_BT_EXT_ADV_MAX_ADV_SET 0
#endif /* CONFIG_BT_ISO_EXT_ADV */
#if CONFIG_BT_ISO_PER_ADV
#define CONFIG_BT_PER_ADV_SYNC_MAX CONFIG_BT_LE_MAX_PERIODIC_SYNCS
#else /* CONFIG_BT_ISO_PER_ADV */
#define CONFIG_BT_PER_ADV_SYNC_MAX 0
#endif /* CONFIG_BT_ISO_PER_ADV */
#if CONFIG_BT_BLE_FEAT_PERIODIC_ADV_SYNC_TRANSFER
#define CONFIG_BT_PER_ADV_SYNC_TRANSFER_SENDER CONFIG_BT_BLE_FEAT_PERIODIC_ADV_SYNC_TRANSFER
#define CONFIG_BT_PER_ADV_SYNC_TRANSFER_RECEIVER CONFIG_BT_BLE_FEAT_PERIODIC_ADV_SYNC_TRANSFER
#else /* CONFIG_BT_BLE_FEAT_PERIODIC_ADV_SYNC_TRANSFER */
#define CONFIG_BT_PER_ADV_SYNC_TRANSFER_SENDER 0
#define CONFIG_BT_PER_ADV_SYNC_TRANSFER_RECEIVER 0
#endif /* CONFIG_BT_BLE_FEAT_PERIODIC_ADV_SYNC_TRANSFER */
#define CONFIG_BT_MAX_CONN CONFIG_BT_ACL_CONNECTIONS
#define CONFIG_BT_SMP CONFIG_BT_BLE_SMP_ENABLE
#define CONFIG_BT_MAX_PAIRED CONFIG_BT_SMP_MAX_BONDS
#if CONFIG_BT_ISO_UNICAST && CONFIG_BT_ISO_BROADCAST
_Static_assert(CONFIG_BT_ISO_MAX_CHAN <= CONFIG_BT_BLE_ISO_CIS_MAX_COUNT + CONFIG_BT_BLE_ISO_BIS_MAX_COUNT, "Too large ISO channels");
#elif CONFIG_BT_ISO_UNICAST && !CONFIG_BT_ISO_BROADCAST
_Static_assert(CONFIG_BT_ISO_MAX_CHAN <= CONFIG_BT_BLE_ISO_CIS_MAX_COUNT, "Too large ISO channels");
#elif !CONFIG_BT_ISO_UNICAST && CONFIG_BT_ISO_BROADCAST
_Static_assert(CONFIG_BT_ISO_MAX_CHAN <= CONFIG_BT_BLE_ISO_BIS_MAX_COUNT, "Too large ISO channels");
#else /* CONFIG_BT_ISO_UNICAST && CONFIG_BT_ISO_BROADCAST */
_Static_assert(CONFIG_BT_ISO_MAX_CHAN == 0, "Too large ISO channels");
#endif /* CONFIG_BT_ISO_UNICAST && CONFIG_BT_ISO_BROADCAST */
#else /* CONFIG_BT_BLUEDROID_ENABLED */
#define CONFIG_BT_MAX_CONN CONFIG_BT_NIMBLE_MAX_CONNECTIONS
#define CONFIG_BT_SMP CONFIG_BT_NIMBLE_SECURITY_ENABLE
#define CONFIG_BT_MAX_PAIRED CONFIG_BT_NIMBLE_MAX_BONDS
@@ -46,3 +89,5 @@ _Static_assert(CONFIG_BT_ISO_MAX_CHAN <= CONFIG_BT_NIMBLE_ISO_BIS, "Too large IS
#else /* CONFIG_BT_ISO_UNICAST && CONFIG_BT_ISO_BROADCAST */
_Static_assert(CONFIG_BT_ISO_MAX_CHAN == 0, "Too large ISO channels");
#endif /* CONFIG_BT_ISO_UNICAST && CONFIG_BT_ISO_BROADCAST */
#endif /* CONFIG_BT_BLUEDROID_ENABLED */
@@ -28,6 +28,7 @@ extern "C" {
/* Mutex */
#define K_MUTEX_FOREVER portMAX_DELAY
#define K_MUTEX_SHORT (5000 / portTICK_PERIOD_MS)
struct k_mutex {
SemaphoreHandle_t handle;
@@ -99,6 +100,94 @@ static inline int k_mutex_unlock(struct k_mutex *mutex)
return 0;
}
/* Semaphore */
#define K_SEM_FOREVER portMAX_DELAY
#define K_SEM_SHORT (5000 / portTICK_PERIOD_MS)
struct k_sem {
SemaphoreHandle_t handle;
int result;
};
static inline void k_sem_create(struct k_sem *sem)
{
assert(sem);
assert(sem->handle == NULL);
sem->handle = xSemaphoreCreateBinary();
assert(sem->handle);
sem->result = 0;
}
static inline void k_sem_delete(struct k_sem *sem)
{
assert(sem);
assert(sem->handle);
vSemaphoreDelete(sem->handle);
sem->handle = NULL;
}
/* Inline log helper with a fixed tag, mirroring K_MUTEX_LOG_ERR. The sem
* helpers are inlined across many TUs, not all of which call
* LOG_MODULE_REGISTER, so we cannot reference the per-TU __iso_log_tag.
* Hardcode the "ISO_SEM" tag and keep the same level gating. */
#if CONFIG_BT_ISO_NO_LOG || (CONFIG_BT_ISO_LOG_LEVEL < BT_ISO_LOG_ERROR)
#define K_SEM_LOG_ERR(fmt, args...)
#else
#define K_SEM_LOG_ERR(fmt, args...) BT_ISO_LOGE("ISO_SEM", fmt, ## args)
#endif
static inline int k_sem_take(struct k_sem *sem, uint32_t timeout)
{
assert(sem);
assert(sem->handle);
/* Do NOT touch sem->result here. The producer may have already written
* it and called k_sem_give before this take ran (BTU/HCI cb on a
* higher-priority task). Clearing now would race-overwrite the
* producer's value. Caller must k_sem_reset() before initiating the
* async op that will produce the result. */
if (xSemaphoreTake(sem->handle, timeout) == pdTRUE) {
return 0;
}
#if !CONFIG_BT_ISO_NO_LOG && (CONFIG_BT_ISO_LOG_LEVEL >= BT_ISO_LOG_ERROR)
K_SEM_LOG_ERR("TakeFail[self=%s]", pcTaskGetName(NULL));
#else
K_SEM_LOG_ERR("TakeFail");
#endif
return -EIO;
}
static inline int k_sem_give(struct k_sem *sem)
{
assert(sem);
assert(sem->handle);
if (xSemaphoreGive(sem->handle) != pdTRUE) {
K_SEM_LOG_ERR("GiveFail");
return -EIO;
}
return 0;
}
/* Discard any pending give. Used by callers that reuse the same sem across
* cmd/response cycles where a previous cycle timed out — without this, a
* late give from the timed-out cycle would unblock the next take and the
* caller would see uninitialized response data. */
static inline void k_sem_reset(struct k_sem *sem)
{
assert(sem);
assert(sem->handle);
xQueueReset(sem->handle);
sem->result = 0;
}
/* Timer */
typedef uint32_t k_timeout_t;