xref: /btstack/src/le-audio/gatt-service/broadcast_audio_scan_service_server.c (revision ae3042838ecab39355f8220511b931dbf8f87c8e)
15deb0bb6SMatthias Ringwald /*
25deb0bb6SMatthias Ringwald  * Copyright (C) 2022 BlueKitchen GmbH
35deb0bb6SMatthias Ringwald  *
45deb0bb6SMatthias Ringwald  * Redistribution and use in source and binary forms, with or without
55deb0bb6SMatthias Ringwald  * modification, are permitted provided that the following conditions
65deb0bb6SMatthias Ringwald  * are met:
75deb0bb6SMatthias Ringwald  *
85deb0bb6SMatthias Ringwald  * 1. Redistributions of source code must retain the above copyright
95deb0bb6SMatthias Ringwald  *    notice, this list of conditions and the following disclaimer.
105deb0bb6SMatthias Ringwald  * 2. Redistributions in binary form must reproduce the above copyright
115deb0bb6SMatthias Ringwald  *    notice, this list of conditions and the following disclaimer in the
125deb0bb6SMatthias Ringwald  *    documentation and/or other materials provided with the distribution.
135deb0bb6SMatthias Ringwald  * 3. Neither the name of the copyright holders nor the names of
145deb0bb6SMatthias Ringwald  *    contributors may be used to endorse or promote products derived
155deb0bb6SMatthias Ringwald  *    from this software without specific prior written permission.
165deb0bb6SMatthias Ringwald  * 4. Any redistribution, use, or modification is done solely for
175deb0bb6SMatthias Ringwald  *    personal benefit and not for any commercial purpose or for
185deb0bb6SMatthias Ringwald  *    monetary gain.
195deb0bb6SMatthias Ringwald  *
205deb0bb6SMatthias Ringwald  * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
215deb0bb6SMatthias Ringwald  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
225deb0bb6SMatthias Ringwald  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
235deb0bb6SMatthias Ringwald  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BLUEKITCHEN
245deb0bb6SMatthias Ringwald  * GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
255deb0bb6SMatthias Ringwald  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
265deb0bb6SMatthias Ringwald  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
275deb0bb6SMatthias Ringwald  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
285deb0bb6SMatthias Ringwald  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
295deb0bb6SMatthias Ringwald  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
305deb0bb6SMatthias Ringwald  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
315deb0bb6SMatthias Ringwald  * SUCH DAMAGE.
325deb0bb6SMatthias Ringwald  *
335deb0bb6SMatthias Ringwald  * Please inquire about commercial licensing options at
345deb0bb6SMatthias Ringwald  * [email protected]
355deb0bb6SMatthias Ringwald  *
365deb0bb6SMatthias Ringwald  */
375deb0bb6SMatthias Ringwald 
385deb0bb6SMatthias Ringwald #define BTSTACK_FILE__ "broadcast_audio_scan_service_server.c"
395deb0bb6SMatthias Ringwald 
405deb0bb6SMatthias Ringwald #include <stdio.h>
415deb0bb6SMatthias Ringwald 
425deb0bb6SMatthias Ringwald #include "ble/att_db.h"
435deb0bb6SMatthias Ringwald #include "ble/att_server.h"
445deb0bb6SMatthias Ringwald #include "bluetooth_gatt.h"
455deb0bb6SMatthias Ringwald #include "btstack_debug.h"
465deb0bb6SMatthias Ringwald #include "btstack_defines.h"
475deb0bb6SMatthias Ringwald #include "btstack_event.h"
485deb0bb6SMatthias Ringwald #include "btstack_util.h"
495deb0bb6SMatthias Ringwald 
505deb0bb6SMatthias Ringwald #include "le-audio/gatt-service/broadcast_audio_scan_service_server.h"
515deb0bb6SMatthias Ringwald #include "le-audio/le_audio_util.h"
525deb0bb6SMatthias Ringwald 
535deb0bb6SMatthias Ringwald #ifdef ENABLE_TESTING_SUPPORT
545deb0bb6SMatthias Ringwald #include <stdio.h>
555deb0bb6SMatthias Ringwald #endif
565deb0bb6SMatthias Ringwald 
575deb0bb6SMatthias Ringwald #define BASS_MAX_NOTIFY_BUFFER_SIZE                             200
585deb0bb6SMatthias Ringwald #define BASS_INVALID_SOURCE_INDEX                               0xFF
595deb0bb6SMatthias Ringwald 
605deb0bb6SMatthias Ringwald static att_service_handler_t    broadcast_audio_scan_service;
61c4f227dfSMilanka Ringwald static btstack_packet_handler_t bass_server_event_callback;
625deb0bb6SMatthias Ringwald 
635deb0bb6SMatthias Ringwald // characteristic: AUDIO_SCAN_CONTROL_POINT
645deb0bb6SMatthias Ringwald static uint16_t bass_audio_scan_control_point_handle;
655deb0bb6SMatthias Ringwald 
665deb0bb6SMatthias Ringwald static uint8_t  bass_logic_time = 0;
675deb0bb6SMatthias Ringwald 
685deb0bb6SMatthias Ringwald static bass_server_source_t * bass_sources;
695deb0bb6SMatthias Ringwald static uint8_t  bass_sources_num = 0;
707f95ef04SMilanka Ringwald static bass_server_connection_t * bass_clients;
715deb0bb6SMatthias Ringwald static uint8_t  bass_clients_num = 0;
725deb0bb6SMatthias Ringwald static btstack_context_callback_registration_t  scheduled_tasks_callback;
735deb0bb6SMatthias Ringwald 
bass_server_get_next_update_counter(void)74c4f227dfSMilanka Ringwald static uint8_t bass_server_get_next_update_counter(void){
755deb0bb6SMatthias Ringwald     uint8_t next_update_counter;
765deb0bb6SMatthias Ringwald     if (bass_logic_time == 0xff) {
775deb0bb6SMatthias Ringwald         next_update_counter = 0;
785deb0bb6SMatthias Ringwald     } else {
795deb0bb6SMatthias Ringwald         next_update_counter = bass_logic_time + 1;
805deb0bb6SMatthias Ringwald     }
815deb0bb6SMatthias Ringwald     bass_logic_time = next_update_counter;
825deb0bb6SMatthias Ringwald     return next_update_counter;
835deb0bb6SMatthias Ringwald }
845deb0bb6SMatthias Ringwald 
855deb0bb6SMatthias Ringwald // returns positive number if counter a > b
bass_server_counter_delta(uint8_t counter_a,uint8_t counter_b)86c4f227dfSMilanka Ringwald static int8_t bass_server_counter_delta(uint8_t counter_a, uint8_t counter_b){
875deb0bb6SMatthias Ringwald     return (int8_t)(counter_a - counter_b);
885deb0bb6SMatthias Ringwald }
895deb0bb6SMatthias Ringwald 
bass_server_find_empty_or_last_used_source_index(void)90c4f227dfSMilanka Ringwald static uint8_t bass_server_find_empty_or_last_used_source_index(void){
915deb0bb6SMatthias Ringwald     bass_server_source_t * last_used_source = NULL;
925deb0bb6SMatthias Ringwald     uint8_t last_used_source_index = BASS_INVALID_SOURCE_INDEX;
935deb0bb6SMatthias Ringwald 
945deb0bb6SMatthias Ringwald     uint8_t i;
955deb0bb6SMatthias Ringwald     for (i = 0; i < bass_sources_num; i++){
965deb0bb6SMatthias Ringwald         if (!bass_sources[i].in_use){
975deb0bb6SMatthias Ringwald             return i;
985deb0bb6SMatthias Ringwald         }
995deb0bb6SMatthias Ringwald         if (last_used_source == NULL){
1005deb0bb6SMatthias Ringwald             last_used_source = &bass_sources[i];
1015deb0bb6SMatthias Ringwald             last_used_source_index = i;
1025deb0bb6SMatthias Ringwald             continue;
1035deb0bb6SMatthias Ringwald         }
104c4f227dfSMilanka Ringwald         if (bass_server_counter_delta(bass_sources[i].update_counter, last_used_source->update_counter) < 0 ){
1055deb0bb6SMatthias Ringwald             last_used_source = &bass_sources[i];
1065deb0bb6SMatthias Ringwald             last_used_source_index = i;
1075deb0bb6SMatthias Ringwald         }
1085deb0bb6SMatthias Ringwald     }
1095deb0bb6SMatthias Ringwald     return last_used_source_index;
1105deb0bb6SMatthias Ringwald }
1115deb0bb6SMatthias Ringwald 
bass_server_find_empty_or_last_used_source(void)112c4f227dfSMilanka Ringwald static bass_server_source_t *  bass_server_find_empty_or_last_used_source(void){
113c4f227dfSMilanka Ringwald     uint8_t last_used_source_index = bass_server_find_empty_or_last_used_source_index();
1145deb0bb6SMatthias Ringwald     if (last_used_source_index == BASS_INVALID_SOURCE_INDEX){
1155deb0bb6SMatthias Ringwald         return NULL;
1165deb0bb6SMatthias Ringwald     }
1175deb0bb6SMatthias Ringwald     return &bass_sources[last_used_source_index];
1185deb0bb6SMatthias Ringwald }
1195deb0bb6SMatthias Ringwald 
bass_server_find_receive_state_for_value_handle(uint16_t attribute_handle)120c4f227dfSMilanka Ringwald static bass_server_source_t * bass_server_find_receive_state_for_value_handle(uint16_t attribute_handle){
1215deb0bb6SMatthias Ringwald     uint16_t i;
1225deb0bb6SMatthias Ringwald     for (i = 0; i < bass_sources_num; i++){
1235deb0bb6SMatthias Ringwald         if (attribute_handle == bass_sources[i].bass_receive_state_handle){
1245deb0bb6SMatthias Ringwald             return &bass_sources[i];
1255deb0bb6SMatthias Ringwald         }
1265deb0bb6SMatthias Ringwald     }
1275deb0bb6SMatthias Ringwald     return NULL;
1285deb0bb6SMatthias Ringwald }
1295deb0bb6SMatthias Ringwald 
bass_server_find_receive_state_for_client_configuration_handle(uint16_t attribute_handle)130c4f227dfSMilanka Ringwald static bass_server_source_t * bass_server_find_receive_state_for_client_configuration_handle(uint16_t attribute_handle){
1315deb0bb6SMatthias Ringwald     if (attribute_handle == 0){
1325deb0bb6SMatthias Ringwald         return NULL;
1335deb0bb6SMatthias Ringwald     }
1345deb0bb6SMatthias Ringwald     uint8_t i;
1355deb0bb6SMatthias Ringwald     for (i = 0; i < bass_sources_num; i++){
1365deb0bb6SMatthias Ringwald         if (bass_sources[i].bass_receive_state_client_configuration_handle == attribute_handle){
1375deb0bb6SMatthias Ringwald             return &bass_sources[i];
1385deb0bb6SMatthias Ringwald         }
1395deb0bb6SMatthias Ringwald     }
1405deb0bb6SMatthias Ringwald     return NULL;
1415deb0bb6SMatthias Ringwald }
1425deb0bb6SMatthias Ringwald 
bass_server_find_source_for_source_id(uint8_t source_id)143c4f227dfSMilanka Ringwald static bass_server_source_t * bass_server_find_source_for_source_id(uint8_t source_id){
1445deb0bb6SMatthias Ringwald     if (source_id < bass_sources_num){
1455deb0bb6SMatthias Ringwald         return &bass_sources[source_id];
1465deb0bb6SMatthias Ringwald     }
1475deb0bb6SMatthias Ringwald     return NULL;
1485deb0bb6SMatthias Ringwald }
1495deb0bb6SMatthias Ringwald 
bass_server_find_client_for_con_handle(hci_con_handle_t con_handle)1507f95ef04SMilanka Ringwald static bass_server_connection_t * bass_server_find_client_for_con_handle(hci_con_handle_t con_handle){
1515deb0bb6SMatthias Ringwald     uint16_t i;
1525deb0bb6SMatthias Ringwald     for (i = 0; i < bass_clients_num; i++){
1535deb0bb6SMatthias Ringwald         if (bass_clients[i].con_handle == con_handle) {
1545deb0bb6SMatthias Ringwald             return &bass_clients[i];
1555deb0bb6SMatthias Ringwald         }
1565deb0bb6SMatthias Ringwald     }
1575deb0bb6SMatthias Ringwald     return NULL;
1585deb0bb6SMatthias Ringwald }
1595deb0bb6SMatthias Ringwald 
bass_server_register_con_handle(hci_con_handle_t con_handle,uint16_t client_configuration)160c4f227dfSMilanka Ringwald static void bass_server_register_con_handle(hci_con_handle_t con_handle, uint16_t client_configuration){
1617f95ef04SMilanka Ringwald     bass_server_connection_t * client = bass_server_find_client_for_con_handle(con_handle);
1625deb0bb6SMatthias Ringwald     if (client == NULL){
163c4f227dfSMilanka Ringwald         client = bass_server_find_client_for_con_handle(HCI_CON_HANDLE_INVALID);
1645deb0bb6SMatthias Ringwald         if (client == NULL){
1655deb0bb6SMatthias Ringwald             return;
1665deb0bb6SMatthias Ringwald         }
1675deb0bb6SMatthias Ringwald 
1685deb0bb6SMatthias Ringwald     }
1695deb0bb6SMatthias Ringwald     client->con_handle = (client_configuration == 0) ? HCI_CON_HANDLE_INVALID : con_handle;
1705deb0bb6SMatthias Ringwald }
1715deb0bb6SMatthias Ringwald 
bass_server_source_emit_scan_stoped(hci_con_handle_t con_handle)172c4f227dfSMilanka Ringwald static void bass_server_source_emit_scan_stoped(hci_con_handle_t con_handle){
173c4f227dfSMilanka Ringwald     btstack_assert(bass_server_event_callback != NULL);
1745deb0bb6SMatthias Ringwald 
1755deb0bb6SMatthias Ringwald     uint8_t event[5];
1765deb0bb6SMatthias Ringwald     uint8_t pos = 0;
177*ae304283SMilanka Ringwald     event[pos++] = HCI_EVENT_LEAUDIO_META;
1785deb0bb6SMatthias Ringwald     event[pos++] = sizeof(event) - 2;
179*ae304283SMilanka Ringwald     event[pos++] = LEAUDIO_SUBEVENT_BASS_SERVER_SCAN_STOPPED;
1805deb0bb6SMatthias Ringwald     little_endian_store_16(event, pos, con_handle);
1815deb0bb6SMatthias Ringwald     pos += 2;
182c4f227dfSMilanka Ringwald     (*bass_server_event_callback)(HCI_EVENT_PACKET, 0, event, sizeof(event));
1835deb0bb6SMatthias Ringwald }
1845deb0bb6SMatthias Ringwald 
bass_server_source_emit_scan_started(hci_con_handle_t con_handle)185c4f227dfSMilanka Ringwald static void bass_server_source_emit_scan_started(hci_con_handle_t con_handle){
186c4f227dfSMilanka Ringwald     btstack_assert(bass_server_event_callback != NULL);
1875deb0bb6SMatthias Ringwald 
1885deb0bb6SMatthias Ringwald     uint8_t event[5];
1895deb0bb6SMatthias Ringwald     uint8_t pos = 0;
190*ae304283SMilanka Ringwald     event[pos++] = HCI_EVENT_LEAUDIO_META;
1915deb0bb6SMatthias Ringwald     event[pos++] = sizeof(event) - 2;
192*ae304283SMilanka Ringwald     event[pos++] = LEAUDIO_SUBEVENT_BASS_SERVER_SCAN_STARTED;
1935deb0bb6SMatthias Ringwald     little_endian_store_16(event, pos, con_handle);
1945deb0bb6SMatthias Ringwald     pos += 2;
195c4f227dfSMilanka Ringwald     (*bass_server_event_callback)(HCI_EVENT_PACKET, 0, event, sizeof(event));
1965deb0bb6SMatthias Ringwald }
1975deb0bb6SMatthias Ringwald 
1985deb0bb6SMatthias Ringwald 
bass_server_source_emit_source_state_changed(uint8_t subevent_id,hci_con_handle_t con_handle,uint8_t source_id,le_audio_pa_sync_t pa_sync)199c4f227dfSMilanka Ringwald static void bass_server_source_emit_source_state_changed(uint8_t subevent_id, hci_con_handle_t con_handle, uint8_t source_id, le_audio_pa_sync_t pa_sync){
200c4f227dfSMilanka Ringwald     btstack_assert(bass_server_event_callback != NULL);
2015deb0bb6SMatthias Ringwald 
2025deb0bb6SMatthias Ringwald     uint8_t event[7];
2035deb0bb6SMatthias Ringwald     uint8_t pos = 0;
204*ae304283SMilanka Ringwald     event[pos++] = HCI_EVENT_LEAUDIO_META;
2055deb0bb6SMatthias Ringwald     event[pos++] = sizeof(event) - 2;
2065deb0bb6SMatthias Ringwald     event[pos++] = subevent_id;
2075deb0bb6SMatthias Ringwald     little_endian_store_16(event, pos, con_handle);
2085deb0bb6SMatthias Ringwald     pos += 2;
2095deb0bb6SMatthias Ringwald     event[pos++] = source_id;
2105deb0bb6SMatthias Ringwald     event[pos++] = (uint8_t)pa_sync;
211c4f227dfSMilanka Ringwald     (*bass_server_event_callback)(HCI_EVENT_PACKET, 0, event, sizeof(event));
2125deb0bb6SMatthias Ringwald }
2135deb0bb6SMatthias Ringwald 
bass_server_source_emit_source_added(hci_con_handle_t con_handle,bass_server_source_t * source)214c4f227dfSMilanka Ringwald static void bass_server_source_emit_source_added(hci_con_handle_t con_handle, bass_server_source_t * source){
215*ae304283SMilanka Ringwald     bass_server_source_emit_source_state_changed(LEAUDIO_SUBEVENT_BASS_SERVER_SOURCE_ADDED, con_handle, source->source_id, source->data.pa_sync);
2165deb0bb6SMatthias Ringwald }
2175deb0bb6SMatthias Ringwald 
bass_server_source_emit_source_modified(hci_con_handle_t con_handle,bass_server_source_t * source)218c4f227dfSMilanka Ringwald static void bass_server_source_emit_source_modified(hci_con_handle_t con_handle, bass_server_source_t * source){
219*ae304283SMilanka Ringwald     bass_server_source_emit_source_state_changed(LEAUDIO_SUBEVENT_BASS_SERVER_SOURCE_MODIFIED, con_handle, source->source_id, source->data.pa_sync);
2205deb0bb6SMatthias Ringwald }
2215deb0bb6SMatthias Ringwald 
bass_server_source_emit_source_deleted(hci_con_handle_t con_handle,bass_server_source_t * source)222c4f227dfSMilanka Ringwald static void bass_server_source_emit_source_deleted(hci_con_handle_t con_handle, bass_server_source_t * source){
223*ae304283SMilanka Ringwald     bass_server_source_emit_source_state_changed(LEAUDIO_SUBEVENT_BASS_SERVER_SOURCE_DELETED, con_handle, source->source_id, LE_AUDIO_PA_SYNC_DO_NOT_SYNCHRONIZE_TO_PA);
2245deb0bb6SMatthias Ringwald }
2255deb0bb6SMatthias Ringwald 
bass_server_source_emit_broadcast_code(hci_con_handle_t con_handle,uint8_t source_id,const uint8_t * broadcast_code)226c4f227dfSMilanka Ringwald static void bass_server_source_emit_broadcast_code(hci_con_handle_t con_handle, uint8_t source_id, const uint8_t * broadcast_code){
227c4f227dfSMilanka Ringwald     btstack_assert(bass_server_event_callback != NULL);
2285deb0bb6SMatthias Ringwald 
2295deb0bb6SMatthias Ringwald     uint8_t event[22];
2305deb0bb6SMatthias Ringwald     uint8_t pos = 0;
231*ae304283SMilanka Ringwald     event[pos++] = HCI_EVENT_LEAUDIO_META;
2325deb0bb6SMatthias Ringwald     event[pos++] = sizeof(event) - 2;
233*ae304283SMilanka Ringwald     event[pos++] = LEAUDIO_SUBEVENT_BASS_SERVER_BROADCAST_CODE;
2345deb0bb6SMatthias Ringwald     little_endian_store_16(event, pos, con_handle);
2355deb0bb6SMatthias Ringwald     pos += 2;
2365deb0bb6SMatthias Ringwald     event[pos++] = source_id;
2375deb0bb6SMatthias Ringwald     reverse_128(broadcast_code, &event[pos]);
2385deb0bb6SMatthias Ringwald     pos += 16;
239c4f227dfSMilanka Ringwald     (*bass_server_event_callback)(HCI_EVENT_PACKET, 0, event, sizeof(event));
2405deb0bb6SMatthias Ringwald }
2415deb0bb6SMatthias Ringwald 
2425deb0bb6SMatthias Ringwald // offset gives position into fully serialized bass record
bass_server_copy_source_to_buffer(bass_server_source_t * source,uint16_t buffer_offset,uint8_t * buffer,uint16_t buffer_size)2435deb0bb6SMatthias Ringwald static uint16_t bass_server_copy_source_to_buffer(bass_server_source_t * source, uint16_t buffer_offset, uint8_t * buffer, uint16_t buffer_size){
2445deb0bb6SMatthias Ringwald     uint8_t  field_data[16];
2455deb0bb6SMatthias Ringwald     uint16_t source_offset = 0;
2465deb0bb6SMatthias Ringwald     uint16_t stored_bytes = 0;
2475deb0bb6SMatthias Ringwald     memset(buffer, 0, buffer_size);
2485deb0bb6SMatthias Ringwald 
2495deb0bb6SMatthias Ringwald     if (!source->in_use){
2505deb0bb6SMatthias Ringwald         return 0;
2515deb0bb6SMatthias Ringwald     }
2525deb0bb6SMatthias Ringwald     field_data[0] = source->source_id;
25354461c80SMatthias Ringwald     stored_bytes += le_audio_util_virtual_memcpy_helper(field_data, 1, source_offset, buffer, buffer_size,
25454461c80SMatthias Ringwald                                                         buffer_offset);
2555deb0bb6SMatthias Ringwald     source_offset++;
2565deb0bb6SMatthias Ringwald 
2576e96f7c7SMatthias Ringwald     stored_bytes += bass_util_source_data_header_virtual_memcpy(&source->data, &source_offset, buffer_offset, buffer,
2586e96f7c7SMatthias Ringwald                                                                 buffer_size);
2595deb0bb6SMatthias Ringwald 
2605deb0bb6SMatthias Ringwald     field_data[0] = (uint8_t)source->data.pa_sync_state;
26154461c80SMatthias Ringwald     stored_bytes += le_audio_util_virtual_memcpy_helper(field_data, 1, source_offset, buffer, buffer_size,
26254461c80SMatthias Ringwald                                                         buffer_offset);
2635deb0bb6SMatthias Ringwald     source_offset++;
2645deb0bb6SMatthias Ringwald 
2655deb0bb6SMatthias Ringwald     field_data[0] = (uint8_t)source->big_encryption;
26654461c80SMatthias Ringwald     stored_bytes += le_audio_util_virtual_memcpy_helper(field_data, 1, source_offset, buffer, buffer_size,
26754461c80SMatthias Ringwald                                                         buffer_offset);
2685deb0bb6SMatthias Ringwald     source_offset++;
2695deb0bb6SMatthias Ringwald 
2705deb0bb6SMatthias Ringwald     if (source->big_encryption == LE_AUDIO_BIG_ENCRYPTION_BAD_CODE){
2715deb0bb6SMatthias Ringwald         reverse_128(source->bad_code, &field_data[0]);
27254461c80SMatthias Ringwald         stored_bytes += le_audio_util_virtual_memcpy_helper(field_data, 16, source_offset, buffer, buffer_size,
27354461c80SMatthias Ringwald                                                             buffer_offset);
2745deb0bb6SMatthias Ringwald         source_offset += 16;
2755deb0bb6SMatthias Ringwald     }
2765deb0bb6SMatthias Ringwald 
2776e96f7c7SMatthias Ringwald     stored_bytes += bass_util_source_data_subgroups_virtual_memcpy(&source->data, true, &source_offset, buffer_offset,
2785deb0bb6SMatthias Ringwald                                                                    buffer,
2795deb0bb6SMatthias Ringwald                                                                    buffer_size);
2805deb0bb6SMatthias Ringwald     return stored_bytes;
2815deb0bb6SMatthias Ringwald }
2825deb0bb6SMatthias Ringwald 
bass_server_read_callback(hci_con_handle_t con_handle,uint16_t attribute_handle,uint16_t offset,uint8_t * buffer,uint16_t buffer_size)283c4f227dfSMilanka Ringwald static uint16_t bass_server_read_callback(hci_con_handle_t con_handle, uint16_t attribute_handle, uint16_t offset, uint8_t * buffer, uint16_t buffer_size){
2845deb0bb6SMatthias Ringwald     UNUSED(con_handle);
2855deb0bb6SMatthias Ringwald 
2867f95ef04SMilanka Ringwald     bass_server_source_t * source = bass_server_find_receive_state_for_value_handle(attribute_handle);
2875deb0bb6SMatthias Ringwald     if (source){
2885deb0bb6SMatthias Ringwald         return bass_server_copy_source_to_buffer(source, offset, buffer, buffer_size);
2895deb0bb6SMatthias Ringwald     }
2905deb0bb6SMatthias Ringwald 
291c4f227dfSMilanka Ringwald     source = bass_server_find_receive_state_for_client_configuration_handle(attribute_handle);
2925deb0bb6SMatthias Ringwald     if (source){
2935deb0bb6SMatthias Ringwald         return att_read_callback_handle_little_endian_16(source->bass_receive_state_client_configuration, offset, buffer, buffer_size);
2945deb0bb6SMatthias Ringwald     }
2955deb0bb6SMatthias Ringwald 
2965deb0bb6SMatthias Ringwald     return 0;
2975deb0bb6SMatthias Ringwald }
2985deb0bb6SMatthias Ringwald 
bass_server_source_remote_modify_source_buffer_valid(uint8_t * buffer,uint16_t buffer_size)299c4f227dfSMilanka Ringwald static bool bass_server_source_remote_modify_source_buffer_valid(uint8_t *buffer, uint16_t buffer_size){
3002d612fc8SMatthias Ringwald     if (buffer_size < 5){
3015deb0bb6SMatthias Ringwald         log_info("Modify Source opcode, buffer too small");
3025deb0bb6SMatthias Ringwald         return false;
3035deb0bb6SMatthias Ringwald     }
3045deb0bb6SMatthias Ringwald 
3055deb0bb6SMatthias Ringwald     uint8_t pos = 1; // source_id
3065deb0bb6SMatthias Ringwald     return bass_util_pa_sync_state_and_subgroups_in_valid_range(buffer+pos, buffer_size-pos);
3075deb0bb6SMatthias Ringwald }
3085deb0bb6SMatthias Ringwald 
bass_server_add_source_from_buffer(uint8_t * buffer,uint16_t buffer_size,bass_server_source_t * source)3095deb0bb6SMatthias Ringwald static void bass_server_add_source_from_buffer(uint8_t *buffer, uint16_t buffer_size, bass_server_source_t * source){
3105deb0bb6SMatthias Ringwald     UNUSED(buffer_size);
3115deb0bb6SMatthias Ringwald 
312c4f227dfSMilanka Ringwald     source->update_counter = bass_server_get_next_update_counter();
3135deb0bb6SMatthias Ringwald     source->in_use = true;
3145deb0bb6SMatthias Ringwald 
3156e96f7c7SMatthias Ringwald     bass_util_source_data_parse(buffer, buffer_size, &source->data, false);
3165deb0bb6SMatthias Ringwald }
3175deb0bb6SMatthias Ringwald 
bass_server_pa_synchronized(bass_server_source_t * source)318c4f227dfSMilanka Ringwald static bool bass_server_pa_synchronized(bass_server_source_t * source){
3195deb0bb6SMatthias Ringwald     return source->data.pa_sync_state == LE_AUDIO_PA_SYNC_STATE_SYNCHRONIZED_TO_PA;
3205deb0bb6SMatthias Ringwald }
3215deb0bb6SMatthias Ringwald 
3225deb0bb6SMatthias Ringwald 
bass_server_bis_synchronized(bass_server_source_t * source)323c4f227dfSMilanka Ringwald static bool bass_server_bis_synchronized(bass_server_source_t * source){
3245deb0bb6SMatthias Ringwald     uint8_t i;
3255deb0bb6SMatthias Ringwald     for (i = 0; i < source->data.subgroups_num; i++){
3265deb0bb6SMatthias Ringwald         if ((source->data.subgroups[i].bis_sync_state > 0) && (source->data.subgroups[i].bis_sync_state < 0xFFFFFFFF)){
3275deb0bb6SMatthias Ringwald             return true;
3285deb0bb6SMatthias Ringwald         }
3295deb0bb6SMatthias Ringwald     }
3305deb0bb6SMatthias Ringwald     return false;
3315deb0bb6SMatthias Ringwald }
3325deb0bb6SMatthias Ringwald 
3335deb0bb6SMatthias Ringwald 
bass_server_reset_source(bass_server_source_t * source)334c4f227dfSMilanka Ringwald static void bass_server_reset_source(bass_server_source_t * source){
3355deb0bb6SMatthias Ringwald     source->in_use = false;
3365deb0bb6SMatthias Ringwald     source->data.address_type = BD_ADDR_TYPE_LE_PUBLIC;
3375deb0bb6SMatthias Ringwald     memset(source->data.address, 0, sizeof(source->data.address));
3385deb0bb6SMatthias Ringwald     source->data.adv_sid = 0;
3395deb0bb6SMatthias Ringwald     source->data.broadcast_id = 0;
3405deb0bb6SMatthias Ringwald     source->data.pa_sync = LE_AUDIO_PA_SYNC_DO_NOT_SYNCHRONIZE_TO_PA;
3415deb0bb6SMatthias Ringwald     source->data.pa_sync_state = LE_AUDIO_PA_SYNC_STATE_NOT_SYNCHRONIZED_TO_PA;
3425deb0bb6SMatthias Ringwald     source->big_encryption = LE_AUDIO_BIG_ENCRYPTION_NOT_ENCRYPTED;
3435deb0bb6SMatthias Ringwald     memset(source->bad_code, 0, sizeof(source->bad_code));
3445deb0bb6SMatthias Ringwald     source->data.pa_interval = 0;
3455deb0bb6SMatthias Ringwald     source->data.subgroups_num = 0;
3465deb0bb6SMatthias Ringwald     memset(source->data.subgroups, 0, sizeof(source->data.subgroups));
3475deb0bb6SMatthias Ringwald }
3485deb0bb6SMatthias Ringwald 
bass_server_reset_client_long_write_buffer(bass_server_connection_t * client)3497f95ef04SMilanka Ringwald static void bass_server_reset_client_long_write_buffer(bass_server_connection_t * client){
3505deb0bb6SMatthias Ringwald     memset(client->long_write_buffer, 0, sizeof(client->long_write_buffer));
3515deb0bb6SMatthias Ringwald     client->long_write_value_size = 0;
3525deb0bb6SMatthias Ringwald }
3535deb0bb6SMatthias Ringwald 
bass_server_write_callback(hci_con_handle_t con_handle,uint16_t attribute_handle,uint16_t transaction_mode,uint16_t offset,uint8_t * buffer,uint16_t buffer_size)354c4f227dfSMilanka Ringwald static int bass_server_write_callback(hci_con_handle_t con_handle, uint16_t attribute_handle, uint16_t transaction_mode, uint16_t offset, uint8_t *buffer, uint16_t buffer_size){
355c4f227dfSMilanka Ringwald     // printf("bass_server_write_callback con_handle 0x%02x, attr_handle 0x%02x \n", con_handle, attribute_handle);
3565deb0bb6SMatthias Ringwald     if (attribute_handle != 0 && attribute_handle != bass_audio_scan_control_point_handle){
357c4f227dfSMilanka Ringwald         bass_server_source_t * source = bass_server_find_receive_state_for_client_configuration_handle(attribute_handle);
3585deb0bb6SMatthias Ringwald         if (source){
3595deb0bb6SMatthias Ringwald             source->bass_receive_state_client_configuration = little_endian_read_16(buffer, 0);
360c4f227dfSMilanka Ringwald             bass_server_register_con_handle(con_handle, source->bass_receive_state_client_configuration);
3615deb0bb6SMatthias Ringwald         }
3625deb0bb6SMatthias Ringwald         return 0;
3635deb0bb6SMatthias Ringwald     }
3645deb0bb6SMatthias Ringwald 
3657f95ef04SMilanka Ringwald     bass_server_connection_t * connection = bass_server_find_client_for_con_handle(con_handle);
3667f95ef04SMilanka Ringwald     if (connection == NULL){
3675deb0bb6SMatthias Ringwald         return ATT_ERROR_WRITE_REQUEST_REJECTED;
3685deb0bb6SMatthias Ringwald     }
3695deb0bb6SMatthias Ringwald 
3705deb0bb6SMatthias Ringwald     uint16_t total_value_len = buffer_size + offset;
3715deb0bb6SMatthias Ringwald 
3725deb0bb6SMatthias Ringwald     switch (transaction_mode){
3735deb0bb6SMatthias Ringwald         case ATT_TRANSACTION_MODE_NONE:
3747f95ef04SMilanka Ringwald             if (buffer_size > sizeof(connection->long_write_buffer)){
3757f95ef04SMilanka Ringwald                 bass_server_reset_client_long_write_buffer(connection);
3765deb0bb6SMatthias Ringwald                 return ATT_ERROR_WRITE_REQUEST_REJECTED;
3775deb0bb6SMatthias Ringwald             }
3787f95ef04SMilanka Ringwald             memcpy(&connection->long_write_buffer[0], buffer, buffer_size);
3797f95ef04SMilanka Ringwald             connection->long_write_value_size = total_value_len;
3805deb0bb6SMatthias Ringwald             break;
3815deb0bb6SMatthias Ringwald 
3825deb0bb6SMatthias Ringwald         case ATT_TRANSACTION_MODE_ACTIVE:
3837f95ef04SMilanka Ringwald             if (total_value_len > sizeof(connection->long_write_buffer)){
3847f95ef04SMilanka Ringwald                 bass_server_reset_client_long_write_buffer(connection);
3855deb0bb6SMatthias Ringwald                 return ATT_ERROR_WRITE_REQUEST_REJECTED;
3865deb0bb6SMatthias Ringwald             }
3875deb0bb6SMatthias Ringwald             // allow overlapped and/or mixed order chunks
3887f95ef04SMilanka Ringwald             connection->long_write_attribute_handle = attribute_handle;
3895deb0bb6SMatthias Ringwald 
3907f95ef04SMilanka Ringwald             memcpy(&connection->long_write_buffer[offset], buffer, buffer_size);
3917f95ef04SMilanka Ringwald             if (total_value_len > connection->long_write_value_size){
3927f95ef04SMilanka Ringwald                 connection->long_write_value_size = total_value_len;
3935deb0bb6SMatthias Ringwald             }
3945deb0bb6SMatthias Ringwald             return 0;
3955deb0bb6SMatthias Ringwald 
3965deb0bb6SMatthias Ringwald         case ATT_TRANSACTION_MODE_VALIDATE:
3975deb0bb6SMatthias Ringwald             return 0;
3985deb0bb6SMatthias Ringwald 
3995deb0bb6SMatthias Ringwald         case ATT_TRANSACTION_MODE_EXECUTE:
4007f95ef04SMilanka Ringwald             attribute_handle = connection->long_write_attribute_handle;
4015deb0bb6SMatthias Ringwald             break;
4025deb0bb6SMatthias Ringwald 
4035deb0bb6SMatthias Ringwald         default:
4045deb0bb6SMatthias Ringwald             return 0;
4055deb0bb6SMatthias Ringwald     }
4065deb0bb6SMatthias Ringwald 
4075deb0bb6SMatthias Ringwald     if (attribute_handle == bass_audio_scan_control_point_handle){
4087f95ef04SMilanka Ringwald         if (connection->long_write_value_size < 2){
4095deb0bb6SMatthias Ringwald             return ATT_ERROR_WRITE_REQUEST_REJECTED;
4105deb0bb6SMatthias Ringwald         }
4115deb0bb6SMatthias Ringwald 
4127f95ef04SMilanka Ringwald         bass_opcode_t opcode = (bass_opcode_t)connection->long_write_buffer[0];
4137f95ef04SMilanka Ringwald         uint8_t  *remote_data = &connection->long_write_buffer[1];
4147f95ef04SMilanka Ringwald         uint16_t remote_data_size = connection->long_write_value_size - 1;
4155deb0bb6SMatthias Ringwald 
4165deb0bb6SMatthias Ringwald         bass_server_source_t * source;
4175deb0bb6SMatthias Ringwald         uint8_t broadcast_code[16];
4185deb0bb6SMatthias Ringwald         switch (opcode){
4195deb0bb6SMatthias Ringwald             case BASS_OPCODE_REMOTE_SCAN_STOPPED:
4205deb0bb6SMatthias Ringwald                 if (remote_data_size != 1){
4215deb0bb6SMatthias Ringwald                     return ATT_ERROR_WRITE_REQUEST_REJECTED;
4225deb0bb6SMatthias Ringwald                 }
423c4f227dfSMilanka Ringwald                 bass_server_source_emit_scan_stoped(con_handle);
4245deb0bb6SMatthias Ringwald                 break;
4255deb0bb6SMatthias Ringwald 
4265deb0bb6SMatthias Ringwald             case BASS_OPCODE_REMOTE_SCAN_STARTED:
4275deb0bb6SMatthias Ringwald                 if (remote_data_size != 1){
4285deb0bb6SMatthias Ringwald                     return ATT_ERROR_WRITE_REQUEST_REJECTED;
4295deb0bb6SMatthias Ringwald                 }
430c4f227dfSMilanka Ringwald                 bass_server_source_emit_scan_started(con_handle);
4315deb0bb6SMatthias Ringwald                 break;
4325deb0bb6SMatthias Ringwald 
4335deb0bb6SMatthias Ringwald             case BASS_OPCODE_ADD_SOURCE:
4346e96f7c7SMatthias Ringwald                 if (!bass_util_source_buffer_in_valid_range(remote_data, remote_data_size)){
4355deb0bb6SMatthias Ringwald                     return ATT_ERROR_WRITE_REQUEST_REJECTED;
4365deb0bb6SMatthias Ringwald                 }
437c4f227dfSMilanka Ringwald                 source = bass_server_find_empty_or_last_used_source();
4385deb0bb6SMatthias Ringwald                 btstack_assert(source != NULL);
4395deb0bb6SMatthias Ringwald                 log_info("add source %d", source->source_id);
4405deb0bb6SMatthias Ringwald                 bass_server_add_source_from_buffer(remote_data, remote_data_size, source);
441c4f227dfSMilanka Ringwald                 bass_server_source_emit_source_added(con_handle, source);
4425deb0bb6SMatthias Ringwald                 // server needs to trigger notification
4435deb0bb6SMatthias Ringwald                 break;
4445deb0bb6SMatthias Ringwald 
4455deb0bb6SMatthias Ringwald             case BASS_OPCODE_MODIFY_SOURCE:
446c4f227dfSMilanka Ringwald                 if (!bass_server_source_remote_modify_source_buffer_valid(remote_data, remote_data_size)){
4475deb0bb6SMatthias Ringwald                     return ATT_ERROR_WRITE_REQUEST_REJECTED;
4485deb0bb6SMatthias Ringwald                 }
4495deb0bb6SMatthias Ringwald 
450c4f227dfSMilanka Ringwald                 source = bass_server_find_source_for_source_id(remote_data[0]);
4515deb0bb6SMatthias Ringwald                 if (source == NULL){
4525deb0bb6SMatthias Ringwald                     return BASS_ERROR_CODE_INVALID_SOURCE_ID;
4535deb0bb6SMatthias Ringwald                 }
4546e96f7c7SMatthias Ringwald                 bass_util_pa_info_and_subgroups_parse(remote_data + 1, remote_data_size - 1, &source->data, false);
455c4f227dfSMilanka Ringwald                 bass_server_source_emit_source_modified(con_handle, source);
4565deb0bb6SMatthias Ringwald                 // server needs to trigger notification
4575deb0bb6SMatthias Ringwald                 break;
4585deb0bb6SMatthias Ringwald 
4595deb0bb6SMatthias Ringwald             case BASS_OPCODE_SET_BROADCAST_CODE:
4605deb0bb6SMatthias Ringwald                 if (remote_data_size != 17){
4615deb0bb6SMatthias Ringwald                     return ATT_ERROR_WRITE_REQUEST_REJECTED;
4625deb0bb6SMatthias Ringwald                 }
4635deb0bb6SMatthias Ringwald 
464c4f227dfSMilanka Ringwald                 source = bass_server_find_source_for_source_id(remote_data[0]);
4655deb0bb6SMatthias Ringwald                 if (source == NULL){
4665deb0bb6SMatthias Ringwald                     return BASS_ERROR_CODE_INVALID_SOURCE_ID;
4675deb0bb6SMatthias Ringwald                 }
4685deb0bb6SMatthias Ringwald                 reverse_128(&remote_data[1], broadcast_code);
469c4f227dfSMilanka Ringwald                 bass_server_source_emit_broadcast_code(con_handle, source->source_id, broadcast_code);
4705deb0bb6SMatthias Ringwald                 break;
4715deb0bb6SMatthias Ringwald 
4725deb0bb6SMatthias Ringwald             case BASS_OPCODE_REMOVE_SOURCE:
4735deb0bb6SMatthias Ringwald                 if (remote_data_size != 1){
4745deb0bb6SMatthias Ringwald                     return ATT_ERROR_WRITE_REQUEST_REJECTED;
4755deb0bb6SMatthias Ringwald                 }
476c4f227dfSMilanka Ringwald                 source = bass_server_find_source_for_source_id(remote_data[0]);
4775deb0bb6SMatthias Ringwald                 if (source == NULL){
4785deb0bb6SMatthias Ringwald                     return BASS_ERROR_CODE_INVALID_SOURCE_ID;
4795deb0bb6SMatthias Ringwald                 }
4805deb0bb6SMatthias Ringwald 
481c4f227dfSMilanka Ringwald                 if (bass_server_pa_synchronized(source)){
4825deb0bb6SMatthias Ringwald                     log_info("remove source %d rejected, PA synchronised", source->source_id);
4835deb0bb6SMatthias Ringwald                     return 0;
4845deb0bb6SMatthias Ringwald                 }
4855deb0bb6SMatthias Ringwald 
486c4f227dfSMilanka Ringwald                 if (bass_server_bis_synchronized(source)){
4875deb0bb6SMatthias Ringwald                     log_info("remove source %d rejected, BIS synchronised", source->source_id);
4885deb0bb6SMatthias Ringwald                     return 0;
4895deb0bb6SMatthias Ringwald                 }
4905deb0bb6SMatthias Ringwald 
491c4f227dfSMilanka Ringwald                 bass_server_reset_source(source);
4925deb0bb6SMatthias Ringwald                 broadcast_audio_scan_service_server_set_pa_sync_state(source->source_id, LE_AUDIO_PA_SYNC_STATE_NOT_SYNCHRONIZED_TO_PA);
493c4f227dfSMilanka Ringwald                 bass_server_source_emit_source_deleted(con_handle, source);
4945deb0bb6SMatthias Ringwald                 break;
4955deb0bb6SMatthias Ringwald 
4965deb0bb6SMatthias Ringwald             default:
4977f95ef04SMilanka Ringwald                 bass_server_reset_client_long_write_buffer(connection);
4985deb0bb6SMatthias Ringwald                 return BASS_ERROR_CODE_OPCODE_NOT_SUPPORTED;
4995deb0bb6SMatthias Ringwald         }
5007f95ef04SMilanka Ringwald         bass_server_reset_client_long_write_buffer(connection);
5015deb0bb6SMatthias Ringwald     }
5025deb0bb6SMatthias Ringwald     return 0;
5035deb0bb6SMatthias Ringwald }
5045deb0bb6SMatthias Ringwald 
bass_server_packet_handler(uint8_t packet_type,uint16_t channel,uint8_t * packet,uint16_t size)505c4f227dfSMilanka Ringwald static void bass_server_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
5065deb0bb6SMatthias Ringwald     UNUSED(channel);
5075deb0bb6SMatthias Ringwald     UNUSED(packet);
5085deb0bb6SMatthias Ringwald     UNUSED(size);
5095deb0bb6SMatthias Ringwald 
5105deb0bb6SMatthias Ringwald     if (packet_type != HCI_EVENT_PACKET){
5115deb0bb6SMatthias Ringwald         return;
5125deb0bb6SMatthias Ringwald     }
5135deb0bb6SMatthias Ringwald 
5145deb0bb6SMatthias Ringwald     hci_con_handle_t con_handle;
5157f95ef04SMilanka Ringwald     bass_server_connection_t * connection;
5165deb0bb6SMatthias Ringwald 
5175deb0bb6SMatthias Ringwald     switch (hci_event_packet_get_type(packet)) {
5185deb0bb6SMatthias Ringwald         case HCI_EVENT_DISCONNECTION_COMPLETE:
5195deb0bb6SMatthias Ringwald             con_handle = hci_event_disconnection_complete_get_connection_handle(packet);
5205deb0bb6SMatthias Ringwald 
5217f95ef04SMilanka Ringwald             connection = bass_server_find_client_for_con_handle(con_handle);
5227f95ef04SMilanka Ringwald             if (connection == NULL){
5235deb0bb6SMatthias Ringwald                 break;
5245deb0bb6SMatthias Ringwald             }
5255deb0bb6SMatthias Ringwald 
5267f95ef04SMilanka Ringwald             memset(connection, 0, sizeof(bass_server_connection_t));
5277f95ef04SMilanka Ringwald             connection->con_handle = HCI_CON_HANDLE_INVALID;
5285deb0bb6SMatthias Ringwald             break;
5295deb0bb6SMatthias Ringwald         default:
5305deb0bb6SMatthias Ringwald             break;
5315deb0bb6SMatthias Ringwald     }
5325deb0bb6SMatthias Ringwald }
5335deb0bb6SMatthias Ringwald 
broadcast_audio_scan_service_server_init(const uint8_t sources_num,bass_server_source_t * sources,const uint8_t clients_num,bass_server_connection_t * clients)5347f95ef04SMilanka Ringwald void broadcast_audio_scan_service_server_init(const uint8_t sources_num, bass_server_source_t * sources, const uint8_t clients_num, bass_server_connection_t * clients){
5355deb0bb6SMatthias Ringwald     // get service handle range
5365deb0bb6SMatthias Ringwald     btstack_assert(sources_num != 0);
5375deb0bb6SMatthias Ringwald     btstack_assert(clients_num != 0);
5385deb0bb6SMatthias Ringwald 
5395deb0bb6SMatthias Ringwald     uint16_t start_handle = 0;
5405deb0bb6SMatthias Ringwald     uint16_t end_handle   = 0xffff;
5415deb0bb6SMatthias Ringwald     bool service_found = gatt_server_get_handle_range_for_service_with_uuid16(ORG_BLUETOOTH_SERVICE_BROADCAST_AUDIO_SCAN_SERVICE, &start_handle, &end_handle);
5425deb0bb6SMatthias Ringwald     btstack_assert(service_found);
5435deb0bb6SMatthias Ringwald 
5445deb0bb6SMatthias Ringwald     UNUSED(service_found);
5455deb0bb6SMatthias Ringwald 
5465deb0bb6SMatthias Ringwald     bass_audio_scan_control_point_handle = gatt_server_get_value_handle_for_characteristic_with_uuid16(start_handle, end_handle, ORG_BLUETOOTH_CHARACTERISTIC_BROADCAST_AUDIO_SCAN_CONTROL_POINT);
5475deb0bb6SMatthias Ringwald     bass_sources_num = 0;
5485deb0bb6SMatthias Ringwald     bass_logic_time = 0;
5495deb0bb6SMatthias Ringwald     bass_sources = sources;
5505deb0bb6SMatthias Ringwald 
5515deb0bb6SMatthias Ringwald #ifdef ENABLE_TESTING_SUPPORT
5525deb0bb6SMatthias Ringwald     printf("BASS 0x%02x - 0x%02x \n", start_handle, end_handle);
5535deb0bb6SMatthias Ringwald #endif
5545deb0bb6SMatthias Ringwald     uint16_t start_chr_handle = start_handle;
5555deb0bb6SMatthias Ringwald     while ( (start_chr_handle < end_handle) && (bass_sources_num < sources_num )) {
5565deb0bb6SMatthias Ringwald         uint16_t chr_value_handle = gatt_server_get_value_handle_for_characteristic_with_uuid16(start_chr_handle, end_handle, ORG_BLUETOOTH_CHARACTERISTIC_BROADCAST_RECEIVE_STATE);
5575deb0bb6SMatthias Ringwald         uint16_t chr_client_configuration_handle = gatt_server_get_client_configuration_handle_for_characteristic_with_uuid16(start_chr_handle, end_handle, ORG_BLUETOOTH_CHARACTERISTIC_BROADCAST_RECEIVE_STATE);
5585deb0bb6SMatthias Ringwald 
5595deb0bb6SMatthias Ringwald         if (chr_value_handle == 0){
5605deb0bb6SMatthias Ringwald             break;
5615deb0bb6SMatthias Ringwald         }
5625deb0bb6SMatthias Ringwald         bass_server_source_t * source = &bass_sources[bass_sources_num];
563c4f227dfSMilanka Ringwald         bass_server_reset_source(source);
5645deb0bb6SMatthias Ringwald 
5655deb0bb6SMatthias Ringwald         source->source_id = bass_sources_num;
566c4f227dfSMilanka Ringwald         source->update_counter = bass_server_get_next_update_counter();
5675deb0bb6SMatthias Ringwald         source->bass_receive_state_client_configuration = 0;
5685deb0bb6SMatthias Ringwald 
5695deb0bb6SMatthias Ringwald         source->bass_receive_state_handle = chr_value_handle;
5705deb0bb6SMatthias Ringwald         source->bass_receive_state_client_configuration_handle = chr_client_configuration_handle;
5715deb0bb6SMatthias Ringwald 
5725deb0bb6SMatthias Ringwald #ifdef ENABLE_TESTING_SUPPORT
5735deb0bb6SMatthias Ringwald     printf("    bass_receive_state_%d                 0x%02x \n", bass_sources_num, source->bass_receive_state_handle);
5745deb0bb6SMatthias Ringwald     printf("    bass_receive_state_%d CCC             0x%02x \n", bass_sources_num, source->bass_receive_state_client_configuration_handle);
5755deb0bb6SMatthias Ringwald #endif
5765deb0bb6SMatthias Ringwald 
5775deb0bb6SMatthias Ringwald         start_chr_handle = chr_client_configuration_handle + 1;
5785deb0bb6SMatthias Ringwald         bass_sources_num++;
5795deb0bb6SMatthias Ringwald     }
5805deb0bb6SMatthias Ringwald 
5815deb0bb6SMatthias Ringwald     bass_clients_num = clients_num;
5825deb0bb6SMatthias Ringwald     bass_clients = clients;
5837f95ef04SMilanka Ringwald     memset(bass_clients, 0, sizeof(bass_server_connection_t) * bass_clients_num);
5845deb0bb6SMatthias Ringwald     uint8_t i;
5855deb0bb6SMatthias Ringwald     for (i = 0; i < bass_clients_num; i++){
5865deb0bb6SMatthias Ringwald         bass_clients[i].con_handle = HCI_CON_HANDLE_INVALID;
5875deb0bb6SMatthias Ringwald     }
5885deb0bb6SMatthias Ringwald 
5895deb0bb6SMatthias Ringwald     log_info("Found BASS service 0x%02x-0x%02x (num sources %d)", start_handle, end_handle, bass_sources_num);
5905deb0bb6SMatthias Ringwald 
5915deb0bb6SMatthias Ringwald     // register service with ATT Server
5925deb0bb6SMatthias Ringwald     broadcast_audio_scan_service.start_handle   = start_handle;
5935deb0bb6SMatthias Ringwald     broadcast_audio_scan_service.end_handle     = end_handle;
594c4f227dfSMilanka Ringwald     broadcast_audio_scan_service.read_callback  = &bass_server_read_callback;
595c4f227dfSMilanka Ringwald     broadcast_audio_scan_service.write_callback = &bass_server_write_callback;
596c4f227dfSMilanka Ringwald     broadcast_audio_scan_service.packet_handler = bass_server_packet_handler;
5975deb0bb6SMatthias Ringwald     att_server_register_service_handler(&broadcast_audio_scan_service);
5985deb0bb6SMatthias Ringwald }
5995deb0bb6SMatthias Ringwald 
broadcast_audio_scan_service_server_register_packet_handler(btstack_packet_handler_t packet_handler)600b973a76fSMilanka Ringwald void broadcast_audio_scan_service_server_register_packet_handler(btstack_packet_handler_t packet_handler){
601b973a76fSMilanka Ringwald     btstack_assert(packet_handler != NULL);
602c4f227dfSMilanka Ringwald     bass_server_event_callback = packet_handler;
6035deb0bb6SMatthias Ringwald }
6045deb0bb6SMatthias Ringwald 
bass_service_can_send_now(void * context)6055deb0bb6SMatthias Ringwald static void bass_service_can_send_now(void * context){
6067f95ef04SMilanka Ringwald     bass_server_connection_t * client = (bass_server_connection_t *) context;
6075deb0bb6SMatthias Ringwald     btstack_assert(client != NULL);
6085deb0bb6SMatthias Ringwald 
6095deb0bb6SMatthias Ringwald     uint8_t source_index;
6105deb0bb6SMatthias Ringwald     for (source_index = 0; source_index < bass_sources_num; source_index++){
6115deb0bb6SMatthias Ringwald         uint8_t task = (1 << source_index);
6125deb0bb6SMatthias Ringwald         if ((client->sources_to_notify & task) != 0){
6135deb0bb6SMatthias Ringwald             client->sources_to_notify &= ~task;
6145deb0bb6SMatthias Ringwald             uint8_t  buffer[BASS_MAX_NOTIFY_BUFFER_SIZE];
6155deb0bb6SMatthias Ringwald             uint16_t bytes_copied = bass_server_copy_source_to_buffer(&bass_sources[source_index], 0, buffer, sizeof(buffer));
6165deb0bb6SMatthias Ringwald             att_server_notify(client->con_handle, bass_sources[source_index].bass_receive_state_handle, &buffer[0], bytes_copied);
6175deb0bb6SMatthias Ringwald             return;
6185deb0bb6SMatthias Ringwald         }
6195deb0bb6SMatthias Ringwald     }
6205deb0bb6SMatthias Ringwald 
6215deb0bb6SMatthias Ringwald     uint8_t i;
6225deb0bb6SMatthias Ringwald     for (i = 0; i < bass_clients_num; i++){
6235deb0bb6SMatthias Ringwald         client = &bass_clients[i];
6245deb0bb6SMatthias Ringwald 
6255deb0bb6SMatthias Ringwald         if (client->sources_to_notify != 0){
6265deb0bb6SMatthias Ringwald             scheduled_tasks_callback.callback = &bass_service_can_send_now;
6275deb0bb6SMatthias Ringwald             scheduled_tasks_callback.context  = (void*) client;
6285deb0bb6SMatthias Ringwald             att_server_register_can_send_now_callback(&scheduled_tasks_callback, client->con_handle);
6295deb0bb6SMatthias Ringwald             return;
6305deb0bb6SMatthias Ringwald         }
6315deb0bb6SMatthias Ringwald     }
6325deb0bb6SMatthias Ringwald }
6335deb0bb6SMatthias Ringwald 
bass_server_set_callback(uint8_t source_index)634c4f227dfSMilanka Ringwald static void bass_server_set_callback(uint8_t source_index){
6355deb0bb6SMatthias Ringwald     // there is only one type of task: notify on source state change
6365deb0bb6SMatthias Ringwald     // as task we register which source is changed, and the change will be propagated to all clients
6375deb0bb6SMatthias Ringwald     uint8_t i;
6385deb0bb6SMatthias Ringwald     uint8_t task = (1 << source_index);
6395deb0bb6SMatthias Ringwald 
6405deb0bb6SMatthias Ringwald     uint8_t scheduled_tasks = 0;
6415deb0bb6SMatthias Ringwald 
6425deb0bb6SMatthias Ringwald     for (i = 0; i < bass_clients_num; i++){
6437f95ef04SMilanka Ringwald         bass_server_connection_t * connection = &bass_clients[i];
6445deb0bb6SMatthias Ringwald 
6457f95ef04SMilanka Ringwald         if (connection->con_handle == HCI_CON_HANDLE_INVALID){
6467f95ef04SMilanka Ringwald             connection->sources_to_notify &= ~task;
6475deb0bb6SMatthias Ringwald             return;
6485deb0bb6SMatthias Ringwald         }
6495deb0bb6SMatthias Ringwald 
6507f95ef04SMilanka Ringwald         scheduled_tasks |= connection->sources_to_notify;
6517f95ef04SMilanka Ringwald         connection->sources_to_notify |= task;
6525deb0bb6SMatthias Ringwald 
6535deb0bb6SMatthias Ringwald         if (scheduled_tasks == 0){
6545deb0bb6SMatthias Ringwald             scheduled_tasks_callback.callback = &bass_service_can_send_now;
6557f95ef04SMilanka Ringwald             scheduled_tasks_callback.context  = (void*) connection;
6567f95ef04SMilanka Ringwald             att_server_register_can_send_now_callback(&scheduled_tasks_callback, connection->con_handle);
6575deb0bb6SMatthias Ringwald         }
6585deb0bb6SMatthias Ringwald     }
6595deb0bb6SMatthias Ringwald }
6605deb0bb6SMatthias Ringwald 
broadcast_audio_scan_service_server_set_pa_sync_state(uint8_t source_index,le_audio_pa_sync_state_t sync_state)6615deb0bb6SMatthias Ringwald void broadcast_audio_scan_service_server_set_pa_sync_state(uint8_t source_index, le_audio_pa_sync_state_t sync_state){
6625deb0bb6SMatthias Ringwald     btstack_assert(source_index < bass_sources_num);
6635deb0bb6SMatthias Ringwald 
6645deb0bb6SMatthias Ringwald     bass_server_source_t * source = &bass_sources[source_index];
6655deb0bb6SMatthias Ringwald     source->data.pa_sync_state = sync_state;
6665deb0bb6SMatthias Ringwald 
6675deb0bb6SMatthias Ringwald     if (source->bass_receive_state_client_configuration != 0){
668c4f227dfSMilanka Ringwald         bass_server_set_callback(source_index);
6695deb0bb6SMatthias Ringwald     }
6705deb0bb6SMatthias Ringwald }
6715deb0bb6SMatthias Ringwald 
broadcast_audio_scan_service_server_add_source(const bass_source_data_t * source_data,uint8_t * source_index)6728d085b97SMatthias Ringwald void broadcast_audio_scan_service_server_add_source(const bass_source_data_t *source_data, uint8_t * source_index){
673c4f227dfSMilanka Ringwald     *source_index = bass_server_find_empty_or_last_used_source_index();
6745deb0bb6SMatthias Ringwald     if (*source_index == BASS_INVALID_SOURCE_INDEX){
6755deb0bb6SMatthias Ringwald         return;
6765deb0bb6SMatthias Ringwald     }
6775deb0bb6SMatthias Ringwald     bass_server_source_t * last_used_source = &bass_sources[*source_index];
678c4f227dfSMilanka Ringwald     last_used_source->update_counter = bass_server_get_next_update_counter();
6795deb0bb6SMatthias Ringwald     last_used_source->in_use = true;
6805deb0bb6SMatthias Ringwald     last_used_source->source_id = *source_index;
6818d085b97SMatthias Ringwald     memcpy(&last_used_source->data, source_data, sizeof(bass_source_data_t));
6825deb0bb6SMatthias Ringwald }
6835deb0bb6SMatthias Ringwald 
broadcast_audio_scan_service_server_deinit(void)6845deb0bb6SMatthias Ringwald void broadcast_audio_scan_service_server_deinit(void){
6857f95ef04SMilanka Ringwald     bass_server_event_callback = NULL;
6865deb0bb6SMatthias Ringwald }
687