From 05e1e3c8dbd631a33297af9ef105ac2a7785ef41 Mon Sep 17 00:00:00 2001 From: Mariusz Skamra Date: Thu, 25 Jan 2024 13:30:12 +0100 Subject: [PATCH] nimble/audio: Add initial audio broadcast sink implementation This adds initial implementation of audio broadcast sink. --- nimble/host/audio/include/audio/ble_audio.h | 28 + .../host/audio/include/audio/ble_audio.h.orig | 937 +++++++++++ .../include/audio/ble_audio_broadcast_sink.h | 264 +++ .../include/audio/ble_audio_scan_delegator.h | 383 +++++ nimble/host/audio/pkg.yml | 9 + nimble/host/audio/src/ble_audio.c | 4 +- .../host/audio/src/ble_audio_broadcast_sink.c | 1417 +++++++++++++++++ .../audio/src/ble_audio_broadcast_sink_priv.h | 34 + nimble/host/audio/src/ble_audio_priv.h | 11 + .../host/audio/src/ble_audio_scan_delegator.c | 474 ++++++ .../audio/src/ble_audio_scan_delegator_priv.h | 33 + nimble/host/audio/syscfg.yml | 59 + nimble/host/audio/test/src/ble_audio_test.h | 53 + .../test/src/testcases/ble_audio_base_parse.c | 398 +++++ nimble/host/audio/test/syscfg.yml | 9 + 15 files changed, 4112 insertions(+), 1 deletion(-) create mode 100644 nimble/host/audio/include/audio/ble_audio.h.orig 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 create mode 100644 nimble/host/audio/src/ble_audio_broadcast_sink_priv.h create mode 100644 nimble/host/audio/src/ble_audio_scan_delegator.c create mode 100644 nimble/host/audio/src/ble_audio_scan_delegator_priv.h create mode 100644 nimble/host/audio/test/src/ble_audio_test.h create mode 100644 nimble/host/audio/test/src/testcases/ble_audio_base_parse.c diff --git a/nimble/host/audio/include/audio/ble_audio.h b/nimble/host/audio/include/audio/ble_audio.h index c87099c2a9..6a11955e83 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 @@ -539,6 +542,9 @@ struct ble_audio_broadcast_name { /** BLE Audio event: BASS - Set Broadcast Code */ #define BLE_AUDIO_EVENT_BASS_SET_BROADCAST_CODE 5 +/** BLE Audio event: Broadcast Sink - Metadata Updated */ +#define BLE_AUDIO_EVENT_BROADCAST_SINK_METADATA 6 + /** @} */ /** @brief Broadcast Announcement */ @@ -589,6 +595,21 @@ struct ble_audio_event_bass_set_broadcast_code { uint8_t broadcast_code[BLE_AUDIO_BROADCAST_CODE_SIZE]; }; +/** @brief BASS Source Removed */ +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 @@ -647,6 +668,13 @@ 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_METADATA + * + * Represents an update in Broadcast Sink Metadata. + */ + struct ble_audio_event_broadcast_sink_metadata broadcast_sink_metadata; }; }; diff --git a/nimble/host/audio/include/audio/ble_audio.h.orig b/nimble/host/audio/include/audio/ble_audio.h.orig new file mode 100644 index 0000000000..e62a3ffe85 --- /dev/null +++ b/nimble/host/audio/include/audio/ble_audio.h.orig @@ -0,0 +1,937 @@ +/* + * 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_ +#define H_BLE_AUDIO_ + +#include +#include + +/** + * @file ble_audio.h + * + * @brief Bluetooth Low Energy Audio API + * + * @defgroup bt_le_audio Bluetooth LE Audio + * @ingroup bt_host + * @{ + */ + +/** + * @cond + * Helper macros for BLE_AUDIO_BUILD_CODEC_CONFIG + * @private @{ + */ +#define FIELD_LEN_2(_len, _type, _field) _len, _type, _field, +#define FIELD_LEN_5(_len, _type, _field) _len, _type, _field, \ + _field >> 8, _field >> 16, \ + _field >> 24, + +#define FIELD_TESTED_0(_len, _type, _field) +#define FIELD_TESTED_1(_len, _type, _field) FIELD_LEN_ ## _len(_len, \ + _type, \ + _field) +#define EMPTY() FIELD_TESTED_0 +#define PRESENT(X) FIELD_TESTED_1 +#define TEST(x, A, FUNC, ...) FUNC +#define TEST_FIELD(...) TEST(, ## __VA_ARGS__, \ + PRESENT(__VA_ARGS__), \ + EMPTY(__VA_ARGS__)) +#define FIELD_TESTED(_test, _len, _type, _field) _test(_len, _type, _field) +#define OPTIONAL_FIELD(_len, _type, ...) FIELD_TESTED(TEST_FIELD \ + (__VA_ARGS__), \ + _len, \ + _type, \ + __VA_ARGS__) + +/** + * @} + * @endcond + */ + +/** 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 + +/** Public Broadcast Announcement Service UUID. */ +#define BLE_BROADCAST_PUB_ANNOUNCEMENT_SVC_UUID 0x1856 + +/** + * @defgroup ble_audio_sampling_rates Bluetooth Low Energy Audio Sampling Rates + * @{ + */ + +/** LE Audio Sampling Rate: 8000 Hz. */ +#define BLE_AUDIO_SAMPLING_RATE_8000_HZ 0x01 + +/** LE Audio Sampling Rate: 11025 Hz. */ +#define BLE_AUDIO_SAMPLING_RATE_11025_HZ 0x02 + +/** LE Audio Sampling Rate: 16000 Hz. */ +#define BLE_AUDIO_SAMPLING_RATE_16000_HZ 0x03 + +/** LE Audio Sampling Rate: 22050 Hz. */ +#define BLE_AUDIO_SAMPLING_RATE_22050_HZ 0x04 + +/** LE Audio Sampling Rate: 24000 Hz. */ +#define BLE_AUDIO_SAMPLING_RATE_24000_HZ 0x05 + +/** LE Audio Sampling Rate: 32000 Hz. */ +#define BLE_AUDIO_SAMPLING_RATE_32000_HZ 0x06 + +/** LE Audio Sampling Rate: 44100 Hz. */ +#define BLE_AUDIO_SAMPLING_RATE_44100_HZ 0x07 + +/** LE Audio Sampling Rate: 48000 Hz. */ +#define BLE_AUDIO_SAMPLING_RATE_48000_HZ 0x08 + +/** LE Audio Sampling Rate: 88200 Hz. */ +#define BLE_AUDIO_SAMPLING_RATE_88200_HZ 0x09 + +/** LE Audio Sampling Rate: 96000 Hz. */ +#define BLE_AUDIO_SAMPLING_RATE_96000_HZ 0x0A + +/** LE Audio Sampling Rate: 176400 Hz. */ +#define BLE_AUDIO_SAMPLING_RATE_176400_HZ 0x0B + +/** LE Audio Sampling Rate: 192000 Hz. */ +#define BLE_AUDIO_SAMPLING_RATE_192000_HZ 0x0C + +/** LE Audio Sampling Rate: 384000 Hz. */ +#define BLE_AUDIO_SAMPLING_RATE_384000_HZ 0x0D + +/** @} */ + +/** + * @defgroup ble_audio_frame_durations Bluetooth Low Energy Audio Frame Durations + * @{ + */ + +/** LE Audio Frame Duration: 7.5 ms. */ +#define BLE_AUDIO_SELECTED_FRAME_DURATION_7_5_MS 0x00 + +/** LE Audio Frame Duration: 10 ms. */ +#define BLE_AUDIO_SELECTED_FRAME_DURATION_10_MS 0x01 + +/** @} */ + +/** + * @defgroup ble_audio_locations Bluetooth Low Energy Audio Locations + * @{ + */ + +/** LE Audio Location: Front Left. */ +#define BLE_AUDIO_LOCATION_FRONT_LEFT (1ULL) + +/** LE Audio Location: Front Right. */ +#define BLE_AUDIO_LOCATION_FRONT_RIGHT (1ULL << 1) + +/** LE Audio Location: Front Center. */ +#define BLE_AUDIO_LOCATION_FRONT_CENTER (1ULL << 2) + +/** LE Audio Location: Low Frequency Effects 1. */ +#define BLE_AUDIO_LOCATION_LOW_FREQ_EFFECTS_1 (1ULL << 3) + +/** LE Audio Location: Back Left. */ +#define BLE_AUDIO_LOCATION_BACK_LEFT (1ULL << 4) + +/** LE Audio Location: Front Left Center. */ +#define BLE_AUDIO_LOCATION_FRONT_LEFT_CENTER (1ULL << 5) + +/** LE Audio Location: Front Right Center. */ +#define BLE_AUDIO_LOCATION_FRONT_RIGHT_CENTER (1ULL << 6) + +/** LE Audio Location: Back Center. */ +#define BLE_AUDIO_LOCATION_BACK_CENTER (1ULL << 7) + +/** LE Audio Location: Low Frequency Effects 2. */ +#define BLE_AUDIO_LOCATION_LOW_FREQ_EFFECTS_2 (1ULL << 8) + +/** LE Audio Location: Side Left. */ +#define BLE_AUDIO_LOCATION_SIDE_LEFT (1ULL << 9) + +/** LE Audio Location: Side Right. */ +#define BLE_AUDIO_LOCATION_SIDE_RIGHT (1ULL << 10) + +/** LE Audio Location: Top Front Left. */ +#define BLE_AUDIO_LOCATION_TOP_FRONT_LEFT (1ULL << 11) + +/** LE Audio Location: Top Front Right. */ +#define BLE_AUDIO_LOCATION_TOP_FRONT_RIGHT (1ULL << 12) + +/** LE Audio Location: Top Front Center. */ +#define BLE_AUDIO_LOCATION_TOP_FRONT_CENTER (1ULL << 13) + +/** LE Audio Location: Top Center. */ +#define BLE_AUDIO_LOCATION_TOP_CENTER (1ULL << 14) + +/** LE Audio Location: Top Back Left. */ +#define BLE_AUDIO_LOCATION_TOP_BACK_LEFT (1ULL << 15) + +/** LE Audio Location: Top Back Right. */ +#define BLE_AUDIO_LOCATION_TOP_BACK_RIGHT (1ULL << 16) + +/** LE Audio Location: Top Side Left. */ +#define BLE_AUDIO_LOCATION_TOP_SIDE_LEFT (1ULL << 17) + +/** LE Audio Location: Top Side Right. */ +#define BLE_AUDIO_LOCATION_TOP_SIDE_RIGHT (1ULL << 18) + +/** LE Audio Location: Top Back Center. */ +#define BLE_AUDIO_LOCATION_TOP_BACK_CENTER (1ULL << 19) + +/** LE Audio Location: Bottom Front Center. */ +#define BLE_AUDIO_LOCATION_BOTTOM_FRONT_CENTER (1ULL << 20) + +/** LE Audio Location: Bottom Front Left. */ +#define BLE_AUDIO_LOCATION_BOTTOM_FRONT_LEFT (1ULL << 21) + +/** LE Audio Location: Bottom Front Right. */ +#define BLE_AUDIO_LOCATION_BOTTOM_FRONT_RIGHT (1ULL << 22) + +/** LE Audio Location: Left Surround. */ +#define BLE_AUDIO_LOCATION_LEFT_SURROUND (1ULL << 23) + +/** LE Audio Location: Right Surround. */ +#define BLE_AUDIO_LOCATION_RIGHT_SURROUND (1ULL << 24) + +/** @} */ + +/** + * @defgroup ble_audio_codec_config Bluetooth Low Energy Audio Codec Specific Config + * @{ + */ + +/** LE Audio Codec Config Type: Sampling Frequency. */ +#define BLE_AUDIO_CODEC_CONF_SAMPLING_FREQ_TYPE 0x01 + +/** LE Audio Codec Config Type: Frame Duration. */ +#define BLE_AUDIO_CODEC_CONF_FRAME_DURATION_TYPE 0x02 + +/** LE Audio Codec Config Type: Channel Allocation. */ +#define BLE_AUDIO_CODEC_CONF_AUDIO_CHANNEL_ALLOCATION_TYPE 0x03 + +/** LE Audio Codec Config Type: Octets Per Codec Frame. */ +#define BLE_AUDIO_CODEC_CONF_OCTETS_PER_CODEC_FRAME_TYPE 0x04 + +/** LE Audio Codec Config Type: Frame Blocks Per SDU. */ +#define BLE_AUDIO_CODEC_CONF_FRAME_BLOCKS_PER_SDU_TYPE 0x05 + +/** @} */ + +/** + * @defgroup ble_audio_codec_caps Bluetooth Low Energy Audio Codec Specific Capabilities + * @{ + */ + + +/** LE Audio Codec Specific Capability: Supported Sampling Frequencies. */ +#define BLE_AUDIO_CODEC_CAPS_SAMPLING_FREQ_TYPE 0x01 + +/** LE Audio Codec Specific Capability: Supported Frame Durations. */ +#define BLE_AUDIO_CODEC_CAPS_FRAME_DURATION_TYPE 0x02 + +/** LE Audio Codec Specific Capability: Supported Audio Channel Counts. */ +#define BLE_AUDIO_CODEC_CAPS_SUP_AUDIO_CHANNEL_COUNTS_TYPE 0x03 + +/** LE Audio Codec Specific Capability: Supported Octets Per Codec Frame. */ +#define BLE_AUDIO_CODEC_CAPS_OCTETS_PER_CODEC_FRAME_TYPE 0x04 + +/** LE Audio Codec Specific Capability: Supported Codec Frames Per SDU. */ +#define BLE_AUDIO_CODEC_CAPS_FRAMES_PER_SDU_TYPE 0x05 + +/** @} */ + +/** + * @defgroup ble_audio_contexts Bluetooth Low Energy Audio Context Types + * @{ + */ + +/** LE Audio Codec Context Type: Prohibited. */ +#define BLE_AUDIO_CONTEXT_TYPE_PROHIBITED 0x0000 + +/** LE Audio Codec Context Type: Unspecified. */ +#define BLE_AUDIO_CONTEXT_TYPE_UNSPECIFIED 0x0001 + +/** LE Audio Codec Context Type: Conversational. */ +#define BLE_AUDIO_CONTEXT_TYPE_CONVERSATIONAL 0x0002 + +/** LE Audio Codec Context Type: Media. */ +#define BLE_AUDIO_CONTEXT_TYPE_MEDIA 0x0004 + +/** LE Audio Codec Context Type: Game. */ +#define BLE_AUDIO_CONTEXT_TYPE_GAME 0x0008 + +/** LE Audio Codec Context Type: Instructional. */ +#define BLE_AUDIO_CONTEXT_TYPE_INSTRUCTIONAL 0x0010 + +/** LE Audio Codec Context Type: Voice Assistants. */ +#define BLE_AUDIO_CONTEXT_TYPE_VOICE_ASSISTANTS 0x0020 + +/** LE Audio Codec Context Type: Live. */ +#define BLE_AUDIO_CONTEXT_TYPE_LIVE 0x0040 + +/** LE Audio Codec Context Type: Sound Effects. */ +#define BLE_AUDIO_CONTEXT_TYPE_SOUND_EFFECTS 0x0080 + +/** LE Audio Codec Context Type: Notifications. */ +#define BLE_AUDIO_CONTEXT_TYPE_NOTIFICATIONS 0x0100 + +/** LE Audio Codec Context Type: Ringtone. */ +#define BLE_AUDIO_CONTEXT_TYPE_RINGTONE 0x0200 + +/** LE Audio Codec Context Type: Alerts. */ +#define BLE_AUDIO_CONTEXT_TYPE_ALERTS 0x0400 + +/** LE Audio Codec Context Type: EmergencyAlarm. */ +#define BLE_AUDIO_CONTEXT_TYPE_EMERGENCY_ALARM 0x0800 + +/** @} */ + +/** + * @defgroup ble_audio_sup_frame_durations Bluetooth Low Energy Audio Supported Frame Durations + * @{ + */ + +/** LE Audio Codec Supported Frame Duration: 7.5 ms frame duration. */ +#define BLE_AUDIO_CODEC_SUPPORTED_FRAME_DURATION_7_5_MS 0x0001 + +/** LE Audio Codec Supported Frame Duration: 10 ms frame duration. */ +#define BLE_AUDIO_CODEC_SUPPORTED_FRAME_DURATION_10_MS 0x0002 + +/** LE Audio Codec Supported Frame Duration: 7.5 ms preferred. */ +#define BLE_AUDIO_CODEC_PREFERED_FRAME_DURATION_7_5_MS 0x0010 + +/** LE Audio Codec Supported Frame Duration: 10 ms preferred. */ +#define BLE_AUDIO_CODEC_PREFERED_FRAME_DURATION_10_MS 0x0020 + +/** @} */ + +/** + * @defgroup ble_audio_sup_sampling_freqs Bluetooth Low Energy Audio Supported Sampling Frequencies + * @{ + */ + +/** LE Audio Codec Supported Sampling Frequency: 8000 Hz. */ +#define BLE_AUDIO_CODEC_SUPPORTED_SAMPLING_RATE_8000_HZ (1ULL << 0) + +/** LE Audio Codec Supported Sampling Frequency: 11025 Hz. */ +#define BLE_AUDIO_CODEC_SUPPORTED_SAMPLING_RATE_11025_HZ (1ULL << 1) + +/** LE Audio Codec Supported Sampling Frequency: 16000 Hz. */ +#define BLE_AUDIO_CODEC_SUPPORTED_SAMPLING_RATE_16000_HZ (1ULL << 2) + +/** LE Audio Codec Supported Sampling Frequency: 22050 Hz. */ +#define BLE_AUDIO_CODEC_SUPPORTED_SAMPLING_RATE_22050_HZ (1ULL << 3) + +/** LE Audio Codec Supported Sampling Frequency: 24000 Hz. */ +#define BLE_AUDIO_CODEC_SUPPORTED_SAMPLING_RATE_24000_HZ (1ULL << 4) + +/** LE Audio Codec Supported Sampling Frequency: 32000 Hz. */ +#define BLE_AUDIO_CODEC_SUPPORTED_SAMPLING_RATE_32000_HZ (1ULL << 5) + +/** LE Audio Codec Supported Sampling Frequency: 44100 Hz. */ +#define BLE_AUDIO_CODEC_SUPPORTED_SAMPLING_RATE_44100_HZ (1ULL << 6) + +/** LE Audio Codec Supported Sampling Frequency: 48000 Hz. */ +#define BLE_AUDIO_CODEC_SUPPORTED_SAMPLING_RATE_48000_HZ (1ULL << 7) + +/** LE Audio Codec Supported Sampling Frequency: 88200 Hz. */ +#define BLE_AUDIO_CODEC_SUPPORTED_SAMPLING_RATE_88200_HZ (1ULL << 8) + +/** LE Audio Codec Supported Sampling Frequency: 96000 Hz. */ +#define BLE_AUDIO_CODEC_SUPPORTED_SAMPLING_RATE_96000_HZ (1ULL << 9) + +/** LE Audio Codec Supported Sampling Frequency: 176400 Hz. */ +#define BLE_AUDIO_CODEC_SUPPORTED_SAMPLING_RATE_176400_HZ (1ULL << 10) + +/** LE Audio Codec Supported Sampling Frequency: 192000 Hz. */ +#define BLE_AUDIO_CODEC_SUPPORTED_SAMPLING_RATE_192000_HZ (1ULL << 11) + +/** LE Audio Codec Supported Sampling Frequency: 384000 Hz. */ +#define BLE_AUDIO_CODEC_SUPPORTED_SAMPLING_RATE_384000_HZ (1ULL << 12) + +/** @} */ + +/** + * @defgroup ble_audio_sup_chan_counts Bluetooth Low Energy Audio Supported Channel Counts + * @{ + */ + +/** LE Audio Codec Supported Channel Count: 1. */ +#define BLE_AUDIO_CODEC_SUPPORTED_CHANNEL_COUNT_1 0x0001 + +/** LE Audio Codec Supported Channel Count: 2. */ +#define BLE_AUDIO_CODEC_SUPPORTED_CHANNEL_COUNT_2 0x0002 + +/** LE Audio Codec Supported Channel Count: 3. */ +#define BLE_AUDIO_CODEC_SUPPORTED_CHANNEL_COUNT_3 0x0004 + +/** LE Audio Codec Supported Channel Count: 4. */ +#define BLE_AUDIO_CODEC_SUPPORTED_CHANNEL_COUNT_4 0x0008 + +/** LE Audio Codec Supported Channel Count: 5. */ +#define BLE_AUDIO_CODEC_SUPPORTED_CHANNEL_COUNT_5 0x0010 + +/** LE Audio Codec Supported Channel Count: 6. */ +#define BLE_AUDIO_CODEC_SUPPORTED_CHANNEL_COUNT_6 0x0020 + +/** LE Audio Codec Supported Channel Count: 7. */ +#define BLE_AUDIO_CODEC_SUPPORTED_CHANNEL_COUNT_7 0x0040 + +/** LE Audio Codec Supported Channel Count: 8. */ +#define BLE_AUDIO_CODEC_SUPPORTED_CHANNEL_COUNT_8 0x0080 + +/** @} */ + +/** + * @brief Helper macro used to build LTV array of Codec_Specific_Configuration. + * + * @param _sampling_freq Sampling_Frequency - single octet value + * @param _frame_duration Frame_Duration - single octet value + * @param _audio_channel_alloc Audio_Channel_Allocation - + * four octet value + * @param _octets_per_codec_frame Octets_Per_Codec_Frame - + * two octet value + * @param _codec_frame_blocks_per_sdu Codec_Frame_Blocks_Per_SDU - + * single octet value + * + * @return Pointer to a `ble_uuid16_t` structure. + */ +#define BLE_AUDIO_BUILD_CODEC_CONFIG(_sampling_freq, \ + _frame_duration, \ + _audio_channel_alloc, \ + _octets_per_codec_frame, \ + _codec_frame_blocks_per_sdu) \ + { \ + 2, BLE_AUDIO_CODEC_CONF_SAMPLING_FREQ_TYPE, _sampling_freq, \ + 2, BLE_AUDIO_CODEC_CONF_FRAME_DURATION_TYPE, _frame_duration, \ + OPTIONAL_FIELD(5, BLE_AUDIO_CODEC_CONF_AUDIO_CHANNEL_ALLOCATION_TYPE, \ + _audio_channel_alloc) \ + 3, BLE_AUDIO_CODEC_CONF_OCTETS_PER_CODEC_FRAME_TYPE, \ + (_octets_per_codec_frame), ((_octets_per_codec_frame) >> 8), \ + OPTIONAL_FIELD(2, BLE_AUDIO_CODEC_CONF_FRAME_BLOCKS_PER_SDU_TYPE, \ + _codec_frame_blocks_per_sdu) \ + } + +/** + * @brief Helper macro used to build LTV array of Codec_Specific_Capabilities. + * + * @param _sampling_freq Supported_Sampling_Frequencies - + * two octet value + * @param _frame_duration Supported_Frame_Durations - single + * octet value + * @param _audio_channel_counts Supported_Audio_Channel_Counts - + * single octet value + * @param _min_octets_per_codec_frame minimum value of + * Supported_Octets_Per_Codec_Frame - + * two octet value + * @param _max_octets_per_codec_frame maximum value of + * Supported_Octets_Per_Codec_Frame - + * two octet value + * @param _codec_frames_per_sdu Supported_Max_Codec_Frames_Per_SDU - + * single octet value + * + * @return Pointer to a `ble_uuid16_t` structure. + */ +#define BLE_AUDIO_BUILD_CODEC_CAPS(_sampling_freq, \ + _frame_duration, \ + _audio_channel_counts, \ + _min_octets_per_codec_frame, \ + _max_octets_per_codec_frame, \ + _codec_frames_per_sdu) \ + { \ + 3, BLE_AUDIO_CODEC_CAPS_SAMPLING_FREQ_TYPE, \ + (_sampling_freq), ((_sampling_freq) >> 8), \ + 2, BLE_AUDIO_CODEC_CAPS_FRAME_DURATION_TYPE, _frame_duration, \ + OPTIONAL_FIELD(2, BLE_AUDIO_CODEC_CAPS_SUP_AUDIO_CHANNEL_COUNTS_TYPE, \ + _audio_channel_counts) \ + 5, BLE_AUDIO_CODEC_CAPS_OCTETS_PER_CODEC_FRAME_TYPE, \ + (_min_octets_per_codec_frame), ((_min_octets_per_codec_frame) >> 8), \ + (_max_octets_per_codec_frame), ((_max_octets_per_codec_frame) >> 8), \ + OPTIONAL_FIELD(2, BLE_AUDIO_CODEC_CAPS_FRAMES_PER_SDU_TYPE, \ + _codec_frames_per_sdu) \ + } + +/** Codec Information */ +struct ble_audio_codec_id { + /** Coding Format */ + uint8_t format; + + /** Company ID */ + uint16_t company_id; + + /** Vendor Specific Codec ID */ + uint16_t vendor_specific; +}; + +/** @brief Public Broadcast Announcement features bits */ +enum ble_audio_pub_broadcast_announcement_feat { + /** Broadcast Stream Encryption */ + BLE_AUDIO_PUB_BROADCAST_ANNOUNCEMENT_FEAT_ENCRYPTION = 1 << 0, + + /** Standard Quality Public Broadcast Audio */ + BLE_AUDIO_PUB_BROADCAST_ANNOUNCEMENT_FEAT_SQ = 1 << 1, + + /** High Quality Public Broadcast Audio */ + BLE_AUDIO_PUB_BROADCAST_ANNOUNCEMENT_FEAT_HQ = 1 << 2, +}; + +/** @brief Public Broadcast Announcement structure */ +struct ble_audio_pub_broadcast_announcement { + /** Public Broadcast Announcement features bitfield */ + enum ble_audio_pub_broadcast_announcement_feat features; + + /** Metadata length */ + uint8_t metadata_len; + + /** Metadata */ + const uint8_t *metadata; +}; + +/** Broadcast Name */ +struct ble_audio_broadcast_name { + /** Broadcast Name length */ + uint8_t name_len; + + /** Broadcast Name */ + const char *name; +}; + +/** + * @defgroup ble_audio_events Bluetooth Low Energy Audio Events + * @{ + */ + +/** BLE Audio event: Broadcast Announcement */ +#define BLE_AUDIO_EVENT_BROADCAST_ANNOUNCEMENT 0 + +/** BLE Audio event: Codec Registered */ +#define BLE_AUDIO_EVENT_CODEC_REGISTERED 1 + +/** BLE Audio event: Codec Unregistered */ +#define BLE_AUDIO_EVENT_CODEC_UNREGISTERED 2 + +/** BLE Audio event: BASS - Remote Scan Stopped */ +#define BLE_AUDIO_EVENT_BASS_REMOTE_SCAN_STOPPED 3 + +/** BLE Audio event: BASS - Remote Scan Started */ +#define BLE_AUDIO_EVENT_BASS_REMOTE_SCAN_STARTED 4 + +/** BLE Audio event: BASS - Set Broadcast Code */ +#define BLE_AUDIO_EVENT_BASS_SET_BROADCAST_CODE 5 + +/** BLE Audio event: Broadcast Sink - BASE Report Received */ +#define BLE_AUDIO_EVENT_BROADCAST_SINK_BASE_REPORT 9 + +/** @} */ + +/** @brief Broadcast Announcement */ +struct ble_audio_event_broadcast_announcement { + /** Extended advertising report */ + const struct ble_gap_ext_disc_desc *ext_disc; + + /** Broadcast ID */ + uint32_t broadcast_id; + + /** Additional service data included in Broadcast Audio Announcement */ + const uint8_t *svc_data; + + /** Additional service data length */ + uint16_t svc_data_len; + + /** Optional Public Broadcast Announcement data */ + struct ble_audio_pub_broadcast_announcement *pub_announcement_data; + + /** Optional Broadcast Name */ + struct ble_audio_broadcast_name *name; +}; + +/** @brief Codec Registered */ +struct ble_audio_event_codec_registered { + /** Codec Record */ + const struct ble_audio_codec_record *record; +}; + +/** @brief Codec Unregistered */ +struct ble_audio_event_codec_unregistered { + /** Codec Record */ + const struct ble_audio_codec_record *record; +}; + +/** @brief BASS Source Removed */ +struct ble_audio_event_bass_remote_scan { + /** Connection Handle of Broadcast Assistant that is performing Scan procedure for us */ + uint16_t conn_handle; +}; + +/** @brief BASS Set Broadcast Code */ +struct ble_audio_event_bass_set_broadcast_code { + /** Source ID */ + uint8_t source_id; + + /** Source ID */ + uint8_t broadcast_code[BLE_AUDIO_BROADCAST_CODE_SIZE]; +}; + +<<<<<<< HEAD +======= +/** @brief BASS Source Removed */ +struct ble_audio_event_bass_source_removed { + /** Source ID */ + uint8_t source_id; +}; + +/** @brief Broadcast Sink BASE Report Received */ +struct ble_audio_event_broadcast_sink_base_report { + /** Instance ID */ + uint8_t instance_id; + + /** Advertiser transmit power in dBm (127 if unavailable) */ + int8_t tx_power; + + /** Received signal strength indication in dBm (127 if unavailable) */ + int8_t rssi; + + /** BASE length */ + uint8_t base_length; + + /** BASE */ + const uint8_t *base; +}; + +>>>>>>> 51d315bd (nimble/audio: Add initial audio broadcast sink implementation) +/** + * 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 + * application-specified callback. + */ +struct ble_audio_event { + /** + * Indicates the type of BLE Audio event that occurred. This is one of the + * BLE_AUDIO_EVENT codes. + */ + uint8_t type; + + /** + * A discriminated union containing additional details concerning the event. + * The 'type' field indicates which member of the union is valid. + */ + union { + /** + * @ref BLE_AUDIO_EVENT_BROADCAST_ANNOUNCEMENT + * + * Represents a received Broadcast Announcement. + */ + struct ble_audio_event_broadcast_announcement broadcast_announcement; + + /** + * @ref BLE_AUDIO_EVENT_CODEC_REGISTERED + * + * Represents a codec registration. + */ + struct ble_audio_event_codec_registered codec_registered; + + /** + * @ref BLE_AUDIO_EVENT_CODEC_UNREGISTERED + * + * Represents a codec registration. + */ + struct ble_audio_event_codec_unregistered codec_unregistered; + + /** + * @ref BLE_AUDIO_EVENT_BASS_REMOTE_SCAN_STOPPED + * + * Represents a Scan procedure termination by Broadcast Assistant. + */ + struct ble_audio_event_bass_remote_scan remote_scan_stopped; + + /** + * @ref BLE_AUDIO_EVENT_BASS_REMOTE_SCAN_STARTED + * + * Represents a Scan procedure start by Broadcast Assistant. + */ + struct ble_audio_event_bass_remote_scan remote_scan_started; + + /** + * @ref BLE_AUDIO_EVENT_BASS_SET_BROADCAST_CODE + * + * Represents a Broadcast Code baing set in BASS. + */ + struct ble_audio_event_bass_set_broadcast_code bass_set_broadcast_code; +<<<<<<< HEAD +======= + + /** + * @ref BLE_AUDIO_EVENT_BASS_SOURCE_REMOVED + * + * Represents a Broadcast Source being removed in BASS. + */ + struct ble_audio_event_bass_source_removed bass_source_removed; + + /** + * @ref BLE_AUDIO_EVENT_BROADCAST_SINK_BASE_REPORT + * + * Represents a BASE Report received. + */ + struct ble_audio_event_broadcast_sink_base_report base_report; +>>>>>>> 51d315bd (nimble/audio: Add initial audio broadcast sink implementation) + }; +}; + +/** Callback function type for handling BLE Audio events. */ +typedef int ble_audio_event_fn(struct ble_audio_event *event, void *arg); + +/** + * Event listener structure + * + * This should be used as an opaque structure and not modified manually. + */ +struct ble_audio_event_listener { + /** The function to call when a BLE Audio event occurs. */ + ble_audio_event_fn *fn; + + /** An optional argument to pass to the event handler function. */ + void *arg; + + /** Singly-linked list entry. */ + SLIST_ENTRY(ble_audio_event_listener) next; +}; + +/** + * Registers listener for BLE Audio events + * + * On success listener structure will be initialized automatically and does not + * need to be initialized prior to calling this function. To change callback + * and/or argument unregister listener first and register it again. + * + * @param[in] listener Listener structure + * @param[in] fn Callback function + * @param[in] arg Optional callback argument + * + * @return 0 on success + * BLE_HS_EINVAL if no callback is specified + * BLE_HS_EALREADY if listener is already registered + */ +int ble_audio_event_listener_register(struct ble_audio_event_listener *listener, + ble_audio_event_fn *fn, void *arg); + +/** + * Unregisters listener for BLE Audio events + * + * @param[in] listener Listener structure + * + * @return 0 on success + * BLE_HS_ENOENT if listener was not registered + */ +int ble_audio_event_listener_unregister(struct ble_audio_event_listener *listener); + +/** + * BASE iterator + * + * The iterator structure used by @ref ble_audio_base_subgroup_iter and + * @ref ble_audio_base_bis_iter functions to iterate the BASE Level 2 and 3 elements + * (Subgroups and BISes). + * This should be used as an opaque structure and not modified manually. + * + * Example: + * @code{.c} + * struct ble_audio_base_iter subgroup_iter; + * struct ble_audio_base_iter bis_iter; + * struct ble_audio_base_group group; + * struct ble_audio_base_subgroup subgroup; + * struct ble_audio_base_bis bis; + * + * rc = ble_audio_base_parse(data, data_size, &group, &subgroup_iter); + * if (rc == 0) { + * for (uint8_t i = 0; i < group->num_subgroups; i++) { + * rc = ble_audio_base_subgroup_iter(&subgroup_iter, &subgroup, &bis_iter); + * if (rc == 0) { + * for (uint8_t j = 0; j < subgroup->num_bis; j++) { + * rc = ble_audio_base_bis_iter(&bis_iter, &bis); + * if (rc == 0) { + * foo(&group, &subgroup, &bis); + * } + * } + * } + * } + * } + * @endcode + */ +struct ble_audio_base_iter { + /** Data pointer */ + const uint8_t *data; + + /** Base length */ + uint8_t buf_len; + + /** Original BASE pointer */ + const uint8_t *buf; + + /** Remaining number of elements */ + uint8_t num_elements; +}; + +/** @brief Broadcast Audio Source Endpoint Group structure */ +struct ble_audio_base_group { + /** Presentation Delay */ + uint32_t presentation_delay; + + /** Number of subgroups */ + uint8_t num_subgroups; +}; + +/** + * Parse the BASE received from Basic Audio Announcement data. + * + * @param[in] data Pointer to the BASE data buffer to parse. + * @param[in] data_len Length of the BASE data buffer. + * @param[out] group Group object. + * @param[out] subgroup_iter Subgroup iterator object. + * + * @return 0 on success; nonzero on failure. + */ +int ble_audio_base_parse(const uint8_t *data, uint8_t data_len, + struct ble_audio_base_group *group, + struct ble_audio_base_iter *subgroup_iter); + +/** @brief Broadcast Audio Source Endpoint Subgroup structure */ +struct ble_audio_base_subgroup { + /** Codec information for the subgroup */ + struct ble_audio_codec_id codec_id; + + /** Length of the Codec Specific Configuration for the subgroup */ + uint8_t codec_spec_config_len; + + /** Codec Specific Configuration for the subgroup */ + const uint8_t *codec_spec_config; + + /** Length of the Metadata for the subgroup */ + uint8_t metadata_len; + + /** Series of LTV structures containing Metadata */ + const uint8_t *metadata; + + /** Number of BISes in the subgroup */ + uint8_t num_bis; +}; + +/** + * @brief Basic Audio Announcement Subgroup information + * + * @param[in] subgroup_iter Subgroup iterator object. + * @param[out] subgroup Subgroup object. + * @param[out] bis_iter BIS iterator object. + * + * @return 0 on success; + * A non-zero value on failure. + */ +int ble_audio_base_subgroup_iter(struct ble_audio_base_iter *subgroup_iter, + struct ble_audio_base_subgroup *subgroup, + struct ble_audio_base_iter *bis_iter); + +/** @brief Broadcast Audio Source Endpoint BIS structure */ +struct ble_audio_base_bis { + /** BIS_index value for the BIS */ + uint8_t index; + + /** Length of the Codec Specific Configuration for the BIS */ + uint8_t codec_spec_config_len; + + /** Codec Specific Configuration for the BIS */ + const uint8_t *codec_spec_config; +}; + +/** + * @brief Basic Audio Announcement Subgroup information + * + * @param[in] bis_iter BIS iterator object. + * @param[out] bis BIS object. + * + * @return 0 on success; + * A non-zero value on failure. + */ +int ble_audio_base_bis_iter(struct ble_audio_base_iter *bis_iter, + struct ble_audio_base_bis *bis); + +/** Broadcast Isochronous Streams (BIS) */ +struct ble_audio_bis { + /** Pointer to next BIS in subgroup */ + STAILQ_ENTRY(ble_audio_bis) next; + + /** BIS index */ + uint8_t idx; + + /** BIS level Codec Specific Configuration length */ + uint8_t codec_spec_config_len; + + /** BIS level Codec Specific Configuration */ + uint8_t *codec_spec_config; +}; + +/** Broadcast Isochronous Group (BIG) Subgroup */ +struct ble_audio_big_subgroup { + /** Pointer to next subgroup in BIG */ + STAILQ_ENTRY(ble_audio_big_subgroup) next; + + /** Number of BISes in subgroup */ + uint8_t bis_cnt; + + /** Codec ID */ + struct ble_audio_codec_id codec_id; + + /** Subgroup level Codec Specific Configuration */ + uint8_t *codec_spec_config; + + /** Subgroup level Codec Specific Configuration length */ + uint8_t codec_spec_config_len; + + /** Subgroup Metadata */ + uint8_t *metadata; + + /** Subgroup Metadata length*/ + uint8_t metadata_len; + + /** Link list of BISes */ + STAILQ_HEAD(, ble_audio_bis) bises; +}; + +/** Broadcast Audio Source Endpoint */ +struct ble_audio_base { + /** Broadcast ID */ + uint32_t broadcast_id; + + /** Presentation Delay */ + uint32_t presentation_delay; + + /** Number of subgroups in BIG */ + uint8_t num_subgroups; + + /** Link list of subgroups */ + STAILQ_HEAD(, ble_audio_big_subgroup) subs; +}; + +/** @} */ + +#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 0000000000..11d89014bd --- /dev/null +++ b/nimble/host/audio/include/audio/ble_audio_broadcast_sink.h @@ -0,0 +1,264 @@ +/* + * 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 +#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; + + /** Pointer to Maximum Subevents value to initialize. */ + uint8_t *out_mse; + + /** Pointer to Sync Timeout value to initialize. */ + 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; + + /** Application ISO data callback. */ + ble_iso_event_fn **out_cb; + + /** The optional argument to pass to the callback function. */ + void **out_cb_arg; + } 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); + +/** + * @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 broadcast_code Broadcast code or NULL. + * + * @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 uint8_t broadcast_code[16]); + +/** + * @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, 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 0000000000..252303eb21 --- /dev/null +++ b/nimble/host/audio/include/audio/ble_audio_scan_delegator.h @@ -0,0 +1,383 @@ +/* + * 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 +#include "audio/ble_audio.h" +#include "nimble/ble.h" +/** 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[ + MYNEWT_VAL(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 discriminated 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 */ + const 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 66d418d311..5dd146166a 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 4253347816..344112722a 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 0000000000..ce81af109e --- /dev/null +++ b/nimble/host/audio/src/ble_audio_broadcast_sink.c @@ -0,0 +1,1417 @@ +/* + * 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 "os/util.h" +#include "sysinit/sysinit.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" + +#if MYNEWT_VAL(BLE_AUDIO_BROADCAST_SINK) +#define BROADCAST_ID_INVALID 0xFFFFFFFF +#define BIS_SYNC_TEST_BIS_INDEX(_bis_sync, _index) (((_bis_sync) & (1 << ((_index) - 1))) > 0) +#define BIS_SYNC_CLEAR_BIS_INDEX(_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_SCAN, + 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_BIG_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; + + uint16_t iso_interval; + + uint16_t pa_interval; + + /** 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; + + /* Flags */ + uint8_t is_encrypted : 1; + uint8_t broadcast_code_is_valid : 1; + + 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; +} + +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 +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; + struct ble_audio_scan_delegator_receive_state receive_state = { 0 }; + int rc; + + if (state_internal == state_internal_old) { + return; + } + + sink->big_sync_state = state_internal; + + 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; + } + + receive_state.bad_code = NULL; + + switch (state_internal) { + case BIG_SYNC_STATE_IDLE: + if (state_internal_old == 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; + receive_state.bad_code = &sink->broadcast_code; + } else if (state_internal_old == BIG_SYNC_STATE_ACTIVE || + state_internal_old == 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_CODE: + receive_state.big_enc = BLE_AUDIO_SCAN_DELEGATOR_BIG_ENC_BROADCAST_CODE_MISSING; + break; + + case BIG_SYNC_STATE_PENDING_BIG_INFO: + case BIG_SYNC_STATE_PENDING_BASE: + case BIG_SYNC_STATE_PENDING_BIG_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 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; + struct ble_audio_scan_delegator_receive_state receive_state; + int rc; + + if (state_internal == state_internal_old) { + return; + } + + sink->pa_sync_state = state_internal; + + 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 (state_internal) { + case PA_SYNC_STATE_IDLE: + case PA_SYNC_STATE_PENDING_SCAN: + 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 (state_internal_old == 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 update failed (%d)\n", rc); + } +} + +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; + + 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); + } else { + 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 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 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 +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; + } + + 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; + } +} + +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; +} + +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; + int rc; + + 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 { + rc = pa_sync_remove(addr, adv_sid); + if (rc != 0) { + /* TODO: */ + } + + 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 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; + + 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) +{ + struct ble_audio_broadcast_sink_action action; + 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 (uint8_t 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 bis_sync_req; + + rc = ble_audio_base_subgroup_iter(subgroup_iter, &subgroup, &bis_iter); + if (rc != 0) { + break; + } + + bis_sync_req = subgroup_bis_sync_get(sink, subgroup_index); + if (bis_sync_req == 0) { + /* No BISes requested for this subgroup */ + continue; + } + + for (uint8_t i = 0; i < subgroup.num_bis; i++) { + 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_SYNC_TEST_BIS_INDEX(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; + + /* NULL the pointers to check that will be set by app */ + (*bis_params[bis_cnt]).cb = NULL; + (*bis_params[bis_cnt]).cb_arg = NULL; + + action.bis_sync.out_cb = &(*bis_params[bis_cnt]).cb; + action.bis_sync.out_cb_arg = &(*bis_params[bis_cnt]).cb_arg; + + rc = action_cb.fn(&action, action_cb.arg); + if (rc != 0) { + BLE_HS_LOG_WARN("bis sync rejected by user (%d)\n", rc); + + /* Update BIS Sync */ + BIS_SYNC_CLEAR_BIS_INDEX(bis_sync_req, bis.index); + continue; + } + + if ((*bis_params[bis_cnt]).cb == NULL) { + BLE_HS_LOG_ERROR("iso cb is NULL\n"); + + /* Update BIS Sync */ + BIS_SYNC_CLEAR_BIS_INDEX(bis_sync_req, bis.index); + continue; + } + + if (++bis_cnt == ARRAY_SIZE(*bis_params)) { + goto done; + } + } + } + +done: + *out_bis_cnt = bis_cnt; + return rc; +} + +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; + } + + 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.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; + } + + memcpy(broadcast_code, sink->broadcast_code, BLE_AUDIO_BROADCAST_CODE_SIZE); + broadcast_code[BLE_AUDIO_BROADCAST_CODE_SIZE] = '\0'; + + big_sync_create_params.sync_handle = sink->pa_sync_handle; + big_sync_create_params.broadcast_code = broadcast_code; + 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; + } + + big_sync_create_params.bis_cnt = bis_cnt; + + 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_BIG_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_SCAN) == 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_SCAN) { + 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.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; +} + +static int +pa_sync(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 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); + /* TODO: destroy sink */ + return rc; + } + + if (action_cb.fn == NULL) { + BLE_HS_LOG_ERROR("action_cb.fn is NULL\n"); + return BLE_HS_EAPP; + } + + if (conn_handle != NULL) { + action.type = BLE_AUDIO_BROADCAST_SINK_ACTION_PA_SYNC; + action.pa_sync.out_params = &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); + /* TODO: destroy sink */ + return rc; + } + + rc = ble_gap_periodic_adv_sync_receive( + *conn_handle, &sync_params, gap_event_handler, NULL); + if (rc != 0) { + BLE_HS_LOG_ERROR("sync receive failed (%d)\n", rc); + /* TODO: destroy sink */ + return rc; + } + + pa_sync_state_set(sink, PA_SYNC_STATE_PENDING_PAST); + } else { + pa_sync_state_set(sink, PA_SYNC_STATE_PENDING_SCAN); + pa_sync_create(); + disc_start(); + } + + return rc; +} + +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; + sink->pa_interval = BLE_AUDIO_SCAN_DELEGATOR_PA_SYNC_UNKNOWN; + + SLIST_INSERT_HEAD(&ble_audio_broadcast_sink_list, sink, next); + + return sink; +} + +static int +broadcast_sink_start( + uint8_t source_id, const uint8_t broadcast_code[BLE_AUDIO_BROADCAST_CODE_SIZE], + uint16_t *conn_handle) +{ + struct ble_audio_broadcast_sink *sink; + + 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); + } + + /* 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_ACTIVE: + break; + case PA_SYNC_STATE_IDLE: + case PA_SYNC_STATE_ERROR: + case PA_SYNC_STATE_TIMEOUT: + return pa_sync(sink, conn_handle); + case PA_SYNC_STATE_PENDING_PAST: + case PA_SYNC_STATE_PENDING_SCAN: + case PA_SYNC_STATE_PENDING_SYNC: + return BLE_HS_EALREADY; + } + + switch (sink->big_sync_state) { + case BIG_SYNC_STATE_ACTIVE: + return BLE_HS_EDONE; + + case BIG_SYNC_STATE_PENDING_BIG_INFO: + case BIG_SYNC_STATE_PENDING_BIG_SYNC: + case BIG_SYNC_STATE_PENDING_BASE: + return BLE_HS_EALREADY; + + case BIG_SYNC_STATE_PENDING_CODE: + if (broadcast_code == NULL) { + return BLE_HS_EALREADY; + } + + 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); + default: + BLE_AUDIO_DBG_ASSERT(false); + ble_audio_broadcast_sink_stop(source_id); + return BLE_HS_EAGAIN; + } + + return 0; +} + +int +ble_audio_broadcast_sink_start( + uint8_t source_id, const uint8_t broadcast_code[16]) +{ + return broadcast_sink_start(source_id, broadcast_code, 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_BIG_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, 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->pa_interval = sync_opt->pa_interval; + 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_broadcast_sink_priv.h b/nimble/host/audio/src/ble_audio_broadcast_sink_priv.h new file mode 100644 index 0000000000..ad7990144d --- /dev/null +++ b/nimble/host/audio/src/ble_audio_broadcast_sink_priv.h @@ -0,0 +1,34 @@ +/* + * 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_PRIV_ +#define H_BLE_AUDIO_BROADCAST_SINK_PRIV_ + +#include +#include "audio/ble_audio.h" +#include "audio/ble_audio_broadcast_sink.h" +#include "audio/ble_audio_scan_delegator.h" + +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_BROADCAST_SINK_PRIV_ */ diff --git a/nimble/host/audio/src/ble_audio_priv.h b/nimble/host/audio/src/ble_audio_priv.h index 10f0109ddc..d575c54969 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 0000000000..6a80b28ce2 --- /dev/null +++ b/nimble/host/audio/src/ble_audio_scan_delegator.c @@ -0,0 +1,474 @@ +/* + * 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 "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" + +#if MYNEWT_VAL(BLE_AUDIO_SCAN_DELEGATOR) +#define BAD_CODE_NULL "BadCode" + +static const uint8_t bad_code_null[BLE_AUDIO_BROADCAST_CODE_SIZE] = BAD_CODE_NULL; +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; + out_state->bad_code = &state->bad_code; + out_state->num_subgroups = 0; + + 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 int +bass_add_source_op_handler(struct ble_svc_audio_bass_operation *op, void *arg) +{ + struct ble_audio_scan_delegator_source_desc *source_desc; + struct ble_audio_scan_delegator_sync_opt *sync_opt; + struct ble_audio_scan_delegator_action action; + const uint8_t source_id = op->add_source.source_id; + int rc; + + source_desc = &action.source_add.source_desc; + source_desc->addr = op->add_source.adv_addr; + source_desc->adv_sid = op->add_source.adv_sid; + source_desc->broadcast_id = op->add_source.broadcast_id; + + sync_opt = &action.source_add.sync_opt; + sync_opt->pa_sync = (uint8_t)op->add_source.pa_sync; + sync_opt->pa_interval = op->add_source.pa_interval; + sync_opt->num_subgroups = op->add_source.num_subgroups; + + 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->add_source.subgroups[i].bis_sync_state; + sync_opt->subgroups[i].metadata_length = op->add_source.subgroups[i].metadata_length; + sync_opt->subgroups[i].metadata = op->add_source.subgroups[i].metadata; + } + + 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, 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->pa_sync = (uint8_t)op->modify_source.pa_sync; + sync_opt->pa_interval = op->modify_source.pa_interval; + sync_opt->num_subgroups = op->modify_source.num_subgroups; + + 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 add_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; + } + + add_params.source_addr = params->source_desc.addr; + add_params.source_adv_sid = params->source_desc.adv_sid; + add_params.broadcast_id = params->source_desc.broadcast_id; + add_params.pa_sync_state = (uint8_t)params->state.pa_sync_state; + add_params.big_encryption = (uint8_t)params->state.big_enc; + if (params->state.big_enc == BLE_AUDIO_SCAN_DELEGATOR_BIG_ENC_BROADCAST_CODE_INVALID) { + const uint8_t (*bad_code)[BLE_AUDIO_BROADCAST_CODE_SIZE]; + + if (params->state.bad_code != NULL) { + bad_code = params->state.bad_code; + } else { + bad_code = &bad_code_null; + } + + memcpy(add_params.bad_code, *bad_code, ARRAY_SIZE(add_params.bad_code)); + } + add_params.num_subgroups = params->state.num_subgroups; + if (add_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 < add_params.num_subgroups; i++) { + add_params.subgroups[i].bis_sync_state = params->state.subgroups->bis_sync; + add_params.subgroups[i].metadata_length = params->state.subgroups->metadata_length; + add_params.subgroups[i].metadata = params->state.subgroups->metadata; + } + + return ble_svc_audio_bass_receive_state_add(&add_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_sync_params sync_params; + struct ble_svc_audio_bass_enc_params enc_params; + struct ble_svc_audio_bass_bis_sync_params bis_sync_params; + struct ble_svc_audio_bass_metadata_params metadata_params; + int rc; + + if (state == NULL) { + BLE_HS_LOG_ERROR("NULL state\n"); + return BLE_HS_EINVAL; + } + + /* FIXME: This function highly ineffective as it generates a bunch of ATT Notifications */ + + sync_params.pa_sync_state = (uint8_t)state->pa_sync_state; + + rc = ble_svc_audio_bass_update_sync(&sync_params, source_id); + if (rc != 0) { + BLE_HS_LOG_ERROR("Failed to update sync (rc %d)\n", rc); + } + + enc_params.big_encryption = (uint8_t)state->big_enc; + + if (state->big_enc == BLE_AUDIO_SCAN_DELEGATOR_BIG_ENC_BROADCAST_CODE_INVALID) { + const uint8_t (*bad_code)[BLE_AUDIO_BROADCAST_CODE_SIZE]; + + if (state->bad_code != NULL) { + bad_code = state->bad_code; + } else { + bad_code = &bad_code_null; + } + + enc_params.bad_code = *bad_code; + } + + rc = ble_svc_audio_bass_update_enc(&enc_params, source_id); + if (rc != 0) { + BLE_HS_LOG_ERROR("Failed to update enc (rc %d)\n", rc); + } + + for (uint8_t i = 0; i < state->num_subgroups; i++) { + bis_sync_params.subgroup_idx = i; + bis_sync_params.bis_sync_state = state->subgroups[i].bis_sync; + + rc = ble_svc_audio_bass_update_bis_sync(&bis_sync_params, source_id); + if (rc != 0) { + BLE_HS_LOG_ERROR("Failed to update bis sync (rc %d)\n", rc); + } + + metadata_params.subgroup_idx = i; + metadata_params.metadata_length = state->subgroups[i].metadata_length; + metadata_params.metadata = state->subgroups[i].metadata; + + rc = ble_svc_audio_bass_update_metadata(&metadata_params, source_id); + if (rc != 0) { + BLE_HS_LOG_ERROR("Failed to update metadata (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; + entry.source_desc.addr = state->source_addr; + entry.source_desc.adv_sid = state->source_adv_sid; + entry.source_desc.broadcast_id = state->broadcast_id; + entry.state.pa_sync_state = (uint8_t)state->pa_sync_state; + entry.state.big_enc = (uint8_t)state->big_encryption; + entry.state.bad_code = &state->bad_code; + entry.state.num_subgroups = state->num_subgroups; + + BLE_AUDIO_DBG_ASSERT(entry.state.num_subgroups < ARRAY_SIZE(subgroups)); + + for (uint8_t j = 0; j < entry.state.num_subgroups; j++) { + entry.state.subgroups[j].bis_sync = state->subgroups[j].bis_sync_state; + entry.state.subgroups[j].metadata_length = state->subgroups[j].metadata_length; + entry.state.subgroups[j].metadata = state->subgroups[j].metadata; + } + + 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_SET_BROADCAST_CODE) { + 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_scan_delegator_priv.h b/nimble/host/audio/src/ble_audio_scan_delegator_priv.h new file mode 100644 index 0000000000..ae5fc6510d --- /dev/null +++ b/nimble/host/audio/src/ble_audio_scan_delegator_priv.h @@ -0,0 +1,33 @@ +/* + * 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_PRIV_ +#define H_BLE_AUDIO_SCAN_DELEGATOR_PRIV_ + +#include +#include "audio/ble_audio.h" +#include "audio/ble_audio_scan_delegator.h" + +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_SCAN_DELEGATOR_PRIV_ */ diff --git a/nimble/host/audio/syscfg.yml b/nimble/host/audio/syscfg.yml index 5f0269e0ab..e0c890c5af 100644 --- a/nimble/host/audio/syscfg.yml +++ b/nimble/host/audio/syscfg.yml @@ -22,4 +22,63 @@ syscfg.defs: Maximum number of registered audio codecs. value: 0 + BLE_AUDIO_BROADCAST_SINK: + description: > + This option enables BLE Audio Sink support. + value: 0 + restrictions: + - '(BLE_ISO_BROADCAST_SINK > 0) if 1' + + BLE_AUDIO_BROADCAST_SINK_MAX: + description: > + Maximum umber of Audio Broadcast Sink instances. + value: 'MYNEWT_VAL_BLE_ISO_MAX_BIGS' + + 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_SCAN_DELEGATOR: + description: > + This option enables BLE Audio Scan Delegator support. + value: 0 + + BLE_AUDIO_SCAN_DELEGATOR_SYSINIT_STAGE: + description: > + Primary sysinit stage for BLE Audio Scan Delegator. + value: 499 + + 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: + BLE_AUDIO_BROADCAST_SINK_LOG: + module: MYNEWT_VAL(BLE_AUDIO_BROADCAST_SINK_LOG_MOD) + level: MYNEWT_VAL(BLE_AUDIO_BROADCAST_SINK_LOG_LVL) + +syscfg.vals.BLE_AUDIO_BROADCAST_SINK: + BLE_AUDIO_SCAN_DELEGATOR: 1 + BLE_AUDIO_SCAN_DELEGATOR_STANDALONE: 0 diff --git a/nimble/host/audio/test/src/ble_audio_test.h b/nimble/host/audio/test/src/ble_audio_test.h new file mode 100644 index 0000000000..4819a5846d --- /dev/null +++ b/nimble/host/audio/test/src/ble_audio_test.h @@ -0,0 +1,53 @@ +/* + * 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 _BLE_AUDIO_BSNK_TEST_H +#define _BLE_AUDIO_BSNK_TEST_H + +#include +#include + +#include "os/mynewt.h" +#include "testutil/testutil.h" + +#include "host/ble_hs.h" +#include "host/audio/ble_audio.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define BLE_AUDIO_TEST_ADDR_VALID (&ble_audio_test_addr) +#define BLE_AUDIO_TEST_ADDR_INVALID NULL +#define BLE_AUDIO_TEST_ADV_SID_VALID 0x0F +#define BLE_AUDIO_TEST_ADV_SID_INVALID 0x10 +#define BLE_AUDIO_TEST_BROADCAST_ID_VALID 0x000000 +#define BLE_AUDIO_TEST_BROADCAST_ID_INVALID (BLE_AUDIO_BROADCAST_ID_MASK + 1) +#define BLE_AUDIO_TEST_BROADCAST_CODE_VALID (&ble_audio_test_broadcast_code[0]) +#define BLE_AUDIO_TEST_BROADCAST_CODE_INVALID (&ble_audio_test_broadcast_code_invalid[0]) +#define BLE_AUDIO_TEST_PA_INTERVAL_VALID 0x0006 +#define BLE_AUDIO_TEST_PA_INTERVAL_INVALID 0x0005 + +extern const ble_addr_t ble_audio_test_addr; +extern const char ble_audio_test_broadcast_code[]; +extern const char ble_audio_test_broadcast_code_invalid[]; + +#ifdef __cplusplus +} +#endif +#endif /* _BLE_AUDIO_BSNK_TEST_H */ diff --git a/nimble/host/audio/test/src/testcases/ble_audio_base_parse.c b/nimble/host/audio/test/src/testcases/ble_audio_base_parse.c new file mode 100644 index 0000000000..40306c27b5 --- /dev/null +++ b/nimble/host/audio/test/src/testcases/ble_audio_base_parse.c @@ -0,0 +1,398 @@ +/* + * 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 "ble_audio_test.h" + +/** + * BAP_v1.0.1 Table 3.16 + * BASE structure for the logical BASE structure example + */ +static const uint8_t example_base[] = { + /* Presentation_Delay: 40 ms */ + 0x1e, 0x00, 0x00, + /* Num_Subgroups: 2 Subgroups */ + 0x02, + /* Num_BIS[0]: 2 BIS in Subgroup[0] */ + 0x02, + /* Codec_ID[0]: LC3 */ + 0x06, 0x00, 0x00, 0x00, 0x00, + /* Codec_Specific_Configuration_Length[0] */ + 0x0a, + /* Codec_Specific_Configuration[0] */ + /* LTV 1: Sampling_Frequency: 48000 Hz */ + 0x02, 0x01, 0x08, + /* LTV 2: Frame_Duration: 10 ms */ + 0x02, 0x02, 0x02, + /* LTV 3: Octets_Per_Codec_Frame: 100 octets */ + 0x03, 0x04, 0x64, 0x00, + /* Metadata_Length[0] */ + 0x09, + /* Metadata[0] */ + /* LTV 1: Streaming_Audio_Contexts: Media */ + 0x03, 0x02, 0x04, 0x00, + /* LTV 2: Language: Spanish */ + 0x04, 0x04, 0x73, 0x70, 0x61, + /* BIS_index[0[0]] */ + 0x01, + /* Codec_Specific_Configuration_Length[0[0]] */ + 0x06, + /* Codec_Specific_Configuration[0[0]] */ + /* LTV 1 = Audio_Channel_Allocation: FL */ + 0x05, 0x03, 0x01, 0x00, 0x00, 0x00, + /* BIS_index[0[1]] */ + 0x02, + /* Codec_Specific_Configuration_Length[0[1]] */ + 0x06, + /* Codec_Specific_Configuration[0[1]] */ + /* LTV 1 = Audio_Channel_Allocation: FR */ + 0x05, 0x03, 0x02, 0x00, 0x00, 0x00, + /* Num_BIS[1]: 2 BIS in Subgroup[0] */ + 0x02, + /* Codec_ID[1]: LC3 */ + 0x06, 0x00, 0x00, 0x00, 0x00, + /* Codec_Specific_Configuration_Length[1] */ + 0x0a, + /* Codec_Specific_Configuration[1] */ + /* LTV 1: Sampling_Frequency: 48000 Hz */ + 0x02, 0x01, 0x08, + /* LTV 2: Frame_Duration: 10 ms */ + 0x02, 0x02, 0x02, + /* LTV 3: Octets_Per_Codec_Frame: 100 octets */ + 0x03, 0x04, 0x64, 0x00, + /* Metadata_Length[1] */ + 0x09, + /* Metadata[0] */ + /* LTV 1: Streaming_Audio_Contexts: Media */ + 0x03, 0x02, 0x04, 0x00, + /* LTV 2: Language: English */ + 0x04, 0x04, 0x65, 0x6e, 0x67, + /* BIS_index[1[0]] */ + 0x03, + /* Codec_Specific_Configuration_Length[1[0]] */ + 0x06, + /* Codec_Specific_Configuration[1[0]] */ + /* LTV 1 = Audio_Channel_Allocation: FL */ + 0x05, 0x03, 0x01, 0x00, 0x00, 0x00, + /* BIS_index[1[1]] */ + 0x04, + /* Codec_Specific_Configuration_Length[1[1]] */ + 0x06, + /* Codec_Specific_Configuration[1[1]] */ + /* LTV 1 = Audio_Channel_Allocation: FR */ + 0x05, 0x03, 0x02, 0x00, 0x00, 0x00, +}; + +TEST_CASE_SELF(ble_audio_base_parse_test) +{ + struct ble_audio_base_subgroup subgroup; + struct ble_audio_base_group group; + struct ble_audio_base_bis bis; + struct ble_audio_base_iter subgroup_iter; + struct ble_audio_base_iter bis_iter; + int rc; + + rc = ble_audio_base_parse(example_base, (uint8_t)sizeof(example_base), + &group, &subgroup_iter); + TEST_ASSERT(rc == 0); + + TEST_ASSERT(group.presentation_delay == 30); + TEST_ASSERT(group.num_subgroups == 2); + + rc = ble_audio_base_subgroup_iter(&subgroup_iter, &subgroup, &bis_iter); + TEST_ASSERT(rc == 0); + + TEST_ASSERT(subgroup.codec_id.format == 0x06); + TEST_ASSERT(subgroup.codec_id.company_id == 0x0000); + TEST_ASSERT(subgroup.codec_id.vendor_specific == 0x0000); + TEST_ASSERT(subgroup.codec_specific_config_len == 10); + TEST_ASSERT(subgroup.codec_specific_config != NULL); + TEST_ASSERT(subgroup.metadata_len == 9); + TEST_ASSERT(subgroup.num_bis == 2); + + rc = ble_audio_base_bis_iter(&bis_iter, &bis); + TEST_ASSERT(rc == 0); + + TEST_ASSERT(bis.index == 0x01); + TEST_ASSERT(bis.codec_specific_config_len == 6); + TEST_ASSERT(bis.codec_specific_config != NULL); + + rc = ble_audio_base_bis_iter(&bis_iter, &bis); + TEST_ASSERT(rc == 0); + + TEST_ASSERT(bis.index == 0x02); + TEST_ASSERT(bis.codec_specific_config_len == 6); + TEST_ASSERT(bis.codec_specific_config != NULL); + + rc = ble_audio_base_bis_iter(&bis_iter, &bis); + TEST_ASSERT(rc == BLE_HS_ENOENT); + + rc = ble_audio_base_subgroup_iter(&subgroup_iter, &subgroup, &bis_iter); + TEST_ASSERT(rc == 0); + + TEST_ASSERT(subgroup.codec_id.format == 0x06); + TEST_ASSERT(subgroup.codec_id.company_id == 0x0000); + TEST_ASSERT(subgroup.codec_id.vendor_specific == 0x0000); + TEST_ASSERT(subgroup.codec_specific_config_len == 10); + TEST_ASSERT(subgroup.codec_specific_config != NULL); + TEST_ASSERT(subgroup.metadata_len == 9); + TEST_ASSERT(subgroup.num_bis == 2); + + rc = ble_audio_base_bis_iter(&bis_iter, &bis); + TEST_ASSERT(rc == 0); + + TEST_ASSERT(bis.index == 0x03); + TEST_ASSERT(bis.codec_specific_config_len == 6); + TEST_ASSERT(bis.codec_specific_config != NULL); + + rc = ble_audio_base_bis_iter(&bis_iter, &bis); + TEST_ASSERT(rc == 0); + + TEST_ASSERT(bis.index == 0x04); + TEST_ASSERT(bis.codec_specific_config_len == 6); + TEST_ASSERT(bis.codec_specific_config != NULL); + + rc = ble_audio_base_bis_iter(&bis_iter, &bis); + TEST_ASSERT(rc == BLE_HS_ENOENT); + + rc = ble_audio_base_subgroup_iter(&subgroup_iter, &subgroup, &bis_iter); + TEST_ASSERT(rc == BLE_HS_ENOENT); +} + +TEST_CASE_SELF(ble_audio_base_parse_test_params) +{ + struct ble_audio_base_subgroup subgroup; + struct ble_audio_base_group group; + struct ble_audio_base_bis bis; + struct ble_audio_base_iter subgroup_iter; + struct ble_audio_base_iter bis_iter; + int rc; + + rc = ble_audio_base_parse(NULL, (uint8_t)sizeof(example_base), &group, &subgroup_iter); + TEST_ASSERT(rc == BLE_HS_EINVAL); + + rc = ble_audio_base_parse(NULL, (uint8_t)sizeof(example_base), NULL, &subgroup_iter); + TEST_ASSERT(rc == BLE_HS_EINVAL); + + rc = ble_audio_base_parse(example_base, (uint8_t)sizeof(example_base), &group, NULL); + TEST_ASSERT(rc == 0); + + rc = ble_audio_base_parse(example_base, (uint8_t)sizeof(example_base), &group, &subgroup_iter); + TEST_ASSERT(rc == 0); + + rc = ble_audio_base_subgroup_iter(NULL, &subgroup, &bis_iter); + TEST_ASSERT(rc == BLE_HS_EINVAL); + + rc = ble_audio_base_subgroup_iter(&subgroup_iter, NULL, &bis_iter); + TEST_ASSERT(rc == BLE_HS_EINVAL); + + rc = ble_audio_base_subgroup_iter(&subgroup_iter, &subgroup, NULL); + TEST_ASSERT(rc == 0); + + rc = ble_audio_base_bis_iter(NULL, &bis); + TEST_ASSERT(rc == BLE_HS_EINVAL); + + rc = ble_audio_base_bis_iter(&bis_iter, NULL); + TEST_ASSERT(rc == BLE_HS_EINVAL); +} + +TEST_CASE_SELF(ble_audio_base_parse_test_length) +{ + struct ble_audio_base_subgroup subgroup; + struct ble_audio_base_group group; + struct ble_audio_base_bis bis; + struct ble_audio_base_iter subgroup_iter; + struct ble_audio_base_iter bis_iter; + int rc; + + /* Incomplete: empty */ + rc = ble_audio_base_parse(example_base, 0, &group, &subgroup_iter); + TEST_ASSERT(rc == BLE_HS_EMSGSIZE); + + /* Truncated: Presentation_Delay Parameter */ + rc = ble_audio_base_parse(example_base, 2, &group, &subgroup_iter); + TEST_ASSERT(rc == BLE_HS_EMSGSIZE); + + /* Incomplete: no Num_Subgroups[0] Parameter */ + rc = ble_audio_base_parse(example_base, 3, &group, &subgroup_iter); + TEST_ASSERT(rc == BLE_HS_EMSGSIZE); + + /* Incomplete: no Num_BIS[0] Parameter */ + rc = ble_audio_base_parse(example_base, 4, &group, &subgroup_iter); + TEST_ASSERT(rc == 0); + rc = ble_audio_base_subgroup_iter(&subgroup_iter, &subgroup, &bis_iter); + TEST_ASSERT(rc == BLE_HS_EMSGSIZE); + + /* Truncated: Codec_ID[0] Parameter */ + rc = ble_audio_base_parse(example_base, 9, &group, &subgroup_iter); + rc = ble_audio_base_subgroup_iter(&subgroup_iter, &subgroup, &bis_iter); + TEST_ASSERT(rc == BLE_HS_EMSGSIZE); + + /* Incomplete: no Codec_Specific_Configuration_Length[0] Parameter */ + rc = ble_audio_base_parse(example_base, 13, &group, &subgroup_iter); + rc = ble_audio_base_subgroup_iter(&subgroup_iter, &subgroup, &bis_iter); + TEST_ASSERT(rc == BLE_HS_EMSGSIZE); + + /* Truncated: Codec_Specific_Configuration[0] Parameter */ + rc = ble_audio_base_parse(example_base, 14, &group, &subgroup_iter); + rc = ble_audio_base_subgroup_iter(&subgroup_iter, &subgroup, &bis_iter); + TEST_ASSERT(rc == BLE_HS_EMSGSIZE); + + /* Incomplete: no Metadata_Length[0] Parameter */ + rc = ble_audio_base_parse(example_base, 21, &group, &subgroup_iter); + rc = ble_audio_base_subgroup_iter(&subgroup_iter, &subgroup, &bis_iter); + TEST_ASSERT(rc == BLE_HS_EMSGSIZE); + + /* Truncated: Metadata[0] Parameter */ + rc = ble_audio_base_parse(example_base, 30, &group, &subgroup_iter); + rc = ble_audio_base_subgroup_iter(&subgroup_iter, &subgroup, &bis_iter); + TEST_ASSERT(rc == BLE_HS_EMSGSIZE); + + /* Incomplete: no BIS_index[0[0]] Parameter */ + rc = ble_audio_base_parse(example_base, 31, &group, &subgroup_iter); + rc = ble_audio_base_subgroup_iter(&subgroup_iter, &subgroup, &bis_iter); + TEST_ASSERT(rc == 0); + rc = ble_audio_base_bis_iter(&bis_iter, &bis); + TEST_ASSERT(rc == BLE_HS_EMSGSIZE); + + /* Incomplete: no Codec_Specific_Configuration_Length[0[0]] Parameter */ + rc = ble_audio_base_parse(example_base, 32, &group, &subgroup_iter); + rc = ble_audio_base_subgroup_iter(&subgroup_iter, &subgroup, &bis_iter); + rc = ble_audio_base_bis_iter(&bis_iter, &bis); + TEST_ASSERT(rc == BLE_HS_EMSGSIZE); + + /* Truncated: Codec_Specific_Configuration_Length[0[0]] Parameter */ + rc = ble_audio_base_parse(example_base, 38, &group, &subgroup_iter); + rc = ble_audio_base_subgroup_iter(&subgroup_iter, &subgroup, &bis_iter); + rc = ble_audio_base_bis_iter(&bis_iter, &bis); + TEST_ASSERT(rc == BLE_HS_EMSGSIZE); + + /* Incomplete: no BIS_index[0[1]] Parameter */ + rc = ble_audio_base_parse(example_base, 39, &group, &subgroup_iter); + rc = ble_audio_base_subgroup_iter(&subgroup_iter, &subgroup, &bis_iter); + rc = ble_audio_base_bis_iter(&bis_iter, &bis); + TEST_ASSERT(rc == 0); + rc = ble_audio_base_bis_iter(&bis_iter, &bis); + TEST_ASSERT(rc == BLE_HS_EMSGSIZE); + + /* Incomplete: no Codec_Specific_Configuration_Length[0[1]] Parameter */ + rc = ble_audio_base_parse(example_base, 40, &group, &subgroup_iter); + rc = ble_audio_base_subgroup_iter(&subgroup_iter, &subgroup, &bis_iter); + rc = ble_audio_base_bis_iter(&bis_iter, &bis); + rc = ble_audio_base_bis_iter(&bis_iter, &bis); + TEST_ASSERT(rc == BLE_HS_EMSGSIZE); + + /* Truncated: Codec_Specific_Configuration_Length[0[1]] Parameter */ + rc = ble_audio_base_parse(example_base, 46, &group, &subgroup_iter); + rc = ble_audio_base_subgroup_iter(&subgroup_iter, &subgroup, &bis_iter); + rc = ble_audio_base_bis_iter(&bis_iter, &bis); + TEST_ASSERT(rc == 0); + rc = ble_audio_base_bis_iter(&bis_iter, &bis); + TEST_ASSERT(rc == BLE_HS_EMSGSIZE); + + /* Incomplete: no Num_BIS[1] Parameter */ + rc = ble_audio_base_parse(example_base, 47, &group, &subgroup_iter); + rc = ble_audio_base_subgroup_iter(&subgroup_iter, &subgroup, &bis_iter); + TEST_ASSERT(rc == 0); + rc = ble_audio_base_subgroup_iter(&subgroup_iter, &subgroup, &bis_iter); + TEST_ASSERT(rc == BLE_HS_EMSGSIZE); + + /* Truncated: Codec_ID[1] Parameter */ + rc = ble_audio_base_parse(example_base, 52, &group, &subgroup_iter); + rc = ble_audio_base_subgroup_iter(&subgroup_iter, &subgroup, &bis_iter); + rc = ble_audio_base_subgroup_iter(&subgroup_iter, &subgroup, &bis_iter); + TEST_ASSERT(rc == BLE_HS_EMSGSIZE); + + /* Incomplete: no Codec_Specific_Configuration_Length[1] Parameter */ + rc = ble_audio_base_parse(example_base, 53, &group, &subgroup_iter); + rc = ble_audio_base_subgroup_iter(&subgroup_iter, &subgroup, &bis_iter); + rc = ble_audio_base_subgroup_iter(&subgroup_iter, &subgroup, &bis_iter); + TEST_ASSERT(rc == BLE_HS_EMSGSIZE); + + /* Truncated: Codec_Specific_Configuration[1] Parameter */ + rc = ble_audio_base_parse(example_base, 63, &group, &subgroup_iter); + rc = ble_audio_base_subgroup_iter(&subgroup_iter, &subgroup, &bis_iter); + rc = ble_audio_base_subgroup_iter(&subgroup_iter, &subgroup, &bis_iter); + TEST_ASSERT(rc == BLE_HS_EMSGSIZE); + + /* Incomplete: no Metadata_Length[1] Parameter */ + rc = ble_audio_base_parse(example_base, 64, &group, &subgroup_iter); + rc = ble_audio_base_subgroup_iter(&subgroup_iter, &subgroup, &bis_iter); + rc = ble_audio_base_subgroup_iter(&subgroup_iter, &subgroup, &bis_iter); + TEST_ASSERT(rc == BLE_HS_EMSGSIZE); + + /* Truncated: Metadata[1] Parameter */ + rc = ble_audio_base_parse(example_base, 73, &group, &subgroup_iter); + rc = ble_audio_base_subgroup_iter(&subgroup_iter, &subgroup, &bis_iter); + rc = ble_audio_base_subgroup_iter(&subgroup_iter, &subgroup, &bis_iter); + TEST_ASSERT(rc == BLE_HS_EMSGSIZE); + + /* Incomplete: no BIS_index[1[0]] Parameter */ + rc = ble_audio_base_parse(example_base, 74, &group, &subgroup_iter); + rc = ble_audio_base_subgroup_iter(&subgroup_iter, &subgroup, &bis_iter); + rc = ble_audio_base_subgroup_iter(&subgroup_iter, &subgroup, &bis_iter); + TEST_ASSERT(rc == 0); + rc = ble_audio_base_bis_iter(&bis_iter, &bis); + TEST_ASSERT(rc == BLE_HS_EMSGSIZE); + + /* Incomplete: no Codec_Specific_Configuration_Length[1[0]] Parameter */ + rc = ble_audio_base_parse(example_base, 75, &group, &subgroup_iter); + rc = ble_audio_base_subgroup_iter(&subgroup_iter, &subgroup, &bis_iter); + rc = ble_audio_base_subgroup_iter(&subgroup_iter, &subgroup, &bis_iter); + rc = ble_audio_base_bis_iter(&bis_iter, &bis); + TEST_ASSERT(rc == BLE_HS_EMSGSIZE); + + /* Truncated: Codec_Specific_Configuration_Length[1[0]] Parameter */ + rc = ble_audio_base_parse(example_base, 81, &group, &subgroup_iter); + rc = ble_audio_base_subgroup_iter(&subgroup_iter, &subgroup, &bis_iter); + rc = ble_audio_base_subgroup_iter(&subgroup_iter, &subgroup, &bis_iter); + rc = ble_audio_base_bis_iter(&bis_iter, &bis); + TEST_ASSERT(rc == BLE_HS_EMSGSIZE); + + /* Incomplete: no BIS_index[1[1]] Parameter */ + rc = ble_audio_base_parse(example_base, 82, &group, &subgroup_iter); + rc = ble_audio_base_subgroup_iter(&subgroup_iter, &subgroup, &bis_iter); + rc = ble_audio_base_subgroup_iter(&subgroup_iter, &subgroup, &bis_iter); + rc = ble_audio_base_bis_iter(&bis_iter, &bis); + TEST_ASSERT(rc == 0); + rc = ble_audio_base_bis_iter(&bis_iter, &bis); + TEST_ASSERT(rc == BLE_HS_EMSGSIZE); + + /* Incomplete: no Codec_Specific_Configuration_Length[1[1]] Parameter */ + rc = ble_audio_base_parse(example_base, 83, &group, &subgroup_iter); + rc = ble_audio_base_subgroup_iter(&subgroup_iter, &subgroup, &bis_iter); + rc = ble_audio_base_subgroup_iter(&subgroup_iter, &subgroup, &bis_iter); + rc = ble_audio_base_bis_iter(&bis_iter, &bis); + rc = ble_audio_base_bis_iter(&bis_iter, &bis); + TEST_ASSERT(rc == BLE_HS_EMSGSIZE); + + /* Truncated: Codec_Specific_Configuration_Length[0[1]] Parameter */ + rc = ble_audio_base_parse(example_base, 89, &group, &subgroup_iter); + rc = ble_audio_base_subgroup_iter(&subgroup_iter, &subgroup, &bis_iter); + rc = ble_audio_base_subgroup_iter(&subgroup_iter, &subgroup, &bis_iter); + rc = ble_audio_base_bis_iter(&bis_iter, &bis); + rc = ble_audio_base_bis_iter(&bis_iter, &bis); + TEST_ASSERT(rc == BLE_HS_EMSGSIZE); +} + +TEST_SUITE(ble_audio_base_parse_test_suite) +{ + ble_audio_base_parse_test(); + ble_audio_base_parse_test_params(); + ble_audio_base_parse_test_length(); +} diff --git a/nimble/host/audio/test/syscfg.yml b/nimble/host/audio/test/syscfg.yml index 7fad93f3fe..eaa7fc5f34 100644 --- a/nimble/host/audio/test/syscfg.yml +++ b/nimble/host/audio/test/syscfg.yml @@ -29,3 +29,12 @@ syscfg.vals: BLE_HS_DEBUG: 1 BLE_EXT_ADV: 1 + BLE_PERIODIC_ADV: 1 + BLE_PERIODIC_ADV_SYNC_TRANSFER: 1 + BLE_PERIODIC_ADV_SYNC_BIGINFO_REPORTS: 1 + + BLE_ISO: 1 + BLE_ISO_BROADCAST_SINK: 1 + + BLE_AUDIO_BSNK: 1 + BLE_AUDIO_BSNK_MAX: 1