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 *
17 * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
18 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
20 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BLUEKITCHEN
21 * GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
23 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
24 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
25 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
27 * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * SUCH DAMAGE.
29 *
30 */
31
32 #define BTSTACK_FILE__ "le_audio_base_parser.c"
33
34 /**
35 * @title Broadcast Audio Source Endpoint AD Parser
36 */
37
38 #include <string.h>
39
40 #include "bluetooth.h"
41 #include "bluetooth_data_types.h"
42 #include "bluetooth_gatt.h"
43 #include "btstack_util.h"
44 #include "btstack_debug.h"
45 #include "le-audio/le_audio_base_parser.h"
46
47 // precondition: subgroup_offset set
le_audio_base_parser_fetch_subgroup_info(le_audio_base_parser_t * parser)48 static void le_audio_base_parser_fetch_subgroup_info(le_audio_base_parser_t * parser){
49 const uint8_t * buffer = parser->buffer;
50 uint16_t offset = parser->subgroup_offset;
51 parser->bis_count = buffer[offset++];
52 // Codec ID
53 offset += 5;
54 parser->subgroup_codec_specific_configuration_len = buffer[offset++];
55 // Codec Specific Configuration
56 offset += parser->subgroup_codec_specific_configuration_len;
57 parser->subgroup_metadata_len = buffer[offset++];
58 // Metadata
59 offset += parser->subgroup_metadata_len;
60 parser->bis_index = 0;
61 parser->bis_offset =offset;
62 }
63
le_audio_base_parser_init(le_audio_base_parser_t * parser,const uint8_t * buffer,uint16_t size)64 bool le_audio_base_parser_init(le_audio_base_parser_t * parser, const uint8_t * buffer, uint16_t size){
65 memset(parser, 0, sizeof(le_audio_base_parser_t));
66 // check buffer
67 if (size < 8) {
68 return false;
69 }
70 uint16_t offset = 1;
71 if (buffer[offset++] != BLUETOOTH_DATA_TYPE_SERVICE_DATA_16_BIT_UUID) {
72 return false;
73 }
74 if (little_endian_read_16(buffer, offset) != ORG_BLUETOOTH_SERVICE_BASIC_AUDIO_ANNOUNCEMENT_SERVICE){
75 return false;
76 }
77 offset += 5;
78
79 parser->buffer = buffer;
80 parser->size = size;
81
82 parser->subgroup_count = buffer[offset++];
83 parser->subgroup_index = 0;
84
85 // fully validate structure
86 uint8_t i;
87 uint8_t k;
88 for (i=0;i<parser->subgroup_count;i++){
89 if ((offset+8) > size) {
90 return false;
91 }
92 uint8_t num_bis = buffer[offset++];
93 offset += 5;
94 uint8_t config_len = buffer[offset++];
95 offset += config_len;
96 if ((offset+1) > size) {
97 return false;
98 }
99 int8_t meta_len = buffer[offset++];
100 offset += meta_len;
101 if (offset > size) {
102 return false;
103 }
104 for (k=0;k<num_bis;k++){
105 if ((offset+2) > size) {
106 return false;
107 }
108 offset++;
109 config_len = buffer[offset++];
110 offset += config_len;
111 if (offset > size) {
112 return false;
113 }
114 }
115 }
116
117 if (parser->subgroup_count > 0){
118 parser->subgroup_offset = 8;
119 le_audio_base_parser_fetch_subgroup_info(parser);
120 }
121
122 return true;
123 }
124
le_audio_base_parser_get_presentation_delay(le_audio_base_parser_t * parser)125 uint32_t le_audio_base_parser_get_presentation_delay(le_audio_base_parser_t * parser){
126 btstack_assert(parser->buffer != NULL);
127 return little_endian_read_24(parser->buffer, 4);
128 }
129
le_audio_base_parser_subgroup_get_num_bis(le_audio_base_parser_t * parser)130 uint8_t le_audio_base_parser_subgroup_get_num_bis(le_audio_base_parser_t * parser){
131 btstack_assert(parser->subgroup_offset > 0);
132 return parser->bis_count;
133 }
134
le_audio_base_parser_get_num_subgroups(le_audio_base_parser_t * parser)135 uint8_t le_audio_base_parser_get_num_subgroups(le_audio_base_parser_t * parser){
136 btstack_assert(parser->buffer != NULL);
137 return parser->subgroup_count;
138 }
139
le_audio_base_parser_subgroup_get_codec_id(le_audio_base_parser_t * parser)140 const uint8_t * le_audio_base_parser_subgroup_get_codec_id(le_audio_base_parser_t * parser){
141 btstack_assert(parser->subgroup_offset > 0);
142 return &parser->buffer[parser->subgroup_offset];
143 }
144
le_audio_base_parser_subgroup_get_codec_specific_configuration_length(le_audio_base_parser_t * parser)145 uint8_t le_audio_base_parser_subgroup_get_codec_specific_configuration_length(le_audio_base_parser_t * parser){
146 btstack_assert(parser->subgroup_offset > 0);
147 return parser->subgroup_codec_specific_configuration_len;
148 }
149
le_audio_base_parser_subgroup_get_codec_specific_configuration(le_audio_base_parser_t * parser)150 const uint8_t * le_audio_base_parser_subgroup_get_codec_specific_configuration(le_audio_base_parser_t * parser){
151 btstack_assert(parser->subgroup_offset > 0);
152 return &parser->buffer[parser->subgroup_offset + 7];
153 }
154
le_audio_base_parser_subgroup_get_metadata_length(le_audio_base_parser_t * parser)155 uint8_t le_audio_base_parser_subgroup_get_metadata_length(le_audio_base_parser_t * parser){
156 btstack_assert(parser->subgroup_offset > 0);
157 return parser->buffer[parser->subgroup_offset + 7 + parser->subgroup_codec_specific_configuration_len];
158 }
159
le_audio_base_subgroup_parser_get_metadata(le_audio_base_parser_t * parser)160 const uint8_t * le_audio_base_subgroup_parser_get_metadata(le_audio_base_parser_t * parser){
161 btstack_assert(parser->subgroup_offset > 0);
162 return &parser->buffer[parser->subgroup_offset + 7 + parser->subgroup_codec_specific_configuration_len + 1];
163 }
164
le_audio_base_parser_bis_get_index(le_audio_base_parser_t * parser)165 uint8_t le_audio_base_parser_bis_get_index(le_audio_base_parser_t * parser){
166 btstack_assert(parser->bis_offset > 0);
167 return parser->buffer[parser->bis_offset];
168 }
169
le_audio_base_parser_bis_get_codec_specific_configuration_length(le_audio_base_parser_t * parser)170 uint8_t le_audio_base_parser_bis_get_codec_specific_configuration_length(le_audio_base_parser_t * parser){
171 btstack_assert(parser->bis_offset > 0);
172 return parser->buffer[parser->bis_offset + 1];
173 }
174
le_audio_base_bis_parser_get_codec_specific_configuration(le_audio_base_parser_t * parser)175 const uint8_t * le_audio_base_bis_parser_get_codec_specific_configuration(le_audio_base_parser_t * parser){
176 btstack_assert(parser->bis_offset > 0);
177 return &parser->buffer[parser->bis_offset + 2];
178 }
179
le_audio_base_parser_bis_next(le_audio_base_parser_t * parser)180 void le_audio_base_parser_bis_next(le_audio_base_parser_t * parser){
181 btstack_assert(parser->bis_offset > 0);
182 parser->bis_index++;
183 if (parser->bis_index < parser->bis_count){
184 parser->bis_offset += 1 + 1 + parser->buffer[parser->bis_offset+1];
185 } else {
186 parser->bis_offset = 0;
187 }
188 }
189
le_audio_base_parser_subgroup_next(le_audio_base_parser_t * parser)190 void le_audio_base_parser_subgroup_next(le_audio_base_parser_t * parser){
191 btstack_assert(parser->subgroup_offset > 0);
192 while (parser->bis_index < parser->bis_count){
193 le_audio_base_parser_bis_next(parser);
194 }
195 parser->subgroup_index++;
196 if (parser->subgroup_index < parser->subgroup_count){
197 parser->subgroup_offset = parser->bis_offset;
198 le_audio_base_parser_fetch_subgroup_info(parser);
199 } else {
200 parser->bis_offset = 0;
201 }
202 }
203