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