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

This commit is contained in:
Liu Linyan
2026-09-04 09:26:24 +08:00
parent 130e4a07bb
commit ed16327b81
47 changed files with 3749 additions and 120 deletions
@@ -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.
*