You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mynewt.apache.org by ry...@apache.org on 2024/07/01 09:41:16 UTC
(mynewt-nimble) branch master updated (cbe729b0 -> 06026609)
This is an automated email from the ASF dual-hosted git repository.
rymek pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/mynewt-nimble.git
from cbe729b0 ci: Update `Check ports syscfg update` workflow
new cf42d74a nimble/audio: Add initial audio broadcast sink implementation
new 868ecb0b apps/btshell: Add Audio Broadcast Sink to shell application
new 06026609 nimble/audio: Add PACS to Broadcast Sink
The 3 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
Summary of changes:
apps/btshell/src/btshell.h | 2 +
apps/btshell/src/cmd.c | 86 +-
apps/btshell/src/cmd.h | 7 +
apps/btshell/src/cmd_leaudio.c | 705 ++++++++-
apps/btshell/src/cmd_leaudio.h | 20 +
apps/btshell/src/main.c | 6 +
apps/btshell/syscfg.yml | 3 +
nimble/host/audio/include/audio/ble_audio.h | 98 ++
.../audio/include/audio/ble_audio_broadcast_sink.h | 281 ++++
.../audio/include/audio/ble_audio_scan_delegator.h | 393 +++++
nimble/host/audio/pkg.yml | 12 +
nimble/host/audio/services/pacs/lc3/syscfg.yml | 9 +
nimble/host/audio/src/ble_audio.c | 4 +-
nimble/host/audio/src/ble_audio_broadcast_sink.c | 1647 ++++++++++++++++++++
...udio_priv.h => ble_audio_broadcast_sink_priv.h} | 15 +-
nimble/host/audio/src/ble_audio_priv.h | 11 +
nimble/host/audio/src/ble_audio_scan_delegator.c | 460 ++++++
...udio_priv.h => ble_audio_scan_delegator_priv.h} | 14 +-
nimble/host/audio/syscfg.yml | 80 +-
nimble/host/audio/targets/btshell_native/pkg.yml | 1 +
.../host/audio/targets/btshell_native/syscfg.yml | 5 +-
21 files changed, 3844 insertions(+), 15 deletions(-)
create mode 100644 nimble/host/audio/include/audio/ble_audio_broadcast_sink.h
create mode 100644 nimble/host/audio/include/audio/ble_audio_scan_delegator.h
create mode 100644 nimble/host/audio/src/ble_audio_broadcast_sink.c
copy nimble/host/audio/src/{ble_audio_priv.h => ble_audio_broadcast_sink_priv.h} (63%)
create mode 100644 nimble/host/audio/src/ble_audio_scan_delegator.c
copy nimble/host/audio/src/{ble_audio_priv.h => ble_audio_scan_delegator_priv.h} (63%)
(mynewt-nimble) 01/03: nimble/audio: Add initial audio broadcast sink implementation
Posted by ry...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
rymek pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/mynewt-nimble.git
commit cf42d74a7041d284892cb717e1a37efa247af2d4
Author: Mariusz Skamra <ma...@codecoup.pl>
AuthorDate: Thu Jan 25 13:30:12 2024 +0100
nimble/audio: Add initial audio broadcast sink implementation
This adds initial implementation of audio broadcast sink.
---
nimble/host/audio/include/audio/ble_audio.h | 98 ++
.../audio/include/audio/ble_audio_broadcast_sink.h | 281 ++++
.../audio/include/audio/ble_audio_scan_delegator.h | 393 +++++
nimble/host/audio/pkg.yml | 9 +
nimble/host/audio/src/ble_audio.c | 4 +-
nimble/host/audio/src/ble_audio_broadcast_sink.c | 1647 ++++++++++++++++++++
...udio_priv.h => ble_audio_broadcast_sink_priv.h} | 15 +-
nimble/host/audio/src/ble_audio_priv.h | 11 +
nimble/host/audio/src/ble_audio_scan_delegator.c | 460 ++++++
...udio_priv.h => ble_audio_scan_delegator_priv.h} | 14 +-
nimble/host/audio/syscfg.yml | 79 +-
11 files changed, 3001 insertions(+), 10 deletions(-)
diff --git a/nimble/host/audio/include/audio/ble_audio.h b/nimble/host/audio/include/audio/ble_audio.h
index 836a1ab5..6fb5f295 100644
--- a/nimble/host/audio/include/audio/ble_audio.h
+++ b/nimble/host/audio/include/audio/ble_audio.h
@@ -68,6 +68,9 @@
/** Broadcast Audio Broadcast Code Size. */
#define BLE_AUDIO_BROADCAST_CODE_SIZE 16
+/** Basic Audio Announcement Service UUID. */
+#define BLE_BASIC_AUDIO_ANNOUNCEMENT_SVC_UUID 0x1851
+
/** Broadcast Audio Announcement Service UUID. */
#define BLE_BROADCAST_AUDIO_ANNOUNCEMENT_SVC_UUID 0x1852
@@ -576,6 +579,15 @@ struct ble_audio_broadcast_name {
/** BLE Audio event: BASS - Set Broadcast Code */
#define BLE_AUDIO_EVENT_BASS_BROADCAST_CODE_SET 6
+/** BLE Audio event: Broadcast Sink - PA Sync state */
+#define BLE_AUDIO_EVENT_BROADCAST_SINK_PA_SYNC_STATE 7
+
+/** BLE Audio event: Broadcast Sink - BIS Sync state */
+#define BLE_AUDIO_EVENT_BROADCAST_SINK_BIS_SYNC_STATE 8
+
+/** BLE Audio event: Broadcast Sink - Metadata Updated */
+#define BLE_AUDIO_EVENT_BROADCAST_SINK_METADATA 9
+
/** @} */
/** @brief Broadcast Announcement */
@@ -650,6 +662,56 @@ struct ble_audio_event_bass_set_broadcast_code {
uint8_t broadcast_code[BLE_AUDIO_BROADCAST_CODE_SIZE];
};
+enum ble_audio_broadcast_sink_sync_state {
+ /** Broadcast Sink Sync State: Not Synced */
+ BLE_AUDIO_BROADCAST_SINK_SYNC_STATE_NOT_SYNCED,
+
+ /** Broadcast Sink Sync State: Sync initiated */
+ BLE_AUDIO_BROADCAST_SINK_SYNC_STATE_INITIATED,
+
+ /** Broadcast Sink Sync State: Sync established */
+ BLE_AUDIO_BROADCAST_SINK_SYNC_STATE_ESTABLISHED,
+};
+
+/** @brief PA Synchronization state */
+struct ble_audio_event_broadcast_sink_pa_sync_state {
+ /** Source ID */
+ uint8_t source_id;
+
+ /** Sync state */
+ enum ble_audio_broadcast_sink_sync_state state;
+};
+
+/** @brief BIS Synchronization state */
+struct ble_audio_event_broadcast_sink_bis_sync_state {
+ /** Source ID */
+ uint8_t source_id;
+
+ /** BIS Index */
+ uint8_t bis_index;
+
+ /** Sync state */
+ enum ble_audio_broadcast_sink_sync_state state;
+
+ /** Connection Handle. Valid if `state` is @ref BLE_AUDIO_BROADCAST_SINK_SYNC_STATE_ESTABLISHED */
+ uint16_t conn_handle;
+};
+
+/** @brief Broadcast Sink Metadata changed */
+struct ble_audio_event_broadcast_sink_metadata {
+ /** Source ID */
+ uint8_t source_id;
+
+ /** BIS Index Mask */
+ uint8_t bis_sync;
+
+ /** Scan Delegator Subgroup: Metadata */
+ const uint8_t *metadata;
+
+ /** Scan Delegator Subgroup: Metadata length */
+ uint8_t metadata_length;
+};
+
/**
* Represents a BLE Audio related event. When such an event occurs, the host
* notifies the application by passing an instance of this structure to an
@@ -715,6 +777,27 @@ struct ble_audio_event {
* Represents a Broadcast Code baing set in BASS.
*/
struct ble_audio_event_bass_set_broadcast_code bass_set_broadcast_code;
+
+ /**
+ * @ref BLE_AUDIO_EVENT_BROADCAST_SINK_PA_SYNC_STATE
+ *
+ * Represents a update of Broadcast Sink PA sync state.
+ */
+ struct ble_audio_event_broadcast_sink_pa_sync_state broadcast_sink_pa_sync_state;
+
+ /**
+ * @ref BLE_AUDIO_EVENT_BROADCAST_SINK_BIS_SYNC_STATE
+ *
+ * Represents an update of Broadcast Sink BIS sync state.
+ */
+ struct ble_audio_event_broadcast_sink_bis_sync_state broadcast_sink_bis_sync_state;
+
+ /**
+ * @ref BLE_AUDIO_EVENT_BROADCAST_SINK_METADATA
+ *
+ * Represents an update in Broadcast Sink Metadata.
+ */
+ struct ble_audio_event_broadcast_sink_metadata broadcast_sink_metadata;
};
};
@@ -950,6 +1033,21 @@ struct ble_audio_base {
STAILQ_HEAD(, ble_audio_big_subgroup) subs;
};
+static inline const char *
+ble_audio_broadcast_sink_sync_state_str(enum ble_audio_broadcast_sink_sync_state state)
+{
+ switch (state) {
+ case BLE_AUDIO_BROADCAST_SINK_SYNC_STATE_NOT_SYNCED:
+ return "not synced";
+ case BLE_AUDIO_BROADCAST_SINK_SYNC_STATE_INITIATED:
+ return "initiated";
+ case BLE_AUDIO_BROADCAST_SINK_SYNC_STATE_ESTABLISHED:
+ return "established";
+ default:
+ return "invalid";
+ }
+}
+
/** @} */
#endif /* H_BLE_AUDIO_ */
diff --git a/nimble/host/audio/include/audio/ble_audio_broadcast_sink.h b/nimble/host/audio/include/audio/ble_audio_broadcast_sink.h
new file mode 100644
index 00000000..36da4e6c
--- /dev/null
+++ b/nimble/host/audio/include/audio/ble_audio_broadcast_sink.h
@@ -0,0 +1,281 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef H_BLE_AUDIO_BROADCAST_SINK_
+#define H_BLE_AUDIO_BROADCAST_SINK_
+
+/**
+ * @file ble_audio_broadcast_sink.h
+ *
+ * @brief Bluetooth LE Audio BAP Broadcast Sink API
+ *
+ * @defgroup ble_audio_broadcast_sink Bluetooth LE Audio BAP Broadcast Sink
+ * @ingroup bt_host
+ * @{
+ *
+ */
+
+#include <stdint.h>
+#include "host/ble_gap.h"
+#include "host/ble_iso.h"
+#include "audio/ble_audio.h"
+#include "audio/ble_audio_scan_delegator.h"
+#include "nimble/ble.h"
+
+enum ble_audio_broadcast_sink_action_type {
+ /** Broadcast Sink Action Type: PA sync */
+ BLE_AUDIO_BROADCAST_SINK_ACTION_PA_SYNC,
+
+ /** Broadcast Sink Action Type: BIG sync */
+ BLE_AUDIO_BROADCAST_SINK_ACTION_BIG_SYNC,
+
+ /** Broadcast Sink Action Type: BIS sync */
+ BLE_AUDIO_BROADCAST_SINK_ACTION_BIS_SYNC,
+
+ /** Broadcast Sink Action Type: Start discovery (scan) */
+ BLE_AUDIO_BROADCAST_SINK_ACTION_DISC_START,
+
+ /** Broadcast Sink Action Type: Start discovery (scan) */
+ BLE_AUDIO_BROADCAST_SINK_ACTION_DISC_STOP,
+};
+
+struct ble_audio_broadcast_sink_action {
+ /**
+ * Indicates the type of action that is requested.
+ */
+ enum ble_audio_broadcast_sink_action_type type;
+
+ /**
+ * A discriminated union containing additional details concerning the action.
+ * The 'type' field indicates which member of the union is valid.
+ */
+ union {
+ /**
+ * Represents PA Sync parameters request.
+ *
+ * The action triggered on locally or remotely initiated PA synchronization request.
+ * The application should initialize the `out_parameters`, or abort the process.
+ *
+ * Valid for the following action types:
+ * o BLE_AUDIO_BROADCAST_SINK_ACTION_PA_SYNC
+ *
+ * Return:
+ * o 0 on success;
+ * o A non-zero value to abort.
+ */
+ struct {
+ /** Pointer to Periodic Sync parameters to initialize. */
+ struct ble_gap_periodic_sync_params *out_params;
+ } pa_sync;
+
+ /**
+ * Represents BIG Sync request.
+ *
+ * The action triggered on locally or remotely initiated BIG synchronization request.
+ * The application should provide the `out_mse` and `out_sync_timeout`,
+ * or reject the request.
+ *
+ * Valid for the following action types:
+ * o BLE_AUDIO_BROADCAST_SINK_ACTION_BIG_SYNC
+ *
+ * Return:
+ * o 0 on success;
+ * o A non-zero value to abort.
+ */
+ struct {
+ /** Source ID. */
+ uint8_t source_id;
+
+ /** ISO Interval. */
+ uint16_t iso_interval;
+
+ /** Presentation delay. */
+ uint32_t presentation_delay;
+
+ /** Number of SubEvents. The total number of subevents that are used to transmit BIS Data. */
+ uint8_t nse;
+
+ /** Burst Number. The number of new payloads for each BIS in a BIS event. */
+ uint8_t bn;
+
+ /**
+ * Pointer to Maximum subevents value to initialize.
+ * Range: 0x00 to 0x1F.
+ * Default: 0x00, meaning the Controller can schedule reception of any number of subevents up to NSE.
+ */
+ uint8_t *out_mse;
+
+ /**
+ * Pointer to Sync Timeout value to initialize.
+ * Range: 0x000A to 0x4000.
+ * Default: @ref iso_interval * 6.
+ */
+ uint16_t *out_sync_timeout;
+ } big_sync;
+
+ /**
+ * Represents BIS Sync request.
+ *
+ * The action triggered on locally or remotely initiated BIS synchronization request.
+ * The application should provide the `out_cb` and optionally `out_cb_arg`,
+ * or reject the request.
+ *
+ * @note The `subgroup` object as well as it's `base` object,
+ * therefore must be copied to in order to cache its information.
+ *
+ * Valid for the following action types:
+ * o BLE_AUDIO_BROADCAST_SINK_ACTION_BIS_SYNC
+ *
+ * Return:
+ * o 0 on success;
+ * o A non-zero value to abort.
+ */
+ struct {
+ /** Source ID. */
+ uint8_t source_id;
+
+ /** Subgroup index. */
+ uint8_t subgroup_index;
+
+ /** Broadcast Audio Source Endpoint BIS. */
+ const struct ble_audio_base_bis *bis;
+
+ /** Broadcast Audio Source Endpoint Subgroup. */
+ const struct ble_audio_base_subgroup *subgroup;
+ } bis_sync;
+
+ /**
+ * Represents discovery start request.
+ *
+ * The action triggered on locally as part of PA synchronization process.
+ *
+ * Valid for the following action types:
+ * o BLE_AUDIO_BROADCAST_SINK_ACTION_SCAN_START
+ *
+ * Return:
+ * o 0 on success;
+ * o A non-zero value to abort.
+ */
+ struct {
+ /** Preferred extended discovery parameters. */
+ const struct ble_gap_ext_disc_params *params_preferred;
+ } disc_start;
+ };
+};
+
+/**
+ * Prototype of Broadcast Sink action callback.
+ * This function shall return 0 if operation is accepted, and error code if rejected.
+ */
+typedef int ble_audio_broadcast_sink_action_fn(struct ble_audio_broadcast_sink_action *action,
+ void *arg);
+
+/**
+ * @brief Sets the application callback function.
+ *
+ * This function sets the callback function and its argument that will be called
+ * when a Broadcast Sink action is triggered.
+ *
+ * @param cb Pointer to the callback function of type ble_audio_scan_delegator_ev_cb.
+ * @param arg Pointer to the argument to be passed to the callback function.
+ *
+ * @return Returns 0 on success, or a non-zero error code otherwise.
+ */
+int ble_audio_broadcast_sink_cb_set(ble_audio_broadcast_sink_action_fn *cb, void *arg);
+
+/** Sink Add function parameters */
+struct ble_audio_broadcast_sink_add_params {
+ /** Broadcast Code */
+ uint8_t broadcast_code[BLE_AUDIO_BROADCAST_CODE_SIZE];
+
+ /** Broadcast Code parameter is valid */
+ uint8_t broadcast_code_is_valid : 1;
+};
+
+/**
+ * @brief Start audio broadcast sink synchronization with the source.
+ *
+ * This function synchronizes the audio broadcast sink with the source
+ * identified by the given source ID.
+ * The source can be added locally using @ref ble_svc_audio_bass_receive_state_add function
+ * or requested by remote device.
+ *
+ * @param source_id Source ID of Broadcast Source to synchronize to.
+ * @param params Parameters to be used.
+ *
+ * @return 0 on success;
+ * BLE_HS_ENOENT if the source ID is invalid;
+ * BLE_HS_EDONE if synced already;
+ * BLE_HS_EALREADY if the synchronization is in progress;
+ * BLE_HS_ENOMEM if memory allocation fails;
+ * Any other non-zero value on failure.
+ */
+int ble_audio_broadcast_sink_start(uint8_t source_id,
+ const struct ble_audio_broadcast_sink_add_params *params);
+
+/**
+ * @brief Stop audio broadcast sink synchronization.
+ *
+ * This function terminates or aborts the pending synchronization with the source
+ * identified by the given source ID.
+ *
+ * @param source_id Source ID of Broadcast Source to synchronize to.
+ *
+ * @return 0 on success;
+ * BLE_HS_ENOENT if the source ID is invalid;
+ * Any other non-zero value on failure.
+ */
+int ble_audio_broadcast_sink_stop(uint8_t source_id);
+
+/** Metadata Update function parameters */
+struct ble_audio_broadcast_sink_metadata_update_params {
+ /** Subgroup index */
+ uint8_t subgroup_index;
+
+ /** Scan Delegator Subgroup: Metadata */
+ uint8_t *metadata;
+
+ /** Scan Delegator Subgroup: Metadata length */
+ uint8_t metadata_length;
+};
+
+/**
+ * @brief Sets audio broadcast sink metadata.
+ *
+ * This function updates the broadcast sink metadata identified by the given source ID.
+ *
+ * @param source_id Source ID of Broadcast Source.
+ * @param params Parameters to be used.
+ *
+ * @return 0 on success;
+ * BLE_HS_ENOENT if the source ID is invalid;
+ * Any other non-zero value on failure.
+ */
+int ble_audio_broadcast_sink_metadata_update(uint8_t source_id,
+ const struct ble_audio_broadcast_sink_metadata_update_params *params);
+
+/**
+ * @brief Initialize Broadcast Sink
+ *
+ * This function is restricted to be called by sysinit.
+ *
+ * @return Returns 0 on success, or a non-zero error code otherwise.
+ */
+int ble_audio_broadcast_sink_init(void);
+#endif /* H_BLE_AUDIO_BROADCAST_SINK_ */
diff --git a/nimble/host/audio/include/audio/ble_audio_scan_delegator.h b/nimble/host/audio/include/audio/ble_audio_scan_delegator.h
new file mode 100644
index 00000000..5c9aa660
--- /dev/null
+++ b/nimble/host/audio/include/audio/ble_audio_scan_delegator.h
@@ -0,0 +1,393 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef H_BLE_AUDIO_SCAN_DELEGATOR_
+#define H_BLE_AUDIO_SCAN_DELEGATOR_
+
+/**
+ * @file ble_audio_scan_delegator.h
+ *
+ * @brief Bluetooth LE Audio BAP Scan Delegator API
+ *
+ * @defgroup ble_audio_scan_delegator Bluetooth LE Audio BAP Scan Delegator
+ * @ingroup bt_host
+ * @{
+ *
+ */
+
+#include <stdint.h>
+#include "audio/ble_audio.h"
+#include "nimble/ble.h"
+
+#if MYNEWT_VAL(BLE_AUDIO_SCAN_DELEGATOR)
+#define BLE_AUDIO_SCAN_DELEGATOR_SUBGROUP_MAX \
+ MYNEWT_VAL(BLE_AUDIO_SCAN_DELEGATOR_SUBGROUP_MAX)
+#else
+#define BLE_AUDIO_SCAN_DELEGATOR_SUBGROUP_MAX 0
+#endif /* BLE_AUDIO_SCAN_DELEGATOR */
+
+/** No preferred BIS Synchronization. Decision is left to application. */
+#define BLE_AUDIO_SCAN_DELEGATOR_BIS_SYNC_ANY 0xFFFFFFFF
+
+/** Unknown PA Interval */
+#define BLE_AUDIO_SCAN_DELEGATOR_PA_SYNC_UNKNOWN 0xFFFF
+
+/** Scan Delegator Source descriptor */
+struct ble_audio_scan_delegator_source_desc {
+ /** Scan Delegator Source: BLE Address */
+ ble_addr_t addr;
+
+ /** Scan Delegator Source: Advertising SID */
+ uint8_t adv_sid;
+
+ /** Scan Delegator Source: Broadcast ID */
+ uint32_t broadcast_id;
+};
+
+/** Scan Delegator Broadcast Encryption States */
+enum ble_audio_scan_delegator_big_enc {
+ /** Scan Delegator BIG Encryption: Not Encrypted */
+ BLE_AUDIO_SCAN_DELEGATOR_BIG_ENC_NONE,
+
+ /** Scan Delegator BIG Encryption: Broadcast Code Required */
+ BLE_AUDIO_SCAN_DELEGATOR_BIG_ENC_BROADCAST_CODE_MISSING,
+
+ /** Scan Delegator BIG Encryption: Decrypting */
+ BLE_AUDIO_SCAN_DELEGATOR_BIG_ENC_DECRYPTING,
+
+ /** Scan Delegator BIG Encryption: Bad Code */
+ BLE_AUDIO_SCAN_DELEGATOR_BIG_ENC_BROADCAST_CODE_INVALID
+};
+
+/** Scan Delegator PA Sync States */
+enum ble_audio_scan_delegator_pa_sync_state {
+ /** Scan Delegator PA Sync State: Not synchronized to PA */
+ BLE_AUDIO_SCAN_DELEGATOR_PA_SYNC_STATE_NOT_SYNCED,
+
+ /** Scan Delegator PA Sync State: SyncInfo Request */
+ BLE_AUDIO_SCAN_DELEGATOR_PA_SYNC_STATE_SYNC_INFO_REQ,
+
+ /** Scan Delegator PA Sync State: Synchronized to PA */
+ BLE_AUDIO_SCAN_DELEGATOR_PA_SYNC_STATE_SYNCED,
+
+ /** Scan Delegator PA Sync State: Failed to synchronize to PAA */
+ BLE_AUDIO_SCAN_DELEGATOR_PA_SYNC_STATE_ERROR,
+
+ /** Scan Delegator PA Sync State: No PAST */
+ BLE_AUDIO_SCAN_DELEGATOR_PA_SYNC_STATE_NO_PAST
+};
+
+/** Scan Delegator Subgroup definition */
+struct ble_audio_scan_delegator_subgroup {
+ /** Scan Delegator Subgroup: BIS Synchronization */
+ uint32_t bis_sync;
+
+ /** Scan Delegator Subgroup: Metadata */
+ uint8_t *metadata;
+
+ /** Scan Delegator Subgroup: Metadata length */
+ uint8_t metadata_length;
+};
+
+/** Scan Delegator PA Sync option */
+enum ble_audio_scan_delegator_pa_sync {
+ /** Scan Delegator PA Sync: Do not synchronize to PA */
+ BLE_AUDIO_SCAN_DELEGATOR_PA_SYNC_DO_NOT_SYNC,
+
+ /** Scan Delegator PA Sync: Synchronize to PA – PAST available */
+ BLE_AUDIO_SCAN_DELEGATOR_PA_SYNC_PAST_AVAILABLE,
+
+ /** Scan Delegator PA Sync: Synchronize to PA – PAST not available */
+ BLE_AUDIO_SCAN_DELEGATOR_PA_SYNC_PAST_NOT_AVAILABLE,
+};
+
+/** Scan Delegator Broadcast Source Synchronization option */
+struct ble_audio_scan_delegator_sync_opt {
+ /** PA Sync option */
+ enum ble_audio_scan_delegator_pa_sync pa_sync;
+
+ /** PA Sync interval */
+ uint16_t pa_interval;
+
+ /** Number of Subgroups */
+ uint8_t num_subgroups;
+
+ /** Subgroup sync option */
+ struct ble_audio_scan_delegator_subgroup subgroups[
+ BLE_AUDIO_SCAN_DELEGATOR_SUBGROUP_MAX];
+};
+
+enum ble_audio_scan_delegator_action_type {
+ /** Scan Delegator Action Type: Add Source */
+ BLE_AUDIO_SCAN_DELEGATOR_ACTION_SOURCE_ADD,
+
+ /** Scan Delegator Action Type: Modify Source */
+ BLE_AUDIO_SCAN_DELEGATOR_ACTION_SOURCE_MODIFY,
+
+ /** Scan Delegator Action Type: Remove Source */
+ BLE_AUDIO_SCAN_DELEGATOR_ACTION_SOURCE_REMOVE,
+};
+
+struct ble_audio_scan_delegator_action {
+ /**
+ * Indicates the type of action that is requested.
+ */
+ enum ble_audio_scan_delegator_action_type type;
+
+ /**
+ * A union containing additional details concerning the action.
+ * The 'type' field indicates which member of the union is valid.
+ */
+ union {
+ /**
+ * Represents remote Add Source operation request.
+ *
+ * Valid for the following action types:
+ * o BLE_AUDIO_SCAN_DELEGATOR_ACTION_SOURCE_ADD
+ *
+ * @note The @ref ble_audio_scan_delegator_subgroup.metadata object is temporary, therefore must be copied to in
+ * order to cache its information.
+ *
+ * Return:
+ * o 0 on success;
+ * o A non-zero value to reject.
+ */
+ struct {
+ /** Source ID */
+ uint8_t source_id;
+
+ /** Broadcast Source descriptor */
+ struct ble_audio_scan_delegator_source_desc source_desc;
+
+ /** Broadcast synchronization option */
+ struct ble_audio_scan_delegator_sync_opt sync_opt;
+
+ /**
+ * Valid pointer to provide source ID to be swapped or NULL.
+ *
+ * If there are insufficient resources to handle the operation, the application is requested to provide
+ * source ID to be removed once accepted.
+ */
+ uint8_t *out_source_id_to_swap;
+ } source_add;
+
+ /**
+ * Represents remote Modify Source operation request.
+ *
+ * Valid for the following action types:
+ * o BLE_AUDIO_SCAN_DELEGATOR_ACTION_SOURCE_MODIFY
+ *
+ * @note The @ref ble_audio_scan_delegator_subgroup.metadata object is temporary, therefore must be copied to in
+ * order to cache its information.
+ *
+ * Return:
+ * o 0 on success;
+ * o A non-zero value to reject.
+ */
+ struct {
+ /** Source ID */
+ uint8_t source_id;
+
+ /** Broadcast synchronization option */
+ struct ble_audio_scan_delegator_sync_opt sync_opt;
+ } source_modify;
+
+ /**
+ * Represents remote Remove Source operation request.
+ *
+ * Valid for the following action types:
+ * o BLE_AUDIO_SCAN_DELEGATOR_ACTION_SOURCE_REMOVE
+ *
+ * Return:
+ * o 0 on success;
+ * o A non-zero value to reject.
+ */
+ struct {
+ /** Source ID */
+ uint8_t source_id;
+ } source_remove;
+
+ /**
+ * Represents remote Broadcast Code Set operation request.
+ *
+ * Valid for the following action types:
+ * o BLE_AUDIO_SCAN_DELEGATOR_ACTION_BROADCAST_CODE
+ *
+ * Return:
+ * o 0 on success;
+ * o A non-zero value on failure.
+ */
+ struct {
+ /** Source ID */
+ uint8_t source_id;
+
+ /** Broadcast Code value to be stored. */
+ const uint8_t value[BLE_AUDIO_BROADCAST_CODE_SIZE];
+ } broadcast_code;
+ };
+};
+
+/**
+ * Prototype of Scan Delegator action callback.
+ * This function shall return 0 if operation is accepted, and error code if rejected.
+ */
+typedef int ble_audio_scan_delegator_action_fn(
+ struct ble_audio_scan_delegator_action *action, void *arg);
+
+/**
+ * @brief Sets the application callback function.
+ *
+ * This function sets the callback function that will be called on remote device request.
+ *
+ * @param[in] cb Pointer to the callback function.
+ * @param[in] arg Pointer to any additional arguments that need to
+ * be passed to the callback function.
+ *
+ * @return 0 on success;
+ * A non-zero value on failure.
+ */
+int ble_audio_scan_delegator_action_fn_set(ble_audio_scan_delegator_action_fn *cb, void *arg);
+
+/** Scan Delegator Receive State definition */
+struct ble_audio_scan_delegator_receive_state {
+ /** Scan Delegator Receive State: PA Sync state */
+ enum ble_audio_scan_delegator_pa_sync_state pa_sync_state;
+
+ /** Scan Delegator Receive State: BIG Encryption */
+ enum ble_audio_scan_delegator_big_enc big_enc;
+
+ /**
+ * Incorrect Bad Broadcast Code.
+ * Valid for @ref BLE_AUDIO_SCAN_DELEGATOR_BIG_ENC_BROADCAST_CODE_INVALID.
+ */
+ uint8_t bad_code[BLE_AUDIO_BROADCAST_CODE_SIZE];
+
+ /** Scan Delegator Receive State: Number of subgroups */
+ uint8_t num_subgroups;
+
+ /** Scan Delegator Receive State: subgroup entries */
+ struct ble_audio_scan_delegator_subgroup subgroups[
+ MYNEWT_VAL(BLE_AUDIO_SCAN_DELEGATOR_SUBGROUP_MAX)];
+};
+
+/** Receive State Add function parameters */
+struct ble_audio_scan_delegator_receive_state_add_params {
+ /** Broadcast Source descriptor */
+ struct ble_audio_scan_delegator_source_desc source_desc;
+
+ /** Receive state */
+ struct ble_audio_scan_delegator_receive_state state;
+};
+
+/**
+ * @brief Adds the receive state.
+ *
+ * This function allocates receive state and returns it's source ID.
+ *
+ * @param[in] params Parameters to be used.
+ * @param[in,out] source_id Unique source ID of receive state added.
+ *
+ * @return 0 on success;
+ * A non-zero value on failure.
+ */
+int ble_audio_scan_delegator_receive_state_add(const struct ble_audio_scan_delegator_receive_state_add_params *params,
+ uint8_t *source_id);
+
+/**
+ * @brief Removes the receive state.
+ *
+ * This function removes the specific receive state identified by source ID.
+ *
+ * @param[in] source_id Source ID of receive state to be removed.
+ * @param[in] params Parameters to be used.
+ *
+ * @return 0 on success;
+ * A non-zero value on failure.
+ */
+int ble_audio_scan_delegator_receive_state_remove(uint8_t source_id);
+
+/**
+ * @brief Set the receive state.
+ *
+ * This function updates the specific receive state identified by source ID.
+ *
+ * @param[in] source_id Source ID of receive state to be updated.
+ * @param[in] state Receive state to be set.
+ *
+ * @return 0 on success;
+ * A non-zero value on failure.
+ */
+int ble_audio_scan_delegator_receive_state_set(uint8_t source_id,
+ const struct ble_audio_scan_delegator_receive_state *state);
+
+/**
+ * @brief get the receive state.
+ *
+ * This function returns the specific receive state identified by source ID.
+ *
+ * @param[in] source_id Source ID of receive state to be updated.
+ * @param[in,out] state Pointer to receive state to be populate.
+ *
+ * @return 0 on success;
+ * A non-zero value on failure.
+ */
+int ble_audio_scan_delegator_receive_state_get(uint8_t source_id,
+ struct ble_audio_scan_delegator_receive_state *state);
+
+/** Scan Delegator Receive State entry definition */
+struct ble_audio_scan_delegator_receive_state_entry {
+ /** Source ID */
+ uint8_t source_id;
+
+ /** Broadcast Source descriptor */
+ struct ble_audio_scan_delegator_source_desc source_desc;
+
+ /** Receive state */
+ struct ble_audio_scan_delegator_receive_state state;
+};
+
+/**
+ * Type definition Receive State iteration callback function.
+ *
+ * @note Return 0 to continue, or a non-zero to abort foreach loop.
+ */
+typedef int ble_audio_scan_delegator_receive_state_foreach_fn(
+ struct ble_audio_scan_delegator_receive_state_entry *entry, void *arg);
+
+/**
+ * @brief Iterate receive states.
+ *
+ * @param[in] cb Callback to be called on codec entries.
+ * @param[in] arg Optional callback argument.
+ *
+ * @return 0 on success;
+ * A non-zero value on failure.
+ */
+void ble_audio_scan_delegator_receive_state_foreach(ble_audio_scan_delegator_receive_state_foreach_fn *cb, void *arg);
+
+/**
+ * @brief Initialize Scan Delegator
+ *
+ * This function is restricted to be called by sysinit.
+ *
+ * @return 0 on success;
+ * A non-zero value on failure.
+ */
+int ble_audio_scan_delegator_init(void);
+#endif /* H_BLE_AUDIO_SCAN_DELEGATOR_ */
diff --git a/nimble/host/audio/pkg.yml b/nimble/host/audio/pkg.yml
index 66d418d3..5dd14616 100644
--- a/nimble/host/audio/pkg.yml
+++ b/nimble/host/audio/pkg.yml
@@ -30,3 +30,12 @@ pkg.keywords:
pkg.deps:
- nimble
- nimble/host
+
+pkg.deps.BLE_AUDIO_SCAN_DELEGATOR:
+ - nimble/host/audio/services/bass
+
+pkg.init.BLE_AUDIO_BROADCAST_SINK:
+ ble_audio_broadcast_sink_init: 'MYNEWT_VAL(BLE_AUDIO_BROADCAST_SINK_SYSINIT_STAGE)'
+
+pkg.init.BLE_AUDIO_SCAN_DELEGATOR:
+ ble_audio_scan_delegator_init: 'MYNEWT_VAL(BLE_AUDIO_SCAN_DELEGATOR_SYSINIT_STAGE)'
diff --git a/nimble/host/audio/src/ble_audio.c b/nimble/host/audio/src/ble_audio.c
index 42533478..f601a07f 100644
--- a/nimble/host/audio/src/ble_audio.c
+++ b/nimble/host/audio/src/ble_audio.c
@@ -136,7 +136,9 @@ ble_audio_gap_event(struct ble_gap_event *gap_event, void *arg)
{
switch (gap_event->type) {
case BLE_GAP_EVENT_EXT_DISC: {
- struct ble_audio_adv_parse_broadcast_announcement_data data = { 0 };
+ struct ble_audio_adv_parse_broadcast_announcement_data data = {
+ .success = false,
+ };
int rc;
rc = ble_hs_adv_parse(gap_event->ext_disc.data,
diff --git a/nimble/host/audio/src/ble_audio_broadcast_sink.c b/nimble/host/audio/src/ble_audio_broadcast_sink.c
new file mode 100644
index 00000000..8c2f0545
--- /dev/null
+++ b/nimble/host/audio/src/ble_audio_broadcast_sink.c
@@ -0,0 +1,1647 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include "sysinit/sysinit.h"
+
+#if MYNEWT_VAL(BLE_AUDIO_BROADCAST_SINK)
+#include "stdlib.h"
+
+#include "audio/ble_audio.h"
+#include "audio/ble_audio_broadcast_sink.h"
+#include "audio/ble_audio_scan_delegator.h"
+#include "host/ble_gap.h"
+#include "host/ble_hs.h"
+#include "host/ble_iso.h"
+#include "host/ble_uuid.h"
+
+#include "ble_audio_priv.h"
+#include "ble_audio_scan_delegator_priv.h"
+
+#define CLAMP(_n, _min, _max) (MAX(_min, MIN(_n, _max)))
+#define BROADCAST_ID_INVALID 0xFFFFFFFF
+#define BIS_INDEX_TEST(_bis_sync, _index) (((_bis_sync) & (1 << ((_index) - 1))) > 0)
+#define BIS_INDEX_SET(_bis_sync, _index) ((_bis_sync) |= (1 << ((_index) - 1)))
+#define BIS_INDEX_CLEAR(_bis_sync, _index) ((_bis_sync) &= ~(1 << ((_index) - 1)))
+
+static struct {
+ ble_audio_broadcast_sink_action_fn *fn;
+ void *arg;
+} action_cb;
+
+enum pa_sync_state_internal {
+ PA_SYNC_STATE_IDLE,
+ PA_SYNC_STATE_PENDING_DISC,
+ PA_SYNC_STATE_PENDING_PAST,
+ PA_SYNC_STATE_PENDING_SYNC,
+ PA_SYNC_STATE_ACTIVE,
+ PA_SYNC_STATE_ERROR,
+ PA_SYNC_STATE_TIMEOUT,
+};
+
+enum big_sync_state_internal {
+ BIG_SYNC_STATE_IDLE,
+ BIG_SYNC_STATE_PENDING_BIG_INFO,
+ BIG_SYNC_STATE_PENDING_CODE,
+ BIG_SYNC_STATE_PENDING_BASE,
+ BIG_SYNC_STATE_PENDING_SYNC,
+ BIG_SYNC_STATE_FAILED,
+ BIG_SYNC_STATE_ACTIVE,
+};
+
+struct ble_audio_broadcast_sink {
+ /** Instance ID, same as BASS Source ID */
+ uint8_t source_id;
+
+ /** Internal PA sync state */
+ enum pa_sync_state_internal pa_sync_state;
+
+ /** Internal BIG sync state */
+ enum big_sync_state_internal big_sync_state;
+
+ /** Periodic sync handle */
+ uint16_t pa_sync_handle;
+
+ /** Connection handle or @ref BLE_HS_CONN_HANDLE_NONE */
+ uint16_t past_conn_handle;
+
+ /** BIG Handle */
+ uint8_t big_handle;
+
+ /** ISO Interval */
+ uint16_t iso_interval;
+
+ /** Burst Number */
+ uint8_t bn;
+
+ /** Number of SubEvents */
+ uint8_t nse;
+
+ /** Callback function */
+ ble_audio_event_fn *cb;
+
+ /** Broadcast code */
+ uint8_t broadcast_code[BLE_AUDIO_BROADCAST_CODE_SIZE];
+
+ /** The optional argument to pass to the callback function. */
+ void *cb_arg;
+
+ /** BIG Sync Parameters */
+ struct ble_audio_broadcast_sink_big_sync_params *big_sync_params;
+
+ /** If true, the broadcast is encrypted and requires broadcast_code */
+ uint8_t is_encrypted : 1;
+ /** If true, the broadcast_code value is valid */
+ uint8_t broadcast_code_is_valid : 1;
+
+ /** Internal subgroups state */
+ uint8_t num_subgroups;
+ struct {
+ uint32_t bis_sync;
+ } subgroups[MYNEWT_VAL(BLE_AUDIO_SCAN_DELEGATOR_SUBGROUP_MAX)];
+
+ /** Singly-linked list entry. */
+ SLIST_ENTRY(ble_audio_broadcast_sink) next;
+};
+
+static SLIST_HEAD(, ble_audio_broadcast_sink) ble_audio_broadcast_sink_list;
+static struct os_mempool ble_audio_broadcast_sink_pool;
+static os_membuf_t ble_audio_broadcast_sink_mem[
+ OS_MEMPOOL_SIZE(MYNEWT_VAL(BLE_AUDIO_BROADCAST_SINK_MAX),
+ sizeof(struct ble_audio_broadcast_sink))];
+
+/** If true, the discovery was started by us, otherwise by someone else */
+static bool disc_self_initiated;
+
+/** If true, the Periodic Advertising Sync is pending */
+static bool periodic_adv_sync_in_progress;
+
+static int gap_event_handler(struct ble_gap_event *event, void *arg);
+static int iso_event_handler(struct ble_iso_event *event, void *arg);
+static void big_sync_state_set(struct ble_audio_broadcast_sink *sink, enum big_sync_state_internal state_internal);
+static void pa_sync_state_set(struct ble_audio_broadcast_sink *sink, enum pa_sync_state_internal state_internal);
+
+static struct ble_audio_broadcast_sink *
+broadcast_sink_get(uint8_t source_id)
+{
+ struct ble_audio_broadcast_sink *sink;
+
+ SLIST_FOREACH(sink, &ble_audio_broadcast_sink_list, next) {
+ if (source_id == sink->source_id) {
+ return sink;
+ }
+ }
+
+ return NULL;
+}
+
+static struct ble_audio_broadcast_sink *
+broadcast_lookup_pa_sync_handle(uint16_t pa_sync_handle)
+{
+ struct ble_audio_broadcast_sink *sink;
+
+ SLIST_FOREACH(sink, &ble_audio_broadcast_sink_list, next) {
+ if (pa_sync_handle == sink->pa_sync_handle) {
+ return sink;
+ }
+ }
+
+ return NULL;
+}
+
+static struct ble_audio_broadcast_sink *
+broadcast_sink_lookup_adv_sid_broadcast_id_pair(uint8_t adv_sid, uint32_t broadcast_id)
+{
+ struct ble_audio_scan_delegator_source_desc source_desc;
+ struct ble_audio_broadcast_sink *sink;
+ int rc;
+
+ SLIST_FOREACH(sink, &ble_audio_broadcast_sink_list, next) {
+ rc = ble_audio_scan_delegator_source_desc_get(sink->source_id, &source_desc);
+ if (rc != 0) {
+ BLE_HS_LOG_ERROR("source desc get failed (%d)\n", rc);
+ continue;
+ }
+
+ if (source_desc.adv_sid == adv_sid && source_desc.broadcast_id == broadcast_id) {
+ return sink;
+ }
+ }
+
+ return NULL;
+}
+
+static struct ble_audio_broadcast_sink *
+broadcast_sink_lookup_addr_adv_sid_pair(const ble_addr_t *addr, uint8_t adv_sid)
+{
+ struct ble_audio_scan_delegator_source_desc source_desc;
+ struct ble_audio_broadcast_sink *sink;
+ int rc;
+
+ SLIST_FOREACH(sink, &ble_audio_broadcast_sink_list, next) {
+ rc = ble_audio_scan_delegator_source_desc_get(sink->source_id, &source_desc);
+ if (rc != 0) {
+ BLE_HS_LOG_ERROR("source desc get failed (%d)\n", rc);
+ continue;
+ }
+
+ if (source_desc.adv_sid == adv_sid && ble_addr_cmp(&source_desc.addr, addr) == 0) {
+ return sink;
+ }
+ }
+
+ return NULL;
+}
+
+static struct ble_audio_broadcast_sink *
+broadcast_sink_lookup_pa_sync_state(enum pa_sync_state_internal state_internal)
+{
+ struct ble_audio_broadcast_sink *sink;
+
+ SLIST_FOREACH(sink, &ble_audio_broadcast_sink_list, next) {
+ if (sink->pa_sync_state == state_internal) {
+ return sink;
+ }
+ }
+
+ return NULL;
+}
+
+static int
+disc_start(void)
+{
+ struct ble_audio_broadcast_sink_action action;
+ struct ble_gap_ext_disc_params disc_params;
+ int rc;
+
+ if (ble_gap_disc_active()) {
+ return 0;
+ }
+
+ disc_params.itvl = BLE_GAP_SCAN_FAST_INTERVAL_MIN;
+ disc_params.window = BLE_GAP_SCAN_FAST_WINDOW;
+ disc_params.passive = true;
+
+ action.type = BLE_AUDIO_BROADCAST_SINK_ACTION_DISC_START;
+ action.disc_start.params_preferred = &disc_params;
+
+ rc = action_cb.fn(&action, action_cb.arg);
+ if (rc != 0) {
+ BLE_HS_LOG_WARN("disc start rejected by user (%d)\n", rc);
+ } else {
+ disc_self_initiated = true;
+ }
+
+ return rc;
+}
+
+static void
+disc_stop(void)
+{
+ struct ble_audio_broadcast_sink_action action;
+ int rc;
+
+ if (!disc_self_initiated || !ble_gap_disc_active()) {
+ return;
+ }
+
+ action.type = BLE_AUDIO_BROADCAST_SINK_ACTION_DISC_STOP;
+
+ rc = action_cb.fn(&action, action_cb.arg);
+ if (rc != 0) {
+ BLE_HS_LOG_WARN("disc stop rejected by user (%d)\n", rc);
+ }
+
+ disc_self_initiated = false;
+}
+
+struct basic_audio_announcement_svc_data {
+ /** BASE length */
+ uint8_t length;
+
+ /** BASE */
+ const uint8_t *base;
+};
+
+struct service_data_uuid16 {
+ struct basic_audio_announcement_svc_data basic_audio_announcement;
+};
+
+static void
+service_data_uuid16_parse(const uint16_t uuid16, const uint8_t * const value, const uint8_t value_len,
+ void *user_data)
+{
+ struct service_data_uuid16 *data = user_data;
+
+ if (uuid16 == BLE_BASIC_AUDIO_ANNOUNCEMENT_SVC_UUID) {
+ data->basic_audio_announcement.base = value;
+ data->basic_audio_announcement.length = value_len;
+ }
+}
+
+struct periodic_report {
+ struct service_data_uuid16 uuid16;
+};
+
+static int
+periodic_report_parse(const struct ble_hs_adv_field *field, void *user_data)
+{
+ struct periodic_report *report = user_data;
+ const uint8_t value_len = field->length - sizeof(field->length);
+ ble_uuid16_t uuid16 = BLE_UUID16_INIT(0);
+ uint8_t offset = 0;
+
+ switch (field->type) {
+ case BLE_HS_ADV_TYPE_SVC_DATA_UUID16:
+ if (value_len < 2) {
+ break;
+ }
+
+ uuid16.value = get_le16(&field->value[offset]);
+ offset += 2;
+
+ service_data_uuid16_parse(uuid16.value, &field->value[offset], value_len - offset, &report->uuid16);
+ break;
+
+ default:
+ /* Continue */
+ return BLE_HS_ENOENT;
+ }
+
+ /* Stop */
+ return 0;
+}
+
+static uint32_t
+subgroup_bis_sync_get(struct ble_audio_broadcast_sink *sink, uint8_t subgroup_index)
+{
+ if (subgroup_index > sink->num_subgroups) {
+ return 0;
+ }
+
+ return sink->subgroups[subgroup_index].bis_sync;
+}
+
+static void
+bass_big_state_update(struct ble_audio_broadcast_sink *sink, enum big_sync_state_internal from,
+ enum big_sync_state_internal to)
+{
+ struct ble_audio_scan_delegator_receive_state receive_state = {0};
+ int rc;
+
+ rc = ble_audio_scan_delegator_receive_state_get(sink->source_id, &receive_state);
+ if (rc != 0) {
+ BLE_HS_LOG_ERROR("receive state get failed (%d)\n", rc);
+ return;
+ }
+
+ switch (to) {
+ case BIG_SYNC_STATE_PENDING_CODE:
+ receive_state.big_enc = BLE_AUDIO_SCAN_DELEGATOR_BIG_ENC_BROADCAST_CODE_MISSING;
+ break;
+
+ case BIG_SYNC_STATE_IDLE:
+ if (from == BIG_SYNC_STATE_PENDING_CODE) {
+ /* FIXME: this does not seem to be right */
+ receive_state.big_enc = BLE_AUDIO_SCAN_DELEGATOR_BIG_ENC_BROADCAST_CODE_INVALID;
+ memcpy(receive_state.bad_code, sink->broadcast_code, sizeof(receive_state.bad_code));
+ } else if (from == BIG_SYNC_STATE_ACTIVE || from == BIG_SYNC_STATE_FAILED) {
+ /* Iterate subgroup indexes to update the BIS Sync state */
+ for (uint8_t index = 0; index < receive_state.num_subgroups; index++) {
+ receive_state.subgroups[index].bis_sync = 0;
+ }
+ }
+ /* fallthrough */
+
+ case BIG_SYNC_STATE_PENDING_BIG_INFO:
+ case BIG_SYNC_STATE_PENDING_BASE:
+ case BIG_SYNC_STATE_PENDING_SYNC:
+ receive_state.big_enc = BLE_AUDIO_SCAN_DELEGATOR_BIG_ENC_NONE;
+ break;
+
+ case BIG_SYNC_STATE_FAILED:
+ receive_state.big_enc = BLE_AUDIO_SCAN_DELEGATOR_BIG_ENC_NONE;
+
+ /* BASS v1.0 3.1.1.4 Add Source operation
+ * "(...) if the server fails to synchronize to the BIG, the server shall write a value of
+ * 0xFFFFFFFF (Failed to synchronize to BIG) to the BIS_Sync_State field
+ */
+ for (uint8_t index = 0; index < receive_state.num_subgroups; index++) {
+ receive_state.subgroups[index].bis_sync = BLE_AUDIO_SCAN_DELEGATOR_BIS_SYNC_ANY;
+ }
+ break;
+
+ case BIG_SYNC_STATE_ACTIVE:
+ if (sink->is_encrypted) {
+ receive_state.big_enc = BLE_AUDIO_SCAN_DELEGATOR_BIG_ENC_DECRYPTING;
+ } else {
+ receive_state.big_enc = BLE_AUDIO_SCAN_DELEGATOR_BIG_ENC_NONE;
+ }
+
+ /* Iterate subgroup indexes to update the BIS Sync state */
+ for (uint8_t index = 0; index < receive_state.num_subgroups; index++) {
+ receive_state.subgroups[index].bis_sync = subgroup_bis_sync_get(sink, index);
+ }
+ break;
+ }
+
+ rc = ble_audio_scan_delegator_receive_state_set(sink->source_id, &receive_state);
+ if (rc != 0) {
+ BLE_HS_LOG_ERROR("receive state update failed (%d)\n", rc);
+ }
+}
+
+static enum ble_audio_broadcast_sink_sync_state
+big_state_internal_to_api(enum big_sync_state_internal state_internal)
+{
+ switch (state_internal) {
+ case BIG_SYNC_STATE_PENDING_CODE:
+ case BIG_SYNC_STATE_PENDING_BIG_INFO:
+ case BIG_SYNC_STATE_PENDING_BASE:
+ case BIG_SYNC_STATE_PENDING_SYNC:
+ return BLE_AUDIO_BROADCAST_SINK_SYNC_STATE_INITIATED;
+
+ case BIG_SYNC_STATE_ACTIVE:
+ return BLE_AUDIO_BROADCAST_SINK_SYNC_STATE_ESTABLISHED;
+
+ default:
+ return BLE_AUDIO_BROADCAST_SINK_SYNC_STATE_NOT_SYNCED;
+ }
+}
+
+static void
+api_bis_state_update(struct ble_audio_broadcast_sink *sink, enum big_sync_state_internal from,
+ enum big_sync_state_internal to)
+{
+ struct ble_audio_event event;
+
+ if (to == BIG_SYNC_STATE_ACTIVE) {
+ /* special case. event already sent from big_sync_established_handler() */
+ return;
+ }
+
+ event.type = BLE_AUDIO_EVENT_BROADCAST_SINK_BIS_SYNC_STATE;
+ event.broadcast_sink_bis_sync_state.source_id = sink->source_id;
+ event.broadcast_sink_bis_sync_state.state = big_state_internal_to_api(to);
+ event.broadcast_sink_bis_sync_state.conn_handle = BLE_HS_CONN_HANDLE_NONE;
+
+ for (uint8_t subgroup_index = 0; subgroup_index < sink->num_subgroups; subgroup_index++) {
+ uint32_t bis_sync = sink->subgroups[subgroup_index].bis_sync;
+
+ if (bis_sync != BLE_AUDIO_SCAN_DELEGATOR_BIS_SYNC_ANY) {
+ for (uint8_t bis_index = 0; bis_sync > 0; bis_index++) {
+ if (BIS_INDEX_TEST(bis_sync, bis_index)) {
+ event.broadcast_sink_bis_sync_state.bis_index = bis_index;
+ ble_audio_event_listener_call(&event);
+ BIS_INDEX_CLEAR(bis_sync, bis_index);
+ }
+ }
+ }
+ }
+}
+
+static void
+big_sync_state_set(struct ble_audio_broadcast_sink *sink, enum big_sync_state_internal state_internal)
+{
+ enum big_sync_state_internal state_internal_old = sink->big_sync_state;
+
+ if (state_internal == state_internal_old) {
+ return;
+ }
+
+ sink->big_sync_state = state_internal;
+
+ api_bis_state_update(sink, state_internal_old, state_internal);
+ bass_big_state_update(sink, state_internal_old, state_internal);
+}
+
+static void
+bass_pa_state_update(struct ble_audio_broadcast_sink *sink, enum pa_sync_state_internal from,
+ enum pa_sync_state_internal to)
+{
+ struct ble_audio_scan_delegator_receive_state receive_state;
+ int rc;
+
+ rc = ble_audio_scan_delegator_receive_state_get(sink->source_id, &receive_state);
+ if (rc != 0) {
+ BLE_HS_LOG_ERROR("receive state get failed (%d)\n", rc);
+ return;
+ }
+
+ switch (to) {
+ case PA_SYNC_STATE_IDLE:
+ case PA_SYNC_STATE_PENDING_DISC:
+ case PA_SYNC_STATE_PENDING_SYNC:
+ receive_state.pa_sync_state = BLE_AUDIO_SCAN_DELEGATOR_PA_SYNC_STATE_NOT_SYNCED;
+ break;
+
+ case PA_SYNC_STATE_PENDING_PAST:
+ receive_state.pa_sync_state = BLE_AUDIO_SCAN_DELEGATOR_PA_SYNC_STATE_SYNC_INFO_REQ;
+ break;
+
+ case PA_SYNC_STATE_ACTIVE:
+ receive_state.pa_sync_state = BLE_AUDIO_SCAN_DELEGATOR_PA_SYNC_STATE_SYNCED;
+ break;
+
+ case PA_SYNC_STATE_ERROR:
+ receive_state.pa_sync_state = BLE_AUDIO_SCAN_DELEGATOR_PA_SYNC_STATE_ERROR;
+ break;
+
+ case PA_SYNC_STATE_TIMEOUT:
+ if (from == PA_SYNC_STATE_PENDING_PAST) {
+ receive_state.pa_sync_state = BLE_AUDIO_SCAN_DELEGATOR_PA_SYNC_STATE_NO_PAST;
+ } else {
+ receive_state.pa_sync_state = BLE_AUDIO_SCAN_DELEGATOR_PA_SYNC_STATE_ERROR;
+ }
+ break;
+ }
+
+ rc = ble_audio_scan_delegator_receive_state_set(sink->source_id, &receive_state);
+ if (rc != 0) {
+ BLE_HS_LOG_ERROR("receive state set failed (%d)\n", rc);
+ }
+}
+
+static enum ble_audio_broadcast_sink_sync_state
+pa_state_internal_to_api(enum pa_sync_state_internal state_internal)
+{
+ switch (state_internal) {
+ case PA_SYNC_STATE_PENDING_DISC:
+ case PA_SYNC_STATE_PENDING_SYNC:
+ case PA_SYNC_STATE_PENDING_PAST:
+ return BLE_AUDIO_BROADCAST_SINK_SYNC_STATE_INITIATED;
+
+ case PA_SYNC_STATE_ACTIVE:
+ return BLE_AUDIO_BROADCAST_SINK_SYNC_STATE_ESTABLISHED;
+
+ default:
+ return BLE_AUDIO_BROADCAST_SINK_SYNC_STATE_NOT_SYNCED;
+ }
+}
+
+static void
+api_pa_state_update(struct ble_audio_broadcast_sink *sink, enum pa_sync_state_internal from,
+ enum pa_sync_state_internal to)
+{
+ struct ble_audio_event event;
+
+ event.type = BLE_AUDIO_EVENT_BROADCAST_SINK_PA_SYNC_STATE;
+ event.broadcast_sink_pa_sync_state.source_id = sink->source_id;
+ event.broadcast_sink_pa_sync_state.state = pa_state_internal_to_api(to);
+
+ if (event.broadcast_sink_pa_sync_state.state != pa_state_internal_to_api(from)) {
+ ble_audio_event_listener_call(&event);
+ }
+}
+
+static void
+pa_sync_state_set(struct ble_audio_broadcast_sink *sink, enum pa_sync_state_internal state_internal)
+{
+ enum pa_sync_state_internal state_internal_old = sink->pa_sync_state;
+
+ if (state_internal == state_internal_old) {
+ return;
+ }
+
+ sink->pa_sync_state = state_internal;
+ api_pa_state_update(sink, state_internal_old, state_internal);
+ bass_pa_state_update(sink, state_internal_old, state_internal);
+}
+
+static uint32_t
+group_bis_sync_get(struct ble_audio_broadcast_sink *sink)
+{
+ uint32_t bis_sync_flat = 0;
+
+ for (uint8_t subgroup_index = 0; subgroup_index < sink->num_subgroups; subgroup_index++) {
+ bis_sync_flat |= sink->subgroups[subgroup_index].bis_sync;
+ }
+
+ return bis_sync_flat;
+}
+
+static void
+pa_sync_create(void)
+{
+ struct ble_gap_periodic_sync_params periodic_sync_params = {0};
+ struct ble_audio_broadcast_sink_action action;
+ int rc;
+
+ if (periodic_adv_sync_in_progress) {
+ return;
+ }
+
+ action.type = BLE_AUDIO_BROADCAST_SINK_ACTION_PA_SYNC;
+ action.pa_sync.out_params = &periodic_sync_params;
+
+ rc = action_cb.fn(&action, action_cb.arg);
+ if (rc != 0) {
+ BLE_HS_LOG_WARN("rejected by user (%d)\n", rc);
+ return;
+ }
+
+ rc = ble_gap_periodic_adv_sync_create(NULL, 0, &periodic_sync_params,
+ gap_event_handler, NULL);
+ if (rc != 0) {
+ BLE_HS_LOG_ERROR("adv sync create failed (%d)\n", rc);
+ } else {
+ periodic_adv_sync_in_progress = true;
+ }
+}
+
+static void
+big_sync_established_handler(uint8_t source_id, uint8_t status, const struct ble_iso_big_desc *desc)
+{
+ struct ble_audio_broadcast_sink *sink;
+ uint32_t bis_sync_state = 0;
+ uint32_t group_bis_sync;
+ uint8_t bis_index;
+
+ /* Restart scan if needed */
+ if (broadcast_sink_lookup_pa_sync_state(PA_SYNC_STATE_PENDING_SYNC) != NULL) {
+ pa_sync_create();
+ disc_start();
+ } else if (broadcast_sink_lookup_pa_sync_state(PA_SYNC_STATE_PENDING_DISC) != NULL) {
+ disc_start();
+ }
+
+ sink = broadcast_sink_get(source_id);
+ if (sink == NULL) {
+ BLE_HS_LOG_DEBUG("Unknown source_id %u\n", source_id);
+ return;
+ }
+
+ if (status != 0) {
+ big_sync_state_set(sink, BIG_SYNC_STATE_FAILED);
+ return;
+ }
+
+ bis_index = 0;
+ group_bis_sync = group_bis_sync_get(sink);
+
+ for (uint8_t i = 0; i < desc->num_bis; i++) {
+ uint16_t conn_handle = desc->conn_handle[i];
+
+ for (; group_bis_sync > 0; bis_index++) {
+ if (BIS_INDEX_TEST(group_bis_sync, bis_index)) {
+ struct ble_audio_event event;
+
+ event.type = BLE_AUDIO_EVENT_BROADCAST_SINK_BIS_SYNC_STATE;
+ event.broadcast_sink_bis_sync_state.source_id = sink->source_id;
+ event.broadcast_sink_bis_sync_state.bis_index = bis_index;
+ event.broadcast_sink_bis_sync_state.state = BLE_AUDIO_BROADCAST_SINK_SYNC_STATE_ESTABLISHED;
+ event.broadcast_sink_bis_sync_state.conn_handle = conn_handle;
+
+ ble_audio_event_listener_call(&event);
+
+ BIS_INDEX_SET(bis_sync_state, bis_index);
+ BIS_INDEX_CLEAR(group_bis_sync, bis_index);
+ break;
+ }
+ }
+ }
+
+ /* Check whether all BISes got conn handle */
+ group_bis_sync = group_bis_sync_get(sink);
+ if (bis_sync_state != group_bis_sync) {
+ BLE_HS_LOG_WARN("not all BISes synced");
+
+ for (uint8_t subgroup_index = 0; subgroup_index < sink->num_subgroups; subgroup_index++) {
+ uint32_t bis_sync_missing;
+
+ bis_sync_missing = sink->subgroups[subgroup_index].bis_sync & ~bis_sync_state;
+ if (bis_sync_missing == 0) {
+ continue;
+ }
+
+ for (bis_index = 0; bis_sync_missing > 0; bis_index++) {
+ if (BIS_INDEX_TEST(bis_sync_missing, bis_index)) {
+ struct ble_audio_event event;
+
+ event.type = BLE_AUDIO_EVENT_BROADCAST_SINK_BIS_SYNC_STATE;
+ event.broadcast_sink_bis_sync_state.source_id = sink->source_id;
+ event.broadcast_sink_bis_sync_state.bis_index = bis_index;
+ event.broadcast_sink_bis_sync_state.state = BLE_AUDIO_BROADCAST_SINK_SYNC_STATE_NOT_SYNCED;
+ event.broadcast_sink_bis_sync_state.conn_handle = BLE_HS_CONN_HANDLE_NONE;
+
+ ble_audio_event_listener_call(&event);
+
+ BIS_INDEX_CLEAR(bis_sync_missing, bis_index);
+ }
+ }
+
+ sink->subgroups[subgroup_index].bis_sync &= bis_sync_state;
+ }
+ }
+
+ big_sync_state_set(sink, BIG_SYNC_STATE_ACTIVE);
+}
+
+static void
+big_sync_terminated_handler(uint8_t source_id, uint8_t reason)
+{
+ struct ble_audio_broadcast_sink *sink;
+
+ sink = broadcast_sink_get(source_id);
+ if (sink == NULL) {
+ BLE_HS_LOG_DEBUG("Unknown source_id %u\n", source_id);
+ return;
+ }
+
+ big_sync_state_set(sink, BIG_SYNC_STATE_IDLE);
+}
+
+static int
+iso_event_handler(struct ble_iso_event *event, void *arg)
+{
+ switch (event->type) {
+ case BLE_ISO_EVENT_BIG_SYNC_ESTABLISHED:
+ big_sync_established_handler(POINTER_TO_UINT(arg), event->big_sync_established.status,
+ &event->big_sync_established.desc);
+ break;
+
+ case BLE_ISO_EVENT_BIG_SYNC_TERMINATED:
+ big_sync_terminated_handler(POINTER_TO_UINT(arg), event->big_terminated.reason);
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int
+pa_sync_create_cancel(void)
+{
+ int rc;
+
+ if (!periodic_adv_sync_in_progress) {
+ return 0;
+ }
+
+ rc = ble_gap_periodic_adv_sync_create_cancel();
+ if (rc != 0) {
+ BLE_HS_LOG_ERROR("adv sync create cancel failed (%d)\n", rc);
+ } else {
+ periodic_adv_sync_in_progress = false;
+ }
+
+ return rc;
+}
+
+static int
+pa_sync_add(const ble_addr_t *addr, uint8_t adv_sid)
+{
+ int rc;
+
+ if (periodic_adv_sync_in_progress) {
+ rc = pa_sync_create_cancel();
+ if (rc != 0) {
+ return rc;
+ }
+ }
+
+ rc = ble_gap_add_dev_to_periodic_adv_list(addr, adv_sid);
+ if (rc != 0) {
+ BLE_HS_LOG_ERROR("add dev to periodic adv list failed (%d)\n", rc);
+ /* TODO: destroy sink */
+ return rc;
+ }
+
+ (void)pa_sync_create();
+
+ return rc;
+}
+
+static int
+pa_sync_remove(const ble_addr_t *addr, uint8_t adv_sid)
+{
+ int rc;
+
+ if (periodic_adv_sync_in_progress) {
+ rc = pa_sync_create_cancel();
+ if (rc != 0) {
+ return rc;
+ }
+ }
+
+ rc = ble_gap_rem_dev_from_periodic_adv_list(addr, adv_sid);
+ if (rc != 0) {
+ BLE_HS_LOG_ERROR("rem dev from periodic adv list failed (%d)\n", rc);
+ }
+
+ return rc;
+}
+
+static void
+periodic_sync_handler(const ble_addr_t *addr, uint8_t adv_sid, uint8_t status, uint16_t pa_sync_handle)
+{
+ struct ble_audio_broadcast_sink *sink;
+
+ periodic_adv_sync_in_progress = false;
+
+ sink = broadcast_sink_lookup_addr_adv_sid_pair(addr, adv_sid);
+ if (sink == NULL) {
+ BLE_HS_LOG_DEBUG("sink not found\n");
+ } else {
+ (void)pa_sync_remove(addr, adv_sid);
+
+ if (status == BLE_ERR_SUCCESS) {
+ sink->pa_sync_handle = pa_sync_handle;
+ pa_sync_state_set(sink, PA_SYNC_STATE_ACTIVE);
+ } else if (status == BLE_ERR_OPERATION_CANCELLED) {
+ pa_sync_state_set(sink, PA_SYNC_STATE_IDLE);
+ } else if (status == BLE_ERR_CONN_ESTABLISHMENT) {
+ pa_sync_state_set(sink, PA_SYNC_STATE_TIMEOUT);
+ } else {
+ pa_sync_state_set(sink, PA_SYNC_STATE_ERROR);
+ }
+ }
+
+ sink = broadcast_sink_lookup_pa_sync_state(PA_SYNC_STATE_PENDING_SYNC);
+ if (sink != NULL) {
+ pa_sync_create();
+ disc_start();
+ } else {
+ disc_stop();
+ }
+}
+
+static void
+periodic_sync_lost_handler(uint16_t sync_handle, int reason)
+{
+ struct ble_audio_broadcast_sink *sink;
+
+ sink = broadcast_lookup_pa_sync_handle(sync_handle);
+ if (sink == NULL) {
+ BLE_HS_LOG_DEBUG("Unknown sync_handle %u\n", sync_handle);
+ return;
+ }
+
+ if (reason == BLE_HS_EDONE) {
+ pa_sync_state_set(sink, PA_SYNC_STATE_IDLE);
+ } else if (reason == BLE_HS_ETIMEOUT) {
+ pa_sync_state_set(sink, PA_SYNC_STATE_TIMEOUT);
+ } else {
+ pa_sync_state_set(sink, PA_SYNC_STATE_ERROR);
+ }
+}
+
+static void
+biginfo_report_handler(uint16_t pa_sync_handle, uint16_t iso_interval, uint8_t nse, uint8_t bn, uint8_t encryption)
+{
+ struct ble_audio_broadcast_sink *sink;
+
+ sink = broadcast_lookup_pa_sync_handle(pa_sync_handle);
+ if (sink == NULL) {
+ BLE_HS_LOG_DEBUG("Unknown pa_sync_handle %u\n", pa_sync_handle);
+ return;
+ }
+
+ if (sink->big_sync_state == BIG_SYNC_STATE_PENDING_BIG_INFO) {
+ sink->is_encrypted = encryption;
+ sink->iso_interval = iso_interval;
+ sink->nse = nse;
+ sink->bn = bn;
+
+ if (sink->is_encrypted && !sink->broadcast_code_is_valid) {
+ big_sync_state_set(sink, BIG_SYNC_STATE_PENDING_CODE);
+ } else {
+ big_sync_state_set(sink, BIG_SYNC_STATE_PENDING_BASE);
+ }
+ }
+}
+
+static int
+bis_params_get(struct ble_audio_broadcast_sink *sink, struct ble_audio_base_group *group,
+ struct ble_audio_base_iter *subgroup_iter,
+ struct ble_iso_bis_params bis_params[MYNEWT_VAL(BLE_ISO_MAX_BISES)], uint8_t *out_bis_cnt)
+{
+ uint8_t subgroup_index;
+ uint32_t group_bis_sync = 0;
+ uint8_t bis_cnt = 0;
+ int rc = 0;
+
+ if (action_cb.fn == NULL) {
+ BLE_HS_LOG_ERROR("action_cb.fn is NULL\n");
+ return BLE_HS_EAPP;
+ }
+
+ for (subgroup_index = 0; subgroup_index < group->num_subgroups; subgroup_index++) {
+ struct ble_audio_base_subgroup subgroup;
+ struct ble_audio_base_iter bis_iter;
+ uint32_t subgroup_bis_sync_req;
+
+ rc = ble_audio_base_subgroup_iter(subgroup_iter, &subgroup, &bis_iter);
+ if (rc != 0) {
+ break;
+ }
+
+ subgroup_bis_sync_req = subgroup_bis_sync_get(sink, subgroup_index);
+ if (subgroup_bis_sync_req == 0) {
+ /* No BISes requested for this subgroup */
+ continue;
+ }
+
+ sink->subgroups[subgroup_index].bis_sync = 0;
+
+ for (uint8_t i = 0; i < subgroup.num_bis && bis_cnt < MYNEWT_VAL(BLE_ISO_MAX_BISES); i++) {
+ struct ble_audio_broadcast_sink_action action = {0};
+ struct ble_audio_base_bis bis;
+
+ rc = ble_audio_base_bis_iter(&bis_iter, &bis);
+ if (rc != 0) {
+ break;
+ }
+
+ /* Core 5.4 | Vol 4, Part E; 7.8.106 LE BIG Create Sync command
+ * BIS[i]: 0x01 to 0x1F
+ */
+ if (bis.index < 0x01 || bis.index > 0x7F) {
+ continue;
+ }
+
+ if (BIS_INDEX_TEST(group_bis_sync, bis.index)) {
+ /* BAP_v1.0.1; 3.7.2.2 Basic Audio Announcements
+ *
+ * Rule 3: Every BIS in the BIG, denoted by its BIS_index value,
+ * shall only be present in one subgroup.
+ */
+ BLE_HS_LOG_WARN("duplicated bis index 0x%02x", bis.index);
+ continue;
+ }
+
+ if (!BIS_INDEX_TEST(subgroup_bis_sync_req, bis.index)) {
+ continue;
+ }
+
+ action.type = BLE_AUDIO_BROADCAST_SINK_ACTION_BIS_SYNC;
+ action.bis_sync.source_id = sink->source_id;
+ action.bis_sync.subgroup_index = subgroup_index;
+ action.bis_sync.bis = &bis;
+ action.bis_sync.subgroup = &subgroup;
+
+ rc = action_cb.fn(&action, action_cb.arg);
+ if (rc != 0) {
+ BLE_HS_LOG_WARN("bis sync rejected by user (%d)\n", rc);
+ } else {
+ BIS_INDEX_SET(sink->subgroups[subgroup_index].bis_sync, bis.index);
+ bis_params[bis_cnt].bis_index = bis.index;
+ bis_cnt++;
+ }
+ }
+
+ group_bis_sync |= sink->subgroups[subgroup_index].bis_sync;
+ }
+
+ sink->num_subgroups = subgroup_index;
+
+ *out_bis_cnt = bis_cnt;
+ return rc;
+}
+
+static int
+bis_params_bis_index_cmp(const void *p1, const void *p2)
+{
+ const struct ble_iso_bis_params *bis_params_1 = p1;
+ const struct ble_iso_bis_params *bis_params_2 = p2;
+
+ return bis_params_1->bis_index - bis_params_2->bis_index;
+}
+
+static void
+big_sync(struct ble_audio_broadcast_sink *sink, const uint8_t *base, uint8_t base_len)
+{
+ struct ble_audio_broadcast_sink_action action;
+ struct ble_audio_base_group group;
+ struct ble_audio_base_iter subgroup_iter;
+ struct ble_iso_big_sync_create_params big_sync_create_params;
+ struct ble_iso_bis_params bis_params[MYNEWT_VAL(BLE_ISO_MAX_BISES)];
+ char broadcast_code[BLE_AUDIO_BROADCAST_CODE_SIZE + 1];
+ uint8_t bis_cnt = 0;
+ int rc;
+
+ rc = ble_audio_base_parse(base, base_len, &group, &subgroup_iter);
+ if (rc != 0) {
+ BLE_HS_LOG_ERROR("base parse failed (%d)\n", rc);
+ ble_audio_broadcast_sink_stop(sink->source_id);
+ return;
+ }
+
+ /* By default, the controller can schedule reception of any number of subevents up to NSE */
+ big_sync_create_params.mse = 0x00;
+
+ /* By default, (6 * ISO_Interval * 1.25) / 10 = 3/4 * ISO_Interval */
+ big_sync_create_params.sync_timeout = (uint16_t)((3.0 * sink->iso_interval) / 4.0);
+ big_sync_create_params.sync_timeout = CLAMP(big_sync_create_params.sync_timeout, 0x000A, 0x4000);
+
+ action.type = BLE_AUDIO_BROADCAST_SINK_ACTION_BIG_SYNC;
+ action.big_sync.source_id = sink->source_id;
+ action.big_sync.iso_interval = sink->iso_interval;
+ action.big_sync.nse = sink->nse;
+ action.big_sync.bn = sink->bn;
+ action.big_sync.presentation_delay = group.presentation_delay;
+ action.big_sync.out_mse = &big_sync_create_params.mse;
+ action.big_sync.out_sync_timeout = &big_sync_create_params.sync_timeout;
+
+ if (action_cb.fn == NULL) {
+ BLE_HS_LOG_ERROR("action_cb.fn is NULL\n");
+ ble_audio_broadcast_sink_stop(sink->source_id);
+ return;
+ }
+
+ rc = action_cb.fn(&action, action_cb.arg);
+ if (rc != 0) {
+ BLE_HS_LOG_WARN("big sync rejected by user (%d)\n", rc);
+ ble_audio_broadcast_sink_stop(sink->source_id);
+ return;
+ }
+
+ if (sink->broadcast_code_is_valid) {
+ memcpy(broadcast_code, sink->broadcast_code, BLE_AUDIO_BROADCAST_CODE_SIZE);
+ broadcast_code[BLE_AUDIO_BROADCAST_CODE_SIZE] = '\0';
+ big_sync_create_params.broadcast_code = broadcast_code;
+ } else {
+ big_sync_create_params.broadcast_code = NULL;
+ }
+
+ big_sync_create_params.sync_handle = sink->pa_sync_handle;
+ big_sync_create_params.cb = iso_event_handler;
+ big_sync_create_params.cb_arg = UINT_TO_POINTER(sink->source_id);
+ big_sync_create_params.bis_params = bis_params;
+
+ rc = bis_params_get(sink, &group, &subgroup_iter, bis_params, &bis_cnt);
+ if (rc != 0 || bis_cnt == 0) {
+ ble_audio_broadcast_sink_stop(sink->source_id);
+ return;
+ }
+
+ /* Sort the parameters by BIS index in ascending order. It is required to properly match the
+ * BIS index with conn handle in BIG Sync Established event handler.
+ */
+ qsort(bis_params, bis_cnt, sizeof(*bis_params), bis_params_bis_index_cmp);
+
+ big_sync_create_params.bis_cnt = bis_cnt;
+
+ /* Stop scan first */
+ disc_stop();
+
+ rc = ble_iso_big_sync_create(&big_sync_create_params, &sink->big_handle);
+ if (rc != 0) {
+ BLE_HS_LOG_ERROR("big sync failed (%d)\n", rc);
+ return;
+ }
+
+ big_sync_state_set(sink, BIG_SYNC_STATE_PENDING_SYNC);
+}
+
+static void
+metadata_update(struct ble_audio_broadcast_sink *sink, const uint8_t *base, uint8_t base_len)
+{
+ struct ble_audio_event event;
+ struct ble_audio_base_group group;
+ struct ble_audio_base_iter subgroup_iter;
+ int rc = 0;
+
+ rc = ble_audio_base_parse(base, base_len, &group, &subgroup_iter);
+ if (rc != 0) {
+ BLE_HS_LOG_WARN("base parse failed (%d)\n", rc);
+ return;
+ }
+
+ for (uint8_t subgroup_index = 0; subgroup_index < group.num_subgroups; subgroup_index++) {
+ struct ble_audio_base_subgroup subgroup;
+ uint32_t bis_sync_state;
+
+ rc = ble_audio_base_subgroup_iter(&subgroup_iter, &subgroup, NULL);
+ if (rc != 0) {
+ break;
+ }
+
+ bis_sync_state = subgroup_bis_sync_get(sink, subgroup_index);
+ if (bis_sync_state == 0) {
+ /* No BISes synced for this subgroup */
+ continue;
+ }
+
+ event.type = BLE_AUDIO_EVENT_BROADCAST_SINK_METADATA;
+ event.broadcast_sink_metadata.source_id = sink->source_id;
+ event.broadcast_sink_metadata.bis_sync = bis_sync_state;
+ event.broadcast_sink_metadata.metadata = subgroup.metadata;
+ event.broadcast_sink_metadata.metadata_length = subgroup.metadata_len;
+
+ /* TODO: filter duplicates */
+
+ ble_audio_event_listener_call(&event);
+ }
+}
+
+static void
+periodic_report_handler(uint16_t pa_sync_handle, const uint8_t *data, uint8_t data_length, uint8_t data_status,
+ int8_t rssi, int8_t tx_power)
+{
+ struct ble_audio_broadcast_sink *sink;
+ struct periodic_report report = {0};
+ int rc;
+
+ if (data_status != BLE_HCI_PERIODIC_DATA_STATUS_COMPLETE) {
+ return;
+ }
+
+ sink = broadcast_lookup_pa_sync_handle(pa_sync_handle);
+ if (sink == NULL) {
+ BLE_HS_LOG_DEBUG("Unknown pa_sync_handle %u\n", pa_sync_handle);
+ return;
+ }
+
+ if (sink->big_sync_state == BIG_SYNC_STATE_PENDING_BASE) {
+ rc = ble_hs_adv_parse(data, data_length, periodic_report_parse, &report);
+ if (rc != 0 || report.uuid16.basic_audio_announcement.length == 0) {
+ BLE_HS_LOG_WARN("source_id %u incorrectly formatted BASE\n", sink->source_id);
+ ble_audio_broadcast_sink_stop(sink->source_id);
+ return;
+ }
+
+ big_sync(sink, report.uuid16.basic_audio_announcement.base, report.uuid16.basic_audio_announcement.length);
+ } else if (sink->big_sync_state == BIG_SYNC_STATE_ACTIVE) {
+ rc = ble_hs_adv_parse(data, data_length, periodic_report_parse, &report);
+ if (rc != 0 || report.uuid16.basic_audio_announcement.length == 0) {
+ BLE_HS_LOG_WARN("source_id %u incorrectly formatted BASE\n", sink->source_id);
+ return;
+ }
+
+ metadata_update(sink, report.uuid16.basic_audio_announcement.base,
+ report.uuid16.basic_audio_announcement.length);
+ }
+}
+
+static int
+broadcast_id_parse_from_adv(const struct ble_hs_adv_field *field, void *user_data)
+{
+ const uint8_t value_len = field->length - sizeof(field->length);
+ uint32_t *broadcast_id = user_data;
+
+ if (field->type == BLE_HS_ADV_TYPE_SVC_DATA_UUID16) {
+ ble_uuid16_t uuid16 = BLE_UUID16_INIT(0);
+ uint8_t offset = 0;
+
+ if (value_len < 2) {
+ /* Continue parsing */
+ return BLE_HS_ENOENT;
+ }
+
+ uuid16.value = get_le16(&field->value[offset]);
+ offset += 2;
+
+ if (uuid16.value == BLE_BROADCAST_AUDIO_ANNOUNCEMENT_SVC_UUID) {
+ if ((value_len - offset) >= 3) {
+ *broadcast_id = get_le24(&field->value[offset]);
+ }
+
+ /* stop parsing */
+ return 0;
+ }
+ }
+
+ /* continue parsing */
+ return BLE_HS_ENOENT;
+}
+
+static void
+ext_disc_handler(const ble_addr_t *addr, uint8_t adv_sid, const uint8_t *data, uint8_t data_length)
+{
+ uint32_t broadcast_id = BROADCAST_ID_INVALID;
+ struct ble_audio_broadcast_sink *sink;
+ int rc;
+
+ if (broadcast_sink_lookup_pa_sync_state(PA_SYNC_STATE_PENDING_DISC) == NULL) {
+ return;
+ }
+
+ rc = ble_hs_adv_parse(data, data_length, broadcast_id_parse_from_adv, &broadcast_id);
+ if (rc != 0 || broadcast_id == BROADCAST_ID_INVALID) {
+ return;
+ }
+
+ sink = broadcast_sink_lookup_adv_sid_broadcast_id_pair(adv_sid, broadcast_id);
+ if (sink == NULL) {
+ return;
+ }
+
+ if (sink->pa_sync_state == PA_SYNC_STATE_PENDING_DISC) {
+ rc = pa_sync_add(addr, adv_sid);
+ if (rc != 0) {
+ /* TODO: */
+ } else {
+ pa_sync_state_set(sink, PA_SYNC_STATE_PENDING_SYNC);
+ }
+ }
+}
+
+static int
+gap_event_handler(struct ble_gap_event *event, void *arg)
+{
+ switch (event->type) {
+ case BLE_GAP_EVENT_PERIODIC_SYNC:
+ periodic_sync_handler(&event->periodic_sync.adv_addr, event->periodic_sync.sid,
+ event->periodic_sync.status, event->periodic_sync.sync_handle);
+ break;
+
+ case BLE_GAP_EVENT_PERIODIC_SYNC_LOST:
+ periodic_sync_lost_handler(event->periodic_sync_lost.sync_handle, event->periodic_sync_lost.reason);
+ break;
+
+ case BLE_GAP_EVENT_PERIODIC_TRANSFER:
+ periodic_sync_handler(&event->periodic_transfer.adv_addr, event->periodic_transfer.sid,
+ event->periodic_transfer.status, event->periodic_transfer.sync_handle);
+ break;
+
+ case BLE_GAP_EVENT_BIGINFO_REPORT:
+ biginfo_report_handler(event->biginfo_report.sync_handle, event->biginfo_report.iso_interval,
+ event->biginfo_report.nse, event->biginfo_report.bn, event->biginfo_report.encryption);
+
+ break;
+
+ case BLE_GAP_EVENT_PERIODIC_REPORT:
+ periodic_report_handler(event->periodic_report.sync_handle, event->periodic_report.data,
+ event->periodic_report.data_length,
+ event->periodic_report.data_status, event->periodic_report.rssi,
+ event->periodic_report.tx_power);
+ break;
+
+ case BLE_GAP_EVENT_EXT_DISC:
+ ext_disc_handler(&event->ext_disc.addr, event->ext_disc.sid, event->ext_disc.data,
+ event->ext_disc.length_data);
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+int
+ble_audio_broadcast_sink_cb_set(ble_audio_broadcast_sink_action_fn *cb, void *arg)
+{
+ if (action_cb.fn != NULL) {
+ return BLE_HS_EALREADY;
+ }
+
+ action_cb.fn = cb;
+ action_cb.arg = arg;
+
+ return 0;
+}
+
+void
+ble_audio_broadcast_sink_code_set(uint8_t source_id, const uint8_t broadcast_code[BLE_AUDIO_BROADCAST_CODE_SIZE])
+{
+ struct ble_audio_broadcast_sink *sink;
+
+ BLE_AUDIO_DBG_ASSERT(broadcast_code != NULL);
+
+ sink = broadcast_sink_get(source_id);
+ if (sink == NULL) {
+ BLE_HS_LOG_DEBUG("Unknown source_id %u\n", source_id);
+ return;
+ }
+
+ if (sink->big_sync_state == BIG_SYNC_STATE_PENDING_CODE) {
+ memcpy(sink->broadcast_code, broadcast_code, BLE_AUDIO_BROADCAST_CODE_SIZE);
+
+ big_sync_state_set(sink, BIG_SYNC_STATE_PENDING_BASE);
+ }
+}
+
+static struct ble_audio_broadcast_sink *
+broadcast_sink_new(uint8_t source_id)
+{
+ struct ble_audio_broadcast_sink *sink;
+
+ sink = os_memblock_get(&ble_audio_broadcast_sink_pool);
+ if (sink == NULL) {
+ BLE_HS_LOG_WARN("Out of memory\n");
+ return NULL;
+ }
+
+ memset(sink, 0, sizeof(*sink));
+
+ sink->source_id = source_id;
+
+ SLIST_INSERT_HEAD(&ble_audio_broadcast_sink_list, sink, next);
+
+ return sink;
+}
+
+static int
+pa_sync_receive(struct ble_audio_broadcast_sink *sink, const uint16_t *conn_handle)
+{
+ struct ble_audio_broadcast_sink_action action;
+ struct ble_audio_scan_delegator_source_desc source_desc;
+ struct ble_gap_periodic_sync_params periodic_sync_params = {0};
+ int rc;
+
+ rc = ble_audio_scan_delegator_source_desc_get(sink->source_id, &source_desc);
+ if (rc != 0) {
+ BLE_HS_LOG_ERROR("source desc get failed (%d)\n", rc);
+ return rc;
+ }
+
+ if (action_cb.fn == NULL) {
+ BLE_HS_LOG_ERROR("action_cb.fn is NULL\n");
+ return BLE_HS_EAPP;
+ }
+
+ action.type = BLE_AUDIO_BROADCAST_SINK_ACTION_PA_SYNC;
+ action.pa_sync.out_params = &periodic_sync_params;
+
+ rc = action_cb.fn(&action, action_cb.arg);
+ if (rc != 0) {
+ BLE_HS_LOG_WARN("pa sync rejected by user (%d)\n", rc);
+ return rc;
+ }
+
+ rc = ble_gap_periodic_adv_sync_receive(
+ *conn_handle, &periodic_sync_params, gap_event_handler, NULL);
+ if (rc != 0) {
+ BLE_HS_LOG_ERROR("sync receive failed (%d)\n", rc);
+ return rc;
+ }
+
+ return 0;
+}
+
+static int
+broadcast_sink_start(uint8_t source_id, const uint8_t *broadcast_code, uint16_t *conn_handle)
+{
+ struct ble_audio_scan_delegator_source_desc source_desc;
+ struct ble_audio_broadcast_sink *sink;
+ int rc;
+
+ rc = ble_audio_scan_delegator_source_desc_get(source_id, &source_desc);
+ if (rc != 0) {
+ BLE_HS_LOG_ERROR("source desc get failed (%d)\n", rc);
+ return rc;
+ }
+
+ sink = broadcast_sink_get(source_id);
+ if (sink == NULL) {
+ sink = broadcast_sink_new(source_id);
+ if (sink == NULL) {
+ return BLE_HS_ENOMEM;
+ }
+ }
+
+ if (broadcast_code != NULL) {
+ memcpy(sink->broadcast_code, broadcast_code, BLE_AUDIO_BROADCAST_CODE_SIZE);
+ sink->broadcast_code_is_valid = true;
+ }
+
+ /* If not previously set, let the application decide which BISes to sync */
+ if (sink->num_subgroups == 0) {
+ sink->num_subgroups = ARRAY_SIZE(sink->subgroups);
+ for (uint8_t i = 0; i < sink->num_subgroups; i++) {
+ sink->subgroups[i].bis_sync = BLE_AUDIO_SCAN_DELEGATOR_BIS_SYNC_ANY;
+ }
+ }
+
+ switch (sink->pa_sync_state) {
+ case PA_SYNC_STATE_PENDING_PAST:
+ case PA_SYNC_STATE_PENDING_DISC:
+ case PA_SYNC_STATE_PENDING_SYNC:
+ case PA_SYNC_STATE_ACTIVE:
+ break;
+
+ case PA_SYNC_STATE_IDLE:
+ case PA_SYNC_STATE_ERROR:
+ case PA_SYNC_STATE_TIMEOUT:
+ if (conn_handle != NULL) {
+ /* sync using PAST procedure */
+ rc = pa_sync_receive(sink, conn_handle);
+ if (rc != 0) {
+ return rc;
+ }
+ pa_sync_state_set(sink, PA_SYNC_STATE_PENDING_PAST);
+ } else if (source_desc.addr.type == BLE_ADDR_PUBLIC) {
+ /* sync to public address (we don't need to scan, as the address won't change) */
+ rc = pa_sync_add(&source_desc.addr, source_desc.adv_sid);
+ if (rc != 0) {
+ return rc;
+ }
+ pa_sync_state_set(sink, PA_SYNC_STATE_PENDING_SYNC);
+ } else {
+ /* scan to find broadcaster using Adv SID and Broadcast ID */
+ rc = disc_start();
+ if (rc != 0) {
+ return rc;
+ }
+ pa_sync_state_set(sink, PA_SYNC_STATE_PENDING_DISC);
+ }
+ break;
+
+ default:
+ BLE_AUDIO_DBG_ASSERT(false);
+ ble_audio_broadcast_sink_stop(source_id);
+ return BLE_HS_EAGAIN;
+ }
+
+ switch (sink->big_sync_state) {
+ case BIG_SYNC_STATE_ACTIVE:
+ case BIG_SYNC_STATE_PENDING_BIG_INFO:
+ case BIG_SYNC_STATE_PENDING_SYNC:
+ case BIG_SYNC_STATE_PENDING_BASE:
+ break;
+
+ case BIG_SYNC_STATE_PENDING_CODE:
+ if (broadcast_code != NULL) {
+ big_sync_state_set(sink, BIG_SYNC_STATE_PENDING_BASE);
+ }
+ break;
+
+ case BIG_SYNC_STATE_FAILED:
+ case BIG_SYNC_STATE_IDLE:
+ big_sync_state_set(sink, BIG_SYNC_STATE_PENDING_BIG_INFO);
+ break;
+
+ default:
+ BLE_AUDIO_DBG_ASSERT(false);
+ ble_audio_broadcast_sink_stop(source_id);
+ return BLE_HS_EAGAIN;
+ }
+
+ if (sink->pa_sync_state == PA_SYNC_STATE_ACTIVE &&
+ sink->big_sync_state == BIG_SYNC_STATE_ACTIVE) {
+ return BLE_HS_EDONE;
+ }
+
+ return 0;
+}
+
+int
+ble_audio_broadcast_sink_start(uint8_t source_id, const struct ble_audio_broadcast_sink_add_params *params)
+{
+ return broadcast_sink_start(source_id,
+ params->broadcast_code_is_valid ? params->broadcast_code : NULL,
+ NULL);
+}
+
+static void
+ble_audio_broadcast_sink_destroy(struct ble_audio_broadcast_sink *sink)
+{
+ os_error_t os_error;
+
+ os_error = os_memblock_put(&ble_audio_broadcast_sink_pool, sink);
+ if (os_error != OS_OK) {
+ BLE_HS_LOG_ERROR("Failed to put memory block (os_error %d)\n", os_error);
+ return;
+ }
+
+ SLIST_REMOVE(&ble_audio_broadcast_sink_list, sink, ble_audio_broadcast_sink, next);
+}
+
+static int
+pa_sync_term(struct ble_audio_broadcast_sink *sink)
+{
+ int rc;
+
+ switch (sink->pa_sync_state) {
+ case PA_SYNC_STATE_ACTIVE:
+ rc = ble_gap_periodic_adv_sync_terminate(sink->pa_sync_handle);
+ if (rc != 0) {
+ BLE_HS_LOG_ERROR("adv sync terminate failed (%d)\n", rc);
+ return rc;
+ }
+ break;
+
+ case PA_SYNC_STATE_PENDING_PAST:
+ rc = ble_gap_periodic_adv_sync_receive(sink->past_conn_handle, NULL, gap_event_handler, NULL);
+ if (rc != 0) {
+ BLE_HS_LOG_ERROR("adv sync receive cancel failed (%d)\n", rc);
+ return rc;
+ }
+ break;
+
+ case PA_SYNC_STATE_PENDING_SYNC: {
+ struct ble_audio_scan_delegator_source_desc source_desc;
+
+ rc = ble_audio_scan_delegator_source_desc_get(sink->source_id, &source_desc);
+ if (rc != 0) {
+ BLE_HS_LOG_ERROR("source desc get failed (%d)\n", rc);
+ return rc;
+ }
+
+ rc = pa_sync_remove(&source_desc.addr, source_desc.adv_sid);
+ if (rc != 0) {
+ return rc;
+ }
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ pa_sync_state_set(sink, PA_SYNC_STATE_IDLE);
+
+ return 0;
+}
+
+static int
+big_sync_term(struct ble_audio_broadcast_sink *sink)
+{
+ int rc;
+
+ switch (sink->big_sync_state) {
+ case BIG_SYNC_STATE_ACTIVE:
+ case BIG_SYNC_STATE_PENDING_SYNC:
+ rc = ble_iso_big_sync_terminate(sink->big_handle);
+ if (rc != 0) {
+ BLE_HS_LOG_ERROR("big sync terminate failed (%d)\n", rc);
+ return rc;
+ }
+ break;
+
+ case BIG_SYNC_STATE_IDLE:
+ case BIG_SYNC_STATE_FAILED:
+ case BIG_SYNC_STATE_PENDING_CODE:
+ case BIG_SYNC_STATE_PENDING_BASE:
+ case BIG_SYNC_STATE_PENDING_BIG_INFO:
+ break;
+ }
+
+ big_sync_state_set(sink, BIG_SYNC_STATE_IDLE);
+
+ return 0;
+}
+
+int
+ble_audio_broadcast_sink_stop(uint8_t source_id)
+{
+ struct ble_audio_broadcast_sink *sink;
+ int rc;
+
+ sink = broadcast_sink_get(source_id);
+ if (sink == NULL) {
+ BLE_HS_LOG_WARN("no sink with source_id=0x%02x\n", source_id);
+ return 0;
+ }
+
+ rc = pa_sync_term(sink);
+ if (rc != 0) {
+ return rc;
+ }
+
+ rc = big_sync_term(sink);
+ if (rc != 0) {
+ return rc;
+ }
+
+ ble_audio_broadcast_sink_destroy(sink);
+
+ return 0;
+}
+
+int
+ble_audio_broadcast_sink_metadata_update(uint8_t source_id,
+ const struct ble_audio_broadcast_sink_metadata_update_params *params)
+{
+ struct ble_audio_broadcast_sink *sink;
+
+ sink = broadcast_sink_get(source_id);
+ if (sink == NULL) {
+ BLE_HS_LOG_WARN("no sink with source_id=0x%02x\n", source_id);
+ return 0;
+ }
+
+ return ble_audio_scan_delegator_metadata_update(sink->source_id, params->subgroup_index, params->metadata,
+ params->metadata_length);
+}
+
+int
+ble_audio_broadcast_sink_config(uint8_t source_id, uint16_t conn_handle,
+ const struct ble_audio_scan_delegator_sync_opt *sync_opt)
+{
+ struct ble_audio_broadcast_sink *sink;
+ int rc;
+
+ BLE_AUDIO_DBG_ASSERT(sync_opt != NULL);
+
+ sink = broadcast_sink_get(source_id);
+ if (sink == NULL) {
+ if (sync_opt->pa_sync != BLE_AUDIO_SCAN_DELEGATOR_PA_SYNC_DO_NOT_SYNC) {
+ sink = broadcast_sink_new(source_id);
+ if (sink == NULL) {
+ return BLE_HS_ENOMEM;
+ }
+ } else {
+ /* nothing to do */
+ return 0;
+ }
+ }
+
+ if (sync_opt->pa_sync != BLE_AUDIO_SCAN_DELEGATOR_PA_SYNC_DO_NOT_SYNC) {
+ /* TODO: Skip if the BIS Sync is same */
+ if (sink->num_subgroups != 0) {
+ rc = big_sync_term(sink);
+ if (rc != 0) {
+ return rc;
+ }
+ }
+
+ sink->num_subgroups = sync_opt->num_subgroups;
+
+ for (uint8_t subgroup_index = 0; subgroup_index < sink->num_subgroups; subgroup_index++) {
+ sink->subgroups[subgroup_index].bis_sync = sync_opt->subgroups[subgroup_index].bis_sync;
+ }
+
+ if (sync_opt->pa_sync == BLE_AUDIO_SCAN_DELEGATOR_PA_SYNC_PAST_NOT_AVAILABLE) {
+ rc = broadcast_sink_start(source_id, NULL, NULL);
+ } else {
+ rc = broadcast_sink_start(source_id, NULL, &conn_handle);
+ }
+ } else {
+ rc = pa_sync_term(sink);
+ }
+
+ return rc;
+}
+
+int
+ble_audio_broadcast_sink_init(void)
+{
+ static struct ble_gap_event_listener gap_event_listener;
+ int rc;
+
+ /* Ensure this function only gets called by sysinit. */
+ SYSINIT_ASSERT_ACTIVE();
+
+ rc = os_mempool_init(&ble_audio_broadcast_sink_pool,
+ MYNEWT_VAL(BLE_AUDIO_BROADCAST_SINK_MAX),
+ sizeof(struct ble_audio_broadcast_sink),
+ ble_audio_broadcast_sink_mem,
+ "ble_audio_broadcast_sink_pool");
+ SYSINIT_PANIC_ASSERT(rc == 0);
+
+ rc = ble_gap_event_listener_register(&gap_event_listener, gap_event_handler, NULL);
+ SYSINIT_PANIC_ASSERT(rc == 0);
+
+ return 0;
+}
+#endif /* BLE_AUDIO_BROADCAST_SINK */
diff --git a/nimble/host/audio/src/ble_audio_priv.h b/nimble/host/audio/src/ble_audio_broadcast_sink_priv.h
similarity index 63%
copy from nimble/host/audio/src/ble_audio_priv.h
copy to nimble/host/audio/src/ble_audio_broadcast_sink_priv.h
index 10f0109d..ad799014 100644
--- a/nimble/host/audio/src/ble_audio_priv.h
+++ b/nimble/host/audio/src/ble_audio_broadcast_sink_priv.h
@@ -17,11 +17,18 @@
* under the License.
*/
-#ifndef H_BLE_AUDIO_PRIV_
-#define H_BLE_AUDIO_PRIV_
+#ifndef H_BLE_AUDIO_BROADCAST_SINK_PRIV_
+#define H_BLE_AUDIO_BROADCAST_SINK_PRIV_
+#include <stdint.h>
#include "audio/ble_audio.h"
+#include "audio/ble_audio_broadcast_sink.h"
+#include "audio/ble_audio_scan_delegator.h"
-int ble_audio_event_listener_call(struct ble_audio_event *event);
+int ble_audio_broadcast_sink_config(
+ uint8_t source_id, uint16_t conn_handle,
+ const struct ble_audio_scan_delegator_sync_opt *sync_opt);
+void ble_audio_broadcast_sink_code_set(
+ uint8_t source_id, const uint8_t broadcast_code[16]);
-#endif /* H_BLE_AUDIO_PRIV_ */
+#endif /* H_BLE_AUDIO_BROADCAST_SINK_PRIV_ */
diff --git a/nimble/host/audio/src/ble_audio_priv.h b/nimble/host/audio/src/ble_audio_priv.h
index 10f0109d..d575c549 100644
--- a/nimble/host/audio/src/ble_audio_priv.h
+++ b/nimble/host/audio/src/ble_audio_priv.h
@@ -22,6 +22,17 @@
#include "audio/ble_audio.h"
+#define MIN(_a, _b) ((_a) < (_b) ? (_a) : (_b))
+#define MAX(_a, _b) ((_a) > (_b) ? (_a) : (_b))
+
+#if MYNEWT_VAL(BLE_HS_DEBUG)
+#define BLE_AUDIO_DBG_ASSERT(x) assert(x)
+#define BLE_AUDIO_DBG_ASSERT_EVAL(x) assert(x)
+#else
+#define BLE_AUDIO_DBG_ASSERT(x)
+#define BLE_AUDIO_DBG_ASSERT_EVAL(x) ((void)(x))
+#endif
+
int ble_audio_event_listener_call(struct ble_audio_event *event);
#endif /* H_BLE_AUDIO_PRIV_ */
diff --git a/nimble/host/audio/src/ble_audio_scan_delegator.c b/nimble/host/audio/src/ble_audio_scan_delegator.c
new file mode 100644
index 00000000..db580c63
--- /dev/null
+++ b/nimble/host/audio/src/ble_audio_scan_delegator.c
@@ -0,0 +1,460 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include "sysinit/sysinit.h"
+
+#if MYNEWT_VAL(BLE_AUDIO_SCAN_DELEGATOR)
+#include "audio/ble_audio.h"
+#include "audio/ble_audio_broadcast_sink.h"
+#include "audio/ble_audio_scan_delegator.h"
+#include "host/ble_hs.h"
+#include "host/ble_uuid.h"
+
+#include "../services/bass/include/services/bass/ble_audio_svc_bass.h"
+
+#include "ble_audio_priv.h"
+#include "ble_audio_broadcast_sink_priv.h"
+
+static ble_audio_scan_delegator_action_fn *action_cb;
+
+int
+ble_audio_scan_delegator_source_desc_get(uint8_t source_id, struct ble_audio_scan_delegator_source_desc *source_desc)
+{
+ struct ble_svc_audio_bass_receiver_state *state;
+ int rc;
+
+ rc = ble_svc_audio_bass_receiver_state_get(source_id, &state);
+ if (rc != 0) {
+ BLE_HS_LOG_ERROR("bass receiver state get failed (%d)\n", rc);
+ return rc;
+ }
+
+ source_desc->addr = state->source_addr;
+ source_desc->adv_sid = state->source_adv_sid;
+ source_desc->broadcast_id = state->broadcast_id;
+
+ return 0;
+}
+
+int
+ble_audio_scan_delegator_receive_state_get(uint8_t source_id, struct ble_audio_scan_delegator_receive_state *out_state)
+{
+ struct ble_svc_audio_bass_receiver_state *state;
+ int rc;
+
+ rc = ble_svc_audio_bass_receiver_state_get(source_id, &state);
+ if (rc != 0) {
+ BLE_HS_LOG_ERROR("bass receiver state get failed (%d)\n", rc);
+ return rc;
+ }
+
+ out_state->pa_sync_state = (uint8_t)state->pa_sync_state;
+ out_state->big_enc = (uint8_t)state->big_encryption;
+ if (out_state->big_enc == BLE_AUDIO_SCAN_DELEGATOR_BIG_ENC_BROADCAST_CODE_INVALID) {
+ memcpy(out_state->bad_code, state->bad_code, sizeof(out_state->bad_code));
+ }
+ out_state->num_subgroups = state->num_subgroups;
+ for (uint8_t i = 0; i < out_state->num_subgroups; i++) {
+ out_state->subgroups[i].bis_sync = state->subgroups[i].bis_sync_state;
+ out_state->subgroups[i].metadata = state->subgroups[i].metadata;
+ out_state->subgroups[i].metadata_length = state->subgroups[i].metadata_length;
+ }
+
+ return 0;
+}
+
+int
+ble_audio_scan_delegator_metadata_update(uint8_t source_id, uint8_t subgroup_index, const uint8_t *metadata,
+ uint8_t metadata_length)
+{
+ struct ble_svc_audio_bass_metadata_params params;
+
+ params.subgroup_idx = subgroup_index;
+ params.metadata = metadata;
+ params.metadata_length = metadata_length;
+
+ return ble_svc_audio_bass_update_metadata(¶ms, source_id);
+}
+
+static int
+action_call(struct ble_audio_scan_delegator_action *action, void *arg)
+{
+ int rc;
+
+ if (action_cb == NULL) {
+ BLE_HS_LOG_ERROR("callback is NULL\n");
+ return BLE_HS_EAPP;
+ }
+
+ rc = action_cb(action, arg);
+ if (rc != 0) {
+ return rc;
+ }
+
+ return 0;
+}
+
+static void
+source_desc_init(struct ble_audio_scan_delegator_source_desc *source_desc, const ble_addr_t *addr, uint8_t adv_sid,
+ uint32_t broadcast_id)
+{
+ source_desc->addr = *addr;
+ source_desc->adv_sid = adv_sid;
+ source_desc->broadcast_id = broadcast_id;
+}
+
+static void
+subgroups_init(struct ble_audio_scan_delegator_subgroup *subgroups,
+ const struct ble_svc_audio_bass_subgroup *bass_subgroups, uint8_t num_subgroups)
+{
+ BLE_AUDIO_DBG_ASSERT(num_subgroups < MYNEWT_VAL(BLE_AUDIO_SCAN_DELEGATOR_SUBGROUP_MAX));
+
+ for (uint8_t i = 0; i < num_subgroups; i++) {
+ subgroups[i].bis_sync = bass_subgroups[i].bis_sync_state;
+ subgroups[i].metadata_length = bass_subgroups[i].metadata_length;
+ subgroups[i].metadata = bass_subgroups[i].metadata;
+ }
+}
+
+static void
+sync_opt_init(struct ble_audio_scan_delegator_sync_opt *sync_opt, enum ble_svc_audio_bass_pa_sync pa_sync,
+ uint16_t pa_interval, const struct ble_svc_audio_bass_subgroup *bass_subgroups, uint8_t num_subgroups)
+{
+ sync_opt->pa_sync = (uint8_t)pa_sync;
+ sync_opt->pa_interval = pa_interval;
+ sync_opt->num_subgroups = num_subgroups;
+ if (sync_opt->num_subgroups > 0) {
+ subgroups_init(sync_opt->subgroups, bass_subgroups, num_subgroups);
+ }
+}
+
+static int
+bass_add_source_op_handler(struct ble_svc_audio_bass_operation *op, void *arg)
+{
+ struct ble_audio_scan_delegator_action action = {0};
+ const uint8_t source_id = op->add_source.source_id;
+ int rc;
+
+ source_desc_init(&action.source_add.source_desc, &op->add_source.adv_addr,
+ op->add_source.adv_sid, op->add_source.broadcast_id);
+ sync_opt_init(&action.source_add.sync_opt, op->add_source.pa_sync, op->add_source.pa_interval,
+ op->add_source.subgroups, op->add_source.num_subgroups);
+
+ action.type = BLE_AUDIO_SCAN_DELEGATOR_ACTION_SOURCE_ADD;
+ action.source_add.source_id = source_id;
+ action.source_add.out_source_id_to_swap = op->add_source.out_source_id_to_swap;
+
+ rc = action_call(&action, arg);
+ if (rc != 0) {
+ BLE_HS_LOG_DEBUG("API callback (%d)\n", rc);
+ return rc;
+ }
+
+#if MYNEWT_VAL(BLE_AUDIO_BROADCAST_SINK)
+ if (op->add_source.out_source_id_to_swap != NULL) {
+ rc = ble_audio_broadcast_sink_stop(*op->add_source.out_source_id_to_swap);
+ if (rc != 0) {
+ BLE_HS_LOG_WARN("sink stop failed (%d)\n", rc);
+ }
+ }
+
+ rc = ble_audio_broadcast_sink_config(source_id, op->conn_handle, &action.source_add.sync_opt);
+ if (rc != 0) {
+ BLE_HS_LOG_WARN("sink config failed (%d)\n", rc);
+ }
+#endif /* BLE_AUDIO_BROADCAST_SINK */
+
+ return 0;
+}
+
+static int
+bass_modify_source_op_handler(struct ble_svc_audio_bass_operation *op, void *arg)
+{
+ struct ble_audio_scan_delegator_action action = {0};
+ struct ble_audio_scan_delegator_sync_opt *sync_opt;
+ const uint8_t source_id = op->modify_source.source_id;
+ int rc;
+
+ sync_opt = &action.source_modify.sync_opt;
+ sync_opt_init(sync_opt, op->modify_source.pa_sync, op->modify_source.pa_interval, NULL, 0);
+
+ BLE_AUDIO_DBG_ASSERT(sync_opt->num_subgroups < ARRAY_SIZE(subgroups));
+
+ for (uint8_t i = 0; i < sync_opt->num_subgroups; i++) {
+ sync_opt->subgroups[i].bis_sync = op->modify_source.bis_sync[i];
+ /* FIXME: Missing metadata in Modify Source */
+ }
+
+ action.type = BLE_AUDIO_SCAN_DELEGATOR_ACTION_SOURCE_MODIFY;
+ action.source_modify.source_id = source_id;
+
+ rc = action_call(&action, arg);
+ if (rc != 0) {
+ return rc;
+ }
+
+#if MYNEWT_VAL(BLE_AUDIO_BROADCAST_SINK)
+ rc = ble_audio_broadcast_sink_config(source_id, op->conn_handle, sync_opt);
+ if (rc != 0) {
+ BLE_HS_LOG_WARN("sink config failed (%d)\n", rc);
+ }
+#endif /* BLE_AUDIO_BROADCAST_SINK */
+
+ return 0;
+}
+
+static int
+bass_remove_source_op_handler(struct ble_svc_audio_bass_operation *op, void *arg)
+{
+ struct ble_audio_scan_delegator_action action;
+ const uint8_t source_id = op->remove_source.source_id;
+ int rc;
+
+ action.type = BLE_AUDIO_SCAN_DELEGATOR_ACTION_SOURCE_REMOVE;
+ action.source_remove.source_id = source_id;
+
+ rc = action_call(&action, arg);
+ if (rc != 0) {
+ return rc;
+ }
+
+#if MYNEWT_VAL(BLE_AUDIO_BROADCAST_SINK)
+ rc = ble_audio_broadcast_sink_stop(source_id);
+ if (rc != 0) {
+ BLE_HS_LOG_WARN("sink stop failed (%d)\n", rc);
+ }
+#endif /* BLE_AUDIO_BROADCAST_SINK */
+
+ return 0;
+}
+
+static int
+bass_accept_fn(struct ble_svc_audio_bass_operation *op, void *arg)
+{
+ switch (op->op) {
+ case BLE_SVC_AUDIO_BASS_OPERATION_ADD_SOURCE:
+ return bass_add_source_op_handler(op, arg);
+
+ case BLE_SVC_AUDIO_BASS_OPERATION_MODIFY_SOURCE:
+ return bass_modify_source_op_handler(op, arg);
+
+ case BLE_SVC_AUDIO_BASS_OPERATION_REMOVE_SOURCE:
+ return bass_remove_source_op_handler(op, arg);
+
+ default:
+ return BLE_HS_ENOTSUP;
+ }
+}
+
+int
+ble_audio_scan_delegator_action_fn_set(ble_audio_scan_delegator_action_fn *fn, void *arg)
+{
+ int rc;
+
+ if (fn == NULL) {
+ BLE_HS_LOG_ERROR("callback is NULL\n");
+ return BLE_HS_EINVAL;
+ }
+
+ if (action_cb != NULL) {
+ return BLE_HS_EALREADY;
+ }
+
+ action_cb = fn;
+
+ rc = ble_svc_audio_bass_accept_fn_set(bass_accept_fn, arg);
+ if (rc != 0) {
+ action_cb = NULL;
+ }
+
+ return 0;
+}
+
+int
+ble_audio_scan_delegator_receive_state_add(const struct ble_audio_scan_delegator_receive_state_add_params *params,
+ uint8_t *source_id)
+{
+ struct ble_svc_audio_bass_receiver_state_add_params bass_params = {0};
+
+ if (params == NULL) {
+ BLE_HS_LOG_ERROR("NULL params\n");
+ return BLE_HS_EINVAL;
+ }
+
+ if (source_id == NULL) {
+ BLE_HS_LOG_ERROR("NULL source_id\n");
+ return BLE_HS_EINVAL;
+ }
+
+ bass_params.source_addr = params->source_desc.addr;
+ bass_params.source_adv_sid = params->source_desc.adv_sid;
+ bass_params.broadcast_id = params->source_desc.broadcast_id;
+ bass_params.pa_sync_state = (uint8_t)params->state.pa_sync_state;
+ bass_params.big_encryption = (uint8_t)params->state.big_enc;
+
+ if (params->state.big_enc == BLE_AUDIO_SCAN_DELEGATOR_BIG_ENC_BROADCAST_CODE_INVALID) {
+ bass_params.bad_code = params->state.bad_code;
+ } else {
+ bass_params.bad_code = NULL;
+ }
+
+ bass_params.num_subgroups = params->state.num_subgroups;
+ if (bass_params.num_subgroups > BLE_SVC_AUDIO_BASS_SUB_NUM_MAX) {
+ BLE_HS_LOG_ERROR("num_subgroups above the limit\n");
+ return BLE_HS_ENOMEM;
+ }
+
+ for (uint8_t i = 0; i < bass_params.num_subgroups; i++) {
+ bass_params.subgroups[i].bis_sync_state = params->state.subgroups->bis_sync;
+ bass_params.subgroups[i].metadata_length = params->state.subgroups->metadata_length;
+ bass_params.subgroups[i].metadata = params->state.subgroups->metadata;
+ }
+
+ return ble_svc_audio_bass_receive_state_add(&bass_params, source_id);
+}
+
+int
+ble_audio_scan_delegator_receive_state_remove(uint8_t source_id)
+{
+ int rc;
+
+#if MYNEWT_VAL(BLE_AUDIO_BROADCAST_SINK)
+ rc = ble_audio_broadcast_sink_stop(source_id);
+ if (rc != 0) {
+ BLE_HS_LOG_WARN("sink stop failed (%d)\n", rc);
+ }
+#endif /* BLE_AUDIO_BROADCAST_SINK */
+
+ return ble_svc_audio_bass_receive_state_remove(source_id);
+}
+
+int
+ble_audio_scan_delegator_receive_state_set(uint8_t source_id,
+ const struct ble_audio_scan_delegator_receive_state *state)
+{
+ struct ble_svc_audio_bass_update_params bass_params;
+ int rc;
+
+ if (state == NULL) {
+ BLE_HS_LOG_ERROR("NULL state\n");
+ return BLE_HS_EINVAL;
+ }
+
+ bass_params.pa_sync_state = (uint8_t)state->pa_sync_state;
+ bass_params.big_encryption = (uint8_t)state->big_enc;
+
+ if (state->big_enc == BLE_AUDIO_SCAN_DELEGATOR_BIG_ENC_BROADCAST_CODE_INVALID) {
+ bass_params.bad_code = state->bad_code;
+ } else {
+ bass_params.bad_code = NULL;
+ }
+
+ bass_params.num_subgroups = state->num_subgroups;
+ if (bass_params.num_subgroups > BLE_SVC_AUDIO_BASS_SUB_NUM_MAX) {
+ BLE_HS_LOG_ERROR("num_subgroups above the limit\n");
+ return BLE_HS_ENOMEM;
+ }
+
+ for (uint8_t i = 0; i < bass_params.num_subgroups; i++) {
+ bass_params.subgroups[i].bis_sync_state = state->subgroups[i].bis_sync;
+ bass_params.subgroups[i].metadata_length = state->subgroups[i].metadata_length;
+ bass_params.subgroups[i].metadata = state->subgroups[i].metadata;
+ }
+
+ rc = ble_svc_audio_bass_receive_state_update(&bass_params, source_id);
+ if (rc != 0) {
+ BLE_HS_LOG_ERROR("Failed to update receive state (rc %d)\n", rc);
+ }
+
+ return 0;
+}
+
+void
+ble_audio_scan_delegator_receive_state_foreach(ble_audio_scan_delegator_receive_state_foreach_fn *fn, void *arg)
+{
+ struct ble_audio_scan_delegator_receive_state_entry entry;
+ int rc;
+
+ if (fn == NULL) {
+ BLE_HS_LOG_ERROR("callback is NULL\n");
+ return;
+ }
+
+ for (int i = 0; i < MYNEWT_VAL(BLE_AUDIO_SCAN_DELEGATOR_RECEIVE_STATE_MAX); i++) {
+ struct ble_svc_audio_bass_receiver_state *state;
+ uint8_t source_id;
+
+ rc = ble_svc_audio_bass_source_id_get(i, &source_id);
+ if (rc != 0) {
+ continue;
+ }
+
+ rc = ble_svc_audio_bass_receiver_state_get(source_id, &state);
+ if (rc != 0) {
+ BLE_HS_LOG_ERROR("Failed to get receiver state (rc %d)\n", rc);
+ continue;
+ }
+
+ entry.source_id = source_id;
+ source_desc_init(&entry.source_desc, &state->source_addr, state->source_adv_sid, state->source_adv_sid);
+ entry.state.pa_sync_state = (uint8_t)state->pa_sync_state;
+ entry.state.big_enc = (uint8_t)state->big_encryption;
+ if (entry.state.big_enc == BLE_AUDIO_SCAN_DELEGATOR_BIG_ENC_BROADCAST_CODE_INVALID) {
+ memcpy(entry.state.bad_code, state->bad_code, sizeof(entry.state.bad_code));
+ }
+ entry.state.num_subgroups = state->num_subgroups;
+ subgroups_init(entry.state.subgroups, state->subgroups, state->num_subgroups);
+
+ if (fn(&entry, arg) != 0) {
+ break;
+ }
+ }
+}
+
+#if MYNEWT_VAL(BLE_AUDIO_BROADCAST_SINK)
+static int
+audio_event_handler(struct ble_audio_event *event, void *arg)
+{
+ if (event->type == BLE_AUDIO_EVENT_BASS_BROADCAST_CODE_SET) {
+ ble_audio_broadcast_sink_code_set(event->bass_set_broadcast_code.source_id,
+ event->bass_set_broadcast_code.broadcast_code);
+ }
+
+ return 0;
+}
+#endif /* BLE_AUDIO_BROADCAST_SINK */
+
+int
+ble_audio_scan_delegator_init(void)
+{
+ int rc;
+
+ /* Ensure this function only gets called by sysinit. */
+ SYSINIT_ASSERT_ACTIVE();
+
+#if MYNEWT_VAL(BLE_AUDIO_BROADCAST_SINK)
+ static struct ble_audio_event_listener listener;
+
+ rc = ble_audio_event_listener_register(&listener, audio_event_handler, NULL);
+ SYSINIT_PANIC_ASSERT(rc == 0);
+#endif /* BLE_AUDIO_BROADCAST_SINK */
+
+ return rc;
+}
+#endif /* BLE_AUDIO_SCAN_DELEGATOR */
diff --git a/nimble/host/audio/src/ble_audio_priv.h b/nimble/host/audio/src/ble_audio_scan_delegator_priv.h
similarity index 63%
copy from nimble/host/audio/src/ble_audio_priv.h
copy to nimble/host/audio/src/ble_audio_scan_delegator_priv.h
index 10f0109d..ae5fc651 100644
--- a/nimble/host/audio/src/ble_audio_priv.h
+++ b/nimble/host/audio/src/ble_audio_scan_delegator_priv.h
@@ -17,11 +17,17 @@
* under the License.
*/
-#ifndef H_BLE_AUDIO_PRIV_
-#define H_BLE_AUDIO_PRIV_
+#ifndef H_BLE_AUDIO_SCAN_DELEGATOR_PRIV_
+#define H_BLE_AUDIO_SCAN_DELEGATOR_PRIV_
+#include <stdint.h>
#include "audio/ble_audio.h"
+#include "audio/ble_audio_scan_delegator.h"
-int ble_audio_event_listener_call(struct ble_audio_event *event);
+int ble_audio_scan_delegator_source_desc_get(
+ uint8_t source_id, struct ble_audio_scan_delegator_source_desc *source_desc);
+int ble_audio_scan_delegator_metadata_update(
+ uint8_t source_id, uint8_t subgroup_index, const uint8_t *metadata,
+ uint8_t metadata_length);
-#endif /* H_BLE_AUDIO_PRIV_ */
+#endif /* H_BLE_AUDIO_SCAN_DELEGATOR_PRIV_ */
diff --git a/nimble/host/audio/syscfg.yml b/nimble/host/audio/syscfg.yml
index 5f0269e0..5368e503 100644
--- a/nimble/host/audio/syscfg.yml
+++ b/nimble/host/audio/syscfg.yml
@@ -16,10 +16,87 @@
# under the License.
#
+# syscfg.defs section
syscfg.defs:
BLE_AUDIO_MAX_CODEC_RECORDS:
description: >
Maximum number of registered audio codecs.
value: 0
-syscfg.logs:
+ BLE_AUDIO_BROADCAST_SINK:
+ description: >
+ This option enables BLE Audio Sink support.
+ value: 0
+ restrictions:
+ - '(BLE_ISO_BROADCAST_SINK > 0) if 1'
+
+ BLE_AUDIO_SCAN_DELEGATOR:
+ description: >
+ This option enables BLE Audio Scan Delegator support.
+ value: 0
+
+syscfg.defs.BLE_AUDIO_BROADCAST_SINK:
+ BLE_AUDIO_BROADCAST_SINK_SYSINIT_STAGE:
+ description: >
+ Primary sysinit stage for BLE Audio Broadcast Sink.
+ value: 500
+
+ BLE_AUDIO_BROADCAST_SINK_LOG_MOD:
+ description: 'Numeric module ID to use for BLE Audio Broadcast Sink log messages.'
+ value: 28
+
+ BLE_AUDIO_BROADCAST_SINK_LOG_LVL:
+ description: 'Minimum level for the BLE Audio Broadcast Sink log log.'
+ value: 1
+
+ BLE_AUDIO_BROADCAST_SINK_MAX:
+ description: >
+ Maximum umber of Audio Broadcast Sink instances.
+ value: 'MYNEWT_VAL_BLE_ISO_MAX_BIGS'
+
+syscfg.defs.BLE_AUDIO_SCAN_DELEGATOR:
+ BLE_AUDIO_SCAN_DELEGATOR_SYSINIT_STAGE:
+ description: >
+ Primary sysinit stage for BLE Audio Scan Delegator.
+ value: 499
+
+ BLE_AUDIO_SCAN_DELEGATOR_LOG_MOD:
+ description: 'Numeric module ID to use for BLE Audio Scan Delegator log messages.'
+ value: 29
+
+ BLE_AUDIO_SCAN_DELEGATOR_LOG_LVL:
+ description: 'Minimum level for the BLE Audio Scan Delegator log log.'
+ value: 1
+
+ BLE_AUDIO_SCAN_DELEGATOR_RECEIVE_STATE_MAX:
+ description: >
+ Maximum number of Receive State instances.
+ value: MYNEWT_VAL(BLE_SVC_AUDIO_BASS_RECEIVE_STATE_MAX)
+
+ BLE_AUDIO_SCAN_DELEGATOR_SUBGROUP_MAX:
+ description: >
+ Maximum number of Subgroups per Receive State.
+ value: MYNEWT_VAL(BLE_SVC_AUDIO_BASS_SUB_NUM_MAX)
+
+ BLE_AUDIO_SCAN_DELEGATOR_STANDALONE:
+ description: >
+ This option enables the BLE Audio Scan Delegator as standalone device.
+ value: 1
+ restrictions:
+ - '(BLE_AUDIO_BROADCAST_SINK == 0) if 1'
+
+# syscfg.logs section
+syscfg.logs.BLE_AUDIO_BROADCAST_SINK:
+ BLE_AUDIO_BROADCAST_SINK_LOG:
+ module: MYNEWT_VAL(BLE_AUDIO_BROADCAST_SINK_LOG_MOD)
+ level: MYNEWT_VAL(BLE_AUDIO_BROADCAST_SINK_LOG_LVL)
+
+syscfg.logs.BLE_AUDIO_SCAN_DELEGATOR:
+ BLE_AUDIO_SCAN_DELEGATOR_LOG:
+ module: MYNEWT_VAL(BLE_AUDIO_SCAN_DELEGATOR_LOG_MOD)
+ level: MYNEWT_VAL(BLE_AUDIO_SCAN_DELEGATOR_LOG_LVL)
+
+# syscfg.vals section
+syscfg.vals.BLE_AUDIO_BROADCAST_SINK:
+ BLE_AUDIO_SCAN_DELEGATOR: 1
+ BLE_AUDIO_SCAN_DELEGATOR_STANDALONE: 0
(mynewt-nimble) 03/03: nimble/audio: Add PACS to Broadcast Sink
Posted by ry...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
rymek pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/mynewt-nimble.git
commit 060266094e2af884f4487fccba391c3e2a96ff23
Author: Mariusz Skamra <ma...@codecoup.pl>
AuthorDate: Tue Apr 30 09:46:10 2024 +0200
nimble/audio: Add PACS to Broadcast Sink
This enables PACS when Broadcast Sink role is enabled. By default the
16_2 and 24_2 mandatory settings are supported as per spec.
---
nimble/host/audio/pkg.yml | 3 +++
nimble/host/audio/services/pacs/lc3/syscfg.yml | 9 +++++++++
nimble/host/audio/syscfg.yml | 1 +
3 files changed, 13 insertions(+)
diff --git a/nimble/host/audio/pkg.yml b/nimble/host/audio/pkg.yml
index 5dd14616..43385d27 100644
--- a/nimble/host/audio/pkg.yml
+++ b/nimble/host/audio/pkg.yml
@@ -31,6 +31,9 @@ pkg.deps:
- nimble
- nimble/host
+pkg.deps.BLE_AUDIO_BROADCAST_SINK:
+ - nimble/host/audio/services/pacs/lc3
+
pkg.deps.BLE_AUDIO_SCAN_DELEGATOR:
- nimble/host/audio/services/bass
diff --git a/nimble/host/audio/services/pacs/lc3/syscfg.yml b/nimble/host/audio/services/pacs/lc3/syscfg.yml
index f2a2ff06..2a12d89b 100644
--- a/nimble/host/audio/services/pacs/lc3/syscfg.yml
+++ b/nimble/host/audio/services/pacs/lc3/syscfg.yml
@@ -129,3 +129,12 @@ syscfg.defs:
Audio Locations supported by sink codec. Value is an any combination of values defined
in Bluetooth Assigned Numbers 6.12.3. Default: Media
value: 0x0004
+
+syscfg.vals.BLE_AUDIO_BROADCAST_SINK:
+ BLE_SVC_AUDIO_PACS_LC3_SNK_SAMPLING_FREQUENCIES: >
+ BLE_AUDIO_CODEC_SUPPORTED_SAMPLING_RATE_16000_HZ || BLE_AUDIO_CODEC_SUPPORTED_SAMPLING_RATE_24000_HZ
+ BLE_SVC_AUDIO_PACS_LC3_SNK_FRAME_DURATIONS: BLE_AUDIO_CODEC_SUPPORTED_FRAME_DURATION_10_MS
+ BLE_SVC_AUDIO_PACS_LC3_SNK_AUDIO_CHANNEL_COUNTS: BLE_AUDIO_CODEC_SUPPORTED_CHANNEL_COUNT_1
+ BLE_SVC_AUDIO_PACS_LC3_SNK_MIN_OCTETS_PER_CODEC_FRAME: 40
+ BLE_SVC_AUDIO_PACS_LC3_SNK_MAX_OCTETS_PER_CODEC_FRAME: 60
+ BLE_SVC_AUDIO_PACS_LC3_SNK_MAX_CODEC_FRAMES_PER_SDU: 1
diff --git a/nimble/host/audio/syscfg.yml b/nimble/host/audio/syscfg.yml
index 5368e503..a5c8907f 100644
--- a/nimble/host/audio/syscfg.yml
+++ b/nimble/host/audio/syscfg.yml
@@ -100,3 +100,4 @@ syscfg.logs.BLE_AUDIO_SCAN_DELEGATOR:
syscfg.vals.BLE_AUDIO_BROADCAST_SINK:
BLE_AUDIO_SCAN_DELEGATOR: 1
BLE_AUDIO_SCAN_DELEGATOR_STANDALONE: 0
+ BLE_AUDIO_MAX_CODEC_RECORDS: 2
(mynewt-nimble) 02/03: apps/btshell: Add Audio Broadcast Sink to shell application
Posted by ry...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
rymek pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/mynewt-nimble.git
commit 868ecb0bdbec073aa6e4830f1cab9da323740193
Author: Mariusz Skamra <ma...@codecoup.pl>
AuthorDate: Thu Jan 25 13:28:54 2024 +0100
apps/btshell: Add Audio Broadcast Sink to shell application
this extends the btshell application with Audio Broadcast Sink
functionality.
---
apps/btshell/src/btshell.h | 2 +
apps/btshell/src/cmd.c | 86 ++-
apps/btshell/src/cmd.h | 7 +
apps/btshell/src/cmd_leaudio.c | 705 ++++++++++++++++++++-
apps/btshell/src/cmd_leaudio.h | 20 +
apps/btshell/src/main.c | 6 +
apps/btshell/syscfg.yml | 3 +
nimble/host/audio/targets/btshell_native/pkg.yml | 1 +
.../host/audio/targets/btshell_native/syscfg.yml | 5 +-
9 files changed, 830 insertions(+), 5 deletions(-)
diff --git a/apps/btshell/src/btshell.h b/apps/btshell/src/btshell.h
index c2a2eb3e..3d94c212 100644
--- a/apps/btshell/src/btshell.h
+++ b/apps/btshell/src/btshell.h
@@ -222,6 +222,8 @@ int btshell_broadcast_start(uint8_t adv_instance);
int btshell_broadcast_stop(uint8_t adv_instance);
#endif
+void btshell_leaudio_init(void);
+
int btshell_gap_event(struct ble_gap_event *event, void *arg);
void btshell_sync_stats(uint16_t handle);
uint8_t btshell_get_default_own_addr_type(void);
diff --git a/apps/btshell/src/cmd.c b/apps/btshell/src/cmd.c
index 50aac794..a9758e14 100644
--- a/apps/btshell/src/cmd.c
+++ b/apps/btshell/src/cmd.c
@@ -87,14 +87,28 @@ static const struct parse_arg_kv_pair cmd_peer_addr_types[] = {
{ NULL }
};
-static const struct parse_arg_kv_pair cmd_addr_type[] = {
+const struct parse_arg_kv_pair cmd_addr_type[] = {
{ "public", BLE_ADDR_PUBLIC },
{ "random", BLE_ADDR_RANDOM },
{ NULL }
};
+const char *
+cmd_addr_type_str(uint8_t type)
+{
+ const struct parse_arg_kv_pair *kvs = cmd_addr_type;
+ int i;
-static int
+ for (i = 0; kvs[i].key != NULL; i++) {
+ if (type == kvs[i].val) {
+ return kvs[i].key;
+ }
+ }
+
+ return "unknown";
+}
+
+int
parse_dev_addr(const char *prefix, const struct parse_arg_kv_pair *addr_types,
ble_addr_t *addr)
{
@@ -152,6 +166,12 @@ parse_dev_addr(const char *prefix, const struct parse_arg_kv_pair *addr_types,
return 0;
}
+int
+cmd_parse_addr(const char *prefix, ble_addr_t *addr)
+{
+ return parse_dev_addr(prefix, cmd_addr_type, addr);
+}
+
/*****************************************************************************
* $advertise *
*****************************************************************************/
@@ -4924,7 +4944,67 @@ static const struct shell_cmd btshell_commands[] = {
.help = &leaudio_broadcast_stop_help,
#endif
},
-#endif /* BLE_ISO_BROADCAST_SOURCE */
+#endif
+#if MYNEWT_VAL(BLE_AUDIO_BROADCAST_SINK)
+ {
+ .sc_cmd = "broadcast-sink-start",
+ .sc_cmd_func = cmd_leaudio_broadcast_sink_start,
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+ .help = &cmd_leaudio_broadcast_sink_start_help,
+#endif
+ },
+ {
+ .sc_cmd = "broadcast-sink-stop",
+ .sc_cmd_func = cmd_leaudio_broadcast_sink_stop,
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+ .help = &cmd_leaudio_broadcast_sink_stop_help,
+#endif
+ },
+ {
+ .sc_cmd = "broadcast-sink-metadata",
+ .sc_cmd_func = cmd_leaudio_broadcast_sink_metadata_update,
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+ .help = &cmd_leaudio_broadcast_sink_metadata_update_help,
+#endif
+ },
+#endif /* BLE_AUDIO_BROADCAST_SINK */
+#if MYNEWT_VAL(BLE_AUDIO_SCAN_DELEGATOR)
+ {
+ .sc_cmd = "scan-delegator-add",
+ .sc_cmd_func = cmd_leaudio_scan_delegator_receive_state_add,
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+ .help = &cmd_leaudio_scan_delegator_receive_state_add_help,
+#endif
+ },
+ {
+ .sc_cmd = "scan-delegator-remove",
+ .sc_cmd_func = cmd_leaudio_scan_delegator_receive_state_remove,
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+ .help = &cmd_leaudio_scan_delegator_receive_state_remove_help,
+#endif
+ },
+ {
+ .sc_cmd = "scan-delegator-set",
+ .sc_cmd_func = cmd_leaudio_scan_delegator_receive_state_set,
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+ .help = &cmd_leaudio_scan_delegator_receive_state_set_help,
+#endif
+ },
+ {
+ .sc_cmd = "scan-delegator-get",
+ .sc_cmd_func = cmd_leaudio_scan_delegator_receive_state_get,
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+ .help = &cmd_leaudio_scan_delegator_receive_state_get_help,
+#endif
+ },
+ {
+ .sc_cmd = "scan-delegator-show",
+ .sc_cmd_func = cmd_leaudio_scan_delegator_receive_state_show,
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+ .help = &cmd_leaudio_scan_delegator_receive_state_show_help,
+#endif
+ },
+#endif /* BLE_AUDIO_SCAN_DELEGATOR */
#if MYNEWT_VAL(BLE_ISO)
#if MYNEWT_VAL(BLE_ISO_BROADCAST_SOURCE)
{
diff --git a/apps/btshell/src/cmd.h b/apps/btshell/src/cmd.h
index 1477ea85..c5532f34 100644
--- a/apps/btshell/src/cmd.h
+++ b/apps/btshell/src/cmd.h
@@ -30,6 +30,13 @@ int parse_eddystone_url(char *full_url, uint8_t *out_scheme, char *out_body,
int cmd_parse_conn_start_end(uint16_t *out_conn, uint16_t *out_start,
uint16_t *out_end);
+int parse_dev_addr(const char *prefix, const struct parse_arg_kv_pair *addr_types,
+ ble_addr_t *addr);
+
+int cmd_parse_addr(const char *prefix, ble_addr_t *addr);
+
+const char *cmd_addr_type_str(uint8_t type);
+
void cmd_init(void);
#endif
diff --git a/apps/btshell/src/cmd_leaudio.c b/apps/btshell/src/cmd_leaudio.c
index 2f31246c..cea206b6 100644
--- a/apps/btshell/src/cmd_leaudio.c
+++ b/apps/btshell/src/cmd_leaudio.c
@@ -20,9 +20,13 @@
#include "cmd_leaudio.h"
#include "btshell.h"
#include "console/console.h"
+#include "shell/shell.h"
+#include "bsp/bsp.h"
#include "errno.h"
-#if (MYNEWT_VAL(BLE_AUDIO))
+#define STR_NULL "null"
+
+#if (MYNEWT_VAL(BLE_ISO_BROADCAST_SOURCE))
#include "audio/ble_audio_broadcast_source.h"
int
cmd_leaudio_base_add(int argc, char **argv)
@@ -382,4 +386,703 @@ cmd_leaudio_broadcast_stop(int argc, char **argv)
return btshell_broadcast_stop(adv_instance);
}
+#endif /* BLE_ISO_BROADCAST_SOURCE */
+
+#if (MYNEWT_VAL(BLE_AUDIO_BROADCAST_SINK))
+#include "audio/ble_audio_broadcast_sink.h"
+
+#define BROADCAST_SINK_PA_SYNC_TIMEOUT_DEFAULT 0x07D0
+
+static int
+broadcast_sink_pa_sync_params_get(struct ble_gap_periodic_sync_params *params)
+{
+ params->skip = 0;
+ params->sync_timeout = BROADCAST_SINK_PA_SYNC_TIMEOUT_DEFAULT;
+ params->reports_disabled = false;
+
+ return 0;
+}
+
+static void
+codec_specific_config_printf(const struct ble_audio_codec_id *unused, const uint8_t *data,
+ uint8_t len)
+{
+ console_printf("data=%p len=%u\n", data, len);
+}
+
+static void
+base_bis_printf(const struct ble_audio_codec_id *codec_id, const struct ble_audio_base_bis *bis)
+{
+ console_printf("BISCodecConfig:\n\t");
+ codec_specific_config_printf(codec_id,bis->codec_spec_config, bis->codec_spec_config_len);
+}
+
+static void
+metadata_printf(const uint8_t *data, uint8_t len)
+{
+ console_printf("data=%p len=%u\n", data, len);
+}
+
+static void
+base_subgroup_printf(uint8_t subgroup_index, const struct ble_audio_base_subgroup *subgroup)
+{
+ console_printf("subgroup_index=%u\n", subgroup_index);
+ console_printf("Codec ID:\n\tformat=0x%02x company_id=0x%04x vendor_specific=0x%02x\n",
+ subgroup->codec_id.format, subgroup->codec_id.company_id,
+ subgroup->codec_id.vendor_specific);
+ console_printf("SubgroupCodecConfig:\n\t");
+ codec_specific_config_printf(&subgroup->codec_id,
+ subgroup->codec_spec_config,
+ subgroup->codec_spec_config_len);
+ console_printf("Metadata:\n\t");
+ metadata_printf(subgroup->metadata, subgroup->metadata_len);
+}
+
+static int
+broadcast_sink_disc_start(const struct ble_gap_ext_disc_params *params)
+{
+ uint8_t own_addr_type;
+ int rc;
+
+ /* Figure out address to use while scanning. */
+ rc = ble_hs_id_infer_auto(0, &own_addr_type);
+ if (rc != 0) {
+ console_printf("determining own address type failed (%d)", rc);
+ assert(0);
+ }
+
+ rc = ble_gap_ext_disc(own_addr_type, 0, 0, 0, 0, 0, params, NULL, NULL, NULL);
+ if (rc != 0) {
+ console_printf("ext disc failed (%d)", rc);
+ }
+
+ return rc;
+}
+
+static int
+broadcast_sink_disc_stop(void)
+{
+ int rc;
+
+ rc = ble_gap_disc_cancel();
+ if (rc != 0) {
+ console_printf("disc cancel failed (%d)", rc);
+ }
+
+ return rc;
+}
+
+static int
+broadcast_sink_action_fn(struct ble_audio_broadcast_sink_action *action, void *arg)
+{
+ switch (action->type) {
+ case BLE_AUDIO_BROADCAST_SINK_ACTION_PA_SYNC:
+ console_printf("PA Sync:\n");
+ return broadcast_sink_pa_sync_params_get(action->pa_sync.out_params);
+ case BLE_AUDIO_BROADCAST_SINK_ACTION_BIG_SYNC:
+ console_printf("BIG Sync:\nsource_id=0x%02x iso_interval=0x%04x"
+ " presentation_delay=%u[us]\n",
+ action->big_sync.source_id, action->big_sync.iso_interval,
+ action->big_sync.presentation_delay);
+ break;
+ case BLE_AUDIO_BROADCAST_SINK_ACTION_BIS_SYNC:
+ console_printf("BIS Sync:\n\tsource_id=0x%02x bis_index=0x%02x\n",
+ action->bis_sync.source_id, action->bis_sync.bis->index);
+ base_subgroup_printf(action->bis_sync.subgroup_index, action->bis_sync.subgroup);
+ base_bis_printf(&action->bis_sync.subgroup->codec_id, action->bis_sync.bis);
+ return 0;
+ case BLE_AUDIO_BROADCAST_SINK_ACTION_DISC_START:
+ return broadcast_sink_disc_start(action->disc_start.params_preferred);
+ case BLE_AUDIO_BROADCAST_SINK_ACTION_DISC_STOP:
+ return broadcast_sink_disc_stop();
+ default:
+ assert(false);
+ return ENOTSUP;
+ }
+
+ return 0;
+}
+
+
+static const struct shell_param cmd_leaudio_broadcast_sink_start_params[] = {
+ {"source_id", "usage: =<UINT8>"},
+ {"broadcast_code", "usage: =[string], default: NULL"},
+ {NULL, NULL}
+};
+
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+const struct shell_cmd_help cmd_leaudio_broadcast_sink_start_help = {
+ .summary = "Start audio Broadcast Sink",
+ .usage = NULL,
+ .params = cmd_leaudio_broadcast_sink_start_params
+};
+#endif
+
+int
+cmd_leaudio_broadcast_sink_start(int argc, char **argv)
+{
+ struct ble_audio_broadcast_sink_add_params params = {0};
+ char *broadcast_code;
+ uint8_t source_id;
+ int rc;
+
+ rc = parse_arg_init(argc - 1, argv + 1);
+ if (rc != 0) {
+ return rc;
+ }
+
+ source_id = parse_arg_uint8("source_id", &rc);
+ if (rc != 0) {
+ console_printf("invalid 'source_id' parameter\n");
+ return rc;
+ }
+
+ broadcast_code = parse_arg_extract("broadcast_code");
+ if (broadcast_code != NULL) {
+ strncpy((char *)params.broadcast_code, broadcast_code, BLE_AUDIO_BROADCAST_CODE_SIZE);
+ params.broadcast_code_is_valid = true;
+ }
+
+ rc = ble_audio_broadcast_sink_start(source_id, ¶ms);
+ if (rc != 0) {
+ console_printf("start failed (%d)\n", rc);
+ }
+
+ return rc;
+}
+
+static const struct shell_param cmd_leaudio_broadcast_sink_stop_params[] = {
+ {"source_id", "usage: =<UINT8>"},
+ {NULL, NULL}
+};
+
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+const struct shell_cmd_help cmd_leaudio_broadcast_sink_stop_help = {
+ .summary = "Stop audio Broadcast Sink",
+ .usage = NULL,
+ .params = cmd_leaudio_broadcast_sink_stop_params
+};
+#endif
+
+int
+cmd_leaudio_broadcast_sink_stop(int argc, char **argv)
+{
+ uint8_t source_id;
+ int rc;
+
+ rc = parse_arg_init(argc - 1, argv + 1);
+ if (rc != 0) {
+ return rc;
+ }
+
+ source_id = parse_arg_uint8("source_id", &rc);
+ if (rc != 0) {
+ console_printf("invalid 'source_id' parameter\n");
+ return rc;
+ }
+
+ rc = ble_audio_broadcast_sink_stop(source_id);
+ if (rc != 0) {
+ console_printf("stop failed (%d)\n", rc);
+ }
+
+ return rc;
+}
+
+static const struct shell_param cmd_leaudio_broadcast_sink_metadata_update_params[] = {
+ {"source_id", "usage: =<UINT8>"},
+ {"subgroup_index", "usage: =<UINT8>"},
+ {"metadata", "usage: =[XX:XX...]"},
+ {NULL, NULL}
+};
+
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+const struct shell_cmd_help cmd_leaudio_broadcast_sink_metadata_update_help = {
+ .summary = "Update Broadcast Sink metadata",
+ .usage = NULL,
+ .params = cmd_leaudio_broadcast_sink_metadata_update_params
+};
#endif
+
+int
+cmd_leaudio_broadcast_sink_metadata_update(int argc, char **argv)
+{
+ struct ble_audio_broadcast_sink_metadata_update_params params = {0};
+ static bssnz_t uint8_t metadata[UINT8_MAX];
+ unsigned int metadata_len;
+ uint8_t source_id;
+ int rc;
+
+ rc = parse_arg_init(argc - 1, argv + 1);
+ if (rc != 0) {
+ return rc;
+ }
+
+ source_id = parse_arg_uint8("source_id", &rc);
+ if (rc != 0) {
+ console_printf("invalid 'source_id' parameter\n");
+ return rc;
+ }
+
+ params.subgroup_index = parse_arg_uint8("subgroup_index", &rc);
+ if (rc != 0) {
+ console_printf("invalid 'subgroup_index' parameter\n");
+ return rc;
+ }
+
+ rc = parse_arg_byte_stream("metadata", UINT8_MAX, metadata, &metadata_len);
+ if (rc == 0) {
+ params.metadata = metadata;
+ params.metadata_length = metadata_len;
+ } else if (rc != ENOENT) {
+ console_printf("invalid 'metadata' parameter\n");
+ return rc;
+ }
+
+ rc = ble_audio_broadcast_sink_metadata_update(source_id, ¶ms);
+ if (rc != 0) {
+ console_printf("metadata update failed (%d)\n", rc);
+ }
+
+ return rc;
+}
+
+static int
+broadcast_sink_audio_event_handler(struct ble_audio_event *event, void *arg)
+{
+ switch (event->type) {
+ case BLE_AUDIO_EVENT_BROADCAST_SINK_PA_SYNC_STATE:
+ console_printf("source_id=0x%02x PA sync: %s\n",
+ event->broadcast_sink_pa_sync_state.source_id,
+ ble_audio_broadcast_sink_sync_state_str(
+ event->broadcast_sink_pa_sync_state.state));
+ break;
+ case BLE_AUDIO_EVENT_BROADCAST_SINK_BIS_SYNC_STATE:
+ console_printf("source_id=0x%02x bis_index=0x%02x BIS sync: %s\n",
+ event->broadcast_sink_bis_sync_state.source_id,
+ event->broadcast_sink_bis_sync_state.bis_index,
+ ble_audio_broadcast_sink_sync_state_str(
+ event->broadcast_sink_bis_sync_state.state));
+ if (event->broadcast_sink_bis_sync_state.state ==
+ BLE_AUDIO_BROADCAST_SINK_SYNC_STATE_ESTABLISHED) {
+ console_printf("conn_handle=0x%04x\n",
+ event->broadcast_sink_bis_sync_state.conn_handle);
+ }
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+#endif /* BLE_AUDIO_BROADCAST_SINK */
+
+#if MYNEWT_VAL(BLE_AUDIO_SCAN_DELEGATOR)
+#include "audio/ble_audio_scan_delegator.h"
+
+static void
+scan_delegator_source_desc_printf(const struct ble_audio_scan_delegator_source_desc *source_desc)
+{
+ console_printf("broadcast_id=0x%6x adv_sid=%d adv_addr_type=%s adv_addr=",
+ source_desc->broadcast_id, source_desc->adv_sid,
+ cmd_addr_type_str(source_desc->addr.type));
+ print_addr(source_desc->addr.val);
+ console_printf("\n");
+}
+
+static void
+scan_delegator_sync_opt_printf(const struct ble_audio_scan_delegator_sync_opt *sync_opt)
+{
+ console_printf("pa_sync=%d pa_interval=0x%04x num_subgroups=%d",
+ sync_opt->pa_sync, sync_opt->pa_interval, sync_opt->num_subgroups);
+ for (uint8_t i = 0; i < sync_opt->num_subgroups; i++) {
+ console_printf("\n\tbis_sync=0x%04x metadata_length=%d metadata=",
+ sync_opt->subgroups[i].bis_sync, sync_opt->subgroups[i].metadata_length);
+ print_bytes(sync_opt->subgroups[i].metadata, sync_opt->subgroups[i].metadata_length);
+ }
+ console_printf("\n");
+}
+
+static int
+scan_delegator_pick_source_id_to_swap(uint8_t *out_source_id_to_swap)
+{
+ /* TODO: Add some logic here */
+ *out_source_id_to_swap = 0;
+
+ return 0;
+}
+
+static int
+scan_delegator_action_fn(struct ble_audio_scan_delegator_action *action, void *arg)
+{
+ switch (action->type) {
+ case BLE_AUDIO_SCAN_DELEGATOR_ACTION_SOURCE_ADD:
+ console_printf("Source Add:\nsource_id=%u\n", action->source_add.source_id);
+ scan_delegator_source_desc_printf(&action->source_add.source_desc);
+ scan_delegator_sync_opt_printf(&action->source_add.sync_opt);
+ if (action->source_add.out_source_id_to_swap == NULL) {
+ return 0;
+ } else {
+ return scan_delegator_pick_source_id_to_swap(action->source_add.out_source_id_to_swap);
+ }
+ case BLE_AUDIO_SCAN_DELEGATOR_ACTION_SOURCE_MODIFY:
+ console_printf("Source Modify:\nsource_id=%u\n", action->source_modify.source_id);
+ scan_delegator_sync_opt_printf(&action->source_modify.sync_opt);
+ break;
+ case BLE_AUDIO_SCAN_DELEGATOR_ACTION_SOURCE_REMOVE:
+ console_printf("Source Remove:\nsource_id=%u\n", action->source_remove.source_id);
+ break;
+ default:
+ assert(false);
+ return ENOTSUP;
+ }
+
+ return 0;
+}
+
+static const struct shell_param cmd_leaudio_scan_delegator_receive_state_add_params[] = {
+ {"addr_type", "usage: =[public|random], default: public"},
+ {"addr", "usage: =[XX:XX:XX:XX:XX:XX]"},
+ {"broadcast_id", "usage: =[0-0xFFFFFF]"},
+ {"adv_sid", "usage: =[UINT8], default: 0"},
+ {NULL, NULL}
+};
+
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+const struct shell_cmd_help cmd_leaudio_scan_delegator_receive_state_add_help = {
+ .summary = "Add receive state",
+ .usage = NULL,
+ .params = cmd_leaudio_scan_delegator_receive_state_add_params
+};
+#endif /* SHELL_CMD_HELP */
+
+int
+cmd_leaudio_scan_delegator_receive_state_add(int argc, char **argv)
+{
+ struct ble_audio_scan_delegator_receive_state_add_params params = {0};
+ uint8_t source_id;
+ int rc;
+
+ rc = parse_arg_init(argc - 1, argv + 1);
+ if (rc != 0) {
+ return rc;
+ }
+
+ rc = cmd_parse_addr(NULL, ¶ms.source_desc.addr);
+ if (rc != 0) {
+ console_printf("invalid 'adv_addr' parameter\n");
+ return rc;
+ }
+
+ params.source_desc.broadcast_id = parse_arg_uint32("broadcast_id", &rc);
+ if (rc != 0) {
+ console_printf("invalid 'broadcast_id' parameter\n");
+ return rc;
+ }
+
+ params.source_desc.adv_sid = parse_arg_uint8_dflt("adv_sid", 0, &rc);
+ if (rc != 0) {
+ console_printf("invalid 'adv_sid' parameter\n");
+ return rc;
+ }
+
+ rc = ble_audio_scan_delegator_receive_state_add(¶ms, &source_id);
+ if (rc != 0) {
+ console_printf("Failed to add receive state (%d)\n", rc);
+ } else {
+ console_printf("New source_id=%u created\n", source_id);
+ }
+
+ return rc;
+}
+
+static const struct shell_param cmd_leaudio_scan_delegator_receive_state_remove_params[] = {
+ {"source_id", "usage: =<UINT8>"},
+ {NULL, NULL}
+};
+
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+const struct shell_cmd_help cmd_leaudio_scan_delegator_receive_state_remove_help = {
+ .summary = "Remove receive state",
+ .usage = NULL,
+ .params = cmd_leaudio_scan_delegator_receive_state_remove_params
+};
+#endif /* SHELL_CMD_HELP */
+
+int
+cmd_leaudio_scan_delegator_receive_state_remove(int argc, char **argv)
+{
+ uint8_t source_id;
+ int rc;
+
+ rc = parse_arg_init(argc - 1, argv + 1);
+ if (rc != 0) {
+ return rc;
+ }
+
+ source_id = parse_arg_uint8("source_id", &rc);
+ if (rc != 0) {
+ console_printf("invalid 'source_id' parameter\n");
+ return rc;
+ }
+
+ rc = ble_audio_scan_delegator_receive_state_remove(source_id);
+ if (rc != 0) {
+ console_printf("remove failed (%d)\n", rc);
+ }
+
+ return rc;
+}
+
+const struct parse_arg_kv_pair cmd_pa_sync_type[] = {
+ { "not_synced", BLE_AUDIO_SCAN_DELEGATOR_PA_SYNC_STATE_NOT_SYNCED },
+ { "sync_info_req", BLE_AUDIO_SCAN_DELEGATOR_PA_SYNC_STATE_SYNC_INFO_REQ },
+ { "synced", BLE_AUDIO_SCAN_DELEGATOR_PA_SYNC_STATE_SYNCED },
+ { "failed", BLE_AUDIO_SCAN_DELEGATOR_PA_SYNC_STATE_ERROR },
+ { "no_past", BLE_AUDIO_SCAN_DELEGATOR_PA_SYNC_STATE_NO_PAST },
+ { NULL }
+};
+
+const struct parse_arg_kv_pair cmd_big_enc_type[] = {
+ { "not_encrypted", BLE_AUDIO_SCAN_DELEGATOR_BIG_ENC_NONE },
+ { "code_req", BLE_AUDIO_SCAN_DELEGATOR_BIG_ENC_BROADCAST_CODE_MISSING },
+ { "decrypting", BLE_AUDIO_SCAN_DELEGATOR_BIG_ENC_DECRYPTING },
+ { "bad_code", BLE_AUDIO_SCAN_DELEGATOR_BIG_ENC_BROADCAST_CODE_INVALID },
+ { NULL }
+};
+
+static const struct shell_param cmd_leaudio_scan_delegator_receive_state_set_params[] = {
+ {"source_id", "usage: =<UINT8>"},
+ {"pa_sync_state", "usage: =[not_synced|sync_info_req|synced|failed|no_past],"
+ " default: not_synced"},
+ {"big_enc", "usage: =[not_encrypted|code_req|decrypting|bad_code],"
+ " default: not_encrypted"},
+ {"bad_code", "usage: =[string], default: NULL"},
+ {NULL, NULL}
+};
+
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+const struct shell_cmd_help cmd_leaudio_scan_delegator_receive_state_set_help = {
+ .summary = "Set receive state",
+ .usage = NULL,
+ .params = cmd_leaudio_scan_delegator_receive_state_set_params
+};
+#endif /* SHELL_CMD_HELP */
+
+int
+cmd_leaudio_scan_delegator_receive_state_set(int argc, char **argv)
+{
+ struct ble_audio_scan_delegator_receive_state state = {0};
+ char *bad_code;
+ uint8_t source_id;
+ int rc;
+
+ rc = parse_arg_init(argc - 1, argv + 1);
+ if (rc != 0) {
+ return rc;
+ }
+
+ source_id = parse_arg_uint8("source_id", &rc);
+ if (rc != 0) {
+ console_printf("invalid 'source_id' parameter\n");
+ return rc;
+ }
+
+ state.pa_sync_state = parse_arg_kv_dflt("pa_sync_state", cmd_pa_sync_type,
+ BLE_AUDIO_SCAN_DELEGATOR_PA_SYNC_STATE_NOT_SYNCED, &rc);
+ if (rc != 0) {
+ console_printf("invalid 'pa_sync_state' parameter\n");
+ return rc;
+ }
+
+ state.big_enc = parse_arg_kv_dflt("big_enc", cmd_big_enc_type,
+ BLE_AUDIO_SCAN_DELEGATOR_BIG_ENC_NONE, &rc);
+ if (rc != 0) {
+ console_printf("invalid 'big_enc' parameter\n");
+ return rc;
+ }
+
+ bad_code = parse_arg_extract("bad_code");
+ if (bad_code != NULL) {
+ strncpy((char *)state.bad_code, bad_code, BLE_AUDIO_BROADCAST_CODE_SIZE);
+ }
+
+ /* TODO: initialize state.subgroups */
+ state.num_subgroups = 0;
+
+ rc = ble_audio_scan_delegator_receive_state_set(source_id, &state);
+ if (rc != 0) {
+ console_printf("set failed (%d)\n", rc);
+ }
+
+ return rc;
+}
+
+static const char *
+pa_sync_type_str(enum ble_audio_scan_delegator_pa_sync_state pa_sync_state)
+{
+ for (size_t i = 0; i < ARRAY_SIZE(cmd_pa_sync_type); i++) {
+ if (cmd_pa_sync_type[i].val == pa_sync_state) {
+ return cmd_pa_sync_type[i].key;
+ }
+ }
+
+ return STR_NULL;
+}
+
+static const char *
+big_enc_type_str(enum ble_audio_scan_delegator_big_enc big_enc)
+{
+ for (size_t i = 0; i < ARRAY_SIZE(cmd_big_enc_type); i++) {
+ if (cmd_big_enc_type[i].val == big_enc) {
+ return cmd_big_enc_type[i].key;
+ }
+ }
+
+ return STR_NULL;
+}
+
+static void
+scan_delegator_receive_state_printf(const struct ble_audio_scan_delegator_receive_state *state)
+{
+ console_printf("pa_sync_state=%s big_enc=%s num_subgroups=%d",
+ pa_sync_type_str(state->pa_sync_state), big_enc_type_str(state->big_enc),
+ state->num_subgroups);
+
+ if (state->big_enc == BLE_AUDIO_SCAN_DELEGATOR_BIG_ENC_BROADCAST_CODE_INVALID) {
+ console_printf("bad_code=");
+ print_bytes(state->bad_code, sizeof(state->bad_code));
+ console_printf("\n");
+ }
+
+ for (uint8_t i = 0; i < state->num_subgroups; i++) {
+ console_printf("\n\tbis_sync=0x%04x metadata_length=%d metadata=",
+ state->subgroups[i].bis_sync, state->subgroups[i].metadata_length);
+ print_bytes(state->subgroups[i].metadata, state->subgroups[i].metadata_length);
+ console_printf("\n");
+ }
+}
+
+static const struct shell_param cmd_leaudio_scan_delegator_receive_state_get_params[] = {
+ {"source_id", "usage: =<UINT8>"},
+ {NULL, NULL}
+};
+
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+const struct shell_cmd_help cmd_leaudio_scan_delegator_receive_state_get_help = {
+ .summary = "Get receive state",
+ .usage = NULL,
+ .params = cmd_leaudio_scan_delegator_receive_state_get_params
+};
+#endif /* SHELL_CMD_HELP */
+
+int
+cmd_leaudio_scan_delegator_receive_state_get(int argc, char **argv)
+{
+ struct ble_audio_scan_delegator_receive_state state = {0};
+ uint8_t source_id;
+ int rc;
+
+ rc = parse_arg_init(argc - 1, argv + 1);
+ if (rc != 0) {
+ return rc;
+ }
+
+ source_id = parse_arg_uint8("source_id", &rc);
+ if (rc != 0) {
+ console_printf("invalid 'source_id' parameter\n");
+ return rc;
+ }
+
+ rc = ble_audio_scan_delegator_receive_state_get(source_id, &state);
+ if (rc != 0) {
+ console_printf("get failed (%d)\n", rc);
+ } else {
+ console_printf("source_id=%u\n", source_id);
+ scan_delegator_receive_state_printf(&state);
+ }
+
+ return rc;
+}
+
+static int
+scan_delegator_receive_state_foreach_fn(struct ble_audio_scan_delegator_receive_state_entry *entry,
+ void *arg)
+{
+ console_printf("source_id=%u\n", entry->source_id);
+ scan_delegator_source_desc_printf(&entry->source_desc);
+ scan_delegator_receive_state_printf(&entry->state);
+
+ return 0;
+}
+
+#if MYNEWT_VAL(SHELL_CMD_HELP)
+const struct shell_cmd_help cmd_leaudio_scan_delegator_receive_state_show_help = {
+ .summary = "List receive states",
+ .usage = NULL,
+ .params = NULL
+};
+#endif /* SHELL_CMD_HELP */
+
+int
+cmd_leaudio_scan_delegator_receive_state_show(int argc, char **argv)
+{
+ uint8_t num_entries = 0;
+
+ ble_audio_scan_delegator_receive_state_foreach(scan_delegator_receive_state_foreach_fn,
+ &num_entries);
+ if (num_entries == 0) {
+ console_printf("No receive state\n");
+ }
+
+ return 0;
+}
+
+static int
+scan_delegator_audio_event_handler(struct ble_audio_event *event, void *arg)
+{
+ switch (event->type) {
+ case BLE_AUDIO_EVENT_BROADCAST_ANNOUNCEMENT:
+ console_printf("Broadcast Announcement\n");
+ console_printf("broadcast_id=0x%6x adv_sid=%d addr_type=%s addr=",
+ event->broadcast_announcement.broadcast_id,
+ event->broadcast_announcement.ext_disc->sid,
+ cmd_addr_type_str(event->broadcast_announcement.ext_disc->addr.type));
+ print_addr(event->broadcast_announcement.ext_disc->addr.val);
+ console_printf("\n");
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+#endif /* BLE_AUDIO_SCAN_DELEGATOR */
+
+void
+btshell_leaudio_init(void)
+{
+ int rc = 0;
+
+#if (MYNEWT_VAL(BLE_AUDIO_SCAN_DELEGATOR))
+ static struct ble_audio_event_listener scan_delegator_listener;
+
+ rc = ble_audio_scan_delegator_action_fn_set(scan_delegator_action_fn, NULL);
+ assert(rc == 0);
+
+ rc = ble_audio_event_listener_register(&scan_delegator_listener,
+ scan_delegator_audio_event_handler, NULL);
+ assert(rc == 0);
+#endif /* BLE_AUDIO_SCAN_DELEGATOR */
+
+#if (MYNEWT_VAL(BLE_AUDIO_BROADCAST_SINK))
+ static struct ble_audio_event_listener broadcast_sink_listener;
+
+ rc = ble_audio_broadcast_sink_cb_set(broadcast_sink_action_fn, NULL);
+ assert(rc == 0);
+
+ rc = ble_audio_event_listener_register(&broadcast_sink_listener,
+ broadcast_sink_audio_event_handler, NULL);
+#endif /* BLE_AUDIO_BROADCAST_SINK */
+ assert(rc == 0);
+}
diff --git a/apps/btshell/src/cmd_leaudio.h b/apps/btshell/src/cmd_leaudio.h
index 84fc4586..477b33a2 100644
--- a/apps/btshell/src/cmd_leaudio.h
+++ b/apps/btshell/src/cmd_leaudio.h
@@ -43,4 +43,24 @@ int cmd_leaudio_broadcast_update(int argc, char **argv);
int cmd_leaudio_broadcast_start(int argc, char **argv);
int cmd_leaudio_broadcast_stop(int argc, char **argv);
+extern const struct shell_cmd_help cmd_leaudio_broadcast_sink_start_help;
+extern const struct shell_cmd_help cmd_leaudio_broadcast_sink_stop_help;
+extern const struct shell_cmd_help cmd_leaudio_broadcast_sink_metadata_update_help;
+
+int cmd_leaudio_broadcast_sink_start(int argc, char **argv);
+int cmd_leaudio_broadcast_sink_stop(int argc, char **argv);
+int cmd_leaudio_broadcast_sink_metadata_update(int argc, char **argv);
+
+extern const struct shell_cmd_help cmd_leaudio_scan_delegator_receive_state_add_help;
+extern const struct shell_cmd_help cmd_leaudio_scan_delegator_receive_state_remove_help;
+extern const struct shell_cmd_help cmd_leaudio_scan_delegator_receive_state_set_help;
+extern const struct shell_cmd_help cmd_leaudio_scan_delegator_receive_state_get_help;
+extern const struct shell_cmd_help cmd_leaudio_scan_delegator_receive_state_show_help;
+
+int cmd_leaudio_scan_delegator_receive_state_add(int argc, char **argv);
+int cmd_leaudio_scan_delegator_receive_state_remove(int argc, char **argv);
+int cmd_leaudio_scan_delegator_receive_state_set(int argc, char **argv);
+int cmd_leaudio_scan_delegator_receive_state_get(int argc, char **argv);
+int cmd_leaudio_scan_delegator_receive_state_show(int argc, char **argv);
+
#endif /* H_CMD_LEAUDIO_ */
diff --git a/apps/btshell/src/main.c b/apps/btshell/src/main.c
index 0ba90ff1..72929276 100644
--- a/apps/btshell/src/main.c
+++ b/apps/btshell/src/main.c
@@ -2078,6 +2078,10 @@ btshell_ext_scan(uint8_t own_addr_type, uint16_t duration, uint16_t period,
const struct ble_gap_ext_disc_params *coded_params,
void *cb_args)
{
+ struct btshell_scan_opts *scan_opts = cb_args;
+
+ console_printf("silent %d.", scan_opts->silent);
+
#if !MYNEWT_VAL(BLE_EXT_ADV)
console_printf("BLE extended advertising not supported.");
console_printf(" Configure nimble host to enable it\n");
@@ -3143,6 +3147,8 @@ mynewt_main(int argc, char **argv)
btshell_init_ext_adv_restart();
+ btshell_leaudio_init();
+
while (1) {
os_eventq_run(os_eventq_dflt_get());
}
diff --git a/apps/btshell/syscfg.yml b/apps/btshell/syscfg.yml
index 091a2df0..3bdfb34a 100644
--- a/apps/btshell/syscfg.yml
+++ b/apps/btshell/syscfg.yml
@@ -50,3 +50,6 @@ syscfg.vals:
syscfg.vals.BLE_MESH:
MSYS_1_BLOCK_COUNT: 16
+
+syscfg.vals.BLE_AUDIO_BROADCAST_SINK:
+ BLE_AUDIO_BROADCAST_SINK_MAX: 1
diff --git a/nimble/host/audio/targets/btshell_native/pkg.yml b/nimble/host/audio/targets/btshell_native/pkg.yml
index 67eb2d6f..7c129c62 100644
--- a/nimble/host/audio/targets/btshell_native/pkg.yml
+++ b/nimble/host/audio/targets/btshell_native/pkg.yml
@@ -24,3 +24,4 @@ pkg.description: Target for native btshell application with LE Audio
pkg.author: "Apache Mynewt <de...@mynewt.apache.org>"
pkg.homepage: "http://mynewt.apache.org/"
pkg.deps:
+ - nimble/host/audio
diff --git a/nimble/host/audio/targets/btshell_native/syscfg.yml b/nimble/host/audio/targets/btshell_native/syscfg.yml
index e9db0070..3c04c0c1 100644
--- a/nimble/host/audio/targets/btshell_native/syscfg.yml
+++ b/nimble/host/audio/targets/btshell_native/syscfg.yml
@@ -30,7 +30,7 @@ syscfg.vals:
# Disable security manager (pairing and bonding).
BLE_SM_LEGACY: 0
- BLE_SM_SC: 0
+ BLE_SM_SC: 1
# Default task settings
OS_MAIN_STACK_SIZE: 512
@@ -64,6 +64,9 @@ syscfg.vals:
BLE_ISO_MAX_BIGS: 1
BLE_ISO_MAX_BISES: 2
+ BLE_AUDIO_BROADCAST_SINK: 1
+ BLE_AUDIO_SCAN_DELEGATOR: 1
+
CONSOLE_UART: 1
CONSOLE_UART_BAUD: 1000000
CONSOLE_STICKY_PROMPT: 1