xref: /btstack/src/le-audio/gatt-service/broadcast_audio_scan_service_client.c (revision 59e9e2d1286b0d5c25365ef87a3c85e9fafe3c50)
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