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 BLUEKITCHEN
24 * GMBH 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__ "broadcast_audio_scan_service_client.c"
39
40 #include "ble/att_db.h"
41 #include "ble/att_server.h"
42 #include "bluetooth_gatt.h"
43 #include "btstack_debug.h"
44 #include "btstack_defines.h"
45 #include "btstack_event.h"
46 #include "btstack_util.h"
47 #include "btstack_memory.h"
48
49 #include "le-audio/le_audio_util.h"
50 #include "le-audio/gatt-service/broadcast_audio_scan_service_client.h"
51
52 #ifdef ENABLE_TESTING_SUPPORT
53 #include <stdio.h>
54 #endif
55
56 static btstack_linked_list_t bass_client_connections;
57
58 static uint16_t bass_client_cid_counter = 0;
59 static btstack_packet_handler_t bass_client_event_callback;
60
61 static void bass_client_handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
62
bass_client_get_next_cid(void)63 static uint16_t bass_client_get_next_cid(void){
64 bass_client_cid_counter = btstack_next_cid_ignoring_zero(bass_client_cid_counter);
65 return bass_client_cid_counter;
66 }
67
bass_client_finalize_connection(bass_client_connection_t * connection)68 static void bass_client_finalize_connection(bass_client_connection_t * connection){
69 btstack_linked_list_remove(&bass_client_connections, (btstack_linked_item_t*) connection);
70 }
71
bass_client_get_connection_for_con_handle(hci_con_handle_t con_handle)72 static bass_client_connection_t * bass_client_get_connection_for_con_handle(hci_con_handle_t con_handle){
73 btstack_linked_list_iterator_t it;
74 btstack_linked_list_iterator_init(&it, (btstack_linked_list_t *) &bass_client_connections);
75 while (btstack_linked_list_iterator_has_next(&it)){
76 bass_client_connection_t * connection = (bass_client_connection_t *)btstack_linked_list_iterator_next(&it);
77 if (connection->con_handle != con_handle) continue;
78 return connection;
79 }
80 return NULL;
81 }
82
bass_client_get_connection_for_cid(uint16_t bass_cid)83 static bass_client_connection_t * bass_client_get_connection_for_cid(uint16_t bass_cid){
84 btstack_linked_list_iterator_t it;
85 btstack_linked_list_iterator_init(&it, (btstack_linked_list_t *) &bass_client_connections);
86 while (btstack_linked_list_iterator_has_next(&it)){
87 bass_client_connection_t * connection = (bass_client_connection_t *)btstack_linked_list_iterator_next(&it);
88 if (connection->cid != bass_cid) continue;
89 return connection;
90 }
91 return NULL;
92 }
93
bass_client_get_receive_state_for_value_handle(bass_client_connection_t * connection,uint16_t value_handle)94 static bass_client_source_t * bass_client_get_receive_state_for_value_handle(bass_client_connection_t * connection, uint16_t value_handle){
95 uint8_t i;
96 for (i = 0; i < connection->receive_states_instances_num; i++){
97 if (connection->receive_states[i].receive_state_value_handle == value_handle){
98 return &connection->receive_states[i];
99 }
100 }
101 return NULL;
102 }
103
bass_client_get_source_for_source_id(bass_client_connection_t * connection,uint8_t source_id)104 static bass_client_source_t * bass_client_get_source_for_source_id(bass_client_connection_t * connection, uint8_t source_id){
105 uint8_t i;
106 for (i = 0; i < connection->receive_states_instances_num; i++){
107 if (connection->receive_states[i].source_id == source_id){
108 return &connection->receive_states[i];
109 }
110 }
111 return NULL;
112 }
113
bass_client_reset_source(bass_client_source_t * source)114 static void bass_client_reset_source(bass_client_source_t * source){
115 if (source == NULL){
116 return;
117 }
118 source->source_id = BASS_INVALID_SOURCE_INDEX;
119 source->in_use = false;
120 memset(&source->data, 0, sizeof(bass_source_data_t));
121 }
122
bass_client_emit_connection_established(bass_client_connection_t * connection,uint8_t status)123 static void bass_client_emit_connection_established(bass_client_connection_t * connection, uint8_t status){
124 btstack_assert(bass_client_event_callback != NULL);
125
126 uint8_t event[8];
127 uint16_t pos = 0;
128 event[pos++] = HCI_EVENT_LEAUDIO_META;
129 event[pos++] = sizeof(event) - 2;
130 event[pos++] = LEAUDIO_SUBEVENT_BASS_CLIENT_CONNECTED;
131 little_endian_store_16(event, pos, connection->con_handle);
132 pos += 2;
133 little_endian_store_16(event, pos, connection->cid);
134 pos += 2;
135 event[pos++] = status;
136 (*bass_client_event_callback)(HCI_EVENT_PACKET, 0, event, sizeof(event));
137 }
138
bass_client_connected(bass_client_connection_t * connection,uint8_t status)139 static void bass_client_connected(bass_client_connection_t *connection, uint8_t status) {
140 if (status == ERROR_CODE_SUCCESS){
141 connection->state = BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_STATE_READY;
142 bass_client_emit_connection_established(connection, status);
143 } else {
144 connection->state = BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_STATE_IDLE;
145 bass_client_emit_connection_established(connection, status);
146 bass_client_finalize_connection(connection);
147 }
148 }
149
bass_client_emit_scan_operation_complete(bass_client_connection_t * connection,uint8_t status,bass_opcode_t opcode)150 static void bass_client_emit_scan_operation_complete(bass_client_connection_t * connection, uint8_t status, bass_opcode_t opcode){
151 btstack_assert(bass_client_event_callback != NULL);
152 uint8_t event[7];
153 uint16_t pos = 0;
154 event[pos++] = HCI_EVENT_LEAUDIO_META;
155 event[pos++] = sizeof(event) - 2;
156 event[pos++] = LEAUDIO_SUBEVENT_BASS_CLIENT_SCAN_OPERATION_COMPLETE;
157 little_endian_store_16(event, pos, connection->cid);
158 pos += 2;
159 event[pos++] = status;
160 event[pos++] = (uint8_t)opcode;
161 (*bass_client_event_callback)(HCI_EVENT_PACKET, 0, event, sizeof(event));
162 }
163
bass_client_emit_source_operation_complete(bass_client_connection_t * connection,uint8_t status,bass_opcode_t opcode,uint8_t source_id)164 static void bass_client_emit_source_operation_complete(bass_client_connection_t * connection, uint8_t status, bass_opcode_t opcode, uint8_t source_id){
165 btstack_assert(bass_client_event_callback != NULL);
166 uint8_t event[8];
167 uint16_t pos = 0;
168 event[pos++] = HCI_EVENT_LEAUDIO_META;
169 event[pos++] = sizeof(event) - 2;
170 event[pos++] = LEAUDIO_SUBEVENT_BASS_CLIENT_SOURCE_OPERATION_COMPLETE;
171 little_endian_store_16(event, pos, connection->cid);
172 pos += 2;
173 event[pos++] = status;
174 event[pos++] = (uint8_t)opcode;
175 event[pos++] = source_id;
176 (*bass_client_event_callback)(HCI_EVENT_PACKET, 0, event, sizeof(event));
177 }
178
bass_client_emit_receive_state(bass_client_connection_t * connection,uint8_t source_id)179 static void bass_client_emit_receive_state(bass_client_connection_t * connection, uint8_t source_id){
180 btstack_assert(bass_client_event_callback != NULL);
181 uint8_t pos = 0;
182 uint8_t event[7];
183 event[pos++] = HCI_EVENT_LEAUDIO_META;
184 event[pos++] = sizeof(event) - 2;
185 event[pos++] = LEAUDIO_SUBEVENT_BASS_CLIENT_NOTIFICATION_COMPLETE;
186 little_endian_store_16(event, pos, connection->cid);
187 pos += 2;
188 event[pos++] = source_id;
189 (*bass_client_event_callback)(HCI_EVENT_PACKET, 0, event, sizeof(event));
190 }
191
bass_client_remote_broadcast_receive_state_buffer_valid(uint8_t * buffer,uint16_t buffer_size)192 static bool bass_client_remote_broadcast_receive_state_buffer_valid(uint8_t *buffer, uint16_t buffer_size){
193 // minimal with zero subgroups
194 if (buffer_size < 15){
195 return false;
196 }
197
198 uint16_t pos = 0;
199
200 // source_id
201 pos++;
202
203 // addr type
204 uint8_t adv_type = buffer[pos++];
205 if (adv_type > (uint8_t)BD_ADDR_TYPE_LE_RANDOM){
206 log_info("Unexpected adv_type 0x%02X", adv_type);
207 return false;
208 }
209
210 // address
211 pos += 6;
212
213 // advertising_sid Range: 0x00-0x0F
214 uint8_t advertising_sid = buffer[pos++];
215 if (advertising_sid > 0x0F){
216 log_info("Advertising sid out of range 0x%02X", advertising_sid);
217 return false;
218 }
219
220 // broadcast_id
221 pos += 3;
222
223 // pa_sync_state
224 uint8_t pa_sync_state = buffer[pos++];
225 if (pa_sync_state >= (uint8_t)LE_AUDIO_PA_SYNC_STATE_RFU){
226 log_info("Unexpected pa_sync_state 0x%02X", pa_sync_state);
227 return false;
228 }
229
230 // big_encryption
231 le_audio_big_encryption_t big_encryption = (le_audio_big_encryption_t) buffer[pos++];
232 if (big_encryption == LE_AUDIO_BIG_ENCRYPTION_BAD_CODE){
233 // Bad Code
234 pos += 16;
235 }
236
237 // num subgroups
238 uint8_t num_subgroups = buffer[pos++];
239 if (num_subgroups > BASS_SUBGROUPS_MAX_NUM){
240 log_info("Number of subgroups %u exceedes maximum %u", num_subgroups, BASS_SUBGROUPS_MAX_NUM);
241 return false;
242 }
243
244 uint8_t i;
245 for (i = 0; i < num_subgroups; i++) {
246 // check if we can read bis_sync_state + meta_data_length
247 // bis_sync_state
248 pos += 4;
249 // meta_data_length
250 if (pos >= buffer_size){
251 return false;
252 }
253 uint8_t metadata_length = buffer[pos++];
254 if ((pos + metadata_length) > buffer_size){
255 return false;
256 }
257 // metadata
258 pos += metadata_length;
259 }
260 return true;
261 }
262
bass_client_handle_gatt_server_notification(uint8_t packet_type,uint16_t channel,uint8_t * packet,uint16_t size)263 static void bass_client_handle_gatt_server_notification(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
264 UNUSED(packet_type);
265 UNUSED(channel);
266 UNUSED(size);
267
268 if (hci_event_packet_get_type(packet) != GATT_EVENT_NOTIFICATION){
269 return;
270 }
271 bass_client_connection_t * connection = bass_client_get_connection_for_con_handle(gatt_event_notification_get_handle(packet));
272 if (connection == NULL){
273 return;
274 }
275
276 uint16_t value_handle = gatt_event_notification_get_value_handle(packet);
277 uint16_t value_length = gatt_event_notification_get_value_length(packet);
278 uint8_t * value = (uint8_t *)gatt_event_notification_get_value(packet);
279
280 if (bass_client_remote_broadcast_receive_state_buffer_valid(value, value_length)){
281 bass_client_source_t * source = bass_client_get_receive_state_for_value_handle(connection, value_handle);
282 if (source == NULL){
283 return;
284 }
285 source->source_id = value[0];
286 bass_util_source_data_parse(&value[1], value_length - 1, &source->data, true);
287
288 // get big encryption + bad code
289 source->big_encryption = (le_audio_big_encryption_t) value[13];
290 if (source->big_encryption == LE_AUDIO_BIG_ENCRYPTION_BAD_CODE){
291 reverse_128(&value[14], source->bad_code);
292 } else {
293 memset(source->bad_code, 0, 16);
294 }
295
296 bass_client_emit_receive_state(connection, source->source_id);
297 }
298 }
299
bass_client_register_notification(bass_client_connection_t * connection)300 static bool bass_client_register_notification(bass_client_connection_t * connection){
301 bass_client_source_t * receive_state = &connection->receive_states[connection->receive_states_index];
302 if (receive_state == NULL){
303 return false;
304 }
305
306 gatt_client_characteristic_t characteristic;
307 characteristic.value_handle = receive_state->receive_state_value_handle;
308 characteristic.properties = receive_state->receive_state_properties;
309 characteristic.end_handle = receive_state->receive_state_end_handle;
310
311 uint8_t status = gatt_client_write_client_characteristic_configuration(
312 &bass_client_handle_gatt_client_event,
313 connection->con_handle,
314 &characteristic,
315 GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION);
316
317 // notification supported, register for value updates
318 if (status == ERROR_CODE_SUCCESS){
319 gatt_client_listen_for_characteristic_value_updates(
320 &connection->notification_listener,
321 &bass_client_handle_gatt_server_notification,
322 connection->con_handle, &characteristic);
323 }
324 return status;
325 }
326
bass_client_prepare_add_source_buffer(bass_client_connection_t * connection)327 static uint16_t bass_client_prepare_add_source_buffer(bass_client_connection_t * connection){
328 const bass_source_data_t * receive_state = connection->control_point_operation_data;
329
330 uint16_t buffer_offset = connection->buffer_offset;
331 uint8_t * buffer = connection->buffer;
332 uint16_t buffer_size = btstack_min(sizeof(connection->buffer), connection->mtu);
333
334 btstack_assert(buffer_offset == 0);
335
336 uint8_t field_data[6];
337 uint16_t source_offset = 0;
338 uint16_t stored_bytes = 0;
339 memset(buffer, 0, buffer_size);
340
341 field_data[0] = (uint8_t)BASS_OPCODE_ADD_SOURCE;
342 stored_bytes += le_audio_util_virtual_memcpy_helper(field_data, 1, source_offset, buffer, buffer_size,
343 buffer_offset);
344 source_offset++;
345
346 stored_bytes += bass_util_source_data_header_virtual_memcpy(receive_state, &source_offset, buffer_offset, buffer,
347 buffer_size);
348
349 field_data[0] = (uint8_t)receive_state->pa_sync;
350 stored_bytes += le_audio_util_virtual_memcpy_helper(field_data, 1, source_offset, buffer, buffer_size,
351 buffer_offset);
352 source_offset++;
353
354 little_endian_store_16(field_data, 0, receive_state->pa_interval);
355 stored_bytes += le_audio_util_virtual_memcpy_helper(field_data, 2, source_offset, buffer, buffer_size,
356 buffer_offset);
357 source_offset += 2;
358
359 stored_bytes += bass_util_source_data_subgroups_virtual_memcpy(receive_state, false, &source_offset, buffer_offset,
360 buffer,
361 buffer_size);
362
363 return stored_bytes;
364 }
365
bass_client_prepare_modify_source_buffer(bass_client_connection_t * connection)366 static uint16_t bass_client_prepare_modify_source_buffer(bass_client_connection_t * connection){
367 const bass_source_data_t * receive_state = connection->control_point_operation_data;
368
369 uint16_t buffer_offset = connection->buffer_offset;
370 uint8_t * buffer = connection->buffer;
371 uint16_t buffer_size = btstack_min(sizeof(connection->buffer), connection->mtu);
372
373 btstack_assert(buffer_offset == 0);
374
375 uint8_t field_data[6];
376 uint16_t source_offset = 0;
377 uint16_t stored_bytes = 0;
378 memset(buffer, 0, buffer_size);
379
380 field_data[0] = (uint8_t)BASS_OPCODE_MODIFY_SOURCE;
381 stored_bytes += le_audio_util_virtual_memcpy_helper(field_data, 1, source_offset, buffer, buffer_size,
382 buffer_offset);
383 source_offset++;
384
385 field_data[0] = connection->control_point_operation_source_id;
386 stored_bytes += le_audio_util_virtual_memcpy_helper(field_data, 1, source_offset, buffer, buffer_size,
387 buffer_offset);
388 source_offset++;
389
390 field_data[0] = (uint8_t)receive_state->pa_sync;
391 stored_bytes += le_audio_util_virtual_memcpy_helper(field_data, 1, source_offset, buffer, buffer_size,
392 buffer_offset);
393 source_offset++;
394
395 little_endian_store_16(field_data, 0, receive_state->pa_interval);
396 stored_bytes += le_audio_util_virtual_memcpy_helper(field_data, 2, source_offset, buffer, buffer_size,
397 buffer_offset);
398 source_offset += 2;
399
400 stored_bytes += bass_util_source_data_subgroups_virtual_memcpy(receive_state, false, &source_offset, buffer_offset,
401 buffer,
402 buffer_size);
403 return stored_bytes;
404 }
405
bass_client_receive_state_len(const bass_source_data_t * source_data)406 static uint16_t bass_client_receive_state_len(const bass_source_data_t * source_data){
407 uint16_t source_len = 0;
408 // opcode(1), address_type(1), address(6), adv_sid(1), broadcast_id(3), pa_sync(1), subgroups_num(1)
409 source_len = 1 + 1 + 6 + 1 + 3 + 1 + 1;
410
411 uint8_t i;
412 for (i = 0; i < source_data->subgroups_num; i++){
413 bass_subgroup_t subgroup = source_data->subgroups[i];
414 // bis_sync(4), metadata_length(1), metadata_length
415 source_len += 4 + 1 + subgroup.metadata_length;
416 }
417 return source_len;
418 }
419
bass_client_run_for_connection(bass_client_connection_t * connection)420 static void bass_client_run_for_connection(bass_client_connection_t * connection){
421 uint8_t status;
422 gatt_client_characteristic_t characteristic;
423 gatt_client_service_t service;
424
425 uint8_t value[18];
426 uint16_t stored_bytes;
427
428 switch (connection->state){
429 case BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_STATE_READY:
430 break;
431
432 case BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_W2_WRITE_CONTROL_POINT_START_SCAN:
433 #ifdef ENABLE_TESTING_SUPPORT
434 printf(" Write START SCAN [0x%04X]:\n",
435 connection->control_point_value_handle);
436 #endif
437
438 connection->state = BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_W4_WRITE_CONTROL_POINT_START_SCAN;
439 value[0] = BASS_OPCODE_REMOTE_SCAN_STARTED;
440 // see GATT_EVENT_QUERY_COMPLETE for end of write
441 status = gatt_client_write_value_of_characteristic(
442 &bass_client_handle_gatt_client_event, connection->con_handle,
443 connection->control_point_value_handle, 1, &value[0]);
444 UNUSED(status);
445 break;
446
447 case BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_W2_WRITE_CONTROL_POINT_STOP_SCAN:
448 #ifdef ENABLE_TESTING_SUPPORT
449 printf(" Write START SCAN [0x%04X]:\n",
450 connection->control_point_value_handle);
451 #endif
452
453 connection->state = BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_W4_WRITE_CONTROL_POINT_STOP_SCAN;
454 value[0] = BASS_OPCODE_REMOTE_SCAN_STOPPED;
455 // see GATT_EVENT_QUERY_COMPLETE for end of write
456 status = gatt_client_write_value_of_characteristic(
457 &bass_client_handle_gatt_client_event, connection->con_handle,
458 connection->control_point_value_handle, 1, &value[0]);
459 UNUSED(status);
460 break;
461
462 case BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_W2_WRITE_CONTROL_POINT_ADD_SOURCE:
463 connection->state = BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_W4_WRITE_CONTROL_POINT_ADD_SOURCE;
464 #ifdef ENABLE_TESTING_SUPPORT
465 printf(" ADD SOURCE [0x%04X]:\n", connection->control_point_value_handle);
466 #endif
467 connection->state = BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_W4_WRITE_CONTROL_POINT_ADD_SOURCE;
468 stored_bytes = bass_client_prepare_add_source_buffer(connection);
469 connection->buffer_offset += stored_bytes;
470
471 status = gatt_client_write_long_value_of_characteristic(&bass_client_handle_gatt_client_event, connection->con_handle,
472 connection->control_point_value_handle, stored_bytes, connection->buffer);
473 UNUSED(status);
474 break;
475
476 case BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_W2_WRITE_CONTROL_POINT_MODIFY_SOURCE:
477 #ifdef ENABLE_TESTING_SUPPORT
478 printf(" MODIFY SOURCE [%d]:\n", connection->control_point_operation_source_id);
479 #endif
480 connection->state = BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_W4_WRITE_CONTROL_POINT_MODIFY_SOURCE;
481 stored_bytes = bass_client_prepare_modify_source_buffer(connection);
482 connection->buffer_offset += stored_bytes;
483
484 status = gatt_client_write_long_value_of_characteristic(&bass_client_handle_gatt_client_event, connection->con_handle,
485 connection->control_point_value_handle, stored_bytes, connection->buffer);
486 UNUSED(status);
487 break;
488
489 case BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_W2_WRITE_CONTROL_POINT_REMOVE_SOURCE:
490 #ifdef ENABLE_TESTING_SUPPORT
491 printf(" REMOVE SOURCE [%d]:\n", connection->control_point_operation_source_id);
492 #endif
493 connection->state = BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_W4_WRITE_CONTROL_POINT_REMOVE_SOURCE;
494 value[0] = BASS_OPCODE_REMOVE_SOURCE;
495 value[1] = connection->control_point_operation_source_id;
496 // see GATT_EVENT_QUERY_COMPLETE for end of write
497 status = gatt_client_write_value_of_characteristic(
498 &bass_client_handle_gatt_client_event, connection->con_handle,
499 connection->control_point_value_handle, 2, &value[0]);
500 UNUSED(status);
501 break;
502
503 case BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_W2_WRITE_CONTROL_POINT_SET_BROADCAST_CODE:
504 #ifdef ENABLE_TESTING_SUPPORT
505 printf(" SET BROADCAST CODE [%d]:\n", connection->control_point_operation_source_id);
506 #endif
507 connection->state = BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_W4_WRITE_CONTROL_POINT_SET_BROADCAST_CODE;
508 value[0] = BASS_OPCODE_SET_BROADCAST_CODE;
509 value[1] = connection->control_point_operation_source_id;
510 reverse_128(connection->broadcast_code, &value[2]);
511
512 // see GATT_EVENT_QUERY_COMPLETE for end of write
513 status = gatt_client_write_value_of_characteristic(
514 &bass_client_handle_gatt_client_event, connection->con_handle,
515 connection->control_point_value_handle, 18, &value[0]);
516 UNUSED(status);
517 break;
518
519 case BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_STATE_W2_QUERY_SERVICE:
520 connection->state = BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_STATE_W4_SERVICE_RESULT;
521 gatt_client_discover_primary_services_by_uuid16(&bass_client_handle_gatt_client_event, connection->con_handle, ORG_BLUETOOTH_SERVICE_BROADCAST_AUDIO_SCAN_SERVICE);
522 break;
523
524 case BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_STATE_W2_QUERY_CHARACTERISTICS:
525 connection->state = BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_STATE_W4_CHARACTERISTIC_RESULT;
526
527 service.start_group_handle = connection->start_handle;
528 service.end_group_handle = connection->end_handle;
529 service.uuid16 = ORG_BLUETOOTH_SERVICE_BROADCAST_AUDIO_SCAN_SERVICE;
530
531 status = gatt_client_discover_characteristics_for_service(
532 &bass_client_handle_gatt_client_event,
533 connection->con_handle,
534 &service);
535 UNUSED(status);
536 break;
537
538 case BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_STATE_W2_QUERY_CHARACTERISTIC_DESCRIPTORS:
539 #ifdef ENABLE_TESTING_SUPPORT
540 printf("Read client characteristic descriptors [index %d]:\n", connection->receive_states_index);
541 #endif
542 connection->state = BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_STATE_W4_CHARACTERISTIC_DESCRIPTORS_RESULT;
543 characteristic.value_handle = connection->receive_states[connection->receive_states_index].receive_state_value_handle;
544 characteristic.properties = connection->receive_states[connection->receive_states_index].receive_state_properties;
545 characteristic.end_handle = connection->receive_states[connection->receive_states_index].receive_state_end_handle;
546
547 (void) gatt_client_discover_characteristic_descriptors(&bass_client_handle_gatt_client_event, connection->con_handle, &characteristic);
548 break;
549
550 case BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_W2_READ_CHARACTERISTIC_CONFIGURATION:
551 #ifdef ENABLE_TESTING_SUPPORT
552 printf("Read client characteristic value [index %d, handle 0x%04X]:\n", connection->receive_states_index, connection->receive_states[connection->receive_states_index].receive_state_ccc_handle);
553 #endif
554 connection->state = BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_W4_CHARACTERISTIC_CONFIGURATION_RESULT;
555
556 // result in GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT
557 (void) gatt_client_read_characteristic_descriptor_using_descriptor_handle(
558 &bass_client_handle_gatt_client_event,
559 connection->con_handle,
560 connection->receive_states[connection->receive_states_index].receive_state_ccc_handle);
561 break;
562
563 case BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_STATE_W2_REGISTER_NOTIFICATION:
564 #ifdef ENABLE_TESTING_SUPPORT
565 printf("Register notification [index %d, handle 0x%04X]:\n", connection->receive_states_index, connection->receive_states[connection->receive_states_index].receive_state_ccc_handle);
566 #endif
567
568 connection->state = BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_STATE_W4_NOTIFICATION_REGISTERED;
569
570 status = bass_client_register_notification(connection);
571 if (status == ERROR_CODE_SUCCESS) return;
572
573 if (connection->receive_states[connection->receive_states_index].receive_state_ccc_handle != 0){
574 connection->state = BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_W2_READ_CHARACTERISTIC_CONFIGURATION;
575 break;
576 }
577
578 bass_client_connected(connection, ERROR_CODE_SUCCESS);
579 break;
580 default:
581 break;
582 }
583 }
584 // @return true if client valid / run function should be called
bass_client_handle_query_complete(bass_client_connection_t * connection,uint8_t status)585 static bool bass_client_handle_query_complete(bass_client_connection_t * connection, uint8_t status){
586 switch (connection->state){
587 case BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_STATE_W4_SERVICE_RESULT:
588 switch (status){
589 case ATT_ERROR_SUCCESS:
590 break;
591 case ATT_ERROR_INVALID_ATTRIBUTE_VALUE_LENGTH:
592 bass_client_connected(connection, ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE);
593 return false;
594 default:
595 bass_client_connected(connection, ERROR_CODE_UNSPECIFIED_ERROR);
596 return false;
597 }
598
599 if (connection->service_instances_num == 0){
600 bass_client_connected(connection, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
601 return false;
602 }
603 connection->receive_states_index = 0;
604 connection->receive_states_instances_num = 0;
605 connection->state = BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_STATE_W2_QUERY_CHARACTERISTICS;
606 break;
607
608 case BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_STATE_W4_CHARACTERISTIC_RESULT:
609 if (status != ATT_ERROR_SUCCESS){
610 bass_client_connected(connection, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
611 return false;
612 }
613
614 connection->receive_states_index = 0;
615 connection->state = BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_STATE_W2_QUERY_CHARACTERISTIC_DESCRIPTORS;
616 break;
617
618 case BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_STATE_W4_CHARACTERISTIC_DESCRIPTORS_RESULT:
619 if (connection->receive_states_index < (connection->receive_states_instances_num - 1)){
620 connection->receive_states_index++;
621 connection->state = BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_STATE_W2_QUERY_CHARACTERISTIC_DESCRIPTORS;
622 break;
623 }
624 connection->receive_states_index = 0;
625 connection->state = BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_STATE_W2_REGISTER_NOTIFICATION;
626 break;
627
628 case BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_STATE_W4_NOTIFICATION_REGISTERED:
629 if (connection->receive_states_index < (connection->receive_states_instances_num - 1)){
630 connection->receive_states_index++;
631 connection->state = BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_STATE_W2_REGISTER_NOTIFICATION;
632 break;
633 }
634
635 bass_client_connected(connection, ERROR_CODE_SUCCESS);
636 break;
637
638 case BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_W4_CHARACTERISTIC_CONFIGURATION_RESULT:
639 bass_client_connected(connection, ERROR_CODE_SUCCESS);
640 break;
641
642 case BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_W4_WRITE_CONTROL_POINT_START_SCAN:
643 connection->state = BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_STATE_READY;
644 bass_client_emit_scan_operation_complete(connection, status, BASS_OPCODE_REMOTE_SCAN_STARTED);
645 break;
646
647 case BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_W4_WRITE_CONTROL_POINT_STOP_SCAN:
648 connection->state = BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_STATE_READY;
649 bass_client_emit_scan_operation_complete(connection, status, BASS_OPCODE_REMOTE_SCAN_STOPPED);
650 break;
651
652 case BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_W4_WRITE_CONTROL_POINT_ADD_SOURCE:
653 connection->state = BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_STATE_READY;
654 bass_client_emit_source_operation_complete(connection, status, BASS_OPCODE_ADD_SOURCE, BASS_INVALID_SOURCE_INDEX);
655 break;
656
657 case BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_W4_WRITE_CONTROL_POINT_MODIFY_SOURCE:
658 connection->state = BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_STATE_READY;
659 bass_client_emit_source_operation_complete(connection, status, BASS_OPCODE_MODIFY_SOURCE, connection->control_point_operation_source_id);
660 break;
661
662 case BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_W4_WRITE_CONTROL_POINT_REMOVE_SOURCE:
663 connection->state = BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_STATE_READY;
664 bass_client_reset_source(
665 bass_client_get_source_for_source_id(connection, connection->control_point_operation_source_id));
666 bass_client_emit_source_operation_complete(connection, status, BASS_OPCODE_REMOVE_SOURCE, connection->control_point_operation_source_id);
667 break;
668
669 case BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_W4_WRITE_CONTROL_POINT_SET_BROADCAST_CODE:
670 connection->state = BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_STATE_READY;
671 bass_client_emit_source_operation_complete(connection, status, BASS_OPCODE_SET_BROADCAST_CODE, connection->control_point_operation_source_id);
672 break;
673
674 case BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_STATE_READY:
675 break;
676
677 default:
678 break;
679
680 }
681 return true;
682 }
683
bass_client_handle_gatt_client_event(uint8_t packet_type,uint16_t channel,uint8_t * packet,uint16_t size)684 static void bass_client_handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
685 UNUSED(packet_type);
686 UNUSED(channel);
687 UNUSED(size);
688
689 bass_client_connection_t * connection = NULL;
690 gatt_client_service_t service;
691 gatt_client_characteristic_t characteristic;
692 gatt_client_characteristic_descriptor_t characteristic_descriptor;
693
694 bool call_run = true;
695
696 switch(hci_event_packet_get_type(packet)){
697 case GATT_EVENT_MTU:
698 connection = bass_client_get_connection_for_con_handle(gatt_event_mtu_get_handle(packet));
699 btstack_assert(connection != NULL);
700 connection->mtu = gatt_event_mtu_get_MTU(packet);
701 break;
702
703 case GATT_EVENT_SERVICE_QUERY_RESULT:
704 connection = bass_client_get_connection_for_con_handle(gatt_event_service_query_result_get_handle(packet));
705 btstack_assert(connection != NULL);
706
707 if (connection->service_instances_num < 1){
708 gatt_event_service_query_result_get_service(packet, &service);
709 connection->start_handle = service.start_group_handle;
710 connection->end_handle = service.end_group_handle;
711
712 #ifdef ENABLE_TESTING_SUPPORT
713 printf("BASS Service: start handle 0x%04X, end handle 0x%04X\n", connection->start_handle, connection->end_handle);
714 #endif
715 connection->service_instances_num++;
716 } else {
717 log_info("Found more then one BASS Service instance. ");
718 }
719 break;
720
721 case GATT_EVENT_CHARACTERISTIC_QUERY_RESULT:
722 connection = bass_client_get_connection_for_con_handle(gatt_event_characteristic_query_result_get_handle(packet));
723 btstack_assert(connection != NULL);
724
725 gatt_event_characteristic_query_result_get_characteristic(packet, &characteristic);
726
727 switch (characteristic.uuid16){
728 case ORG_BLUETOOTH_CHARACTERISTIC_BROADCAST_AUDIO_SCAN_CONTROL_POINT:
729 connection->control_point_value_handle = characteristic.value_handle;
730 break;
731 case ORG_BLUETOOTH_CHARACTERISTIC_BROADCAST_RECEIVE_STATE:
732 if (connection->receive_states_instances_num < connection->max_receive_states_num){
733 connection->receive_states[connection->receive_states_instances_num].receive_state_value_handle = characteristic.value_handle;
734 connection->receive_states[connection->receive_states_instances_num].receive_state_properties = characteristic.properties;
735 connection->receive_states[connection->receive_states_index].receive_state_end_handle = characteristic.end_handle;
736 connection->receive_states_instances_num++;
737 } else {
738 log_info("Found more BASS receive_states chrs then it can be stored. ");
739 }
740 break;
741 default:
742 btstack_assert(false);
743 return;
744 }
745
746 #ifdef ENABLE_TESTING_SUPPORT
747 printf("BASS Characteristic:\n Attribute Handle 0x%04X, Properties 0x%02X, Handle 0x%04X, UUID 0x%04X, %s\n",
748 characteristic.start_handle,
749 characteristic.properties,
750 characteristic.value_handle, characteristic.uuid16,
751 characteristic.uuid16 == ORG_BLUETOOTH_CHARACTERISTIC_BROADCAST_RECEIVE_STATE ? "RECEIVE_STATE" : "CONTROL_POINT");
752 #endif
753 break;
754
755
756 case GATT_EVENT_ALL_CHARACTERISTIC_DESCRIPTORS_QUERY_RESULT:
757 connection = bass_client_get_connection_for_con_handle(gatt_event_all_characteristic_descriptors_query_result_get_handle(packet));
758 btstack_assert(connection != NULL);
759 gatt_event_all_characteristic_descriptors_query_result_get_characteristic_descriptor(packet, &characteristic_descriptor);
760
761 if (characteristic_descriptor.uuid16 == ORG_BLUETOOTH_DESCRIPTOR_GATT_CLIENT_CHARACTERISTIC_CONFIGURATION){
762 connection->receive_states[connection->receive_states_index].receive_state_ccc_handle = characteristic_descriptor.handle;
763
764 #ifdef ENABLE_TESTING_SUPPORT
765 printf(" BASS Characteristic Configuration Descriptor: Handle 0x%04X, UUID 0x%04X\n",
766 characteristic_descriptor.handle,
767 characteristic_descriptor.uuid16);
768 #endif
769 }
770 break;
771
772
773 case GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT:
774 connection = bass_client_get_connection_for_con_handle(gatt_event_characteristic_value_query_result_get_handle(packet));
775 btstack_assert(connection != NULL);
776
777 if (connection->state == BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_W4_CHARACTERISTIC_CONFIGURATION_RESULT){
778 #ifdef ENABLE_TESTING_SUPPORT
779 printf(" Received CCC value: ");
780 printf_hexdump(gatt_event_characteristic_value_query_result_get_value(packet), gatt_event_characteristic_value_query_result_get_value_length(packet));
781 #endif
782 break;
783 }
784
785 if (gatt_event_characteristic_value_query_result_get_value_length(packet) == 0 ){
786 break;
787 }
788
789 // TODO
790 // bass_client_emit_receive_state(connection,
791 // gatt_event_characteristic_value_query_result_get_value_handle(packet),
792 // ATT_ERROR_SUCCESS,
793 // gatt_event_notification_get_value(packet),
794 // gatt_event_notification_get_value_length(packet));
795 break;
796
797 case GATT_EVENT_QUERY_COMPLETE:
798 connection = bass_client_get_connection_for_con_handle(gatt_event_query_complete_get_handle(packet));
799 btstack_assert(connection != NULL);
800 call_run = bass_client_handle_query_complete(connection, gatt_event_query_complete_get_att_status(packet));
801 break;
802
803 default:
804 break;
805 }
806
807 if (call_run && (connection != NULL)){
808 bass_client_run_for_connection(connection);
809 }
810 }
811
broadcast_audio_scan_service_client_init(btstack_packet_handler_t packet_handler)812 void broadcast_audio_scan_service_client_init(btstack_packet_handler_t packet_handler){
813 btstack_assert(packet_handler != NULL);
814 bass_client_event_callback = packet_handler;
815 }
816
broadcast_audio_scan_service_client_connect(bass_client_connection_t * connection,bass_client_source_t * receive_states,uint8_t receive_states_num,hci_con_handle_t con_handle,uint16_t * bass_cid)817 uint8_t broadcast_audio_scan_service_client_connect(bass_client_connection_t * connection,
818 bass_client_source_t * receive_states, uint8_t receive_states_num,
819 hci_con_handle_t con_handle, uint16_t * bass_cid){
820
821 btstack_assert(receive_states != NULL);
822 btstack_assert(receive_states_num > 0);
823
824 if (bass_client_get_connection_for_con_handle(con_handle) != NULL){
825 return ERROR_CODE_COMMAND_DISALLOWED;
826 }
827
828 uint16_t cid = bass_client_get_next_cid();
829 if (bass_cid != NULL) {
830 *bass_cid = cid;
831 }
832
833 connection->cid = cid;
834 connection->con_handle = con_handle;
835 connection->mtu = ATT_DEFAULT_MTU;
836
837 connection->max_receive_states_num = receive_states_num;
838 connection->receive_states = receive_states;
839
840 uint8_t i;
841 for (i = 0; i < connection->max_receive_states_num; i++){
842 connection->receive_states[i].in_use = false;
843 connection->receive_states[i].source_id = BASS_INVALID_SOURCE_INDEX;
844 }
845
846 connection->service_instances_num = 0;
847 connection->receive_states_instances_num = 0;
848 connection->receive_states_index = 0;
849
850 connection->state = BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_STATE_W2_QUERY_SERVICE;
851 btstack_linked_list_add(&bass_client_connections, (btstack_linked_item_t *) connection);
852
853 bass_client_run_for_connection(connection);
854 return ERROR_CODE_SUCCESS;
855 }
856
broadcast_audio_scan_service_client_scanning_started(uint16_t bass_cid)857 uint8_t broadcast_audio_scan_service_client_scanning_started(uint16_t bass_cid){
858 bass_client_connection_t * connection = bass_client_get_connection_for_cid(bass_cid);
859 if (connection == NULL){
860 return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
861 }
862 if (connection->state != BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_STATE_READY){
863 return ERROR_CODE_COMMAND_DISALLOWED;
864 }
865 connection->state = BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_W2_WRITE_CONTROL_POINT_START_SCAN;
866 bass_client_run_for_connection(connection);
867 return ERROR_CODE_SUCCESS;
868 }
869
broadcast_audio_scan_service_client_scanning_stopped(uint16_t bass_cid)870 uint8_t broadcast_audio_scan_service_client_scanning_stopped(uint16_t bass_cid){
871 bass_client_connection_t * connection = bass_client_get_connection_for_cid(bass_cid);
872 if (connection == NULL){
873 return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
874 }
875 if (connection->state != BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_STATE_READY){
876 return ERROR_CODE_COMMAND_DISALLOWED;
877 }
878 connection->state = BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_W2_WRITE_CONTROL_POINT_STOP_SCAN;
879 bass_client_run_for_connection(connection);
880 return ERROR_CODE_SUCCESS;
881 }
882
broadcast_audio_scan_service_client_add_source(uint16_t bass_cid,const bass_source_data_t * add_source_data)883 uint8_t broadcast_audio_scan_service_client_add_source(uint16_t bass_cid, const bass_source_data_t * add_source_data){
884 bass_client_connection_t * connection = bass_client_get_connection_for_cid(bass_cid);
885 if (connection == NULL){
886 return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
887 }
888 if (connection->state != BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_STATE_READY){
889 return ERROR_CODE_COMMAND_DISALLOWED;
890 }
891
892 connection->state = BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_W2_WRITE_CONTROL_POINT_ADD_SOURCE;
893 connection->control_point_operation_data = add_source_data;
894 connection->buffer_offset = 0;
895 connection->data_size = bass_client_receive_state_len(add_source_data);
896
897 bass_client_run_for_connection(connection);
898 return ERROR_CODE_SUCCESS;
899 }
900
broadcast_audio_scan_service_client_modify_source(uint16_t bass_cid,uint8_t source_id,const bass_source_data_t * modify_source_data)901 uint8_t broadcast_audio_scan_service_client_modify_source(uint16_t bass_cid, uint8_t source_id, const bass_source_data_t * modify_source_data){
902 bass_client_connection_t * connection = bass_client_get_connection_for_cid(bass_cid);
903 if (connection == NULL){
904 return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
905 }
906 if (connection->state != BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_STATE_READY){
907 return ERROR_CODE_COMMAND_DISALLOWED;
908 }
909
910 connection->state = BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_W2_WRITE_CONTROL_POINT_MODIFY_SOURCE;
911 connection->control_point_operation_data = modify_source_data;
912 connection->control_point_operation_source_id = source_id;
913 connection->buffer_offset = 0;
914 connection->data_size = bass_client_receive_state_len(modify_source_data);
915
916 bass_client_run_for_connection(connection);
917 return ERROR_CODE_SUCCESS;
918 }
919
broadcast_audio_scan_service_client_remove_source(uint16_t bass_cid,uint8_t source_id)920 uint8_t broadcast_audio_scan_service_client_remove_source(uint16_t bass_cid, uint8_t source_id){
921 bass_client_connection_t * connection = bass_client_get_connection_for_cid(bass_cid);
922 if (connection == NULL){
923 return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
924 }
925 if (connection->state != BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_STATE_READY){
926 return ERROR_CODE_COMMAND_DISALLOWED;
927 }
928
929 connection->state = BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_W2_WRITE_CONTROL_POINT_REMOVE_SOURCE;
930 connection->control_point_operation_source_id = source_id;
931
932 bass_client_run_for_connection(connection);
933 return ERROR_CODE_SUCCESS;
934 }
935
broadcast_audio_scan_service_client_set_broadcast_code(uint16_t bass_cid,uint8_t source_id,const uint8_t * broadcast_code)936 uint8_t broadcast_audio_scan_service_client_set_broadcast_code(uint16_t bass_cid, uint8_t source_id, const uint8_t * broadcast_code){
937 bass_client_connection_t * connection = bass_client_get_connection_for_cid(bass_cid);
938 if (connection == NULL){
939 return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
940 }
941 if (connection->state != BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_STATE_READY){
942 return ERROR_CODE_COMMAND_DISALLOWED;
943 }
944
945 connection->state = BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_W2_WRITE_CONTROL_POINT_SET_BROADCAST_CODE;
946 connection->control_point_operation_source_id = source_id;
947 connection->broadcast_code = broadcast_code;
948
949 bass_client_run_for_connection(connection);
950 return ERROR_CODE_SUCCESS;
951 }
952
broadcast_audio_scan_service_client_get_source_data(uint16_t bass_cid,uint8_t source_id)953 const bass_source_data_t * broadcast_audio_scan_service_client_get_source_data(uint16_t bass_cid, uint8_t source_id){
954 bass_client_connection_t * connection = bass_client_get_connection_for_cid(bass_cid);
955 if (connection == NULL){
956 return NULL;
957 }
958 if (connection->state != BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_STATE_READY){
959 return NULL;
960 }
961 return (const bass_source_data_t *) &bass_client_get_source_for_source_id(connection, source_id)->data;
962 }
963
broadcast_audio_scan_service_client_get_encryption_state(uint16_t bass_cid,uint8_t source_id,le_audio_big_encryption_t * out_big_encryption,uint8_t * out_bad_code)964 uint8_t broadcast_audio_scan_service_client_get_encryption_state(uint16_t bass_cid, uint8_t source_id,
965 le_audio_big_encryption_t * out_big_encryption, uint8_t * out_bad_code){
966 btstack_assert(out_big_encryption != NULL);
967 btstack_assert(out_bad_code != NULL);
968
969 bass_client_connection_t * connection = bass_client_get_connection_for_cid(bass_cid);
970 if (connection == NULL){
971 return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
972 }
973 if (connection->state != BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_STATE_READY){
974 return ERROR_CODE_COMMAND_DISALLOWED;
975 }
976 bass_client_source_t * source = bass_client_get_source_for_source_id(connection, source_id);
977 if (source == NULL){
978 return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
979 }
980 *out_big_encryption = source->big_encryption;
981 memcpy(out_bad_code, source->bad_code, 16);
982 return ERROR_CODE_SUCCESS;
983 }
984
broadcast_audio_scan_service_client_deinit(uint16_t bass_cid)985 void broadcast_audio_scan_service_client_deinit(uint16_t bass_cid){
986 bass_client_event_callback = NULL;
987 bass_client_connection_t * connection = bass_client_get_connection_for_cid(bass_cid);
988 if (connection == NULL){
989 return;
990 }
991 // finalize connections
992 bass_client_finalize_connection(connection);
993 }
994
995