Merge branch 'idf/ble_audio_cap_handover' into 'master'

feat(ble_audio): Add CAP Handover (U2B & B2U) example

See merge request espressif/esp-idf!52061
This commit is contained in:
Island
2026-09-02 19:35:13 +08:00
47 changed files with 3749 additions and 120 deletions
@@ -102,7 +102,17 @@ esp_err_t esp_ble_audio_gattc_disc_start(uint16_t conn_handle);
#define ESP_BLE_AUDIO_GAP_EVENT_ACL_DISCONNECT BT_LE_GAP_APP_EVENT_ACL_DISCONNECT
/*!< Audio GAP Security Change event */
#define ESP_BLE_AUDIO_GAP_EVENT_SECURITY_CHANGE BT_LE_GAP_APP_EVENT_SECURITY_CHANGE
/** Audio GAP application event structure */
/**
* @brief Audio GAP application event structure
*
* @note Addresses carried by these events are in the **active host's own byte
* order**: on-air/LSB-first under NimBLE, MSB-first under Bluedroid
* (the order every `esp_ble_gap_*` API takes). Feeding one straight back
* to a host API is therefore always correct; comparing one against an
* address the audio layer holds, such as a Broadcast Receive State,
* needs a reversal under Bluedroid. See `struct bt_le_addr` in
* common/app/gap.h for the full convention.
*/
typedef struct bt_le_gap_app_event esp_ble_audio_gap_app_event_t;
/*!< Audio GATT MTU exchange complete event */
@@ -185,7 +185,7 @@ static const uint16_t ext_structs[] = {
sizeof(struct bt_bond_info),
};
#define LEA_VERSION (0x20260824)
#define LEA_VERSION (0x20260828)
struct lib_ext_cfgs {
/* BLE */
@@ -1976,7 +1976,8 @@ struct bt_bap_unicast_client_cb {
* @param dir The type of remote endpoints and capabilities discovered.
* @param codec_cap Remote capabilities.
*
* If discovery procedure has complete both @p codec and @p ep are set to NULL.
* Called once per record; the end of the procedure is reported by the
* discover callback below, not by a NULL @p codec_cap.
*/
void (*pac_record)(struct bt_conn *conn, enum bt_audio_dir dir,
const struct bt_audio_codec_cap *codec_cap);
@@ -1990,21 +1991,21 @@ struct bt_bap_unicast_client_cb {
* @param dir The type of remote endpoints and capabilities discovered.
* @param ep Remote endpoint.
*
* If discovery procedure has complete both @p codec and @p ep are set to NULL.
* Called once per endpoint; the end of the procedure is reported by the
* discover callback below, not by a NULL @p ep.
*/
void (*endpoint)(struct bt_conn *conn, enum bt_audio_dir dir, struct bt_bap_ep *ep);
/**
* @brief BAP discovery callback function.
*
* If discovery procedure has completed @p ep is set to NULL and @p err is 0.
* Called once the discovery procedure has completed, for the direction it
* covered.
*
* @param conn Connection to the remote unicast server.
* @param err Error value. 0 on success, GATT error on positive value or errno on
* negative value.
* @param dir The type of remote endpoints and capabilities discovered.
*
* If discovery procedure has complete both @p codec and @p ep are set to NULL.
*/
void (*discover)(struct bt_conn *conn, int err, enum bt_audio_dir dir);
@@ -996,19 +996,21 @@ struct bt_cap_handover_broadcast_to_unicast_param {
/** @brief Broadcast ID of the @p broadcast_source
*
* Ignored if @p reception_stop_param is not NULL.
* Always required: receive state notifications are matched against the
* {broadcast_id, adv_sid, adv_type} triple to decide that reception stopped,
* whether or not @p reception_stop_param is given.
*/
uint32_t broadcast_id;
/** @brief Advertising set ID of the @p broadcast_source
*
* Ignored if @p reception_stop_param is not NULL.
* Always required, see @p broadcast_id.
*/
uint8_t adv_sid;
/** @brief Advertising type of the advertising address of @p broadcast_source
*
* Ignored if @p reception_stop_param is not NULL.
* Always required, see @p broadcast_id.
*/
uint8_t adv_type;
@@ -571,7 +571,17 @@ esp_err_t esp_ble_iso_chan_send_ts(esp_ble_iso_chan_t *chan,
#define ESP_BLE_ISO_GAP_EVENT_SECURITY_CHANGE BT_LE_GAP_APP_EVENT_SECURITY_CHANGE
/*!< ISO GAP BIGInfo Adv Report event */
#define ESP_BLE_ISO_GAP_EVENT_BIGINFO_RECV BT_LE_GAP_APP_EVENT_BIGINFO_RECV
/** ISO GAP application event structure */
/**
* @brief ISO GAP application event structure
*
* @note Addresses carried by these events are in the **active host's own byte
* order**: on-air/LSB-first under NimBLE, MSB-first under Bluedroid
* (the order every `esp_ble_gap_*` API takes). Feeding one straight back
* to a host API is therefore always correct; comparing one against an
* address the audio layer holds, such as a Broadcast Receive State,
* needs a reversal under Bluedroid. See `struct bt_le_addr` in
* common/app/gap.h for the full convention.
*/
typedef struct bt_le_gap_app_event esp_ble_iso_gap_app_event_t;
/** ISO initialization information structure */
@@ -1881,7 +1881,7 @@ static void handle_gattc_notify_event(struct bt_le_gattc_notify_rx_event *event)
* 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);
params->notify(conn, params, NOTIFY_VALUE(event), event->len);
}
}
}
@@ -221,7 +221,7 @@ static void handle_gattc_notify_rx_event_safe(struct bt_le_gattc_notify_rx_event
* 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);
params->notify(conn, params, NOTIFY_VALUE(event), event->len);
}
}
}
+22 -4
View File
@@ -27,8 +27,7 @@ static struct bt_le_ext_adv *ext_adv_find(uint8_t adv_handle)
struct bt_le_ext_adv *adv = NULL;
for (size_t i = 0; i < ARRAY_SIZE(ext_adv_pool); i++) {
if (atomic_test_bit(ext_adv_pool[i].flags,
BT_PER_ADV_PARAMS_SET) &&
if (atomic_test_bit(ext_adv_pool[i].flags, BT_PER_ADV_PARAMS_SET) &&
ext_adv_pool[i].handle == adv_handle) {
LOG_DBG("ExtAdvFound[%u][%u]", i, adv_handle);
adv = &ext_adv_pool[i];
@@ -44,8 +43,7 @@ static struct bt_le_ext_adv *ext_adv_new(void)
struct bt_le_ext_adv *adv = NULL;
for (size_t i = 0; i < ARRAY_SIZE(ext_adv_pool); i++) {
if (atomic_test_bit(ext_adv_pool[i].flags,
BT_PER_ADV_PARAMS_SET) == false) {
if (atomic_test_bit(ext_adv_pool[i].flags, BT_PER_ADV_PARAMS_SET) == false) {
adv = &ext_adv_pool[i];
memset(adv, 0, sizeof(*adv));
@@ -160,3 +158,23 @@ int bt_le_ext_adv_get_info(const struct bt_le_ext_adv *adv,
return 0;
}
_LIB_ONLY
struct bt_le_ext_adv *bt_le_ext_adv_lookup_addr(const bt_addr_le_t *adv_addr, uint8_t sid)
{
struct bt_le_ext_adv *adv = NULL;
BT_LE_ASSERT(adv_addr);
for (size_t i = 0; i < ARRAY_SIZE(ext_adv_pool); i++) {
if (atomic_test_bit(ext_adv_pool[i].flags, BT_PER_ADV_PARAMS_SET) &&
bt_addr_le_eq(&ext_adv_pool[i].addr, adv_addr) &&
ext_adv_pool[i].sid == sid) {
LOG_INF("ExtAdvLookupAddrFound[%u][%u]", i, sid);
adv = &ext_adv_pool[i];
break;
}
}
return adv;
}
@@ -21,6 +21,30 @@
extern "C" {
#endif
/* Byte order of `val` is the active host's, NOT a single fixed convention:
*
* NimBLE on-air / LSB-first, val[0] is the least significant octet.
* Bluedroid MSB-first, val[0] is the most significant octet - the order
* esp_bd_addr_t uses and the order every esp_ble_gap_* API expects.
*
* That is deliberate: an address delivered by an event is normally handed
* straight back to the same host's API (create sync, connect, disconnect), so
* leaving it untouched keeps those paths correct on both hosts.
*
* The cost is that `bt_addr_le_t` in this port does not mean one thing either.
* On Bluedroid its contents follow whichever path filled it:
*
* conn->le.dst MSB - stored verbatim from the connect event and
* passed back to BTA_GATTC_* unchanged.
* bt_bond_info.addr MSB - read from the Bluedroid bond store as is.
* per_adv_sync->addr LSB - bt_le_per_adv_sync_new() reverses it, because
* it is compared against addresses off the air.
* BASS recv_state->addr LSB - arrives over ATT, where BASS defines LSB-first.
*
* So an application only has to convert where the two meet: comparing an event
* address against one the audio layer holds (a Broadcast Receive State, say)
* needs a reversal under CONFIG_BT_BLUEDROID_ENABLED and none under NimBLE.
*/
struct bt_le_addr {
uint8_t type;
uint8_t val[6];
@@ -72,6 +72,15 @@ struct bt_le_gattc_notify_rx_event {
uint8_t *value;
};
/* Neither adapter allocates a buffer for a zero-length notification, but NULL data is
* how gatt.c completes an unsubscribe: a lib notify handler that sees it drops its
* subscription. A zero-length notification is a real PDU (BASS sends one for an emptied
* Broadcast Receive State), so keep the pointer non-NULL when handing it to the lib.
*/
#define NOTIFY_VALUE(_event) \
((const void *)((_event)->value != NULL ? (const uint8_t *)(_event)->value \
: (const uint8_t *)""))
struct bt_le_gatts_notify_tx_event {
bool is_notify;
uint16_t conn_handle;
@@ -406,6 +406,16 @@ int bt_le_per_adv_sync_get_info(struct bt_le_per_adv_sync *per_adv_sync,
struct bt_le_per_adv_sync *bt_le_per_adv_sync_lookup_addr(const bt_addr_le_t *adv_addr,
uint8_t sid);
/**
* @brief Look up a local extended advertising set by advertiser address.
*
* @param adv_addr Advertiser address.
* @param sid The advertising set ID.
*
* @return Extended advertising set object or NULL if not found.
*/
struct bt_le_ext_adv *bt_le_ext_adv_lookup_addr(const bt_addr_le_t *adv_addr, uint8_t sid);
/**
* @brief Register periodic advertising sync callbacks.
*