1*65dc4892SMatthias Ringwald /* 2*65dc4892SMatthias Ringwald * Copyright (C) 2024 BlueKitchen GmbH 3*65dc4892SMatthias Ringwald * 4*65dc4892SMatthias Ringwald * Redistribution and use in source and binary forms, with or without 5*65dc4892SMatthias Ringwald * modification, are permitted provided that the following conditions 6*65dc4892SMatthias Ringwald * are met: 7*65dc4892SMatthias Ringwald * 8*65dc4892SMatthias Ringwald * 1. Redistributions of source code must retain the above copyright 9*65dc4892SMatthias Ringwald * notice, this list of conditions and the following disclaimer. 10*65dc4892SMatthias Ringwald * 2. Redistributions in binary form must reproduce the above copyright 11*65dc4892SMatthias Ringwald * notice, this list of conditions and the following disclaimer in the 12*65dc4892SMatthias Ringwald * documentation and/or other materials provided with the distribution. 13*65dc4892SMatthias Ringwald * 3. Neither the name of the copyright holders nor the names of 14*65dc4892SMatthias Ringwald * contributors may be used to endorse or promote products derived 15*65dc4892SMatthias Ringwald * from this software without specific prior written permission. 16*65dc4892SMatthias Ringwald * 4. Any redistribution, use, or modification is done solely for 17*65dc4892SMatthias Ringwald * personal benefit and not for any commercial purpose or for 18*65dc4892SMatthias Ringwald * monetary gain. 19*65dc4892SMatthias Ringwald * 20*65dc4892SMatthias Ringwald * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS 21*65dc4892SMatthias Ringwald * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22*65dc4892SMatthias Ringwald * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 23*65dc4892SMatthias Ringwald * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BLUEKITCHEN 24*65dc4892SMatthias Ringwald * GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 25*65dc4892SMatthias Ringwald * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 26*65dc4892SMatthias Ringwald * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 27*65dc4892SMatthias Ringwald * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 28*65dc4892SMatthias Ringwald * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 29*65dc4892SMatthias Ringwald * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 30*65dc4892SMatthias Ringwald * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31*65dc4892SMatthias Ringwald * SUCH DAMAGE. 32*65dc4892SMatthias Ringwald * 33*65dc4892SMatthias Ringwald * Please inquire about commercial licensing options at 34*65dc4892SMatthias Ringwald * [email protected] 35*65dc4892SMatthias Ringwald * 36*65dc4892SMatthias Ringwald */ 37*65dc4892SMatthias Ringwald 38*65dc4892SMatthias Ringwald #define BTSTACK_FILE__ "broadcast_audio_uri_builder.c" 39*65dc4892SMatthias Ringwald 40*65dc4892SMatthias Ringwald #include "le-audio/broadcast_audio_uri_builder.h" 41*65dc4892SMatthias Ringwald #include "btstack_util.h" 42*65dc4892SMatthias Ringwald #include <stdint.h> 43*65dc4892SMatthias Ringwald #include <string.h> 44*65dc4892SMatthias Ringwald #include <inttypes.h> 45*65dc4892SMatthias Ringwald 46*65dc4892SMatthias Ringwald static void broadcast_audio_uri_builder_string_hexdump(uint8_t * buffer, const uint8_t * data, uint16_t size){ 47*65dc4892SMatthias Ringwald uint8_t i; 48*65dc4892SMatthias Ringwald for (i = 0; i < size ; i++) { 49*65dc4892SMatthias Ringwald uint8_t byte = data[i]; 50*65dc4892SMatthias Ringwald buffer[2*i+0] = char_for_nibble(byte >> 4); 51*65dc4892SMatthias Ringwald buffer[2*i+1] = char_for_nibble(byte & 0x0f); 52*65dc4892SMatthias Ringwald } 53*65dc4892SMatthias Ringwald } 54*65dc4892SMatthias Ringwald 55*65dc4892SMatthias Ringwald static inline bool broadcast_audio_uri_builder_have_space(const broadcast_audio_uri_builder_t * builder, uint16_t len){ 56*65dc4892SMatthias Ringwald return builder->len + len <= builder->size; 57*65dc4892SMatthias Ringwald } 58*65dc4892SMatthias Ringwald 59*65dc4892SMatthias Ringwald void broadcast_audio_uri_builder_init(broadcast_audio_uri_builder_t * builder, uint8_t * buffer, uint16_t size){ 60*65dc4892SMatthias Ringwald builder->buffer = buffer; 61*65dc4892SMatthias Ringwald builder->size = size; 62*65dc4892SMatthias Ringwald builder->len = 0; 63*65dc4892SMatthias Ringwald } 64*65dc4892SMatthias Ringwald 65*65dc4892SMatthias Ringwald uint16_t broadcast_audio_uri_builder_get_remaining_space(const broadcast_audio_uri_builder_t * builder){ 66*65dc4892SMatthias Ringwald return builder->size - builder->len; 67*65dc4892SMatthias Ringwald } 68*65dc4892SMatthias Ringwald 69*65dc4892SMatthias Ringwald uint16_t broadcast_audio_uri_builder_get_size(const broadcast_audio_uri_builder_t * builder){ 70*65dc4892SMatthias Ringwald return builder->len; 71*65dc4892SMatthias Ringwald } 72*65dc4892SMatthias Ringwald 73*65dc4892SMatthias Ringwald bool broadcast_audio_uri_builder_append_bytes(broadcast_audio_uri_builder_t * builder, const uint8_t * data, uint16_t len){ 74*65dc4892SMatthias Ringwald bool ok = broadcast_audio_uri_builder_have_space(builder, len); 75*65dc4892SMatthias Ringwald if (ok){ 76*65dc4892SMatthias Ringwald memcpy(&builder->buffer[builder->len], data, len); 77*65dc4892SMatthias Ringwald builder->len += len; 78*65dc4892SMatthias Ringwald } 79*65dc4892SMatthias Ringwald return ok; 80*65dc4892SMatthias Ringwald } 81*65dc4892SMatthias Ringwald 82*65dc4892SMatthias Ringwald bool broadcast_audio_uri_builder_append_string(broadcast_audio_uri_builder_t * builder, const char * text){ 83*65dc4892SMatthias Ringwald uint16_t len = (uint16_t) strlen(text); 84*65dc4892SMatthias Ringwald return broadcast_audio_uri_builder_append_bytes(builder, (const uint8_t *) text, len); 85*65dc4892SMatthias Ringwald } 86*65dc4892SMatthias Ringwald 87*65dc4892SMatthias Ringwald bool broadcast_audio_uri_builder_append_broadcast_name(broadcast_audio_uri_builder_t * builder, const char * broadcast_name){ 88*65dc4892SMatthias Ringwald // TODO: base64 89*65dc4892SMatthias Ringwald return broadcast_audio_uri_builder_append_string(builder, "BN:QnJvYWRjYXN0IE5hbWU=;"); 90*65dc4892SMatthias Ringwald } 91*65dc4892SMatthias Ringwald 92*65dc4892SMatthias Ringwald bool broadcast_audio_uri_builder_append_advertiser_address_type(broadcast_audio_uri_builder_t * builder, bd_addr_type_t advertiser_address_type){ 93*65dc4892SMatthias Ringwald char buffer[10]; 94*65dc4892SMatthias Ringwald btstack_snprintf_assert_complete(buffer, sizeof(buffer), "AT:%u;",advertiser_address_type == BD_ADDR_TYPE_LE_RANDOM ? 1 : 0); 95*65dc4892SMatthias Ringwald return broadcast_audio_uri_builder_append_string(builder, buffer); 96*65dc4892SMatthias Ringwald } 97*65dc4892SMatthias Ringwald 98*65dc4892SMatthias Ringwald bool broadcast_audio_uri_builder_append_standard_quality(broadcast_audio_uri_builder_t * builder, bool standard_quality){ 99*65dc4892SMatthias Ringwald char buffer[10]; 100*65dc4892SMatthias Ringwald btstack_snprintf_assert_complete(buffer, sizeof(buffer), "SQ:%u;", (int) standard_quality); 101*65dc4892SMatthias Ringwald return broadcast_audio_uri_builder_append_string(builder, buffer); 102*65dc4892SMatthias Ringwald } 103*65dc4892SMatthias Ringwald 104*65dc4892SMatthias Ringwald bool broadcast_audio_uri_builder_append_high_quality(broadcast_audio_uri_builder_t * builder, bool high_quality){ 105*65dc4892SMatthias Ringwald char buffer[10]; 106*65dc4892SMatthias Ringwald btstack_snprintf_assert_complete(buffer, sizeof(buffer), "HQ:%u;", (int) high_quality); 107*65dc4892SMatthias Ringwald return broadcast_audio_uri_builder_append_string(builder, buffer); 108*65dc4892SMatthias Ringwald } 109*65dc4892SMatthias Ringwald 110*65dc4892SMatthias Ringwald bool broadcast_audio_uri_builder_append_advertiser_address(broadcast_audio_uri_builder_t * builder, bd_addr_t advertiser_address){ 111*65dc4892SMatthias Ringwald uint16_t len = builder->len; 112*65dc4892SMatthias Ringwald bool ok = broadcast_audio_uri_builder_append_string(builder,"AD:"); 113*65dc4892SMatthias Ringwald if (ok){ 114*65dc4892SMatthias Ringwald char buffer[13]; 115*65dc4892SMatthias Ringwald broadcast_audio_uri_builder_string_hexdump((uint8_t *)buffer, (const uint8_t *) advertiser_address, 6); 116*65dc4892SMatthias Ringwald buffer[12] = ';'; 117*65dc4892SMatthias Ringwald broadcast_audio_uri_builder_append_bytes(builder, (const uint8_t *) buffer, sizeof(buffer)); 118*65dc4892SMatthias Ringwald } else { 119*65dc4892SMatthias Ringwald builder->len = len; 120*65dc4892SMatthias Ringwald } 121*65dc4892SMatthias Ringwald return ok; 122*65dc4892SMatthias Ringwald } 123*65dc4892SMatthias Ringwald 124*65dc4892SMatthias Ringwald bool broadcast_audio_uri_builder_append_broadcast_id(broadcast_audio_uri_builder_t * builder, uint32_t broadcast_id){ 125*65dc4892SMatthias Ringwald uint16_t len = builder->len; 126*65dc4892SMatthias Ringwald bool ok = broadcast_audio_uri_builder_append_string(builder,"BI:"); 127*65dc4892SMatthias Ringwald if (ok){ 128*65dc4892SMatthias Ringwald uint8_t buffer[7]; 129*65dc4892SMatthias Ringwald uint8_t big_endian_id[3]; 130*65dc4892SMatthias Ringwald big_endian_store_24(big_endian_id, 0, broadcast_id); 131*65dc4892SMatthias Ringwald broadcast_audio_uri_builder_string_hexdump(buffer, big_endian_id, 3); 132*65dc4892SMatthias Ringwald buffer[6] = ';'; 133*65dc4892SMatthias Ringwald broadcast_audio_uri_builder_append_bytes(builder, (const uint8_t *) buffer, sizeof(buffer)); 134*65dc4892SMatthias Ringwald } else { 135*65dc4892SMatthias Ringwald builder->len = len; 136*65dc4892SMatthias Ringwald } 137*65dc4892SMatthias Ringwald return ok; 138*65dc4892SMatthias Ringwald 139*65dc4892SMatthias Ringwald } 140*65dc4892SMatthias Ringwald 141*65dc4892SMatthias Ringwald bool broadcast_audio_uri_builder_append_broadcast_code(broadcast_audio_uri_builder_t * builder, const uint8_t * broadcast_code){ 142*65dc4892SMatthias Ringwald uint16_t len = builder->len; 143*65dc4892SMatthias Ringwald bool ok = broadcast_audio_uri_builder_append_string(builder,"BC:"); 144*65dc4892SMatthias Ringwald if (ok){ 145*65dc4892SMatthias Ringwald uint8_t buffer[25]; 146*65dc4892SMatthias Ringwald // TODO: base64 147*65dc4892SMatthias Ringwald memcpy(buffer, "MDEyMzQ1Njc4OWFiY2RlZg==", 24); 148*65dc4892SMatthias Ringwald buffer[24] = ';'; 149*65dc4892SMatthias Ringwald broadcast_audio_uri_builder_append_bytes(builder, (const uint8_t *) buffer, sizeof(buffer)); 150*65dc4892SMatthias Ringwald } else { 151*65dc4892SMatthias Ringwald builder->len = len; 152*65dc4892SMatthias Ringwald } 153*65dc4892SMatthias Ringwald return ok; 154*65dc4892SMatthias Ringwald } 155*65dc4892SMatthias Ringwald 156*65dc4892SMatthias Ringwald 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){ 157*65dc4892SMatthias Ringwald uint16_t len = builder->len; 158*65dc4892SMatthias Ringwald bool ok = broadcast_audio_uri_builder_append_string(builder, "VS:"); 159*65dc4892SMatthias Ringwald if (ok){ 160*65dc4892SMatthias Ringwald uint8_t vendor_id_big_endian_id[2]; 161*65dc4892SMatthias Ringwald big_endian_store_16(vendor_id_big_endian_id, 0, vendor_id); 162*65dc4892SMatthias Ringwald uint8_t vendor_id_hex[4]; 163*65dc4892SMatthias Ringwald broadcast_audio_uri_builder_string_hexdump(vendor_id_hex, vendor_id_hex, 2); 164*65dc4892SMatthias Ringwald // TODO: base64(vendor_id_hex + data) 165*65dc4892SMatthias Ringwald ok = broadcast_audio_uri_builder_append_string(builder, ";"); 166*65dc4892SMatthias Ringwald } 167*65dc4892SMatthias Ringwald if (ok == false){ 168*65dc4892SMatthias Ringwald builder->len = len; 169*65dc4892SMatthias Ringwald } 170*65dc4892SMatthias Ringwald return ok; 171*65dc4892SMatthias Ringwald } 172*65dc4892SMatthias Ringwald 173*65dc4892SMatthias Ringwald bool broadcast_audio_uri_builder_append_advertising_sid(broadcast_audio_uri_builder_t * builder, uint8_t advertising_sid){ 174*65dc4892SMatthias Ringwald char buffer[10]; 175*65dc4892SMatthias Ringwald btstack_snprintf_assert_complete(buffer, sizeof(buffer), "AS:%02X;", advertising_sid); 176*65dc4892SMatthias Ringwald return broadcast_audio_uri_builder_append_string(builder, buffer); 177*65dc4892SMatthias Ringwald } 178*65dc4892SMatthias Ringwald 179*65dc4892SMatthias Ringwald bool broadcast_audio_uri_builder_append_pa_interval(broadcast_audio_uri_builder_t * builder, uint16_t pa_interval){ 180*65dc4892SMatthias Ringwald char buffer[10]; 181*65dc4892SMatthias Ringwald btstack_snprintf_assert_complete(buffer, sizeof(buffer), "PI:%04" PRIX32 ";", pa_interval); 182*65dc4892SMatthias Ringwald return broadcast_audio_uri_builder_append_string(builder, buffer); 183*65dc4892SMatthias Ringwald } 184*65dc4892SMatthias Ringwald 185*65dc4892SMatthias Ringwald bool broadcast_audio_uri_builder_append_num_subgroups(broadcast_audio_uri_builder_t * builder, uint8_t num_subgroups){ 186*65dc4892SMatthias Ringwald char buffer[10]; 187*65dc4892SMatthias Ringwald btstack_snprintf_assert_complete(buffer, sizeof(buffer), "NS:%02X", num_subgroups); 188*65dc4892SMatthias Ringwald return broadcast_audio_uri_builder_append_string(builder, buffer); 189*65dc4892SMatthias Ringwald } 190*65dc4892SMatthias Ringwald 191*65dc4892SMatthias Ringwald bool broadcast_audio_uri_builder_append_bis_sync(broadcast_audio_uri_builder_t * builder, uint32_t bis_sync){ 192*65dc4892SMatthias Ringwald char buffer[10]; 193*65dc4892SMatthias Ringwald btstack_snprintf_assert_complete(buffer, sizeof(buffer), "BS:%04" PRIX32 ";", bis_sync); 194*65dc4892SMatthias Ringwald return broadcast_audio_uri_builder_append_string(builder, buffer); 195*65dc4892SMatthias Ringwald } 196*65dc4892SMatthias Ringwald 197*65dc4892SMatthias Ringwald bool broadcast_audio_uri_builder_append_sg_number_of_bises(broadcast_audio_uri_builder_t * builder, uint32_t sg_number_of_bises){ 198*65dc4892SMatthias Ringwald char buffer[10]; 199*65dc4892SMatthias Ringwald btstack_snprintf_assert_complete(buffer, sizeof(buffer), "NB:%04" PRIX32 ";", sg_number_of_bises); 200*65dc4892SMatthias Ringwald return broadcast_audio_uri_builder_append_string(builder, buffer); 201*65dc4892SMatthias Ringwald } 202*65dc4892SMatthias Ringwald 203*65dc4892SMatthias Ringwald bool broadcast_audio_uri_builder_append_sg_metadata(broadcast_audio_uri_builder_t * builder, const uint8_t * metadata, uint16_t metadata_len){ 204*65dc4892SMatthias Ringwald uint16_t len = builder->len; 205*65dc4892SMatthias Ringwald bool ok = broadcast_audio_uri_builder_append_string(builder, "SM:"); 206*65dc4892SMatthias Ringwald if (ok && (metadata_len > 0)) { 207*65dc4892SMatthias Ringwald // TODO: base64(data) 208*65dc4892SMatthias Ringwald ok = broadcast_audio_uri_builder_append_bytes(builder, metadata, metadata_len); 209*65dc4892SMatthias Ringwald } 210*65dc4892SMatthias Ringwald if (ok) { 211*65dc4892SMatthias Ringwald ok = broadcast_audio_uri_builder_append_string(builder, ";"); 212*65dc4892SMatthias Ringwald } else { 213*65dc4892SMatthias Ringwald builder->len = len; 214*65dc4892SMatthias Ringwald } 215*65dc4892SMatthias Ringwald return ok; 216*65dc4892SMatthias Ringwald } 217*65dc4892SMatthias Ringwald 218*65dc4892SMatthias Ringwald bool broadcast_audio_uri_builder_append_public_broadcast_announcement_metadata(broadcast_audio_uri_builder_t * builder, const uint8_t * metadata, uint16_t metadata_len){ 219*65dc4892SMatthias Ringwald uint16_t len = builder->len; 220*65dc4892SMatthias Ringwald bool ok = broadcast_audio_uri_builder_append_string(builder, "PM:"); 221*65dc4892SMatthias Ringwald if (ok && metadata_len > 0) { 222*65dc4892SMatthias Ringwald // TODO: base64(data) 223*65dc4892SMatthias Ringwald ok = broadcast_audio_uri_builder_append_bytes(builder, metadata, metadata_len); 224*65dc4892SMatthias Ringwald } 225*65dc4892SMatthias Ringwald if (ok) { 226*65dc4892SMatthias Ringwald ok = broadcast_audio_uri_builder_append_string(builder, ";"); 227*65dc4892SMatthias Ringwald } else { 228*65dc4892SMatthias Ringwald builder->len = len; 229*65dc4892SMatthias Ringwald } 230*65dc4892SMatthias Ringwald return ok; 231*65dc4892SMatthias Ringwald } 232