feat(ble_audio): Add BAP Broadcast Assistant example

This commit is contained in:
Liu Linyan
2026-09-09 09:51:40 +08:00
parent 3c633f037d
commit 19b8637502
39 changed files with 2538 additions and 285 deletions
@@ -1564,36 +1564,40 @@ esp_err_t esp_ble_audio_bap_base_get_subgroup_codec_id(const esp_ble_audio_bap_b
}
esp_err_t esp_ble_audio_bap_base_get_subgroup_codec_data(const esp_ble_audio_bap_base_subgroup_t *subgroup,
uint8_t **data)
uint8_t **data, size_t *data_len)
{
int err;
int ret;
if (subgroup == NULL || data == NULL) {
if (subgroup == NULL || data == NULL || data_len == NULL) {
return ESP_ERR_INVALID_ARG;
}
err = bt_bap_base_get_subgroup_codec_data(subgroup, data);
if (err) {
ret = bt_bap_base_get_subgroup_codec_data(subgroup, data);
if (ret < 0) {
return ESP_FAIL;
}
*data_len = ret;
return ESP_OK;
}
esp_err_t esp_ble_audio_bap_base_get_subgroup_codec_meta(const esp_ble_audio_bap_base_subgroup_t *subgroup,
uint8_t **meta)
uint8_t **meta, size_t *meta_len)
{
int err;
int ret;
if (subgroup == NULL || meta == NULL) {
if (subgroup == NULL || meta == NULL || meta_len == NULL) {
return ESP_ERR_INVALID_ARG;
}
err = bt_bap_base_get_subgroup_codec_meta(subgroup, meta);
if (err) {
ret = bt_bap_base_get_subgroup_codec_meta(subgroup, meta);
if (ret < 0) {
return ESP_FAIL;
}
*meta_len = ret;
return ESP_OK;
}
@@ -1036,8 +1036,21 @@ esp_err_t esp_ble_audio_bap_broadcast_assistant_discover(uint16_t conn_handle);
* to start scanning itself.
*
* @param conn_handle Connection handle.
* @param start_scan Start scanning if true. If false, the application should
* enable scan itself.
* @param start_scan Deliver scan results to the `scan` callback if true.
*
* @note The application always owns the scanner: start it first with the
* host's own GAP API (esp_ble_gap_start_ext_scan / ble_gap_disc), then
* call this. The BASS Remote Scan Started operation is written either
* way; start_scan only decides whether the Broadcast Audio
* Announcements that scanner reports are also parsed and handed to the
* `scan` member of esp_ble_audio_bap_broadcast_assistant_cb_t.
* Leave that member NULL when passing false. Note the callback carries
* no advertising data, so filtering on anything besides the Broadcast
* ID belongs in the application's scan handler.
*
* @note start_scan is therefore redundant with that member being set, and
* esp_ble_audio_bap_broadcast_assistant_scan_stop has no counterpart
* to it. It is kept only for API compatibility.
*
* @return ESP_OK on success, or an error code on failure.
*/
@@ -1284,24 +1297,30 @@ esp_err_t esp_ble_audio_bap_base_get_subgroup_codec_id(const esp_ble_audio_bap_b
/**
* @brief Get the codec configuration data of a subgroup.
*
* @note The data points into the BASE, and stays valid only as long as it does.
*
* @param subgroup The subgroup pointer.
* @param data Pointer that will point to the resulting codec configuration data.
* @param data_len The length of the @p data (may be 0) on success.
*
* @return ESP_OK on success, or an error code on failure.
*/
esp_err_t esp_ble_audio_bap_base_get_subgroup_codec_data(const esp_ble_audio_bap_base_subgroup_t *subgroup,
uint8_t **data);
uint8_t **data, size_t *data_len);
/**
* @brief Get the codec metadata of a subgroup.
*
* @note The metadata points into the BASE, and stays valid only as long as it does.
*
* @param subgroup The subgroup pointer.
* @param meta Pointer that will point to the resulting codec metadata.
* @param meta_len The length of the @p meta (may be 0) on success.
*
* @return ESP_OK on success, or an error code on failure.
*/
esp_err_t esp_ble_audio_bap_base_get_subgroup_codec_meta(const esp_ble_audio_bap_base_subgroup_t *subgroup,
uint8_t **meta);
uint8_t **meta, size_t *meta_len);
/**
* @brief Store subgroup codec data in a esp_ble_audio_codec_cfg_t.
@@ -96,6 +96,8 @@ esp_err_t esp_ble_audio_gattc_disc_start(uint16_t conn_handle);
#define ESP_BLE_AUDIO_GAP_EVENT_PA_SYNC_PAST BT_LE_GAP_APP_EVENT_PA_SYNC_PAST
/*!< Audio GAP Periodic Sync Lost event */
#define ESP_BLE_AUDIO_GAP_EVENT_PA_SYNC_LOST BT_LE_GAP_APP_EVENT_PA_SYNC_LOST
/*!< Audio GAP Periodic Advertising Report event */
#define ESP_BLE_AUDIO_GAP_EVENT_PA_SYNC_RECV BT_LE_GAP_APP_EVENT_PA_SYNC_RECV
/*!< Audio GAP Connection Complete event */
#define ESP_BLE_AUDIO_GAP_EVENT_ACL_CONNECT BT_LE_GAP_APP_EVENT_ACL_CONNECT
/*!< Audio GAP Disconnection Complete event */
@@ -185,7 +185,7 @@ static const uint16_t ext_structs[] = {
sizeof(struct bt_bond_info),
};
#define LEA_VERSION (0x20260903)
#define LEA_VERSION (0x20260905)
struct lib_ext_cfgs {
/* BLE */
@@ -1086,8 +1086,6 @@ struct lib_ext_funcs {
/* Scan */
int (*_scan_cb_register)(struct bt_le_scan_cb *cb);
void (*_scan_cb_unregister)(struct bt_le_scan_cb *cb);
int (*_scan_start)(const struct bt_le_scan_param *param, void *cb);
int (*_scan_stop)(void);
int (*_pa_sync_cb_register)(struct bt_le_per_adv_sync_cb *cb);
int (*_pa_sync_cb_unregister)(struct bt_le_per_adv_sync_cb *cb);
int (*_pa_sync_get_info)(struct bt_le_per_adv_sync *per_adv_sync,
@@ -1300,8 +1298,6 @@ static const struct lib_ext_funcs ext_funcs = {
._scan_cb_register = (void *)bt_le_scan_cb_register,
._scan_cb_unregister = (void *)bt_le_scan_cb_unregister,
._scan_start = (void *)bt_le_scan_start,
._scan_stop = (void *)bt_le_scan_stop,
._pa_sync_cb_register = (void *)bt_le_per_adv_sync_cb_register,
._pa_sync_cb_unregister = (void *)bt_le_per_adv_sync_cb_unregister,
._pa_sync_get_info = (void *)bt_le_per_adv_sync_get_info,
@@ -1297,7 +1297,7 @@ int bt_bap_stream_release(struct bt_bap_stream *stream);
* @param seq_num Packet Sequence number. This value shall be incremented for each call to this
* function and at least once per SDU interval for a specific channel.
*
* @return Bytes sent in case of success or negative value in case of error.
* @return 0 in case of success or negative value in case of error.
*/
int bt_bap_stream_send(struct bt_bap_stream *stream, struct net_buf *buf, uint16_t seq_num);
@@ -1315,7 +1315,7 @@ int bt_bap_stream_send(struct bt_bap_stream *stream, struct net_buf *buf, uint16
* @param ts Timestamp of the SDU in microseconds (us). This value can be used to transmit
* multiple SDUs in the same SDU interval in a CIG or BIG.
*
* @return Bytes sent in case of success or negative value in case of error.
* @return 0 in case of success or negative value in case of error.
*/
int bt_bap_stream_send_ts(struct bt_bap_stream *stream, struct net_buf *buf, uint16_t seq_num,
uint32_t ts);
@@ -1336,7 +1336,7 @@ int bt_bap_stream_send_ts(struct bt_bap_stream *stream, struct net_buf *buf, uin
* @retval 0 on success
* @retval -EINVAL if the stream is invalid, if the stream is not configured for sending or if it is
* not connected with a isochronous stream
* @retval Any return value from bt_iso_chan_get_tx_sync()
* @retval 0 on success, or any negative value from bt_iso_chan_get_tx_sync()
*/
int bt_bap_stream_get_tx_sync(struct bt_bap_stream *stream, struct bt_iso_tx_info *info);
@@ -2891,6 +2891,13 @@ struct bt_bap_broadcast_assistant_cb {
* Called when the scanner finds an advertiser that advertises the
* BT_UUID_BROADCAST_AUDIO UUID.
*
* Delivered only while bt_bap_broadcast_assistant_scan_start() has been
* called with start_scan set to true, and only for what the application's
* own scanner reports — this port never starts a scanner of its own. Leave
* the member NULL if the application already parses its own scan results.
* Note it carries no advertising data either, so filtering on anything
* besides the Broadcast ID has to happen in that scanner.
*
* @param info Advertiser information.
* @param broadcast_id 24-bit broadcast ID.
*/
@@ -2993,16 +3000,25 @@ int bt_bap_broadcast_assistant_discover(struct bt_conn *conn);
*
* This will let the Broadcast Audio Scan Service server know that this device
* is actively scanning for broadcast sources.
* The function can optionally also start scanning, if the caller does not want
* to start scanning itself.
*
* Scan results, if @p start_scan is true, is sent to the
* bt_bap_broadcast_assistant_scan_cb callback.
* Unlike upstream Zephyr, this port never touches the scanner: the application
* starts it first, through whichever GAP API its host provides, and then calls
* this. @p start_scan therefore no longer means "start scanning" — it only says
* whether the Broadcast Audio Announcements the application's scanner picks up
* should also be parsed and delivered to the `scan` member of
* @ref bt_bap_broadcast_assistant_cb. The Remote Scan Started operation is
* written to the server either way.
*
* That makes @p start_scan redundant with the `scan` member being set: true
* without a `scan` callback only costs the parsing, false with one means it
* never fires. It stays for API compatibility - upstream plans to drop it - and
* bt_bap_broadcast_assistant_scan_stop() has no counterpart to it, always
* taking delivery back down.
*
* @param conn Connection to the Broadcast Audio Scan Service server.
* Used to let the server know that we are scanning.
* @param start_scan Start scanning if true. If false, the application should
* enable scan itself.
* @param start_scan Deliver scan results to the `scan` callback if true.
* Either way the application owns the scanner itself.
* @retval 0 Success
* @retval -EINVAL @p conn is NULL of if @p conn has not done discovery
@@ -3018,6 +3034,11 @@ int bt_bap_broadcast_assistant_scan_start(struct bt_conn *conn,
/**
* @brief Stop remote scanning for BISes for a server.
*
* Writes the Remote Scan Stopped operation and, if this @p conn had asked for
* scan results, stops delivering them to the `scan` callback. The application's
* own scanner is left running — it started it, and this port never drives the
* scanner from here (see bt_bap_broadcast_assistant_scan_start()).
*
* @param conn Connection to the server.
* @retval 0 Success
@@ -221,7 +221,7 @@ void bt_cap_stream_ops_register(struct bt_cap_stream *stream, struct bt_bap_stre
* function and at least once per SDU interval for a specific channel.
*
* @retval -EINVAL if stream object is NULL
* @retval Any return value from bt_bap_stream_send()
* @retval 0 on success, or any negative value from bt_bap_stream_send()
*/
int bt_cap_stream_send(struct bt_cap_stream *stream, struct net_buf *buf, uint16_t seq_num);
@@ -240,7 +240,7 @@ int bt_cap_stream_send(struct bt_cap_stream *stream, struct net_buf *buf, uint16
* multiple SDUs in the same SDU interval in a CIG or BIG.
*
* @retval -EINVAL if stream object is NULL
* @retval Any return value from bt_bap_stream_send()
* @retval 0 on success, or any negative value from bt_bap_stream_send()
*/
int bt_cap_stream_send_ts(struct bt_cap_stream *stream, struct net_buf *buf, uint16_t seq_num,
uint32_t ts);
@@ -256,7 +256,7 @@ int bt_cap_stream_send_ts(struct bt_cap_stream *stream, struct net_buf *buf, uin
* @param[out] info Transmit info object.
*
* @retval -EINVAL if stream object is NULL
* @retval Any return value from bt_bap_stream_get_tx_sync()
* @retval 0 on success, or any negative value from bt_bap_stream_get_tx_sync()
*/
int bt_cap_stream_get_tx_sync(struct bt_cap_stream *stream, struct bt_iso_tx_info *info);
@@ -421,106 +421,6 @@ free:
bt_le_gap_event_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_gap_init(void)
{
#if (BLE_50_EXTEND_SYNC_EN == TRUE)
@@ -17,10 +17,6 @@ extern "C" {
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_gap_init(void);
#ifdef __cplusplus
@@ -273,46 +273,3 @@ void bt_le_nimble_gap_post_event(void *param)
free:
bt_le_gap_event_free(qev);
}
int bt_le_nimble_scan_start(const struct bt_le_scan_param *param, ble_gap_event_fn *cb)
{
struct ble_gap_ext_disc_params uncoded = {0};
int rc;
LOG_DBG("[N]ScanStart[%u][%u][%u]", param->type, param->interval, param->window);
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)
{
int rc;
LOG_DBG("[N]ScanStop");
rc = ble_gap_disc_cancel();
if (rc && rc != BLE_HS_EALREADY) {
LOG_ERR("[N]ScanStopFail[%d]", rc);
}
/* EALREADY (not scanning, e.g. after privacy preemption): treat as success
* so bt_le_scan_stop clears a stale BT_DEV_SCANNING instead of locking out. */
if (rc == BLE_HS_EALREADY) {
rc = 0;
}
return nimble_err_to_errno(rc);
}
@@ -19,10 +19,6 @@ extern "C" {
void bt_le_nimble_gap_post_event(void *param);
int bt_le_nimble_scan_start(const struct bt_le_scan_param *param, ble_gap_event_fn *cb);
int bt_le_nimble_scan_stop(void);
#ifdef __cplusplus
}
#endif
@@ -413,7 +413,12 @@ static void handle_security_change_event_safe(struct bt_le_gap_app_param *param)
event.security_change.dst.val,
false);
if (gatt_conn == NULL) {
LOG_ERR("GapSecChgUnknownDev");
/* ACL disconnected between the BTC post and this handler — the same
* race as GapPastUnknownSrc, and routine on a link that drops while
* pairing. The producer leaves conn_handle at 0, which an app would
* read as its own connection, so invalidate it before dispatch. */
LOG_WRN("GapSecChgUnknownDev");
event.security_change.conn_handle = BT_CONN_HANDLE_INVALID;
goto end;
}
+4 -43
View File
@@ -431,7 +431,8 @@ int bt_le_per_adv_sync_report_recv_listener(uint16_t sync_handle,
per_adv_sync = bt_le_per_adv_sync_find(sync_handle);
if (per_adv_sync == NULL) {
LOG_ERR("PaSyncNotFound[%u]", sync_handle);
/* Reports queued before a terminate arrive after the sync is deleted. */
LOG_INF("PaSyncNotFound[%u]", sync_handle);
return -ENODEV;
}
@@ -463,7 +464,8 @@ void hci_le_biginfo_adv_report(struct net_buf *buf)
per_adv_sync = bt_le_per_adv_sync_find(evt->sync_handle);
if (per_adv_sync == NULL) {
LOG_ERR("PaSyncNotFound[%u]", evt->sync_handle);
/* Same post-teardown race as the PA report path above. */
LOG_INF("PaSyncNotFound[%u]", evt->sync_handle);
return;
}
@@ -489,47 +491,6 @@ void hci_le_biginfo_adv_report(struct net_buf *buf)
}
}
_LIB_ONLY
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);
}
} else {
err = -EALREADY;
}
return err;
}
_LIB_ONLY
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);
}
}
return err;
}
static void past_features_set(void)
{
#if CONFIG_BT_PER_ADV_SYNC_TRANSFER_SENDER
@@ -933,7 +933,9 @@ int bt_iso_chan_disconnect(struct bt_iso_chan *chan);
* each call to this function and at least once per SDU
* interval for a specific channel.
*
* @return Number of octets sent in case of success or negative value in case of error.
* @note Unlike the upstream API, this returns 0 rather than the octet count.
*
* @return 0 in case of success or negative value in case of error.
*/
int bt_iso_chan_send(struct bt_iso_chan *chan, struct net_buf *buf, uint16_t seq_num);
@@ -957,7 +959,9 @@ int bt_iso_chan_send(struct bt_iso_chan *chan, struct net_buf *buf, uint16_t seq
* This value can be used to transmit multiple
* SDUs in the same SDU interval in a CIG or BIG.
*
* @return Number of octets sent in case of success or negative value in case of error.
* @note Unlike the upstream API, this returns 0 rather than the octet count.
*
* @return 0 in case of success or negative value in case of error.
*/
int bt_iso_chan_send_ts(struct bt_iso_chan *chan, struct net_buf *buf, uint16_t seq_num,
uint32_t ts);