xref: /btstack/src/le-audio/gatt-service/broadcast_audio_scan_service_util.c (revision 5d948380659552a5b126327382a4073a5ed10338)
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_util.h"
515deb0bb6SMatthias Ringwald #include "le-audio/le_audio.h"
525deb0bb6SMatthias Ringwald #include "le-audio/le_audio_util.h"
535deb0bb6SMatthias Ringwald 
545deb0bb6SMatthias Ringwald #ifdef ENABLE_TESTING_SUPPORT
555deb0bb6SMatthias Ringwald #include <stdio.h>
565deb0bb6SMatthias Ringwald #endif
575deb0bb6SMatthias Ringwald 
585deb0bb6SMatthias Ringwald // offset gives position into fully serialized bass record
bass_util_source_data_header_virtual_memcpy(const bass_source_data_t * data,uint16_t * source_offset,uint16_t buffer_offset,uint8_t * buffer,uint16_t buffer_size)596e96f7c7SMatthias Ringwald uint16_t bass_util_source_data_header_virtual_memcpy(const bass_source_data_t * data, uint16_t *source_offset, uint16_t buffer_offset, uint8_t * buffer, uint16_t buffer_size){
605deb0bb6SMatthias Ringwald     uint16_t stored_bytes = 0;
615deb0bb6SMatthias Ringwald     uint8_t  field_data[16];
625deb0bb6SMatthias Ringwald 
635deb0bb6SMatthias Ringwald     field_data[0] = (uint8_t)data->address_type;
6454461c80SMatthias Ringwald     stored_bytes += le_audio_util_virtual_memcpy_helper(field_data, 1, *source_offset, buffer, buffer_size,
6554461c80SMatthias Ringwald                                                         buffer_offset);
665deb0bb6SMatthias Ringwald     (*source_offset)++;
675deb0bb6SMatthias Ringwald 
685deb0bb6SMatthias Ringwald     reverse_bd_addr(data->address, &field_data[0]);
6954461c80SMatthias Ringwald     stored_bytes += le_audio_util_virtual_memcpy_helper(field_data, 6, *source_offset, buffer, buffer_size,
7054461c80SMatthias Ringwald                                                         buffer_offset);
715deb0bb6SMatthias Ringwald     (*source_offset) += 6;
725deb0bb6SMatthias Ringwald 
735deb0bb6SMatthias Ringwald     field_data[0] = data->adv_sid;
7454461c80SMatthias Ringwald     stored_bytes += le_audio_util_virtual_memcpy_helper(field_data, 1, *source_offset, buffer, buffer_size,
7554461c80SMatthias Ringwald                                                         buffer_offset);
765deb0bb6SMatthias Ringwald     (*source_offset)++;
775deb0bb6SMatthias Ringwald 
785deb0bb6SMatthias Ringwald     little_endian_store_24(field_data, 0, data->broadcast_id);
7954461c80SMatthias Ringwald     stored_bytes += le_audio_util_virtual_memcpy_helper(field_data, 3, *source_offset, buffer, buffer_size,
8054461c80SMatthias Ringwald                                                         buffer_offset);
815deb0bb6SMatthias Ringwald     (*source_offset) += 3;
825deb0bb6SMatthias Ringwald 
835deb0bb6SMatthias Ringwald     return stored_bytes;
845deb0bb6SMatthias Ringwald }
855deb0bb6SMatthias Ringwald 
865deb0bb6SMatthias Ringwald // offset gives position into fully serialized bass record
875deb0bb6SMatthias Ringwald uint16_t
bass_util_source_data_subgroups_virtual_memcpy(const bass_source_data_t * data,bool use_state_fields,uint16_t * source_offset,uint16_t buffer_offset,uint8_t * buffer,uint16_t buffer_size)886e96f7c7SMatthias Ringwald bass_util_source_data_subgroups_virtual_memcpy(const bass_source_data_t *data, bool use_state_fields, uint16_t *source_offset,
895deb0bb6SMatthias Ringwald                                                uint16_t buffer_offset, uint8_t *buffer, uint16_t buffer_size) {
905deb0bb6SMatthias Ringwald     uint16_t stored_bytes = 0;
915deb0bb6SMatthias Ringwald     uint8_t  field_data[16];
925deb0bb6SMatthias Ringwald 
935deb0bb6SMatthias Ringwald     field_data[0] = (uint8_t)data->subgroups_num;
9454461c80SMatthias Ringwald     stored_bytes += le_audio_util_virtual_memcpy_helper(field_data, 1, *source_offset, buffer, buffer_size,
9554461c80SMatthias Ringwald                                                         buffer_offset);
965deb0bb6SMatthias Ringwald     (*source_offset)++;
975deb0bb6SMatthias Ringwald 
985deb0bb6SMatthias Ringwald     uint8_t i;
995deb0bb6SMatthias Ringwald     for (i = 0; i < data->subgroups_num; i++){
1005deb0bb6SMatthias Ringwald         bass_subgroup_t subgroup = data->subgroups[i];
1015deb0bb6SMatthias Ringwald 
1025deb0bb6SMatthias Ringwald         if (use_state_fields) {
1035deb0bb6SMatthias Ringwald             little_endian_store_32(field_data, 0, subgroup.bis_sync_state);
1045deb0bb6SMatthias Ringwald         } else {
1055deb0bb6SMatthias Ringwald             little_endian_store_32(field_data, 0, subgroup.bis_sync);
1065deb0bb6SMatthias Ringwald         }
10754461c80SMatthias Ringwald         stored_bytes += le_audio_util_virtual_memcpy_helper(field_data, 4, *source_offset, buffer, buffer_size,
10854461c80SMatthias Ringwald                                                             buffer_offset);
1095deb0bb6SMatthias Ringwald         (*source_offset) += 4;
11054461c80SMatthias Ringwald         stored_bytes += le_audio_util_metadata_virtual_memcpy(&subgroup.metadata, subgroup.metadata_length,
11154461c80SMatthias Ringwald                                                               source_offset, buffer, buffer_size, buffer_offset);
1125deb0bb6SMatthias Ringwald     }
1135deb0bb6SMatthias Ringwald     return stored_bytes;
1145deb0bb6SMatthias Ringwald }
1155deb0bb6SMatthias Ringwald 
bass_util_pa_sync_state_and_subgroups_in_valid_range(const uint8_t * buffer,uint16_t buffer_size)11618f91b9cSMatthias Ringwald bool bass_util_pa_sync_state_and_subgroups_in_valid_range(const uint8_t *buffer, uint16_t buffer_size){
1175deb0bb6SMatthias Ringwald     uint8_t pos = 0;
1185deb0bb6SMatthias Ringwald     // pa_sync_state
1195deb0bb6SMatthias Ringwald     uint8_t pa_sync_state = buffer[pos++];
1205deb0bb6SMatthias Ringwald     if (pa_sync_state >= (uint8_t)LE_AUDIO_PA_SYNC_STATE_RFU){
1215deb0bb6SMatthias Ringwald         log_info("Unexpected pa_sync_state 0x%02X", pa_sync_state);
1225deb0bb6SMatthias Ringwald         return false;
1235deb0bb6SMatthias Ringwald     }
1245deb0bb6SMatthias Ringwald 
1255deb0bb6SMatthias Ringwald     // pa_interval
1265deb0bb6SMatthias Ringwald     pos += 2;
1275deb0bb6SMatthias Ringwald     uint8_t num_subgroups = buffer[pos++];
1285deb0bb6SMatthias Ringwald     if (num_subgroups > BASS_SUBGROUPS_MAX_NUM){
1295deb0bb6SMatthias Ringwald         log_info("Number of subgroups %u exceedes maximum %u", num_subgroups, BASS_SUBGROUPS_MAX_NUM);
1305deb0bb6SMatthias Ringwald         return false;
1315deb0bb6SMatthias Ringwald     }
1325deb0bb6SMatthias Ringwald 
1335deb0bb6SMatthias Ringwald     // If a BIS_Sync parameter value is not 0xFFFFFFFF for a subgroup,
1345deb0bb6SMatthias Ringwald     // and if a BIS_index value written by a client is set to a value of 0b1 in more than one subgroup,
1355deb0bb6SMatthias Ringwald     // the server shall ignore the operation.
1365deb0bb6SMatthias Ringwald     uint8_t i;
1375deb0bb6SMatthias Ringwald     uint32_t mask_total = 0;
1385deb0bb6SMatthias Ringwald     for (i = 0; i < num_subgroups; i++){
1395deb0bb6SMatthias Ringwald         // bis_sync
1405deb0bb6SMatthias Ringwald         uint32_t bis_sync = little_endian_read_32(buffer, pos);
1415deb0bb6SMatthias Ringwald         pos += 4;
1425deb0bb6SMatthias Ringwald 
1435deb0bb6SMatthias Ringwald         if (bis_sync != 0xFFFFFFFF){
1445deb0bb6SMatthias Ringwald             uint32_t mask_add = mask_total + ~bis_sync;
1455deb0bb6SMatthias Ringwald             uint32_t mask_or  = mask_total | ~bis_sync;
1465deb0bb6SMatthias Ringwald             if (mask_add != mask_or){
1475deb0bb6SMatthias Ringwald                 // not disjunct
1485deb0bb6SMatthias Ringwald                 return false;
1495deb0bb6SMatthias Ringwald             }
1505deb0bb6SMatthias Ringwald             mask_total = mask_add;
1515deb0bb6SMatthias Ringwald         }
1525deb0bb6SMatthias Ringwald 
1535deb0bb6SMatthias Ringwald         // check if we can read metadata_length
1545deb0bb6SMatthias Ringwald         if (pos >= buffer_size){
1555deb0bb6SMatthias Ringwald             log_info("Metadata length not specified, subgroup %u", i);
1565deb0bb6SMatthias Ringwald             return false;
1575deb0bb6SMatthias Ringwald         }
1585deb0bb6SMatthias Ringwald 
1595deb0bb6SMatthias Ringwald         uint8_t metadata_length = buffer[pos++];
1605deb0bb6SMatthias Ringwald 
1615deb0bb6SMatthias Ringwald         if ((buffer_size - pos) < metadata_length){
1625deb0bb6SMatthias Ringwald             log_info("Metadata length %u exceedes remaining buffer %u", metadata_length, buffer_size - pos);
1635deb0bb6SMatthias Ringwald             return false;
1645deb0bb6SMatthias Ringwald         }
1655deb0bb6SMatthias Ringwald         // metadata
1665deb0bb6SMatthias Ringwald         pos += metadata_length;
1675deb0bb6SMatthias Ringwald     }
1685deb0bb6SMatthias Ringwald 
1695deb0bb6SMatthias Ringwald     return (pos == buffer_size);
1705deb0bb6SMatthias Ringwald }
1715deb0bb6SMatthias Ringwald 
bass_util_source_buffer_in_valid_range(const uint8_t * buffer,uint16_t buffer_size)17218f91b9cSMatthias Ringwald bool bass_util_source_buffer_in_valid_range(const uint8_t *buffer, uint16_t buffer_size){
1735deb0bb6SMatthias Ringwald     if (buffer_size < 15){
1745deb0bb6SMatthias Ringwald         log_info("Add Source opcode, buffer too small");
1755deb0bb6SMatthias Ringwald         return false;
1765deb0bb6SMatthias Ringwald     }
1775deb0bb6SMatthias Ringwald 
1785deb0bb6SMatthias Ringwald     uint8_t pos = 0;
1795deb0bb6SMatthias Ringwald     // addr type
1805deb0bb6SMatthias Ringwald     uint8_t adv_type = buffer[pos++];
1815deb0bb6SMatthias Ringwald     if (adv_type > (uint8_t)BD_ADDR_TYPE_LE_RANDOM){
1825deb0bb6SMatthias Ringwald         log_info("Unexpected adv_type 0x%02X", adv_type);
1835deb0bb6SMatthias Ringwald         return false;
1845deb0bb6SMatthias Ringwald     }
1855deb0bb6SMatthias Ringwald 
1865deb0bb6SMatthias Ringwald     // address
1875deb0bb6SMatthias Ringwald     pos += 6;
1885deb0bb6SMatthias Ringwald 
1895deb0bb6SMatthias Ringwald     // advertising_sid Range: 0x00-0x0F
1905deb0bb6SMatthias Ringwald     uint8_t advertising_sid = buffer[pos++];
1915deb0bb6SMatthias Ringwald     if (advertising_sid > 0x0F){
1925deb0bb6SMatthias Ringwald         log_info("Advertising sid out of range 0x%02X", advertising_sid);
1935deb0bb6SMatthias Ringwald         return false;
1945deb0bb6SMatthias Ringwald     }
1955deb0bb6SMatthias Ringwald 
1965deb0bb6SMatthias Ringwald     // broadcast_id
1975deb0bb6SMatthias Ringwald     pos += 3;
1985deb0bb6SMatthias Ringwald     return bass_util_pa_sync_state_and_subgroups_in_valid_range(buffer+pos, buffer_size-pos);
1995deb0bb6SMatthias Ringwald }
2005deb0bb6SMatthias Ringwald 
2015deb0bb6SMatthias Ringwald void
bass_util_pa_info_and_subgroups_parse(const uint8_t * buffer,uint16_t buffer_size,bass_source_data_t * source_data,bool is_broadcast_receive_state)20218f91b9cSMatthias Ringwald bass_util_pa_info_and_subgroups_parse(const uint8_t *buffer, uint16_t buffer_size, bass_source_data_t *source_data,
2035deb0bb6SMatthias Ringwald                                       bool is_broadcast_receive_state) {
2045deb0bb6SMatthias Ringwald     UNUSED(buffer_size);
2055deb0bb6SMatthias Ringwald     uint8_t pos = 0;
2065deb0bb6SMatthias Ringwald     // for Broadcast Receive state, we have BIG_Encryption + Bad_Code, while for Add/Modify we have PA_Interval
2075deb0bb6SMatthias Ringwald     if (is_broadcast_receive_state){
2085deb0bb6SMatthias Ringwald         source_data->pa_sync_state = (le_audio_pa_sync_state_t)buffer[pos++];
209*5d948380SMatthias Ringwald         source_data->big_encryption = (le_audio_big_encryption_t) buffer[pos++];
210*5d948380SMatthias Ringwald         if (source_data->big_encryption == LE_AUDIO_BIG_ENCRYPTION_BAD_CODE){
211*5d948380SMatthias Ringwald             memcpy(source_data->bad_code, &buffer[pos], 16);
2125deb0bb6SMatthias Ringwald             pos += 16;
213*5d948380SMatthias Ringwald         } else {
214*5d948380SMatthias Ringwald             memset(source_data->bad_code, 0, 16);
2155deb0bb6SMatthias Ringwald         }
2165deb0bb6SMatthias Ringwald     } else {
2175deb0bb6SMatthias Ringwald         source_data->pa_sync       = (le_audio_pa_sync_t)buffer[pos++];
2185deb0bb6SMatthias Ringwald         source_data->pa_interval = little_endian_read_16(buffer, pos);
2195deb0bb6SMatthias Ringwald         pos += 2;
2205deb0bb6SMatthias Ringwald     }
2215deb0bb6SMatthias Ringwald 
2225deb0bb6SMatthias Ringwald     source_data->subgroups_num = buffer[pos++];
2235deb0bb6SMatthias Ringwald     uint8_t i;
2245deb0bb6SMatthias Ringwald     for (i = 0; i < source_data->subgroups_num; i++){
2255deb0bb6SMatthias Ringwald         // bis_sync / state
2265deb0bb6SMatthias Ringwald         if (is_broadcast_receive_state){
2275deb0bb6SMatthias Ringwald             source_data->subgroups[i].bis_sync_state = little_endian_read_32(buffer, pos);
2285deb0bb6SMatthias Ringwald         } else {
2295deb0bb6SMatthias Ringwald             source_data->subgroups[i].bis_sync = little_endian_read_32(buffer, pos);
2305deb0bb6SMatthias Ringwald         }
2315deb0bb6SMatthias Ringwald         pos += 4;
2325deb0bb6SMatthias Ringwald 
23354461c80SMatthias Ringwald         uint8_t metadata_bytes_read = le_audio_util_metadata_parse(&buffer[pos], buffer_size - pos,
23454461c80SMatthias Ringwald                                                                    &source_data->subgroups[i].metadata);
2355deb0bb6SMatthias Ringwald         source_data->subgroups[i].metadata_length = metadata_bytes_read - 1;
2365deb0bb6SMatthias Ringwald         pos += metadata_bytes_read;
2375deb0bb6SMatthias Ringwald     }
2385deb0bb6SMatthias Ringwald }
2395deb0bb6SMatthias Ringwald 
bass_util_source_data_parse(const uint8_t * buffer,uint16_t buffer_size,bass_source_data_t * source_data,bool is_broadcast_receive_state)24018f91b9cSMatthias Ringwald void bass_util_source_data_parse(const uint8_t *buffer, uint16_t buffer_size, bass_source_data_t *source_data,
2415deb0bb6SMatthias Ringwald                                  bool is_broadcast_receive_state) {
2425deb0bb6SMatthias Ringwald     UNUSED(buffer_size);
2435deb0bb6SMatthias Ringwald     uint8_t pos = 0;
2445deb0bb6SMatthias Ringwald 
2455deb0bb6SMatthias Ringwald     source_data->address_type = (bd_addr_type_t)buffer[pos++];
2465deb0bb6SMatthias Ringwald 
2475deb0bb6SMatthias Ringwald     reverse_bd_addr(&buffer[pos], source_data->address);
2485deb0bb6SMatthias Ringwald     pos += 6;
2495deb0bb6SMatthias Ringwald 
2505deb0bb6SMatthias Ringwald     source_data->adv_sid = buffer[pos++];
2515deb0bb6SMatthias Ringwald 
2525deb0bb6SMatthias Ringwald     source_data->broadcast_id = little_endian_read_24(buffer, pos);
2535deb0bb6SMatthias Ringwald     pos += 3;
2545deb0bb6SMatthias Ringwald 
2556e96f7c7SMatthias Ringwald     bass_util_pa_info_and_subgroups_parse(buffer + pos, buffer_size - pos, source_data, is_broadcast_receive_state);
2565deb0bb6SMatthias Ringwald }