1 /* 2 * Copyright (C) 2024 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_uri_builder.c" 39 40 #include "le-audio/broadcast_audio_uri_builder.h" 41 #include "btstack_util.h" 42 #include <stdint.h> 43 #include <string.h> 44 #include <inttypes.h> 45 46 static void broadcast_audio_uri_builder_string_hexdump(uint8_t * buffer, const uint8_t * data, uint16_t size){ 47 uint8_t i; 48 for (i = 0; i < size ; i++) { 49 uint8_t byte = data[i]; 50 buffer[2*i+0] = char_for_nibble(byte >> 4); 51 buffer[2*i+1] = char_for_nibble(byte & 0x0f); 52 } 53 } 54 55 static inline bool broadcast_audio_uri_builder_have_space(const broadcast_audio_uri_builder_t * builder, uint16_t len){ 56 return builder->len + len <= builder->size; 57 } 58 59 void broadcast_audio_uri_builder_init(broadcast_audio_uri_builder_t * builder, uint8_t * buffer, uint16_t size){ 60 builder->buffer = buffer; 61 builder->size = size; 62 builder->len = 0; 63 } 64 65 uint16_t broadcast_audio_uri_builder_get_remaining_space(const broadcast_audio_uri_builder_t * builder){ 66 return builder->size - builder->len; 67 } 68 69 uint16_t broadcast_audio_uri_builder_get_size(const broadcast_audio_uri_builder_t * builder){ 70 return builder->len; 71 } 72 73 bool broadcast_audio_uri_builder_append_bytes(broadcast_audio_uri_builder_t * builder, const uint8_t * data, uint16_t len){ 74 bool ok = broadcast_audio_uri_builder_have_space(builder, len); 75 if (ok){ 76 memcpy(&builder->buffer[builder->len], data, len); 77 builder->len += len; 78 } 79 return ok; 80 } 81 82 bool broadcast_audio_uri_builder_append_string(broadcast_audio_uri_builder_t * builder, const char * text){ 83 uint16_t len = (uint16_t) strlen(text); 84 return broadcast_audio_uri_builder_append_bytes(builder, (const uint8_t *) text, len); 85 } 86 87 bool broadcast_audio_uri_builder_append_broadcast_name(broadcast_audio_uri_builder_t * builder, const char * broadcast_name){ 88 UNUSED(broadcast_name); 89 // TODO: base64 90 return broadcast_audio_uri_builder_append_string(builder, "BN:QnJvYWRjYXN0IE5hbWU=;"); 91 } 92 93 bool broadcast_audio_uri_builder_append_advertiser_address_type(broadcast_audio_uri_builder_t * builder, bd_addr_type_t advertiser_address_type){ 94 char buffer[10]; 95 btstack_snprintf_assert_complete(buffer, sizeof(buffer), "AT:%u;",advertiser_address_type == BD_ADDR_TYPE_LE_RANDOM ? 1 : 0); 96 return broadcast_audio_uri_builder_append_string(builder, buffer); 97 } 98 99 bool broadcast_audio_uri_builder_append_standard_quality(broadcast_audio_uri_builder_t * builder, bool standard_quality){ 100 char buffer[10]; 101 btstack_snprintf_assert_complete(buffer, sizeof(buffer), "SQ:%u;", (int) standard_quality); 102 return broadcast_audio_uri_builder_append_string(builder, buffer); 103 } 104 105 bool broadcast_audio_uri_builder_append_high_quality(broadcast_audio_uri_builder_t * builder, bool high_quality){ 106 char buffer[10]; 107 btstack_snprintf_assert_complete(buffer, sizeof(buffer), "HQ:%u;", (int) high_quality); 108 return broadcast_audio_uri_builder_append_string(builder, buffer); 109 } 110 111 bool broadcast_audio_uri_builder_append_advertiser_address(broadcast_audio_uri_builder_t * builder, bd_addr_t advertiser_address){ 112 uint16_t len = builder->len; 113 bool ok = broadcast_audio_uri_builder_append_string(builder,"AD:"); 114 if (ok){ 115 char buffer[13]; 116 broadcast_audio_uri_builder_string_hexdump((uint8_t *)buffer, (const uint8_t *) advertiser_address, 6); 117 buffer[12] = ';'; 118 broadcast_audio_uri_builder_append_bytes(builder, (const uint8_t *) buffer, sizeof(buffer)); 119 } else { 120 builder->len = len; 121 } 122 return ok; 123 } 124 125 bool broadcast_audio_uri_builder_append_broadcast_id(broadcast_audio_uri_builder_t * builder, uint32_t broadcast_id){ 126 uint16_t len = builder->len; 127 bool ok = broadcast_audio_uri_builder_append_string(builder,"BI:"); 128 if (ok){ 129 uint8_t buffer[7]; 130 uint8_t big_endian_id[3]; 131 big_endian_store_24(big_endian_id, 0, broadcast_id); 132 broadcast_audio_uri_builder_string_hexdump(buffer, big_endian_id, 3); 133 buffer[6] = ';'; 134 broadcast_audio_uri_builder_append_bytes(builder, (const uint8_t *) buffer, sizeof(buffer)); 135 } else { 136 builder->len = len; 137 } 138 return ok; 139 140 } 141 142 bool broadcast_audio_uri_builder_append_broadcast_code(broadcast_audio_uri_builder_t * builder, const uint8_t * broadcast_code){ 143 UNUSED(broadcast_code); 144 uint16_t len = builder->len; 145 bool ok = broadcast_audio_uri_builder_append_string(builder,"BC:"); 146 if (ok){ 147 uint8_t buffer[25]; 148 // TODO: base64 149 memcpy(buffer, "MDEyMzQ1Njc4OWFiY2RlZg==", 24); 150 buffer[24] = ';'; 151 broadcast_audio_uri_builder_append_bytes(builder, (const uint8_t *) buffer, sizeof(buffer)); 152 } else { 153 builder->len = len; 154 } 155 return ok; 156 } 157 158 bool broadcast_audio_uri_builder_append_vendor_specific(broadcast_audio_uri_builder_t * builder, uint16_t vendor_id, const uint8_t * data, uint16_t data_len){ 159 UNUSED(data); 160 UNUSED(data_len); 161 uint16_t len = builder->len; 162 bool ok = broadcast_audio_uri_builder_append_string(builder, "VS:"); 163 if (ok){ 164 uint8_t vendor_id_big_endian_id[2]; 165 big_endian_store_16(vendor_id_big_endian_id, 0, vendor_id); 166 uint8_t vendor_id_hex[4]; 167 memset(vendor_id_hex, 0, sizeof(vendor_id_hex)); 168 broadcast_audio_uri_builder_string_hexdump(vendor_id_hex, vendor_id_hex, 2); 169 // TODO: base64(vendor_id_hex + data) 170 ok = broadcast_audio_uri_builder_append_string(builder, ";"); 171 } 172 if (ok == false){ 173 builder->len = len; 174 } 175 return ok; 176 } 177 178 bool broadcast_audio_uri_builder_append_advertising_sid(broadcast_audio_uri_builder_t * builder, uint8_t advertising_sid){ 179 char buffer[10]; 180 btstack_snprintf_assert_complete(buffer, sizeof(buffer), "AS:%02X;", advertising_sid); 181 return broadcast_audio_uri_builder_append_string(builder, buffer); 182 } 183 184 bool broadcast_audio_uri_builder_append_pa_interval(broadcast_audio_uri_builder_t * builder, uint16_t pa_interval){ 185 char buffer[10]; 186 btstack_snprintf_assert_complete(buffer, sizeof(buffer), "PI:%04" PRIX32 ";", pa_interval); 187 return broadcast_audio_uri_builder_append_string(builder, buffer); 188 } 189 190 bool broadcast_audio_uri_builder_append_num_subgroups(broadcast_audio_uri_builder_t * builder, uint8_t num_subgroups){ 191 char buffer[10]; 192 btstack_snprintf_assert_complete(buffer, sizeof(buffer), "NS:%02X", num_subgroups); 193 return broadcast_audio_uri_builder_append_string(builder, buffer); 194 } 195 196 bool broadcast_audio_uri_builder_append_bis_sync(broadcast_audio_uri_builder_t * builder, uint32_t bis_sync){ 197 char buffer[10]; 198 btstack_snprintf_assert_complete(buffer, sizeof(buffer), "BS:%04" PRIX32 ";", bis_sync); 199 return broadcast_audio_uri_builder_append_string(builder, buffer); 200 } 201 202 bool broadcast_audio_uri_builder_append_sg_number_of_bises(broadcast_audio_uri_builder_t * builder, uint32_t sg_number_of_bises){ 203 char buffer[10]; 204 btstack_snprintf_assert_complete(buffer, sizeof(buffer), "NB:%04" PRIX32 ";", sg_number_of_bises); 205 return broadcast_audio_uri_builder_append_string(builder, buffer); 206 } 207 208 bool broadcast_audio_uri_builder_append_sg_metadata(broadcast_audio_uri_builder_t * builder, const uint8_t * metadata, uint16_t metadata_len){ 209 uint16_t len = builder->len; 210 bool ok = broadcast_audio_uri_builder_append_string(builder, "SM:"); 211 if (ok && (metadata_len > 0)) { 212 // TODO: base64(data) 213 ok = broadcast_audio_uri_builder_append_bytes(builder, metadata, metadata_len); 214 } 215 if (ok) { 216 ok = broadcast_audio_uri_builder_append_string(builder, ";"); 217 } else { 218 builder->len = len; 219 } 220 return ok; 221 } 222 223 bool broadcast_audio_uri_builder_append_public_broadcast_announcement_metadata(broadcast_audio_uri_builder_t * builder, const uint8_t * metadata, uint16_t metadata_len){ 224 uint16_t len = builder->len; 225 bool ok = broadcast_audio_uri_builder_append_string(builder, "PM:"); 226 if (ok && metadata_len > 0) { 227 // TODO: base64(data) 228 ok = broadcast_audio_uri_builder_append_bytes(builder, metadata, metadata_len); 229 } 230 if (ok) { 231 ok = broadcast_audio_uri_builder_append_string(builder, ";"); 232 } else { 233 builder->len = len; 234 } 235 return ok; 236 } 237