1*d15b3374SMatthias Ringwald /* 2*d15b3374SMatthias Ringwald * Copyright (C) 2022 BlueKitchen GmbH 3*d15b3374SMatthias Ringwald * 4*d15b3374SMatthias Ringwald * Redistribution and use in source and binary forms, with or without 5*d15b3374SMatthias Ringwald * modification, are permitted provided that the following conditions 6*d15b3374SMatthias Ringwald * are met: 7*d15b3374SMatthias Ringwald * 8*d15b3374SMatthias Ringwald * 1. Redistributions of source code must retain the above copyright 9*d15b3374SMatthias Ringwald * notice, this list of conditions and the following disclaimer. 10*d15b3374SMatthias Ringwald * 2. Redistributions in binary form must reproduce the above copyright 11*d15b3374SMatthias Ringwald * notice, this list of conditions and the following disclaimer in the 12*d15b3374SMatthias Ringwald * documentation and/or other materials provided with the distribution. 13*d15b3374SMatthias Ringwald * 3. Neither the name of the copyright holders nor the names of 14*d15b3374SMatthias Ringwald * contributors may be used to endorse or promote products derived 15*d15b3374SMatthias Ringwald * from this software without specific prior written permission. 16*d15b3374SMatthias Ringwald * 4. Any redistribution, use, or modification is done solely for 17*d15b3374SMatthias Ringwald * personal benefit and not for any commercial purpose or for 18*d15b3374SMatthias Ringwald * monetary gain. 19*d15b3374SMatthias Ringwald * 20*d15b3374SMatthias Ringwald * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS 21*d15b3374SMatthias Ringwald * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22*d15b3374SMatthias Ringwald * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 23*d15b3374SMatthias Ringwald * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS 24*d15b3374SMatthias Ringwald * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 25*d15b3374SMatthias Ringwald * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 26*d15b3374SMatthias Ringwald * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 27*d15b3374SMatthias Ringwald * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 28*d15b3374SMatthias Ringwald * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 29*d15b3374SMatthias Ringwald * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 30*d15b3374SMatthias Ringwald * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31*d15b3374SMatthias Ringwald * SUCH DAMAGE. 32*d15b3374SMatthias Ringwald * 33*d15b3374SMatthias Ringwald * Please inquire about commercial licensing options at 34*d15b3374SMatthias Ringwald * [email protected] 35*d15b3374SMatthias Ringwald * 36*d15b3374SMatthias Ringwald */ 37*d15b3374SMatthias Ringwald 38*d15b3374SMatthias Ringwald #define BTSTACK_FILE__ "le_audio_broadcast_assistant.c" 39*d15b3374SMatthias Ringwald 40*d15b3374SMatthias Ringwald /* 41*d15b3374SMatthias Ringwald * LE Audio Broadcast Assistant 42*d15b3374SMatthias Ringwald */ 43*d15b3374SMatthias Ringwald 44*d15b3374SMatthias Ringwald 45*d15b3374SMatthias Ringwald #include "btstack_config.h" 46*d15b3374SMatthias Ringwald 47*d15b3374SMatthias Ringwald #include <stdint.h> 48*d15b3374SMatthias Ringwald #include <stdio.h> 49*d15b3374SMatthias Ringwald #include <stdlib.h> 50*d15b3374SMatthias Ringwald #include <string.h> 51*d15b3374SMatthias Ringwald #include <inttypes.h> 52*d15b3374SMatthias Ringwald 53*d15b3374SMatthias Ringwald #include "ad_parser.h" 54*d15b3374SMatthias Ringwald #include "ble/gatt-service/broadcast_audio_scan_service_client.h" 55*d15b3374SMatthias Ringwald #include "bluetooth_data_types.h" 56*d15b3374SMatthias Ringwald #include "bluetooth_gatt.h" 57*d15b3374SMatthias Ringwald #include "btstack_audio.h" 58*d15b3374SMatthias Ringwald #include "btstack_event.h" 59*d15b3374SMatthias Ringwald #include "btstack_lc3.h" 60*d15b3374SMatthias Ringwald #include "btstack_run_loop.h" 61*d15b3374SMatthias Ringwald #include "btstack_stdin.h" 62*d15b3374SMatthias Ringwald #include "btstack_util.h" 63*d15b3374SMatthias Ringwald #include "gap.h" 64*d15b3374SMatthias Ringwald #include "hci.h" 65*d15b3374SMatthias Ringwald 66*d15b3374SMatthias Ringwald static void show_usage(void); 67*d15b3374SMatthias Ringwald 68*d15b3374SMatthias Ringwald static enum { 69*d15b3374SMatthias Ringwald APP_W4_WORKING, 70*d15b3374SMatthias Ringwald APP_W4_BROADCAST_SINK_AND_SCAN_DELEGATOR_ADV, 71*d15b3374SMatthias Ringwald APP_W4_PA_AND_BIG_INFO, 72*d15b3374SMatthias Ringwald APP_W4_BIG_SYNC_ESTABLISHED, 73*d15b3374SMatthias Ringwald APP_W4_SCAN_DELEGATOR_CONNECTION, 74*d15b3374SMatthias Ringwald APP_IDLE 75*d15b3374SMatthias Ringwald } app_state = APP_W4_WORKING; 76*d15b3374SMatthias Ringwald 77*d15b3374SMatthias Ringwald // 78*d15b3374SMatthias Ringwald static btstack_packet_callback_registration_t hci_event_callback_registration; 79*d15b3374SMatthias Ringwald 80*d15b3374SMatthias Ringwald static bool have_scan_delegator; 81*d15b3374SMatthias Ringwald static bool have_broadcast_source; 82*d15b3374SMatthias Ringwald static bool have_base; 83*d15b3374SMatthias Ringwald static bool have_big_info; 84*d15b3374SMatthias Ringwald 85*d15b3374SMatthias Ringwald // broadcast sink info 86*d15b3374SMatthias Ringwald static char broadcast_source_name[20]; 87*d15b3374SMatthias Ringwald static bd_addr_t broadcast_source; 88*d15b3374SMatthias Ringwald static bd_addr_type_t broadcast_source_type; 89*d15b3374SMatthias Ringwald static uint8_t broadcast_source_sid; 90*d15b3374SMatthias Ringwald static uint32_t broadcast_id; 91*d15b3374SMatthias Ringwald static uint16_t broadcast_source_pa_interval; 92*d15b3374SMatthias Ringwald 93*d15b3374SMatthias Ringwald // broadcast info 94*d15b3374SMatthias Ringwald static hci_con_handle_t sync_handle; 95*d15b3374SMatthias Ringwald static uint8_t encryption; 96*d15b3374SMatthias Ringwald static uint8_t broadcast_code [] = {0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}; 97*d15b3374SMatthias Ringwald static uint8_t num_bis; 98*d15b3374SMatthias Ringwald static uint32_t sampling_frequency_hz; 99*d15b3374SMatthias Ringwald static btstack_lc3_frame_duration_t frame_duration; 100*d15b3374SMatthias Ringwald static uint16_t octets_per_frame; 101*d15b3374SMatthias Ringwald 102*d15b3374SMatthias Ringwald // scan delegator info 103*d15b3374SMatthias Ringwald static char scan_delegator_name[20]; 104*d15b3374SMatthias Ringwald static bd_addr_t scan_delegator; 105*d15b3374SMatthias Ringwald static bd_addr_type_t scan_delegator_type; 106*d15b3374SMatthias Ringwald static hci_con_handle_t scan_delegator_handle; 107*d15b3374SMatthias Ringwald 108*d15b3374SMatthias Ringwald // BASS 109*d15b3374SMatthias Ringwald #define BASS_CLIENT_NUM_SOURCES 1 110*d15b3374SMatthias Ringwald static bass_client_connection_t bass_connection; 111*d15b3374SMatthias Ringwald static bass_client_source_t bass_sources[BASS_CLIENT_NUM_SOURCES]; 112*d15b3374SMatthias Ringwald static bass_source_data_t bass_source_new; 113*d15b3374SMatthias Ringwald static uint16_t bass_cid; 114*d15b3374SMatthias Ringwald 115*d15b3374SMatthias Ringwald static void handle_periodic_advertisement(const uint8_t * packet, uint16_t size){ 116*d15b3374SMatthias Ringwald 117*d15b3374SMatthias Ringwald // periodic advertisement contains the BASE 118*d15b3374SMatthias Ringwald // TODO: BASE might be split across multiple advertisements 119*d15b3374SMatthias Ringwald const uint8_t * adv_data = hci_subevent_le_periodic_advertising_report_get_data(packet); 120*d15b3374SMatthias Ringwald uint16_t adv_size = hci_subevent_le_periodic_advertising_report_get_data_length(packet); 121*d15b3374SMatthias Ringwald uint8_t adv_status = hci_subevent_le_periodic_advertising_report_get_data_status(packet); 122*d15b3374SMatthias Ringwald 123*d15b3374SMatthias Ringwald if (adv_status != 0) { 124*d15b3374SMatthias Ringwald printf("Periodic Advertisement (status %u): ", adv_status); 125*d15b3374SMatthias Ringwald printf_hexdump(adv_data, adv_size); 126*d15b3374SMatthias Ringwald return; 127*d15b3374SMatthias Ringwald } 128*d15b3374SMatthias Ringwald 129*d15b3374SMatthias Ringwald ad_context_t context; 130*d15b3374SMatthias Ringwald for (ad_iterator_init(&context, adv_size, adv_data) ; ad_iterator_has_more(&context) ; ad_iterator_next(&context)) { 131*d15b3374SMatthias Ringwald uint8_t data_type = ad_iterator_get_data_type(&context); 132*d15b3374SMatthias Ringwald // TODO: avoid out-of-bounds read 133*d15b3374SMatthias Ringwald // uint8_t data_size = ad_iterator_get_data_len(&context); 134*d15b3374SMatthias Ringwald const uint8_t * data = ad_iterator_get_data(&context); 135*d15b3374SMatthias Ringwald uint16_t uuid; 136*d15b3374SMatthias Ringwald switch (data_type){ 137*d15b3374SMatthias Ringwald case BLUETOOTH_DATA_TYPE_SERVICE_DATA_16_BIT_UUID: 138*d15b3374SMatthias Ringwald uuid = little_endian_read_16(data, 0); 139*d15b3374SMatthias Ringwald if (uuid == ORG_BLUETOOTH_SERVICE_BASIC_AUDIO_ANNOUNCEMENT_SERVICE){ 140*d15b3374SMatthias Ringwald have_base = true; 141*d15b3374SMatthias Ringwald // Level 1: Group Level 142*d15b3374SMatthias Ringwald const uint8_t * base_data = &data[2]; 143*d15b3374SMatthias Ringwald // TODO: avoid out-of-bounds read 144*d15b3374SMatthias Ringwald // uint16_t base_len = data_size - 2; 145*d15b3374SMatthias Ringwald printf("BASE:\n"); 146*d15b3374SMatthias Ringwald uint32_t presentation_delay = little_endian_read_24(base_data, 0); 147*d15b3374SMatthias Ringwald printf("- presentation delay: %"PRIu32" us\n", presentation_delay); 148*d15b3374SMatthias Ringwald uint8_t num_subgroups = base_data[3]; 149*d15b3374SMatthias Ringwald // Cache in new source struct 150*d15b3374SMatthias Ringwald bass_source_new.subgroups_num = num_subgroups; 151*d15b3374SMatthias Ringwald printf("- num subgroups: %u\n", num_subgroups); 152*d15b3374SMatthias Ringwald uint8_t i; 153*d15b3374SMatthias Ringwald uint16_t offset = 4; 154*d15b3374SMatthias Ringwald for (i=0;i<num_subgroups;i++){ 155*d15b3374SMatthias Ringwald 156*d15b3374SMatthias Ringwald // Cache in new source struct 157*d15b3374SMatthias Ringwald bass_source_new.subgroups[i].bis_sync_state = 0; 158*d15b3374SMatthias Ringwald 159*d15b3374SMatthias Ringwald // Level 2: Subgroup Level 160*d15b3374SMatthias Ringwald num_bis = base_data[offset++]; 161*d15b3374SMatthias Ringwald printf(" - num bis[%u]: %u\n", i, num_bis); 162*d15b3374SMatthias Ringwald // codec_id: coding format = 0x06, vendor and coded id = 0 163*d15b3374SMatthias Ringwald offset += 5; 164*d15b3374SMatthias Ringwald uint8_t codec_specific_configuration_length = base_data[offset++]; 165*d15b3374SMatthias Ringwald const uint8_t * codec_specific_configuration = &base_data[offset]; 166*d15b3374SMatthias Ringwald printf(" - codec specific config[%u]: ", i); 167*d15b3374SMatthias Ringwald printf_hexdump(codec_specific_configuration, codec_specific_configuration_length); 168*d15b3374SMatthias Ringwald // parse config to get sampling frequency and frame duration 169*d15b3374SMatthias Ringwald uint8_t codec_offset = 0; 170*d15b3374SMatthias Ringwald while ((codec_offset + 1) < codec_specific_configuration_length){ 171*d15b3374SMatthias Ringwald uint8_t ltv_len = codec_specific_configuration[codec_offset++]; 172*d15b3374SMatthias Ringwald uint8_t ltv_type = codec_specific_configuration[codec_offset]; 173*d15b3374SMatthias Ringwald const uint32_t sampling_frequency_map[] = { 8000, 11025, 16000, 22050, 24000, 32000, 44100, 48000, 88200, 96000, 176400, 192000, 384000 }; 174*d15b3374SMatthias Ringwald uint8_t sampling_frequency_index; 175*d15b3374SMatthias Ringwald uint8_t frame_duration_index; 176*d15b3374SMatthias Ringwald switch (ltv_type){ 177*d15b3374SMatthias Ringwald case 0x01: // sampling frequency 178*d15b3374SMatthias Ringwald sampling_frequency_index = codec_specific_configuration[codec_offset+1]; 179*d15b3374SMatthias Ringwald // TODO: check range 180*d15b3374SMatthias Ringwald sampling_frequency_hz = sampling_frequency_map[sampling_frequency_index - 1]; 181*d15b3374SMatthias Ringwald printf(" - sampling frequency[%u]: %u\n", i, sampling_frequency_hz); 182*d15b3374SMatthias Ringwald break; 183*d15b3374SMatthias Ringwald case 0x02: // 0 = 7.5, 1 = 10 ms 184*d15b3374SMatthias Ringwald frame_duration_index = codec_specific_configuration[codec_offset+1]; 185*d15b3374SMatthias Ringwald frame_duration = (frame_duration_index == 0) ? BTSTACK_LC3_FRAME_DURATION_7500US : BTSTACK_LC3_FRAME_DURATION_10000US; 186*d15b3374SMatthias Ringwald printf(" - frame duration[%u]: %s ms\n", i, (frame_duration == BTSTACK_LC3_FRAME_DURATION_7500US) ? "7.5" : "10"); 187*d15b3374SMatthias Ringwald break; 188*d15b3374SMatthias Ringwald case 0x04: // octets per coding frame 189*d15b3374SMatthias Ringwald octets_per_frame = little_endian_read_16(codec_specific_configuration, codec_offset+1); 190*d15b3374SMatthias Ringwald printf(" - octets per codec frame[%u]: %u\n", i, octets_per_frame); 191*d15b3374SMatthias Ringwald break; 192*d15b3374SMatthias Ringwald default: 193*d15b3374SMatthias Ringwald break; 194*d15b3374SMatthias Ringwald } 195*d15b3374SMatthias Ringwald codec_offset += ltv_len; 196*d15b3374SMatthias Ringwald } 197*d15b3374SMatthias Ringwald // 198*d15b3374SMatthias Ringwald offset += codec_specific_configuration_length; 199*d15b3374SMatthias Ringwald uint8_t metadata_length = base_data[offset++]; 200*d15b3374SMatthias Ringwald const uint8_t * meta_data = &base_data[offset]; 201*d15b3374SMatthias Ringwald offset += metadata_length; 202*d15b3374SMatthias Ringwald printf(" - meta data[%u]: ", i); 203*d15b3374SMatthias Ringwald printf_hexdump(meta_data, metadata_length); 204*d15b3374SMatthias Ringwald uint8_t k; 205*d15b3374SMatthias Ringwald for (k=0;k<num_bis;k++){ 206*d15b3374SMatthias Ringwald // Level 3: BIS Level 207*d15b3374SMatthias Ringwald uint8_t bis_index = base_data[offset++]; 208*d15b3374SMatthias Ringwald 209*d15b3374SMatthias Ringwald // Cache in new source struct 210*d15b3374SMatthias Ringwald bass_source_new.subgroups[i].bis_sync_state |= 1 << bis_index; 211*d15b3374SMatthias Ringwald bass_source_new.subgroups[i].metadata_length = 0; 212*d15b3374SMatthias Ringwald memset(&bass_source_new.subgroups[i].metadata, 0, sizeof(le_audio_metadata_t)); 213*d15b3374SMatthias Ringwald 214*d15b3374SMatthias Ringwald printf(" - bis index[%u][%u]: %u\n", i, k, bis_index); 215*d15b3374SMatthias Ringwald uint8_t codec_specific_configuration_length2 = base_data[offset++]; 216*d15b3374SMatthias Ringwald const uint8_t * codec_specific_configuration2 = &base_data[offset]; 217*d15b3374SMatthias Ringwald printf(" - codec specific config[%u][%u]: ", i, k); 218*d15b3374SMatthias Ringwald printf_hexdump(codec_specific_configuration2, codec_specific_configuration_length2); 219*d15b3374SMatthias Ringwald offset += codec_specific_configuration_length2; 220*d15b3374SMatthias Ringwald } 221*d15b3374SMatthias Ringwald } 222*d15b3374SMatthias Ringwald } 223*d15b3374SMatthias Ringwald break; 224*d15b3374SMatthias Ringwald default: 225*d15b3374SMatthias Ringwald break; 226*d15b3374SMatthias Ringwald } 227*d15b3374SMatthias Ringwald } 228*d15b3374SMatthias Ringwald } 229*d15b3374SMatthias Ringwald 230*d15b3374SMatthias Ringwald static void start_scanning() { 231*d15b3374SMatthias Ringwald app_state = APP_W4_BROADCAST_SINK_AND_SCAN_DELEGATOR_ADV; 232*d15b3374SMatthias Ringwald have_base = false; 233*d15b3374SMatthias Ringwald have_big_info = false; 234*d15b3374SMatthias Ringwald gap_set_scan_params(1, 0x30, 0x30, 0); 235*d15b3374SMatthias Ringwald gap_start_scan(); 236*d15b3374SMatthias Ringwald printf("Start scan..\n"); 237*d15b3374SMatthias Ringwald } 238*d15b3374SMatthias Ringwald 239*d15b3374SMatthias Ringwald static void have_base_and_big_info(void){ 240*d15b3374SMatthias Ringwald printf("Ready to stream!\n"); 241*d15b3374SMatthias Ringwald 242*d15b3374SMatthias Ringwald // todo: connect to scan delegator 243*d15b3374SMatthias Ringwald app_state = APP_W4_SCAN_DELEGATOR_CONNECTION; 244*d15b3374SMatthias Ringwald gap_connect(scan_delegator, scan_delegator_type); 245*d15b3374SMatthias Ringwald } 246*d15b3374SMatthias Ringwald 247*d15b3374SMatthias Ringwald static void handle_big_info(const uint8_t * packet, uint16_t size){ 248*d15b3374SMatthias Ringwald printf("BIG Info advertising report\n"); 249*d15b3374SMatthias Ringwald sync_handle = hci_subevent_le_biginfo_advertising_report_get_sync_handle(packet); 250*d15b3374SMatthias Ringwald encryption = hci_subevent_le_biginfo_advertising_report_get_encryption(packet); 251*d15b3374SMatthias Ringwald if (encryption) { 252*d15b3374SMatthias Ringwald printf("Stream is encrypted\n"); 253*d15b3374SMatthias Ringwald } 254*d15b3374SMatthias Ringwald have_big_info = true; 255*d15b3374SMatthias Ringwald } 256*d15b3374SMatthias Ringwald static void bass_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ 257*d15b3374SMatthias Ringwald UNUSED(channel); 258*d15b3374SMatthias Ringwald UNUSED(size); 259*d15b3374SMatthias Ringwald 260*d15b3374SMatthias Ringwald if (packet_type != HCI_EVENT_PACKET) return; 261*d15b3374SMatthias Ringwald if (hci_event_packet_get_type(packet) != HCI_EVENT_GATTSERVICE_META) return; 262*d15b3374SMatthias Ringwald 263*d15b3374SMatthias Ringwald switch (hci_event_gattservice_meta_get_subevent_code(packet)) { 264*d15b3374SMatthias Ringwald case GATTSERVICE_SUBEVENT_BASS_CONNECTED: 265*d15b3374SMatthias Ringwald if (gattservice_subevent_bass_connected_get_status(packet) != ERROR_CODE_SUCCESS){ 266*d15b3374SMatthias Ringwald printf("BASS client connection failed, cid 0x%02x, con_handle 0x%02x, status 0x%02x\n", 267*d15b3374SMatthias Ringwald bass_cid, scan_delegator_handle, 268*d15b3374SMatthias Ringwald gattservice_subevent_bass_connected_get_status(packet)); 269*d15b3374SMatthias Ringwald return; 270*d15b3374SMatthias Ringwald } 271*d15b3374SMatthias Ringwald printf("BASS client connected, cid 0x%02x\n", bass_cid); 272*d15b3374SMatthias Ringwald 273*d15b3374SMatthias Ringwald // setup bass source info 274*d15b3374SMatthias Ringwald bass_source_new.address_type = broadcast_source_type; 275*d15b3374SMatthias Ringwald memcpy(bass_source_new.address, broadcast_source, 6); 276*d15b3374SMatthias Ringwald bass_source_new.adv_sid = broadcast_source_sid; 277*d15b3374SMatthias Ringwald bass_source_new.broadcast_id = broadcast_id; 278*d15b3374SMatthias Ringwald bass_source_new.pa_sync = LE_AUDIO_PA_SYNC_SYNCHRONIZE_TO_PA_PAST_AVAILABLE; 279*d15b3374SMatthias Ringwald bass_source_new.pa_interval = broadcast_source_pa_interval; 280*d15b3374SMatthias Ringwald // bass_source_new.subgroups_num set in BASE parser 281*d15b3374SMatthias Ringwald // bass_source_new.subgroup[i].* set in BASE parsaer 282*d15b3374SMatthias Ringwald 283*d15b3374SMatthias Ringwald // add bass source 284*d15b3374SMatthias Ringwald broadcast_audio_scan_service_client_add_source(bass_cid, &bass_source_new); 285*d15b3374SMatthias Ringwald break; 286*d15b3374SMatthias Ringwald case GATTSERVICE_SUBEVENT_BASS_SOURCE_OPERATION_COMPLETE: 287*d15b3374SMatthias Ringwald if (gattservice_subevent_bass_source_operation_complete_get_status(packet) != ERROR_CODE_SUCCESS){ 288*d15b3374SMatthias Ringwald printf("BASS client source operation failed, status 0x%02x\n", gattservice_subevent_bass_source_operation_complete_get_status(packet)); 289*d15b3374SMatthias Ringwald return; 290*d15b3374SMatthias Ringwald } 291*d15b3374SMatthias Ringwald 292*d15b3374SMatthias Ringwald if ( gattservice_subevent_bass_source_operation_complete_get_opcode(packet) == (uint8_t)BASS_OPCODE_ADD_SOURCE ){ 293*d15b3374SMatthias Ringwald // omit source ID, it will be received via notification 294*d15b3374SMatthias Ringwald printf("BASS client add source operation completed\n"); 295*d15b3374SMatthias Ringwald 296*d15b3374SMatthias Ringwald // start pa sync 297*d15b3374SMatthias Ringwald printf("Start PA Sync\n"); 298*d15b3374SMatthias Ringwald uint16_t service_data = 0; 299*d15b3374SMatthias Ringwald gap_periodic_advertising_sync_transfer_send(scan_delegator_handle, service_data, sync_handle); 300*d15b3374SMatthias Ringwald } 301*d15b3374SMatthias Ringwald break; 302*d15b3374SMatthias Ringwald default: 303*d15b3374SMatthias Ringwald break; 304*d15b3374SMatthias Ringwald } 305*d15b3374SMatthias Ringwald } 306*d15b3374SMatthias Ringwald 307*d15b3374SMatthias Ringwald static void packet_handler (uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ 308*d15b3374SMatthias Ringwald UNUSED(channel); 309*d15b3374SMatthias Ringwald if (packet_type != HCI_EVENT_PACKET) return; 310*d15b3374SMatthias Ringwald switch (packet[0]) { 311*d15b3374SMatthias Ringwald case BTSTACK_EVENT_STATE: 312*d15b3374SMatthias Ringwald switch(btstack_event_state_get_state(packet)) { 313*d15b3374SMatthias Ringwald case HCI_STATE_WORKING: 314*d15b3374SMatthias Ringwald app_state = APP_IDLE; 315*d15b3374SMatthias Ringwald #ifdef ENABLE_DEMO_MODE 316*d15b3374SMatthias Ringwald start_scanning(); 317*d15b3374SMatthias Ringwald #else 318*d15b3374SMatthias Ringwald show_usage(); 319*d15b3374SMatthias Ringwald #endif 320*d15b3374SMatthias Ringwald break; 321*d15b3374SMatthias Ringwald case HCI_STATE_OFF: 322*d15b3374SMatthias Ringwald printf("Goodbye\n"); 323*d15b3374SMatthias Ringwald exit(0); 324*d15b3374SMatthias Ringwald break; 325*d15b3374SMatthias Ringwald default: 326*d15b3374SMatthias Ringwald break; 327*d15b3374SMatthias Ringwald } 328*d15b3374SMatthias Ringwald break; 329*d15b3374SMatthias Ringwald case GAP_EVENT_EXTENDED_ADVERTISING_REPORT: 330*d15b3374SMatthias Ringwald { 331*d15b3374SMatthias Ringwald if (app_state != APP_W4_BROADCAST_SINK_AND_SCAN_DELEGATOR_ADV) break; 332*d15b3374SMatthias Ringwald 333*d15b3374SMatthias Ringwald gap_event_extended_advertising_report_get_address(packet, broadcast_source); 334*d15b3374SMatthias Ringwald uint8_t adv_size = gap_event_extended_advertising_report_get_data_length(packet); 335*d15b3374SMatthias Ringwald const uint8_t * adv_data = gap_event_extended_advertising_report_get_data(packet); 336*d15b3374SMatthias Ringwald 337*d15b3374SMatthias Ringwald ad_context_t context; 338*d15b3374SMatthias Ringwald bool found_scan_delegator = false; 339*d15b3374SMatthias Ringwald bool found_broadcast_source = false; 340*d15b3374SMatthias Ringwald bool found_name = false; 341*d15b3374SMatthias Ringwald uint8_t adv_name[31]; 342*d15b3374SMatthias Ringwald 343*d15b3374SMatthias Ringwald uint16_t uuid; 344*d15b3374SMatthias Ringwald for (ad_iterator_init(&context, adv_size, adv_data) ; ad_iterator_has_more(&context) ; ad_iterator_next(&context)) { 345*d15b3374SMatthias Ringwald uint8_t data_type = ad_iterator_get_data_type(&context); 346*d15b3374SMatthias Ringwald uint8_t size = ad_iterator_get_data_len(&context); 347*d15b3374SMatthias Ringwald const uint8_t *data = ad_iterator_get_data(&context); 348*d15b3374SMatthias Ringwald switch (data_type){ 349*d15b3374SMatthias Ringwald case BLUETOOTH_DATA_TYPE_SERVICE_DATA_16_BIT_UUID: 350*d15b3374SMatthias Ringwald uuid = little_endian_read_16(data, 0); 351*d15b3374SMatthias Ringwald switch (uuid){ 352*d15b3374SMatthias Ringwald case ORG_BLUETOOTH_SERVICE_BROADCAST_AUDIO_ANNOUNCEMENT_SERVICE: 353*d15b3374SMatthias Ringwald broadcast_id = little_endian_read_24(data, 2); 354*d15b3374SMatthias Ringwald found_broadcast_source = true; 355*d15b3374SMatthias Ringwald break; 356*d15b3374SMatthias Ringwald case ORG_BLUETOOTH_SERVICE_BROADCAST_AUDIO_SCAN_SERVICE: 357*d15b3374SMatthias Ringwald found_scan_delegator = true; 358*d15b3374SMatthias Ringwald break; 359*d15b3374SMatthias Ringwald default: 360*d15b3374SMatthias Ringwald break; 361*d15b3374SMatthias Ringwald } 362*d15b3374SMatthias Ringwald break; 363*d15b3374SMatthias Ringwald case BLUETOOTH_DATA_TYPE_SHORTENED_LOCAL_NAME: 364*d15b3374SMatthias Ringwald case BLUETOOTH_DATA_TYPE_COMPLETE_LOCAL_NAME: 365*d15b3374SMatthias Ringwald size = btstack_min(sizeof(adv_name) - 1, size); 366*d15b3374SMatthias Ringwald memcpy(adv_name, data, size); 367*d15b3374SMatthias Ringwald adv_name[size] = 0; 368*d15b3374SMatthias Ringwald found_name = true; 369*d15b3374SMatthias Ringwald break; 370*d15b3374SMatthias Ringwald default: 371*d15b3374SMatthias Ringwald break; 372*d15b3374SMatthias Ringwald } 373*d15b3374SMatthias Ringwald } 374*d15b3374SMatthias Ringwald if ((found_scan_delegator == false) && (found_broadcast_source == false)) break; 375*d15b3374SMatthias Ringwald if ((have_scan_delegator == false) && found_scan_delegator){ 376*d15b3374SMatthias Ringwald have_scan_delegator = true; 377*d15b3374SMatthias Ringwald gap_event_extended_advertising_report_get_address(packet, scan_delegator); 378*d15b3374SMatthias Ringwald scan_delegator_type = gap_event_extended_advertising_report_get_address_type(packet); 379*d15b3374SMatthias Ringwald if (found_name){ 380*d15b3374SMatthias Ringwald btstack_strcpy(scan_delegator_name, sizeof(scan_delegator_name), (const char *) adv_name); 381*d15b3374SMatthias Ringwald } else { 382*d15b3374SMatthias Ringwald scan_delegator_name[0] = 0; 383*d15b3374SMatthias Ringwald } 384*d15b3374SMatthias Ringwald printf("Broadcast sink found, addr %s, name: '%s'\n", bd_addr_to_str(scan_delegator), scan_delegator_name); 385*d15b3374SMatthias Ringwald gap_whitelist_add(scan_delegator_type, scan_delegator); 386*d15b3374SMatthias Ringwald } 387*d15b3374SMatthias Ringwald 388*d15b3374SMatthias Ringwald if ((have_broadcast_source == false) && found_broadcast_source){ 389*d15b3374SMatthias Ringwald have_broadcast_source = true; 390*d15b3374SMatthias Ringwald gap_event_extended_advertising_report_get_address(packet, broadcast_source); 391*d15b3374SMatthias Ringwald broadcast_source_type = gap_event_extended_advertising_report_get_address_type(packet); 392*d15b3374SMatthias Ringwald broadcast_source_sid = gap_event_extended_advertising_report_get_advertising_sid(packet); 393*d15b3374SMatthias Ringwald if (found_name){ 394*d15b3374SMatthias Ringwald btstack_strcpy(broadcast_source_name, sizeof(broadcast_source_name), (const char *) adv_name); 395*d15b3374SMatthias Ringwald } else { 396*d15b3374SMatthias Ringwald broadcast_source_name[0] = 0; 397*d15b3374SMatthias Ringwald } 398*d15b3374SMatthias Ringwald printf("Broadcast sink found, addr %s, name: '%s'\n", bd_addr_to_str(broadcast_source), broadcast_source_name); 399*d15b3374SMatthias Ringwald gap_whitelist_add(broadcast_source_type, broadcast_source); 400*d15b3374SMatthias Ringwald } 401*d15b3374SMatthias Ringwald 402*d15b3374SMatthias Ringwald if ((have_broadcast_source && have_scan_delegator) == false) break; 403*d15b3374SMatthias Ringwald 404*d15b3374SMatthias Ringwald printf("Start Periodic Advertising Sync\n"); 405*d15b3374SMatthias Ringwald 406*d15b3374SMatthias Ringwald // ignore other advertisements 407*d15b3374SMatthias Ringwald gap_set_scan_params(1, 0x30, 0x30, 1); 408*d15b3374SMatthias Ringwald // sync to PA 409*d15b3374SMatthias Ringwald gap_periodic_advertiser_list_clear(); 410*d15b3374SMatthias Ringwald gap_periodic_advertiser_list_add(broadcast_source_type, broadcast_source, broadcast_source_sid); 411*d15b3374SMatthias Ringwald app_state = APP_W4_PA_AND_BIG_INFO; 412*d15b3374SMatthias Ringwald gap_periodic_advertising_create_sync(0x01, broadcast_source_sid, broadcast_source_type, broadcast_source, 0, 1000, 0); 413*d15b3374SMatthias Ringwald break; 414*d15b3374SMatthias Ringwald } 415*d15b3374SMatthias Ringwald 416*d15b3374SMatthias Ringwald case HCI_EVENT_LE_META: 417*d15b3374SMatthias Ringwald switch(hci_event_le_meta_get_subevent_code(packet)) { 418*d15b3374SMatthias Ringwald case HCI_SUBEVENT_LE_PERIODIC_ADVERTISING_SYNC_ESTABLISHMENT: 419*d15b3374SMatthias Ringwald sync_handle = hci_subevent_le_periodic_advertising_sync_establishment_get_sync_handle(packet); 420*d15b3374SMatthias Ringwald broadcast_source_pa_interval = hci_subevent_le_periodic_advertising_sync_establishment_get_periodic_advertising_interval(packet); 421*d15b3374SMatthias Ringwald printf("Periodic advertising sync with handle 0x%04x established\n", sync_handle); 422*d15b3374SMatthias Ringwald break; 423*d15b3374SMatthias Ringwald case HCI_SUBEVENT_LE_PERIODIC_ADVERTISING_REPORT: 424*d15b3374SMatthias Ringwald if (have_base) break; 425*d15b3374SMatthias Ringwald handle_periodic_advertisement(packet, size); 426*d15b3374SMatthias Ringwald if (have_base & have_big_info){ 427*d15b3374SMatthias Ringwald have_base_and_big_info(); 428*d15b3374SMatthias Ringwald } 429*d15b3374SMatthias Ringwald break; 430*d15b3374SMatthias Ringwald case HCI_SUBEVENT_LE_BIGINFO_ADVERTISING_REPORT: 431*d15b3374SMatthias Ringwald if (have_big_info) break; 432*d15b3374SMatthias Ringwald handle_big_info(packet, size); 433*d15b3374SMatthias Ringwald if (have_base & have_big_info){ 434*d15b3374SMatthias Ringwald have_base_and_big_info(); 435*d15b3374SMatthias Ringwald } 436*d15b3374SMatthias Ringwald break; 437*d15b3374SMatthias Ringwald case HCI_SUBEVENT_LE_BIG_SYNC_LOST: 438*d15b3374SMatthias Ringwald printf("BIG Sync Lost\n"); 439*d15b3374SMatthias Ringwald { 440*d15b3374SMatthias Ringwald const btstack_audio_sink_t * sink = btstack_audio_sink_get_instance(); 441*d15b3374SMatthias Ringwald if (sink != NULL) { 442*d15b3374SMatthias Ringwald sink->stop_stream(); 443*d15b3374SMatthias Ringwald sink->close(); 444*d15b3374SMatthias Ringwald } 445*d15b3374SMatthias Ringwald } 446*d15b3374SMatthias Ringwald // start over 447*d15b3374SMatthias Ringwald start_scanning(); 448*d15b3374SMatthias Ringwald break; 449*d15b3374SMatthias Ringwald case HCI_SUBEVENT_LE_CONNECTION_COMPLETE: 450*d15b3374SMatthias Ringwald if (hci_subevent_le_connection_complete_get_status(packet) != ERROR_CODE_SUCCESS) break; 451*d15b3374SMatthias Ringwald scan_delegator_handle = hci_subevent_le_connection_complete_get_connection_handle(packet); 452*d15b3374SMatthias Ringwald printf("Connection complete, handle 0x%04x\n", scan_delegator_handle); 453*d15b3374SMatthias Ringwald broadcast_audio_scan_service_client_connect(&bass_connection, 454*d15b3374SMatthias Ringwald bass_sources, 455*d15b3374SMatthias Ringwald BASS_CLIENT_NUM_SOURCES, 456*d15b3374SMatthias Ringwald scan_delegator_handle, 457*d15b3374SMatthias Ringwald &bass_cid); 458*d15b3374SMatthias Ringwald break; 459*d15b3374SMatthias Ringwald default: 460*d15b3374SMatthias Ringwald break; 461*d15b3374SMatthias Ringwald } 462*d15b3374SMatthias Ringwald break; 463*d15b3374SMatthias Ringwald default: 464*d15b3374SMatthias Ringwald break; 465*d15b3374SMatthias Ringwald } 466*d15b3374SMatthias Ringwald } 467*d15b3374SMatthias Ringwald 468*d15b3374SMatthias Ringwald static void show_usage(void){ 469*d15b3374SMatthias Ringwald printf("\n--- LE Audio Broadcast Assistant Test Console ---\n"); 470*d15b3374SMatthias Ringwald printf("s - start scanning for LE Broadcast Source & Scan Delegator\n"); 471*d15b3374SMatthias Ringwald printf("---\n"); 472*d15b3374SMatthias Ringwald } 473*d15b3374SMatthias Ringwald 474*d15b3374SMatthias Ringwald static void stdin_process(char c){ 475*d15b3374SMatthias Ringwald switch (c){ 476*d15b3374SMatthias Ringwald case 's': 477*d15b3374SMatthias Ringwald if (app_state != APP_IDLE) break; 478*d15b3374SMatthias Ringwald start_scanning(); 479*d15b3374SMatthias Ringwald break; 480*d15b3374SMatthias Ringwald case '\n': 481*d15b3374SMatthias Ringwald case '\r': 482*d15b3374SMatthias Ringwald break; 483*d15b3374SMatthias Ringwald default: 484*d15b3374SMatthias Ringwald show_usage(); 485*d15b3374SMatthias Ringwald break; 486*d15b3374SMatthias Ringwald 487*d15b3374SMatthias Ringwald } 488*d15b3374SMatthias Ringwald } 489*d15b3374SMatthias Ringwald 490*d15b3374SMatthias Ringwald int btstack_main(int argc, const char * argv[]); 491*d15b3374SMatthias Ringwald int btstack_main(int argc, const char * argv[]){ 492*d15b3374SMatthias Ringwald (void) argv; 493*d15b3374SMatthias Ringwald (void) argc; 494*d15b3374SMatthias Ringwald 495*d15b3374SMatthias Ringwald // register for HCI events 496*d15b3374SMatthias Ringwald hci_event_callback_registration.callback = &packet_handler; 497*d15b3374SMatthias Ringwald hci_add_event_handler(&hci_event_callback_registration); 498*d15b3374SMatthias Ringwald 499*d15b3374SMatthias Ringwald broadcast_audio_scan_service_client_init(&packet_handler); 500*d15b3374SMatthias Ringwald 501*d15b3374SMatthias Ringwald // turn on! 502*d15b3374SMatthias Ringwald hci_power_control(HCI_POWER_ON); 503*d15b3374SMatthias Ringwald 504*d15b3374SMatthias Ringwald btstack_stdin_setup(stdin_process); 505*d15b3374SMatthias Ringwald return 0; 506*d15b3374SMatthias Ringwald } 507