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_client.c"
395deb0bb6SMatthias Ringwald
405deb0bb6SMatthias Ringwald #include "ble/att_db.h"
415deb0bb6SMatthias Ringwald #include "ble/att_server.h"
425deb0bb6SMatthias Ringwald #include "bluetooth_gatt.h"
435deb0bb6SMatthias Ringwald #include "btstack_debug.h"
445deb0bb6SMatthias Ringwald #include "btstack_defines.h"
455deb0bb6SMatthias Ringwald #include "btstack_event.h"
465deb0bb6SMatthias Ringwald #include "btstack_util.h"
475deb0bb6SMatthias Ringwald #include "btstack_memory.h"
485deb0bb6SMatthias Ringwald
495deb0bb6SMatthias Ringwald #include "le-audio/le_audio_util.h"
505deb0bb6SMatthias Ringwald #include "le-audio/gatt-service/broadcast_audio_scan_service_client.h"
515deb0bb6SMatthias Ringwald
525deb0bb6SMatthias Ringwald #ifdef ENABLE_TESTING_SUPPORT
535deb0bb6SMatthias Ringwald #include <stdio.h>
545deb0bb6SMatthias Ringwald #endif
555deb0bb6SMatthias Ringwald
5697c78dc6SMilanka Ringwald static btstack_linked_list_t bass_client_connections;
575deb0bb6SMatthias Ringwald
585deb0bb6SMatthias Ringwald static uint16_t bass_client_cid_counter = 0;
5997c78dc6SMilanka Ringwald static btstack_packet_handler_t bass_client_event_callback;
605deb0bb6SMatthias Ringwald
6197c78dc6SMilanka Ringwald static void bass_client_handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
625deb0bb6SMatthias Ringwald
bass_client_get_next_cid(void)635deb0bb6SMatthias Ringwald static uint16_t bass_client_get_next_cid(void){
645deb0bb6SMatthias Ringwald bass_client_cid_counter = btstack_next_cid_ignoring_zero(bass_client_cid_counter);
655deb0bb6SMatthias Ringwald return bass_client_cid_counter;
665deb0bb6SMatthias Ringwald }
675deb0bb6SMatthias Ringwald
bass_client_finalize_connection(bass_client_connection_t * connection)685deb0bb6SMatthias Ringwald static void bass_client_finalize_connection(bass_client_connection_t * connection){
6997c78dc6SMilanka Ringwald btstack_linked_list_remove(&bass_client_connections, (btstack_linked_item_t*) connection);
705deb0bb6SMatthias Ringwald }
715deb0bb6SMatthias Ringwald
bass_client_get_connection_for_con_handle(hci_con_handle_t con_handle)725deb0bb6SMatthias Ringwald static bass_client_connection_t * bass_client_get_connection_for_con_handle(hci_con_handle_t con_handle){
735deb0bb6SMatthias Ringwald btstack_linked_list_iterator_t it;
7497c78dc6SMilanka Ringwald btstack_linked_list_iterator_init(&it, (btstack_linked_list_t *) &bass_client_connections);
755deb0bb6SMatthias Ringwald while (btstack_linked_list_iterator_has_next(&it)){
765deb0bb6SMatthias Ringwald bass_client_connection_t * connection = (bass_client_connection_t *)btstack_linked_list_iterator_next(&it);
775deb0bb6SMatthias Ringwald if (connection->con_handle != con_handle) continue;
785deb0bb6SMatthias Ringwald return connection;
795deb0bb6SMatthias Ringwald }
805deb0bb6SMatthias Ringwald return NULL;
815deb0bb6SMatthias Ringwald }
825deb0bb6SMatthias Ringwald
bass_client_get_connection_for_cid(uint16_t bass_cid)8397c78dc6SMilanka Ringwald static bass_client_connection_t * bass_client_get_connection_for_cid(uint16_t bass_cid){
845deb0bb6SMatthias Ringwald btstack_linked_list_iterator_t it;
8597c78dc6SMilanka Ringwald btstack_linked_list_iterator_init(&it, (btstack_linked_list_t *) &bass_client_connections);
865deb0bb6SMatthias Ringwald while (btstack_linked_list_iterator_has_next(&it)){
875deb0bb6SMatthias Ringwald bass_client_connection_t * connection = (bass_client_connection_t *)btstack_linked_list_iterator_next(&it);
885deb0bb6SMatthias Ringwald if (connection->cid != bass_cid) continue;
895deb0bb6SMatthias Ringwald return connection;
905deb0bb6SMatthias Ringwald }
915deb0bb6SMatthias Ringwald return NULL;
925deb0bb6SMatthias Ringwald }
935deb0bb6SMatthias Ringwald
bass_client_get_receive_state_for_value_handle(bass_client_connection_t * connection,uint16_t value_handle)9497c78dc6SMilanka Ringwald static bass_client_source_t * bass_client_get_receive_state_for_value_handle(bass_client_connection_t * connection, uint16_t value_handle){
955deb0bb6SMatthias Ringwald uint8_t i;
965deb0bb6SMatthias Ringwald for (i = 0; i < connection->receive_states_instances_num; i++){
975deb0bb6SMatthias Ringwald if (connection->receive_states[i].receive_state_value_handle == value_handle){
985deb0bb6SMatthias Ringwald return &connection->receive_states[i];
995deb0bb6SMatthias Ringwald }
1005deb0bb6SMatthias Ringwald }
1015deb0bb6SMatthias Ringwald return NULL;
1025deb0bb6SMatthias Ringwald }
1035deb0bb6SMatthias Ringwald
bass_client_get_source_for_source_id(bass_client_connection_t * connection,uint8_t source_id)10497c78dc6SMilanka Ringwald static bass_client_source_t * bass_client_get_source_for_source_id(bass_client_connection_t * connection, uint8_t source_id){
1055deb0bb6SMatthias Ringwald uint8_t i;
1065deb0bb6SMatthias Ringwald for (i = 0; i < connection->receive_states_instances_num; i++){
1075deb0bb6SMatthias Ringwald if (connection->receive_states[i].source_id == source_id){
1085deb0bb6SMatthias Ringwald return &connection->receive_states[i];
1095deb0bb6SMatthias Ringwald }
1105deb0bb6SMatthias Ringwald }
1115deb0bb6SMatthias Ringwald return NULL;
1125deb0bb6SMatthias Ringwald }
1135deb0bb6SMatthias Ringwald
bass_client_reset_source(bass_client_source_t * source)1145deb0bb6SMatthias Ringwald static void bass_client_reset_source(bass_client_source_t * source){
1155deb0bb6SMatthias Ringwald if (source == NULL){
1165deb0bb6SMatthias Ringwald return;
1175deb0bb6SMatthias Ringwald }
1185deb0bb6SMatthias Ringwald source->source_id = BASS_INVALID_SOURCE_INDEX;
1195deb0bb6SMatthias Ringwald source->in_use = false;
1205deb0bb6SMatthias Ringwald memset(&source->data, 0, sizeof(bass_source_data_t));
1215deb0bb6SMatthias Ringwald }
1225deb0bb6SMatthias Ringwald
bass_client_emit_connection_established(bass_client_connection_t * connection,uint8_t status)1235deb0bb6SMatthias Ringwald static void bass_client_emit_connection_established(bass_client_connection_t * connection, uint8_t status){
12497c78dc6SMilanka Ringwald btstack_assert(bass_client_event_callback != NULL);
12580af3ed2SMilanka Ringwald
1265deb0bb6SMatthias Ringwald uint8_t event[8];
1275deb0bb6SMatthias Ringwald uint16_t pos = 0;
128ae304283SMilanka Ringwald event[pos++] = HCI_EVENT_LEAUDIO_META;
1295deb0bb6SMatthias Ringwald event[pos++] = sizeof(event) - 2;
130ae304283SMilanka Ringwald event[pos++] = LEAUDIO_SUBEVENT_BASS_CLIENT_CONNECTED;
1315deb0bb6SMatthias Ringwald little_endian_store_16(event, pos, connection->con_handle);
1325deb0bb6SMatthias Ringwald pos += 2;
1335deb0bb6SMatthias Ringwald little_endian_store_16(event, pos, connection->cid);
1345deb0bb6SMatthias Ringwald pos += 2;
1355deb0bb6SMatthias Ringwald event[pos++] = status;
13697c78dc6SMilanka Ringwald (*bass_client_event_callback)(HCI_EVENT_PACKET, 0, event, sizeof(event));
1375deb0bb6SMatthias Ringwald }
1385deb0bb6SMatthias Ringwald
bass_client_connected(bass_client_connection_t * connection,uint8_t status)139*59e9e2d1SMatthias Ringwald static void bass_client_connected(bass_client_connection_t *connection, uint8_t status) {
140*59e9e2d1SMatthias Ringwald if (status == ERROR_CODE_SUCCESS){
141*59e9e2d1SMatthias Ringwald connection->state = BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_STATE_READY;
142*59e9e2d1SMatthias Ringwald bass_client_emit_connection_established(connection, status);
143*59e9e2d1SMatthias Ringwald } else {
144*59e9e2d1SMatthias Ringwald connection->state = BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_STATE_IDLE;
145*59e9e2d1SMatthias Ringwald bass_client_emit_connection_established(connection, status);
146*59e9e2d1SMatthias Ringwald bass_client_finalize_connection(connection);
147*59e9e2d1SMatthias Ringwald }
148*59e9e2d1SMatthias Ringwald }
149*59e9e2d1SMatthias Ringwald
bass_client_emit_scan_operation_complete(bass_client_connection_t * connection,uint8_t status,bass_opcode_t opcode)1505deb0bb6SMatthias Ringwald static void bass_client_emit_scan_operation_complete(bass_client_connection_t * connection, uint8_t status, bass_opcode_t opcode){
15197c78dc6SMilanka Ringwald btstack_assert(bass_client_event_callback != NULL);
1525deb0bb6SMatthias Ringwald uint8_t event[7];
1535deb0bb6SMatthias Ringwald uint16_t pos = 0;
154ae304283SMilanka Ringwald event[pos++] = HCI_EVENT_LEAUDIO_META;
1555deb0bb6SMatthias Ringwald event[pos++] = sizeof(event) - 2;
156ae304283SMilanka Ringwald event[pos++] = LEAUDIO_SUBEVENT_BASS_CLIENT_SCAN_OPERATION_COMPLETE;
1575deb0bb6SMatthias Ringwald little_endian_store_16(event, pos, connection->cid);
1585deb0bb6SMatthias Ringwald pos += 2;
1595deb0bb6SMatthias Ringwald event[pos++] = status;
1605deb0bb6SMatthias Ringwald event[pos++] = (uint8_t)opcode;
16197c78dc6SMilanka Ringwald (*bass_client_event_callback)(HCI_EVENT_PACKET, 0, event, sizeof(event));
1625deb0bb6SMatthias Ringwald }
1635deb0bb6SMatthias Ringwald
bass_client_emit_source_operation_complete(bass_client_connection_t * connection,uint8_t status,bass_opcode_t opcode,uint8_t source_id)1645deb0bb6SMatthias Ringwald static void bass_client_emit_source_operation_complete(bass_client_connection_t * connection, uint8_t status, bass_opcode_t opcode, uint8_t source_id){
16597c78dc6SMilanka Ringwald btstack_assert(bass_client_event_callback != NULL);
1665deb0bb6SMatthias Ringwald uint8_t event[8];
1675deb0bb6SMatthias Ringwald uint16_t pos = 0;
168ae304283SMilanka Ringwald event[pos++] = HCI_EVENT_LEAUDIO_META;
1695deb0bb6SMatthias Ringwald event[pos++] = sizeof(event) - 2;
170ae304283SMilanka Ringwald event[pos++] = LEAUDIO_SUBEVENT_BASS_CLIENT_SOURCE_OPERATION_COMPLETE;
1715deb0bb6SMatthias Ringwald little_endian_store_16(event, pos, connection->cid);
1725deb0bb6SMatthias Ringwald pos += 2;
1735deb0bb6SMatthias Ringwald event[pos++] = status;
1745deb0bb6SMatthias Ringwald event[pos++] = (uint8_t)opcode;
1755deb0bb6SMatthias Ringwald event[pos++] = source_id;
17697c78dc6SMilanka Ringwald (*bass_client_event_callback)(HCI_EVENT_PACKET, 0, event, sizeof(event));
1775deb0bb6SMatthias Ringwald }
1785deb0bb6SMatthias Ringwald
bass_client_emit_receive_state(bass_client_connection_t * connection,uint8_t source_id)1795deb0bb6SMatthias Ringwald static void bass_client_emit_receive_state(bass_client_connection_t * connection, uint8_t source_id){
18097c78dc6SMilanka Ringwald btstack_assert(bass_client_event_callback != NULL);
1815deb0bb6SMatthias Ringwald uint8_t pos = 0;
1825deb0bb6SMatthias Ringwald uint8_t event[7];
183ae304283SMilanka Ringwald event[pos++] = HCI_EVENT_LEAUDIO_META;
1845deb0bb6SMatthias Ringwald event[pos++] = sizeof(event) - 2;
185ae304283SMilanka Ringwald event[pos++] = LEAUDIO_SUBEVENT_BASS_CLIENT_NOTIFICATION_COMPLETE;
1865deb0bb6SMatthias Ringwald little_endian_store_16(event, pos, connection->cid);
1875deb0bb6SMatthias Ringwald pos += 2;
1885deb0bb6SMatthias Ringwald event[pos++] = source_id;
18997c78dc6SMilanka Ringwald (*bass_client_event_callback)(HCI_EVENT_PACKET, 0, event, sizeof(event));
1905deb0bb6SMatthias Ringwald }
1915deb0bb6SMatthias Ringwald
bass_client_remote_broadcast_receive_state_buffer_valid(uint8_t * buffer,uint16_t buffer_size)1925deb0bb6SMatthias Ringwald static bool bass_client_remote_broadcast_receive_state_buffer_valid(uint8_t *buffer, uint16_t buffer_size){
1935deb0bb6SMatthias Ringwald // minimal with zero subgroups
1945deb0bb6SMatthias Ringwald if (buffer_size < 15){
1955deb0bb6SMatthias Ringwald return false;
1965deb0bb6SMatthias Ringwald }
1975deb0bb6SMatthias Ringwald
1985deb0bb6SMatthias Ringwald uint16_t pos = 0;
1995deb0bb6SMatthias Ringwald
2005deb0bb6SMatthias Ringwald // source_id
2015deb0bb6SMatthias Ringwald pos++;
2025deb0bb6SMatthias Ringwald
2035deb0bb6SMatthias Ringwald // addr type
2045deb0bb6SMatthias Ringwald uint8_t adv_type = buffer[pos++];
2055deb0bb6SMatthias Ringwald if (adv_type > (uint8_t)BD_ADDR_TYPE_LE_RANDOM){
2065deb0bb6SMatthias Ringwald log_info("Unexpected adv_type 0x%02X", adv_type);
2075deb0bb6SMatthias Ringwald return false;
2085deb0bb6SMatthias Ringwald }
2095deb0bb6SMatthias Ringwald
2105deb0bb6SMatthias Ringwald // address
2115deb0bb6SMatthias Ringwald pos += 6;
2125deb0bb6SMatthias Ringwald
2135deb0bb6SMatthias Ringwald // advertising_sid Range: 0x00-0x0F
2145deb0bb6SMatthias Ringwald uint8_t advertising_sid = buffer[pos++];
2155deb0bb6SMatthias Ringwald if (advertising_sid > 0x0F){
2165deb0bb6SMatthias Ringwald log_info("Advertising sid out of range 0x%02X", advertising_sid);
2175deb0bb6SMatthias Ringwald return false;
2185deb0bb6SMatthias Ringwald }
2195deb0bb6SMatthias Ringwald
2205deb0bb6SMatthias Ringwald // broadcast_id
2215deb0bb6SMatthias Ringwald pos += 3;
2225deb0bb6SMatthias Ringwald
2235deb0bb6SMatthias Ringwald // pa_sync_state
2245deb0bb6SMatthias Ringwald uint8_t pa_sync_state = buffer[pos++];
2255deb0bb6SMatthias Ringwald if (pa_sync_state >= (uint8_t)LE_AUDIO_PA_SYNC_STATE_RFU){
2265deb0bb6SMatthias Ringwald log_info("Unexpected pa_sync_state 0x%02X", pa_sync_state);
2275deb0bb6SMatthias Ringwald return false;
2285deb0bb6SMatthias Ringwald }
2295deb0bb6SMatthias Ringwald
2305deb0bb6SMatthias Ringwald // big_encryption
2315deb0bb6SMatthias Ringwald le_audio_big_encryption_t big_encryption = (le_audio_big_encryption_t) buffer[pos++];
2325deb0bb6SMatthias Ringwald if (big_encryption == LE_AUDIO_BIG_ENCRYPTION_BAD_CODE){
2335deb0bb6SMatthias Ringwald // Bad Code
2345deb0bb6SMatthias Ringwald pos += 16;
2355deb0bb6SMatthias Ringwald }
2365deb0bb6SMatthias Ringwald
2375deb0bb6SMatthias Ringwald // num subgroups
2385deb0bb6SMatthias Ringwald uint8_t num_subgroups = buffer[pos++];
2395deb0bb6SMatthias Ringwald if (num_subgroups > BASS_SUBGROUPS_MAX_NUM){
2405deb0bb6SMatthias Ringwald log_info("Number of subgroups %u exceedes maximum %u", num_subgroups, BASS_SUBGROUPS_MAX_NUM);
2415deb0bb6SMatthias Ringwald return false;
2425deb0bb6SMatthias Ringwald }
2435deb0bb6SMatthias Ringwald
2445deb0bb6SMatthias Ringwald uint8_t i;
2455deb0bb6SMatthias Ringwald for (i = 0; i < num_subgroups; i++) {
2465deb0bb6SMatthias Ringwald // check if we can read bis_sync_state + meta_data_length
2475deb0bb6SMatthias Ringwald // bis_sync_state
2485deb0bb6SMatthias Ringwald pos += 4;
2495deb0bb6SMatthias Ringwald // meta_data_length
2505deb0bb6SMatthias Ringwald if (pos >= buffer_size){
2515deb0bb6SMatthias Ringwald return false;
2525deb0bb6SMatthias Ringwald }
2535deb0bb6SMatthias Ringwald uint8_t metadata_length = buffer[pos++];
2545deb0bb6SMatthias Ringwald if ((pos + metadata_length) > buffer_size){
2555deb0bb6SMatthias Ringwald return false;
2565deb0bb6SMatthias Ringwald }
2575deb0bb6SMatthias Ringwald // metadata
2585deb0bb6SMatthias Ringwald pos += metadata_length;
2595deb0bb6SMatthias Ringwald }
2605deb0bb6SMatthias Ringwald return true;
2615deb0bb6SMatthias Ringwald }
2625deb0bb6SMatthias Ringwald
bass_client_handle_gatt_server_notification(uint8_t packet_type,uint16_t channel,uint8_t * packet,uint16_t size)26397c78dc6SMilanka Ringwald static void bass_client_handle_gatt_server_notification(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
2645deb0bb6SMatthias Ringwald UNUSED(packet_type);
2655deb0bb6SMatthias Ringwald UNUSED(channel);
2665deb0bb6SMatthias Ringwald UNUSED(size);
2675deb0bb6SMatthias Ringwald
2685deb0bb6SMatthias Ringwald if (hci_event_packet_get_type(packet) != GATT_EVENT_NOTIFICATION){
2695deb0bb6SMatthias Ringwald return;
2705deb0bb6SMatthias Ringwald }
2715483d01aSMilanka Ringwald bass_client_connection_t * connection = bass_client_get_connection_for_con_handle(gatt_event_notification_get_handle(packet));
2725deb0bb6SMatthias Ringwald if (connection == NULL){
2735deb0bb6SMatthias Ringwald return;
2745deb0bb6SMatthias Ringwald }
2755deb0bb6SMatthias Ringwald
2765deb0bb6SMatthias Ringwald uint16_t value_handle = gatt_event_notification_get_value_handle(packet);
2775deb0bb6SMatthias Ringwald uint16_t value_length = gatt_event_notification_get_value_length(packet);
2785deb0bb6SMatthias Ringwald uint8_t * value = (uint8_t *)gatt_event_notification_get_value(packet);
2795deb0bb6SMatthias Ringwald
2805deb0bb6SMatthias Ringwald if (bass_client_remote_broadcast_receive_state_buffer_valid(value, value_length)){
28197c78dc6SMilanka Ringwald bass_client_source_t * source = bass_client_get_receive_state_for_value_handle(connection, value_handle);
2825deb0bb6SMatthias Ringwald if (source == NULL){
2835deb0bb6SMatthias Ringwald return;
2845deb0bb6SMatthias Ringwald }
2855deb0bb6SMatthias Ringwald source->source_id = value[0];
2866e96f7c7SMatthias Ringwald bass_util_source_data_parse(&value[1], value_length - 1, &source->data, true);
2875deb0bb6SMatthias Ringwald
2885deb0bb6SMatthias Ringwald // get big encryption + bad code
2895deb0bb6SMatthias Ringwald source->big_encryption = (le_audio_big_encryption_t) value[13];
2905deb0bb6SMatthias Ringwald if (source->big_encryption == LE_AUDIO_BIG_ENCRYPTION_BAD_CODE){
2915deb0bb6SMatthias Ringwald reverse_128(&value[14], source->bad_code);
2925deb0bb6SMatthias Ringwald } else {
2935deb0bb6SMatthias Ringwald memset(source->bad_code, 0, 16);
2945deb0bb6SMatthias Ringwald }
2955deb0bb6SMatthias Ringwald
2965deb0bb6SMatthias Ringwald bass_client_emit_receive_state(connection, source->source_id);
2975deb0bb6SMatthias Ringwald }
2985deb0bb6SMatthias Ringwald }
2995deb0bb6SMatthias Ringwald
bass_client_register_notification(bass_client_connection_t * connection)3005deb0bb6SMatthias Ringwald static bool bass_client_register_notification(bass_client_connection_t * connection){
3015deb0bb6SMatthias Ringwald bass_client_source_t * receive_state = &connection->receive_states[connection->receive_states_index];
3025deb0bb6SMatthias Ringwald if (receive_state == NULL){
3035deb0bb6SMatthias Ringwald return false;
3045deb0bb6SMatthias Ringwald }
3055deb0bb6SMatthias Ringwald
3065deb0bb6SMatthias Ringwald gatt_client_characteristic_t characteristic;
3075deb0bb6SMatthias Ringwald characteristic.value_handle = receive_state->receive_state_value_handle;
3085deb0bb6SMatthias Ringwald characteristic.properties = receive_state->receive_state_properties;
309b0c37fffSMilanka Ringwald characteristic.end_handle = receive_state->receive_state_end_handle;
3105deb0bb6SMatthias Ringwald
3115deb0bb6SMatthias Ringwald uint8_t status = gatt_client_write_client_characteristic_configuration(
31297c78dc6SMilanka Ringwald &bass_client_handle_gatt_client_event,
3135deb0bb6SMatthias Ringwald connection->con_handle,
3145deb0bb6SMatthias Ringwald &characteristic,
3155deb0bb6SMatthias Ringwald GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION);
3165deb0bb6SMatthias Ringwald
3175deb0bb6SMatthias Ringwald // notification supported, register for value updates
3185deb0bb6SMatthias Ringwald if (status == ERROR_CODE_SUCCESS){
3195deb0bb6SMatthias Ringwald gatt_client_listen_for_characteristic_value_updates(
3205deb0bb6SMatthias Ringwald &connection->notification_listener,
32197c78dc6SMilanka Ringwald &bass_client_handle_gatt_server_notification,
3225deb0bb6SMatthias Ringwald connection->con_handle, &characteristic);
3235deb0bb6SMatthias Ringwald }
3245deb0bb6SMatthias Ringwald return status;
3255deb0bb6SMatthias Ringwald }
3265deb0bb6SMatthias Ringwald
bass_client_prepare_add_source_buffer(bass_client_connection_t * connection)3275deb0bb6SMatthias Ringwald static uint16_t bass_client_prepare_add_source_buffer(bass_client_connection_t * connection){
3285deb0bb6SMatthias Ringwald const bass_source_data_t * receive_state = connection->control_point_operation_data;
3295deb0bb6SMatthias Ringwald
3305deb0bb6SMatthias Ringwald uint16_t buffer_offset = connection->buffer_offset;
3315deb0bb6SMatthias Ringwald uint8_t * buffer = connection->buffer;
3325deb0bb6SMatthias Ringwald uint16_t buffer_size = btstack_min(sizeof(connection->buffer), connection->mtu);
3335deb0bb6SMatthias Ringwald
3345deb0bb6SMatthias Ringwald btstack_assert(buffer_offset == 0);
3355deb0bb6SMatthias Ringwald
3365deb0bb6SMatthias Ringwald uint8_t field_data[6];
3375deb0bb6SMatthias Ringwald uint16_t source_offset = 0;
3385deb0bb6SMatthias Ringwald uint16_t stored_bytes = 0;
3395deb0bb6SMatthias Ringwald memset(buffer, 0, buffer_size);
3405deb0bb6SMatthias Ringwald
3415deb0bb6SMatthias Ringwald field_data[0] = (uint8_t)BASS_OPCODE_ADD_SOURCE;
34254461c80SMatthias Ringwald stored_bytes += le_audio_util_virtual_memcpy_helper(field_data, 1, source_offset, buffer, buffer_size,
34354461c80SMatthias Ringwald buffer_offset);
3445deb0bb6SMatthias Ringwald source_offset++;
3455deb0bb6SMatthias Ringwald
3466e96f7c7SMatthias Ringwald stored_bytes += bass_util_source_data_header_virtual_memcpy(receive_state, &source_offset, buffer_offset, buffer,
3476e96f7c7SMatthias Ringwald buffer_size);
3485deb0bb6SMatthias Ringwald
3495deb0bb6SMatthias Ringwald field_data[0] = (uint8_t)receive_state->pa_sync;
35054461c80SMatthias Ringwald stored_bytes += le_audio_util_virtual_memcpy_helper(field_data, 1, source_offset, buffer, buffer_size,
35154461c80SMatthias Ringwald buffer_offset);
3525deb0bb6SMatthias Ringwald source_offset++;
3535deb0bb6SMatthias Ringwald
3545deb0bb6SMatthias Ringwald little_endian_store_16(field_data, 0, receive_state->pa_interval);
35554461c80SMatthias Ringwald stored_bytes += le_audio_util_virtual_memcpy_helper(field_data, 2, source_offset, buffer, buffer_size,
35654461c80SMatthias Ringwald buffer_offset);
3575deb0bb6SMatthias Ringwald source_offset += 2;
3585deb0bb6SMatthias Ringwald
3596e96f7c7SMatthias Ringwald stored_bytes += bass_util_source_data_subgroups_virtual_memcpy(receive_state, false, &source_offset, buffer_offset,
3605deb0bb6SMatthias Ringwald buffer,
3615deb0bb6SMatthias Ringwald buffer_size);
3625deb0bb6SMatthias Ringwald
3635deb0bb6SMatthias Ringwald return stored_bytes;
3645deb0bb6SMatthias Ringwald }
3655deb0bb6SMatthias Ringwald
bass_client_prepare_modify_source_buffer(bass_client_connection_t * connection)3665deb0bb6SMatthias Ringwald static uint16_t bass_client_prepare_modify_source_buffer(bass_client_connection_t * connection){
3675deb0bb6SMatthias Ringwald const bass_source_data_t * receive_state = connection->control_point_operation_data;
3685deb0bb6SMatthias Ringwald
3695deb0bb6SMatthias Ringwald uint16_t buffer_offset = connection->buffer_offset;
3705deb0bb6SMatthias Ringwald uint8_t * buffer = connection->buffer;
3715deb0bb6SMatthias Ringwald uint16_t buffer_size = btstack_min(sizeof(connection->buffer), connection->mtu);
3725deb0bb6SMatthias Ringwald
3735deb0bb6SMatthias Ringwald btstack_assert(buffer_offset == 0);
3745deb0bb6SMatthias Ringwald
3755deb0bb6SMatthias Ringwald uint8_t field_data[6];
3765deb0bb6SMatthias Ringwald uint16_t source_offset = 0;
3775deb0bb6SMatthias Ringwald uint16_t stored_bytes = 0;
3785deb0bb6SMatthias Ringwald memset(buffer, 0, buffer_size);
3795deb0bb6SMatthias Ringwald
3805deb0bb6SMatthias Ringwald field_data[0] = (uint8_t)BASS_OPCODE_MODIFY_SOURCE;
38154461c80SMatthias Ringwald stored_bytes += le_audio_util_virtual_memcpy_helper(field_data, 1, source_offset, buffer, buffer_size,
38254461c80SMatthias Ringwald buffer_offset);
3835deb0bb6SMatthias Ringwald source_offset++;
3845deb0bb6SMatthias Ringwald
3855deb0bb6SMatthias Ringwald field_data[0] = connection->control_point_operation_source_id;
38654461c80SMatthias Ringwald stored_bytes += le_audio_util_virtual_memcpy_helper(field_data, 1, source_offset, buffer, buffer_size,
38754461c80SMatthias Ringwald buffer_offset);
3885deb0bb6SMatthias Ringwald source_offset++;
3895deb0bb6SMatthias Ringwald
3905deb0bb6SMatthias Ringwald field_data[0] = (uint8_t)receive_state->pa_sync;
39154461c80SMatthias Ringwald stored_bytes += le_audio_util_virtual_memcpy_helper(field_data, 1, source_offset, buffer, buffer_size,
39254461c80SMatthias Ringwald buffer_offset);
3935deb0bb6SMatthias Ringwald source_offset++;
3945deb0bb6SMatthias Ringwald
3955deb0bb6SMatthias Ringwald little_endian_store_16(field_data, 0, receive_state->pa_interval);
39654461c80SMatthias Ringwald stored_bytes += le_audio_util_virtual_memcpy_helper(field_data, 2, source_offset, buffer, buffer_size,
39754461c80SMatthias Ringwald buffer_offset);
3985deb0bb6SMatthias Ringwald source_offset += 2;
3995deb0bb6SMatthias Ringwald
4006e96f7c7SMatthias Ringwald stored_bytes += bass_util_source_data_subgroups_virtual_memcpy(receive_state, false, &source_offset, buffer_offset,
4015deb0bb6SMatthias Ringwald buffer,
4025deb0bb6SMatthias Ringwald buffer_size);
4035deb0bb6SMatthias Ringwald return stored_bytes;
4045deb0bb6SMatthias Ringwald }
4055deb0bb6SMatthias Ringwald
bass_client_receive_state_len(const bass_source_data_t * source_data)4065deb0bb6SMatthias Ringwald static uint16_t bass_client_receive_state_len(const bass_source_data_t * source_data){
4075deb0bb6SMatthias Ringwald uint16_t source_len = 0;
4085deb0bb6SMatthias Ringwald // opcode(1), address_type(1), address(6), adv_sid(1), broadcast_id(3), pa_sync(1), subgroups_num(1)
4095deb0bb6SMatthias Ringwald source_len = 1 + 1 + 6 + 1 + 3 + 1 + 1;
4105deb0bb6SMatthias Ringwald
4115deb0bb6SMatthias Ringwald uint8_t i;
4125deb0bb6SMatthias Ringwald for (i = 0; i < source_data->subgroups_num; i++){
4135deb0bb6SMatthias Ringwald bass_subgroup_t subgroup = source_data->subgroups[i];
4145deb0bb6SMatthias Ringwald // bis_sync(4), metadata_length(1), metadata_length
4155deb0bb6SMatthias Ringwald source_len += 4 + 1 + subgroup.metadata_length;
4165deb0bb6SMatthias Ringwald }
4175deb0bb6SMatthias Ringwald return source_len;
4185deb0bb6SMatthias Ringwald }
4195deb0bb6SMatthias Ringwald
bass_client_run_for_connection(bass_client_connection_t * connection)4205deb0bb6SMatthias Ringwald static void bass_client_run_for_connection(bass_client_connection_t * connection){
4215deb0bb6SMatthias Ringwald uint8_t status;
4225deb0bb6SMatthias Ringwald gatt_client_characteristic_t characteristic;
4235deb0bb6SMatthias Ringwald gatt_client_service_t service;
4245deb0bb6SMatthias Ringwald
4255deb0bb6SMatthias Ringwald uint8_t value[18];
4265deb0bb6SMatthias Ringwald uint16_t stored_bytes;
4275deb0bb6SMatthias Ringwald
4285deb0bb6SMatthias Ringwald switch (connection->state){
429*59e9e2d1SMatthias Ringwald case BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_STATE_READY:
4305deb0bb6SMatthias Ringwald break;
4315deb0bb6SMatthias Ringwald
4325deb0bb6SMatthias Ringwald case BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_W2_WRITE_CONTROL_POINT_START_SCAN:
4335deb0bb6SMatthias Ringwald #ifdef ENABLE_TESTING_SUPPORT
4345deb0bb6SMatthias Ringwald printf(" Write START SCAN [0x%04X]:\n",
4355deb0bb6SMatthias Ringwald connection->control_point_value_handle);
4365deb0bb6SMatthias Ringwald #endif
4375deb0bb6SMatthias Ringwald
4385deb0bb6SMatthias Ringwald connection->state = BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_W4_WRITE_CONTROL_POINT_START_SCAN;
4395deb0bb6SMatthias Ringwald value[0] = BASS_OPCODE_REMOTE_SCAN_STARTED;
4405deb0bb6SMatthias Ringwald // see GATT_EVENT_QUERY_COMPLETE for end of write
4415deb0bb6SMatthias Ringwald status = gatt_client_write_value_of_characteristic(
44297c78dc6SMilanka Ringwald &bass_client_handle_gatt_client_event, connection->con_handle,
4435deb0bb6SMatthias Ringwald connection->control_point_value_handle, 1, &value[0]);
4445deb0bb6SMatthias Ringwald UNUSED(status);
4455deb0bb6SMatthias Ringwald break;
4465deb0bb6SMatthias Ringwald
4475deb0bb6SMatthias Ringwald case BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_W2_WRITE_CONTROL_POINT_STOP_SCAN:
4485deb0bb6SMatthias Ringwald #ifdef ENABLE_TESTING_SUPPORT
4495deb0bb6SMatthias Ringwald printf(" Write START SCAN [0x%04X]:\n",
4505deb0bb6SMatthias Ringwald connection->control_point_value_handle);
4515deb0bb6SMatthias Ringwald #endif
4525deb0bb6SMatthias Ringwald
4535deb0bb6SMatthias Ringwald connection->state = BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_W4_WRITE_CONTROL_POINT_STOP_SCAN;
4545deb0bb6SMatthias Ringwald value[0] = BASS_OPCODE_REMOTE_SCAN_STOPPED;
4555deb0bb6SMatthias Ringwald // see GATT_EVENT_QUERY_COMPLETE for end of write
4565deb0bb6SMatthias Ringwald status = gatt_client_write_value_of_characteristic(
45797c78dc6SMilanka Ringwald &bass_client_handle_gatt_client_event, connection->con_handle,
4585deb0bb6SMatthias Ringwald connection->control_point_value_handle, 1, &value[0]);
4595deb0bb6SMatthias Ringwald UNUSED(status);
4605deb0bb6SMatthias Ringwald break;
4615deb0bb6SMatthias Ringwald
4625deb0bb6SMatthias Ringwald case BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_W2_WRITE_CONTROL_POINT_ADD_SOURCE:
4635deb0bb6SMatthias Ringwald connection->state = BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_W4_WRITE_CONTROL_POINT_ADD_SOURCE;
4645deb0bb6SMatthias Ringwald #ifdef ENABLE_TESTING_SUPPORT
4655deb0bb6SMatthias Ringwald printf(" ADD SOURCE [0x%04X]:\n", connection->control_point_value_handle);
4665deb0bb6SMatthias Ringwald #endif
4675deb0bb6SMatthias Ringwald connection->state = BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_W4_WRITE_CONTROL_POINT_ADD_SOURCE;
4685deb0bb6SMatthias Ringwald stored_bytes = bass_client_prepare_add_source_buffer(connection);
4695deb0bb6SMatthias Ringwald connection->buffer_offset += stored_bytes;
4705deb0bb6SMatthias Ringwald
471*59e9e2d1SMatthias Ringwald status = gatt_client_write_long_value_of_characteristic(&bass_client_handle_gatt_client_event, connection->con_handle,
4725deb0bb6SMatthias Ringwald connection->control_point_value_handle, stored_bytes, connection->buffer);
473*59e9e2d1SMatthias Ringwald UNUSED(status);
4745deb0bb6SMatthias Ringwald break;
4755deb0bb6SMatthias Ringwald
4765deb0bb6SMatthias Ringwald case BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_W2_WRITE_CONTROL_POINT_MODIFY_SOURCE:
4775deb0bb6SMatthias Ringwald #ifdef ENABLE_TESTING_SUPPORT
4785deb0bb6SMatthias Ringwald printf(" MODIFY SOURCE [%d]:\n", connection->control_point_operation_source_id);
4795deb0bb6SMatthias Ringwald #endif
4805deb0bb6SMatthias Ringwald connection->state = BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_W4_WRITE_CONTROL_POINT_MODIFY_SOURCE;
4815deb0bb6SMatthias Ringwald stored_bytes = bass_client_prepare_modify_source_buffer(connection);
4825deb0bb6SMatthias Ringwald connection->buffer_offset += stored_bytes;
4835deb0bb6SMatthias Ringwald
484*59e9e2d1SMatthias Ringwald status = gatt_client_write_long_value_of_characteristic(&bass_client_handle_gatt_client_event, connection->con_handle,
4855deb0bb6SMatthias Ringwald connection->control_point_value_handle, stored_bytes, connection->buffer);
486*59e9e2d1SMatthias Ringwald UNUSED(status);
4875deb0bb6SMatthias Ringwald break;
4885deb0bb6SMatthias Ringwald
4895deb0bb6SMatthias Ringwald case BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_W2_WRITE_CONTROL_POINT_REMOVE_SOURCE:
4905deb0bb6SMatthias Ringwald #ifdef ENABLE_TESTING_SUPPORT
4915deb0bb6SMatthias Ringwald printf(" REMOVE SOURCE [%d]:\n", connection->control_point_operation_source_id);
4925deb0bb6SMatthias Ringwald #endif
4935deb0bb6SMatthias Ringwald connection->state = BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_W4_WRITE_CONTROL_POINT_REMOVE_SOURCE;
4945deb0bb6SMatthias Ringwald value[0] = BASS_OPCODE_REMOVE_SOURCE;
4955deb0bb6SMatthias Ringwald value[1] = connection->control_point_operation_source_id;
4965deb0bb6SMatthias Ringwald // see GATT_EVENT_QUERY_COMPLETE for end of write
4975deb0bb6SMatthias Ringwald status = gatt_client_write_value_of_characteristic(
49897c78dc6SMilanka Ringwald &bass_client_handle_gatt_client_event, connection->con_handle,
4995deb0bb6SMatthias Ringwald connection->control_point_value_handle, 2, &value[0]);
5005deb0bb6SMatthias Ringwald UNUSED(status);
5015deb0bb6SMatthias Ringwald break;
5025deb0bb6SMatthias Ringwald
5035deb0bb6SMatthias Ringwald case BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_W2_WRITE_CONTROL_POINT_SET_BROADCAST_CODE:
5045deb0bb6SMatthias Ringwald #ifdef ENABLE_TESTING_SUPPORT
5055deb0bb6SMatthias Ringwald printf(" SET BROADCAST CODE [%d]:\n", connection->control_point_operation_source_id);
5065deb0bb6SMatthias Ringwald #endif
5075deb0bb6SMatthias Ringwald connection->state = BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_W4_WRITE_CONTROL_POINT_SET_BROADCAST_CODE;
5085deb0bb6SMatthias Ringwald value[0] = BASS_OPCODE_SET_BROADCAST_CODE;
5095deb0bb6SMatthias Ringwald value[1] = connection->control_point_operation_source_id;
5105deb0bb6SMatthias Ringwald reverse_128(connection->broadcast_code, &value[2]);
5115deb0bb6SMatthias Ringwald
5125deb0bb6SMatthias Ringwald // see GATT_EVENT_QUERY_COMPLETE for end of write
5135deb0bb6SMatthias Ringwald status = gatt_client_write_value_of_characteristic(
51497c78dc6SMilanka Ringwald &bass_client_handle_gatt_client_event, connection->con_handle,
5155deb0bb6SMatthias Ringwald connection->control_point_value_handle, 18, &value[0]);
5165deb0bb6SMatthias Ringwald UNUSED(status);
5175deb0bb6SMatthias Ringwald break;
5185deb0bb6SMatthias Ringwald
5195deb0bb6SMatthias Ringwald case BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_STATE_W2_QUERY_SERVICE:
5205deb0bb6SMatthias Ringwald connection->state = BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_STATE_W4_SERVICE_RESULT;
52197c78dc6SMilanka Ringwald gatt_client_discover_primary_services_by_uuid16(&bass_client_handle_gatt_client_event, connection->con_handle, ORG_BLUETOOTH_SERVICE_BROADCAST_AUDIO_SCAN_SERVICE);
5225deb0bb6SMatthias Ringwald break;
5235deb0bb6SMatthias Ringwald
5245deb0bb6SMatthias Ringwald case BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_STATE_W2_QUERY_CHARACTERISTICS:
5255deb0bb6SMatthias Ringwald connection->state = BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_STATE_W4_CHARACTERISTIC_RESULT;
5265deb0bb6SMatthias Ringwald
5275deb0bb6SMatthias Ringwald service.start_group_handle = connection->start_handle;
5285deb0bb6SMatthias Ringwald service.end_group_handle = connection->end_handle;
5295deb0bb6SMatthias Ringwald service.uuid16 = ORG_BLUETOOTH_SERVICE_BROADCAST_AUDIO_SCAN_SERVICE;
5305deb0bb6SMatthias Ringwald
531*59e9e2d1SMatthias Ringwald status = gatt_client_discover_characteristics_for_service(
53297c78dc6SMilanka Ringwald &bass_client_handle_gatt_client_event,
5335deb0bb6SMatthias Ringwald connection->con_handle,
5345deb0bb6SMatthias Ringwald &service);
535*59e9e2d1SMatthias Ringwald UNUSED(status);
5365deb0bb6SMatthias Ringwald break;
5375deb0bb6SMatthias Ringwald
5385deb0bb6SMatthias Ringwald case BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_STATE_W2_QUERY_CHARACTERISTIC_DESCRIPTORS:
5395deb0bb6SMatthias Ringwald #ifdef ENABLE_TESTING_SUPPORT
5405deb0bb6SMatthias Ringwald printf("Read client characteristic descriptors [index %d]:\n", connection->receive_states_index);
5415deb0bb6SMatthias Ringwald #endif
5425deb0bb6SMatthias Ringwald connection->state = BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_STATE_W4_CHARACTERISTIC_DESCRIPTORS_RESULT;
5435deb0bb6SMatthias Ringwald characteristic.value_handle = connection->receive_states[connection->receive_states_index].receive_state_value_handle;
5445deb0bb6SMatthias Ringwald characteristic.properties = connection->receive_states[connection->receive_states_index].receive_state_properties;
545b0c37fffSMilanka Ringwald characteristic.end_handle = connection->receive_states[connection->receive_states_index].receive_state_end_handle;
5465deb0bb6SMatthias Ringwald
54797c78dc6SMilanka Ringwald (void) gatt_client_discover_characteristic_descriptors(&bass_client_handle_gatt_client_event, connection->con_handle, &characteristic);
5485deb0bb6SMatthias Ringwald break;
5495deb0bb6SMatthias Ringwald
5505deb0bb6SMatthias Ringwald case BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_W2_READ_CHARACTERISTIC_CONFIGURATION:
5515deb0bb6SMatthias Ringwald #ifdef ENABLE_TESTING_SUPPORT
5525deb0bb6SMatthias Ringwald printf("Read client characteristic value [index %d, handle 0x%04X]:\n", connection->receive_states_index, connection->receive_states[connection->receive_states_index].receive_state_ccc_handle);
5535deb0bb6SMatthias Ringwald #endif
5545deb0bb6SMatthias Ringwald connection->state = BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_W4_CHARACTERISTIC_CONFIGURATION_RESULT;
5555deb0bb6SMatthias Ringwald
5565deb0bb6SMatthias Ringwald // result in GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT
5575deb0bb6SMatthias Ringwald (void) gatt_client_read_characteristic_descriptor_using_descriptor_handle(
55897c78dc6SMilanka Ringwald &bass_client_handle_gatt_client_event,
5595deb0bb6SMatthias Ringwald connection->con_handle,
5605deb0bb6SMatthias Ringwald connection->receive_states[connection->receive_states_index].receive_state_ccc_handle);
5615deb0bb6SMatthias Ringwald break;
5625deb0bb6SMatthias Ringwald
5635deb0bb6SMatthias Ringwald case BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_STATE_W2_REGISTER_NOTIFICATION:
5645deb0bb6SMatthias Ringwald #ifdef ENABLE_TESTING_SUPPORT
5655deb0bb6SMatthias Ringwald printf("Register notification [index %d, handle 0x%04X]:\n", connection->receive_states_index, connection->receive_states[connection->receive_states_index].receive_state_ccc_handle);
5665deb0bb6SMatthias Ringwald #endif
5675deb0bb6SMatthias Ringwald
5685deb0bb6SMatthias Ringwald connection->state = BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_STATE_W4_NOTIFICATION_REGISTERED;
5695deb0bb6SMatthias Ringwald
5705deb0bb6SMatthias Ringwald status = bass_client_register_notification(connection);
5715deb0bb6SMatthias Ringwald if (status == ERROR_CODE_SUCCESS) return;
5725deb0bb6SMatthias Ringwald
5735deb0bb6SMatthias Ringwald if (connection->receive_states[connection->receive_states_index].receive_state_ccc_handle != 0){
5745deb0bb6SMatthias Ringwald connection->state = BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_W2_READ_CHARACTERISTIC_CONFIGURATION;
5755deb0bb6SMatthias Ringwald break;
5765deb0bb6SMatthias Ringwald }
5775deb0bb6SMatthias Ringwald
578*59e9e2d1SMatthias Ringwald bass_client_connected(connection, ERROR_CODE_SUCCESS);
5795deb0bb6SMatthias Ringwald break;
5805deb0bb6SMatthias Ringwald default:
5815deb0bb6SMatthias Ringwald break;
5825deb0bb6SMatthias Ringwald }
5835deb0bb6SMatthias Ringwald }
5845deb0bb6SMatthias Ringwald // @return true if client valid / run function should be called
bass_client_handle_query_complete(bass_client_connection_t * connection,uint8_t status)5855deb0bb6SMatthias Ringwald static bool bass_client_handle_query_complete(bass_client_connection_t * connection, uint8_t status){
5865deb0bb6SMatthias Ringwald switch (connection->state){
5875deb0bb6SMatthias Ringwald case BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_STATE_W4_SERVICE_RESULT:
588*59e9e2d1SMatthias Ringwald switch (status){
589*59e9e2d1SMatthias Ringwald case ATT_ERROR_SUCCESS:
590*59e9e2d1SMatthias Ringwald break;
591*59e9e2d1SMatthias Ringwald case ATT_ERROR_INVALID_ATTRIBUTE_VALUE_LENGTH:
592*59e9e2d1SMatthias Ringwald bass_client_connected(connection, ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE);
593*59e9e2d1SMatthias Ringwald return false;
594*59e9e2d1SMatthias Ringwald default:
595*59e9e2d1SMatthias Ringwald bass_client_connected(connection, ERROR_CODE_UNSPECIFIED_ERROR);
5965deb0bb6SMatthias Ringwald return false;
5975deb0bb6SMatthias Ringwald }
5985deb0bb6SMatthias Ringwald
5995deb0bb6SMatthias Ringwald if (connection->service_instances_num == 0){
600*59e9e2d1SMatthias Ringwald bass_client_connected(connection, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
6015deb0bb6SMatthias Ringwald return false;
6025deb0bb6SMatthias Ringwald }
6035deb0bb6SMatthias Ringwald connection->receive_states_index = 0;
6045deb0bb6SMatthias Ringwald connection->receive_states_instances_num = 0;
6055deb0bb6SMatthias Ringwald connection->state = BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_STATE_W2_QUERY_CHARACTERISTICS;
6065deb0bb6SMatthias Ringwald break;
6075deb0bb6SMatthias Ringwald
6085deb0bb6SMatthias Ringwald case BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_STATE_W4_CHARACTERISTIC_RESULT:
6095deb0bb6SMatthias Ringwald if (status != ATT_ERROR_SUCCESS){
610*59e9e2d1SMatthias Ringwald bass_client_connected(connection, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
6115deb0bb6SMatthias Ringwald return false;
6125deb0bb6SMatthias Ringwald }
6135deb0bb6SMatthias Ringwald
6145deb0bb6SMatthias Ringwald connection->receive_states_index = 0;
6155deb0bb6SMatthias Ringwald connection->state = BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_STATE_W2_QUERY_CHARACTERISTIC_DESCRIPTORS;
6165deb0bb6SMatthias Ringwald break;
6175deb0bb6SMatthias Ringwald
6185deb0bb6SMatthias Ringwald case BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_STATE_W4_CHARACTERISTIC_DESCRIPTORS_RESULT:
6195deb0bb6SMatthias Ringwald if (connection->receive_states_index < (connection->receive_states_instances_num - 1)){
6205deb0bb6SMatthias Ringwald connection->receive_states_index++;
6215deb0bb6SMatthias Ringwald connection->state = BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_STATE_W2_QUERY_CHARACTERISTIC_DESCRIPTORS;
6225deb0bb6SMatthias Ringwald break;
6235deb0bb6SMatthias Ringwald }
6245deb0bb6SMatthias Ringwald connection->receive_states_index = 0;
6255deb0bb6SMatthias Ringwald connection->state = BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_STATE_W2_REGISTER_NOTIFICATION;
6265deb0bb6SMatthias Ringwald break;
6275deb0bb6SMatthias Ringwald
6285deb0bb6SMatthias Ringwald case BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_STATE_W4_NOTIFICATION_REGISTERED:
6295deb0bb6SMatthias Ringwald if (connection->receive_states_index < (connection->receive_states_instances_num - 1)){
6305deb0bb6SMatthias Ringwald connection->receive_states_index++;
6315deb0bb6SMatthias Ringwald connection->state = BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_STATE_W2_REGISTER_NOTIFICATION;
6325deb0bb6SMatthias Ringwald break;
6335deb0bb6SMatthias Ringwald }
6345deb0bb6SMatthias Ringwald
635*59e9e2d1SMatthias Ringwald bass_client_connected(connection, ERROR_CODE_SUCCESS);
6365deb0bb6SMatthias Ringwald break;
6375deb0bb6SMatthias Ringwald
6385deb0bb6SMatthias Ringwald case BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_W4_CHARACTERISTIC_CONFIGURATION_RESULT:
639*59e9e2d1SMatthias Ringwald bass_client_connected(connection, ERROR_CODE_SUCCESS);
6405deb0bb6SMatthias Ringwald break;
6415deb0bb6SMatthias Ringwald
6425deb0bb6SMatthias Ringwald case BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_W4_WRITE_CONTROL_POINT_START_SCAN:
643*59e9e2d1SMatthias Ringwald connection->state = BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_STATE_READY;
6445deb0bb6SMatthias Ringwald bass_client_emit_scan_operation_complete(connection, status, BASS_OPCODE_REMOTE_SCAN_STARTED);
6455deb0bb6SMatthias Ringwald break;
6465deb0bb6SMatthias Ringwald
6475deb0bb6SMatthias Ringwald case BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_W4_WRITE_CONTROL_POINT_STOP_SCAN:
648*59e9e2d1SMatthias Ringwald connection->state = BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_STATE_READY;
6495deb0bb6SMatthias Ringwald bass_client_emit_scan_operation_complete(connection, status, BASS_OPCODE_REMOTE_SCAN_STOPPED);
6505deb0bb6SMatthias Ringwald break;
6515deb0bb6SMatthias Ringwald
6525deb0bb6SMatthias Ringwald case BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_W4_WRITE_CONTROL_POINT_ADD_SOURCE:
653*59e9e2d1SMatthias Ringwald connection->state = BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_STATE_READY;
6545deb0bb6SMatthias Ringwald bass_client_emit_source_operation_complete(connection, status, BASS_OPCODE_ADD_SOURCE, BASS_INVALID_SOURCE_INDEX);
6555deb0bb6SMatthias Ringwald break;
6565deb0bb6SMatthias Ringwald
6575deb0bb6SMatthias Ringwald case BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_W4_WRITE_CONTROL_POINT_MODIFY_SOURCE:
658*59e9e2d1SMatthias Ringwald connection->state = BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_STATE_READY;
6595deb0bb6SMatthias Ringwald bass_client_emit_source_operation_complete(connection, status, BASS_OPCODE_MODIFY_SOURCE, connection->control_point_operation_source_id);
6605deb0bb6SMatthias Ringwald break;
6615deb0bb6SMatthias Ringwald
6625deb0bb6SMatthias Ringwald case BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_W4_WRITE_CONTROL_POINT_REMOVE_SOURCE:
663*59e9e2d1SMatthias Ringwald connection->state = BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_STATE_READY;
66497c78dc6SMilanka Ringwald bass_client_reset_source(
66597c78dc6SMilanka Ringwald bass_client_get_source_for_source_id(connection, connection->control_point_operation_source_id));
6665deb0bb6SMatthias Ringwald bass_client_emit_source_operation_complete(connection, status, BASS_OPCODE_REMOVE_SOURCE, connection->control_point_operation_source_id);
6675deb0bb6SMatthias Ringwald break;
6685deb0bb6SMatthias Ringwald
6695deb0bb6SMatthias Ringwald case BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_W4_WRITE_CONTROL_POINT_SET_BROADCAST_CODE:
670*59e9e2d1SMatthias Ringwald connection->state = BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_STATE_READY;
6715deb0bb6SMatthias Ringwald bass_client_emit_source_operation_complete(connection, status, BASS_OPCODE_SET_BROADCAST_CODE, connection->control_point_operation_source_id);
6725deb0bb6SMatthias Ringwald break;
6735deb0bb6SMatthias Ringwald
674*59e9e2d1SMatthias Ringwald case BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_STATE_READY:
6755deb0bb6SMatthias Ringwald break;
6765deb0bb6SMatthias Ringwald
6775deb0bb6SMatthias Ringwald default:
6785deb0bb6SMatthias Ringwald break;
6795deb0bb6SMatthias Ringwald
6805deb0bb6SMatthias Ringwald }
6815deb0bb6SMatthias Ringwald return true;
6825deb0bb6SMatthias Ringwald }
6835deb0bb6SMatthias Ringwald
bass_client_handle_gatt_client_event(uint8_t packet_type,uint16_t channel,uint8_t * packet,uint16_t size)68497c78dc6SMilanka Ringwald static void bass_client_handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
6855deb0bb6SMatthias Ringwald UNUSED(packet_type);
6865deb0bb6SMatthias Ringwald UNUSED(channel);
6875deb0bb6SMatthias Ringwald UNUSED(size);
6885deb0bb6SMatthias Ringwald
6895deb0bb6SMatthias Ringwald bass_client_connection_t * connection = NULL;
6905deb0bb6SMatthias Ringwald gatt_client_service_t service;
6915deb0bb6SMatthias Ringwald gatt_client_characteristic_t characteristic;
6925deb0bb6SMatthias Ringwald gatt_client_characteristic_descriptor_t characteristic_descriptor;
6935deb0bb6SMatthias Ringwald
6945deb0bb6SMatthias Ringwald bool call_run = true;
6955deb0bb6SMatthias Ringwald
6965deb0bb6SMatthias Ringwald switch(hci_event_packet_get_type(packet)){
6975deb0bb6SMatthias Ringwald case GATT_EVENT_MTU:
6985deb0bb6SMatthias Ringwald connection = bass_client_get_connection_for_con_handle(gatt_event_mtu_get_handle(packet));
6995deb0bb6SMatthias Ringwald btstack_assert(connection != NULL);
7005deb0bb6SMatthias Ringwald connection->mtu = gatt_event_mtu_get_MTU(packet);
7015deb0bb6SMatthias Ringwald break;
7025deb0bb6SMatthias Ringwald
7035deb0bb6SMatthias Ringwald case GATT_EVENT_SERVICE_QUERY_RESULT:
7045deb0bb6SMatthias Ringwald connection = bass_client_get_connection_for_con_handle(gatt_event_service_query_result_get_handle(packet));
7055deb0bb6SMatthias Ringwald btstack_assert(connection != NULL);
7065deb0bb6SMatthias Ringwald
7075deb0bb6SMatthias Ringwald if (connection->service_instances_num < 1){
7085deb0bb6SMatthias Ringwald gatt_event_service_query_result_get_service(packet, &service);
7095deb0bb6SMatthias Ringwald connection->start_handle = service.start_group_handle;
7105deb0bb6SMatthias Ringwald connection->end_handle = service.end_group_handle;
7115deb0bb6SMatthias Ringwald
7125deb0bb6SMatthias Ringwald #ifdef ENABLE_TESTING_SUPPORT
7135deb0bb6SMatthias Ringwald printf("BASS Service: start handle 0x%04X, end handle 0x%04X\n", connection->start_handle, connection->end_handle);
7145deb0bb6SMatthias Ringwald #endif
7155deb0bb6SMatthias Ringwald connection->service_instances_num++;
7165deb0bb6SMatthias Ringwald } else {
7175deb0bb6SMatthias Ringwald log_info("Found more then one BASS Service instance. ");
7185deb0bb6SMatthias Ringwald }
7195deb0bb6SMatthias Ringwald break;
7205deb0bb6SMatthias Ringwald
7215deb0bb6SMatthias Ringwald case GATT_EVENT_CHARACTERISTIC_QUERY_RESULT:
7225deb0bb6SMatthias Ringwald connection = bass_client_get_connection_for_con_handle(gatt_event_characteristic_query_result_get_handle(packet));
7235deb0bb6SMatthias Ringwald btstack_assert(connection != NULL);
7245deb0bb6SMatthias Ringwald
7255deb0bb6SMatthias Ringwald gatt_event_characteristic_query_result_get_characteristic(packet, &characteristic);
7265deb0bb6SMatthias Ringwald
7275deb0bb6SMatthias Ringwald switch (characteristic.uuid16){
7285deb0bb6SMatthias Ringwald case ORG_BLUETOOTH_CHARACTERISTIC_BROADCAST_AUDIO_SCAN_CONTROL_POINT:
7295deb0bb6SMatthias Ringwald connection->control_point_value_handle = characteristic.value_handle;
7305deb0bb6SMatthias Ringwald break;
7315deb0bb6SMatthias Ringwald case ORG_BLUETOOTH_CHARACTERISTIC_BROADCAST_RECEIVE_STATE:
7325deb0bb6SMatthias Ringwald if (connection->receive_states_instances_num < connection->max_receive_states_num){
7335deb0bb6SMatthias Ringwald connection->receive_states[connection->receive_states_instances_num].receive_state_value_handle = characteristic.value_handle;
7345deb0bb6SMatthias Ringwald connection->receive_states[connection->receive_states_instances_num].receive_state_properties = characteristic.properties;
735b0c37fffSMilanka Ringwald connection->receive_states[connection->receive_states_index].receive_state_end_handle = characteristic.end_handle;
7365deb0bb6SMatthias Ringwald connection->receive_states_instances_num++;
7375deb0bb6SMatthias Ringwald } else {
7385deb0bb6SMatthias Ringwald log_info("Found more BASS receive_states chrs then it can be stored. ");
7395deb0bb6SMatthias Ringwald }
7405deb0bb6SMatthias Ringwald break;
7415deb0bb6SMatthias Ringwald default:
7425deb0bb6SMatthias Ringwald btstack_assert(false);
7435deb0bb6SMatthias Ringwald return;
7445deb0bb6SMatthias Ringwald }
7455deb0bb6SMatthias Ringwald
7465deb0bb6SMatthias Ringwald #ifdef ENABLE_TESTING_SUPPORT
7475deb0bb6SMatthias Ringwald printf("BASS Characteristic:\n Attribute Handle 0x%04X, Properties 0x%02X, Handle 0x%04X, UUID 0x%04X, %s\n",
7485deb0bb6SMatthias Ringwald characteristic.start_handle,
7495deb0bb6SMatthias Ringwald characteristic.properties,
7505deb0bb6SMatthias Ringwald characteristic.value_handle, characteristic.uuid16,
7515deb0bb6SMatthias Ringwald characteristic.uuid16 == ORG_BLUETOOTH_CHARACTERISTIC_BROADCAST_RECEIVE_STATE ? "RECEIVE_STATE" : "CONTROL_POINT");
7525deb0bb6SMatthias Ringwald #endif
7535deb0bb6SMatthias Ringwald break;
7545deb0bb6SMatthias Ringwald
7555deb0bb6SMatthias Ringwald
7565deb0bb6SMatthias Ringwald case GATT_EVENT_ALL_CHARACTERISTIC_DESCRIPTORS_QUERY_RESULT:
7575deb0bb6SMatthias Ringwald connection = bass_client_get_connection_for_con_handle(gatt_event_all_characteristic_descriptors_query_result_get_handle(packet));
7585deb0bb6SMatthias Ringwald btstack_assert(connection != NULL);
7595deb0bb6SMatthias Ringwald gatt_event_all_characteristic_descriptors_query_result_get_characteristic_descriptor(packet, &characteristic_descriptor);
7605deb0bb6SMatthias Ringwald
7615deb0bb6SMatthias Ringwald if (characteristic_descriptor.uuid16 == ORG_BLUETOOTH_DESCRIPTOR_GATT_CLIENT_CHARACTERISTIC_CONFIGURATION){
7625deb0bb6SMatthias Ringwald connection->receive_states[connection->receive_states_index].receive_state_ccc_handle = characteristic_descriptor.handle;
7635deb0bb6SMatthias Ringwald
7645deb0bb6SMatthias Ringwald #ifdef ENABLE_TESTING_SUPPORT
7655deb0bb6SMatthias Ringwald printf(" BASS Characteristic Configuration Descriptor: Handle 0x%04X, UUID 0x%04X\n",
7665deb0bb6SMatthias Ringwald characteristic_descriptor.handle,
7675deb0bb6SMatthias Ringwald characteristic_descriptor.uuid16);
7685deb0bb6SMatthias Ringwald #endif
7695deb0bb6SMatthias Ringwald }
7705deb0bb6SMatthias Ringwald break;
7715deb0bb6SMatthias Ringwald
7725deb0bb6SMatthias Ringwald
7735deb0bb6SMatthias Ringwald case GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT:
7745deb0bb6SMatthias Ringwald connection = bass_client_get_connection_for_con_handle(gatt_event_characteristic_value_query_result_get_handle(packet));
7755deb0bb6SMatthias Ringwald btstack_assert(connection != NULL);
7765deb0bb6SMatthias Ringwald
7775deb0bb6SMatthias Ringwald if (connection->state == BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_W4_CHARACTERISTIC_CONFIGURATION_RESULT){
7785deb0bb6SMatthias Ringwald #ifdef ENABLE_TESTING_SUPPORT
7795deb0bb6SMatthias Ringwald printf(" Received CCC value: ");
7805deb0bb6SMatthias Ringwald printf_hexdump(gatt_event_characteristic_value_query_result_get_value(packet), gatt_event_characteristic_value_query_result_get_value_length(packet));
7815deb0bb6SMatthias Ringwald #endif
7825deb0bb6SMatthias Ringwald break;
7835deb0bb6SMatthias Ringwald }
7845deb0bb6SMatthias Ringwald
7855deb0bb6SMatthias Ringwald if (gatt_event_characteristic_value_query_result_get_value_length(packet) == 0 ){
7865deb0bb6SMatthias Ringwald break;
7875deb0bb6SMatthias Ringwald }
7885deb0bb6SMatthias Ringwald
7895deb0bb6SMatthias Ringwald // TODO
7905deb0bb6SMatthias Ringwald // bass_client_emit_receive_state(connection,
7915deb0bb6SMatthias Ringwald // gatt_event_characteristic_value_query_result_get_value_handle(packet),
7925deb0bb6SMatthias Ringwald // ATT_ERROR_SUCCESS,
7935deb0bb6SMatthias Ringwald // gatt_event_notification_get_value(packet),
7945deb0bb6SMatthias Ringwald // gatt_event_notification_get_value_length(packet));
7955deb0bb6SMatthias Ringwald break;
7965deb0bb6SMatthias Ringwald
7975deb0bb6SMatthias Ringwald case GATT_EVENT_QUERY_COMPLETE:
7985deb0bb6SMatthias Ringwald connection = bass_client_get_connection_for_con_handle(gatt_event_query_complete_get_handle(packet));
7995deb0bb6SMatthias Ringwald btstack_assert(connection != NULL);
8005deb0bb6SMatthias Ringwald call_run = bass_client_handle_query_complete(connection, gatt_event_query_complete_get_att_status(packet));
8015deb0bb6SMatthias Ringwald break;
8025deb0bb6SMatthias Ringwald
8035deb0bb6SMatthias Ringwald default:
8045deb0bb6SMatthias Ringwald break;
8055deb0bb6SMatthias Ringwald }
8065deb0bb6SMatthias Ringwald
8075deb0bb6SMatthias Ringwald if (call_run && (connection != NULL)){
8085deb0bb6SMatthias Ringwald bass_client_run_for_connection(connection);
8095deb0bb6SMatthias Ringwald }
8105deb0bb6SMatthias Ringwald }
8115deb0bb6SMatthias Ringwald
broadcast_audio_scan_service_client_init(btstack_packet_handler_t packet_handler)8125deb0bb6SMatthias Ringwald void broadcast_audio_scan_service_client_init(btstack_packet_handler_t packet_handler){
8135deb0bb6SMatthias Ringwald btstack_assert(packet_handler != NULL);
81497c78dc6SMilanka Ringwald bass_client_event_callback = packet_handler;
8155deb0bb6SMatthias Ringwald }
8165deb0bb6SMatthias Ringwald
broadcast_audio_scan_service_client_connect(bass_client_connection_t * connection,bass_client_source_t * receive_states,uint8_t receive_states_num,hci_con_handle_t con_handle,uint16_t * bass_cid)8175deb0bb6SMatthias Ringwald uint8_t broadcast_audio_scan_service_client_connect(bass_client_connection_t * connection,
8185deb0bb6SMatthias Ringwald bass_client_source_t * receive_states, uint8_t receive_states_num,
8195deb0bb6SMatthias Ringwald hci_con_handle_t con_handle, uint16_t * bass_cid){
8205deb0bb6SMatthias Ringwald
8215deb0bb6SMatthias Ringwald btstack_assert(receive_states != NULL);
8225deb0bb6SMatthias Ringwald btstack_assert(receive_states_num > 0);
8235deb0bb6SMatthias Ringwald
8245deb0bb6SMatthias Ringwald if (bass_client_get_connection_for_con_handle(con_handle) != NULL){
8255deb0bb6SMatthias Ringwald return ERROR_CODE_COMMAND_DISALLOWED;
8265deb0bb6SMatthias Ringwald }
8275deb0bb6SMatthias Ringwald
8285deb0bb6SMatthias Ringwald uint16_t cid = bass_client_get_next_cid();
8295deb0bb6SMatthias Ringwald if (bass_cid != NULL) {
8305deb0bb6SMatthias Ringwald *bass_cid = cid;
8315deb0bb6SMatthias Ringwald }
8325deb0bb6SMatthias Ringwald
8335deb0bb6SMatthias Ringwald connection->cid = cid;
8345deb0bb6SMatthias Ringwald connection->con_handle = con_handle;
8355deb0bb6SMatthias Ringwald connection->mtu = ATT_DEFAULT_MTU;
8365deb0bb6SMatthias Ringwald
8375deb0bb6SMatthias Ringwald connection->max_receive_states_num = receive_states_num;
8385deb0bb6SMatthias Ringwald connection->receive_states = receive_states;
8395deb0bb6SMatthias Ringwald
8405deb0bb6SMatthias Ringwald uint8_t i;
8415deb0bb6SMatthias Ringwald for (i = 0; i < connection->max_receive_states_num; i++){
8425deb0bb6SMatthias Ringwald connection->receive_states[i].in_use = false;
8435deb0bb6SMatthias Ringwald connection->receive_states[i].source_id = BASS_INVALID_SOURCE_INDEX;
8445deb0bb6SMatthias Ringwald }
8455deb0bb6SMatthias Ringwald
8465deb0bb6SMatthias Ringwald connection->service_instances_num = 0;
8475deb0bb6SMatthias Ringwald connection->receive_states_instances_num = 0;
8485deb0bb6SMatthias Ringwald connection->receive_states_index = 0;
8495deb0bb6SMatthias Ringwald
8505deb0bb6SMatthias Ringwald connection->state = BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_STATE_W2_QUERY_SERVICE;
85197c78dc6SMilanka Ringwald btstack_linked_list_add(&bass_client_connections, (btstack_linked_item_t *) connection);
8525deb0bb6SMatthias Ringwald
8535deb0bb6SMatthias Ringwald bass_client_run_for_connection(connection);
8545deb0bb6SMatthias Ringwald return ERROR_CODE_SUCCESS;
8555deb0bb6SMatthias Ringwald }
8565deb0bb6SMatthias Ringwald
broadcast_audio_scan_service_client_scanning_started(uint16_t bass_cid)8575deb0bb6SMatthias Ringwald uint8_t broadcast_audio_scan_service_client_scanning_started(uint16_t bass_cid){
85897c78dc6SMilanka Ringwald bass_client_connection_t * connection = bass_client_get_connection_for_cid(bass_cid);
8595deb0bb6SMatthias Ringwald if (connection == NULL){
8605deb0bb6SMatthias Ringwald return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
8615deb0bb6SMatthias Ringwald }
862*59e9e2d1SMatthias Ringwald if (connection->state != BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_STATE_READY){
8635deb0bb6SMatthias Ringwald return ERROR_CODE_COMMAND_DISALLOWED;
8645deb0bb6SMatthias Ringwald }
8655deb0bb6SMatthias Ringwald connection->state = BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_W2_WRITE_CONTROL_POINT_START_SCAN;
8665deb0bb6SMatthias Ringwald bass_client_run_for_connection(connection);
8675deb0bb6SMatthias Ringwald return ERROR_CODE_SUCCESS;
8685deb0bb6SMatthias Ringwald }
8695deb0bb6SMatthias Ringwald
broadcast_audio_scan_service_client_scanning_stopped(uint16_t bass_cid)8705deb0bb6SMatthias Ringwald uint8_t broadcast_audio_scan_service_client_scanning_stopped(uint16_t bass_cid){
87197c78dc6SMilanka Ringwald bass_client_connection_t * connection = bass_client_get_connection_for_cid(bass_cid);
8725deb0bb6SMatthias Ringwald if (connection == NULL){
8735deb0bb6SMatthias Ringwald return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
8745deb0bb6SMatthias Ringwald }
875*59e9e2d1SMatthias Ringwald if (connection->state != BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_STATE_READY){
8765deb0bb6SMatthias Ringwald return ERROR_CODE_COMMAND_DISALLOWED;
8775deb0bb6SMatthias Ringwald }
8785deb0bb6SMatthias Ringwald connection->state = BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_W2_WRITE_CONTROL_POINT_STOP_SCAN;
8795deb0bb6SMatthias Ringwald bass_client_run_for_connection(connection);
8805deb0bb6SMatthias Ringwald return ERROR_CODE_SUCCESS;
8815deb0bb6SMatthias Ringwald }
8825deb0bb6SMatthias Ringwald
broadcast_audio_scan_service_client_add_source(uint16_t bass_cid,const bass_source_data_t * add_source_data)8835deb0bb6SMatthias Ringwald uint8_t broadcast_audio_scan_service_client_add_source(uint16_t bass_cid, const bass_source_data_t * add_source_data){
88497c78dc6SMilanka Ringwald bass_client_connection_t * connection = bass_client_get_connection_for_cid(bass_cid);
8855deb0bb6SMatthias Ringwald if (connection == NULL){
8865deb0bb6SMatthias Ringwald return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
8875deb0bb6SMatthias Ringwald }
888*59e9e2d1SMatthias Ringwald if (connection->state != BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_STATE_READY){
8895deb0bb6SMatthias Ringwald return ERROR_CODE_COMMAND_DISALLOWED;
8905deb0bb6SMatthias Ringwald }
8915deb0bb6SMatthias Ringwald
8925deb0bb6SMatthias Ringwald connection->state = BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_W2_WRITE_CONTROL_POINT_ADD_SOURCE;
8935deb0bb6SMatthias Ringwald connection->control_point_operation_data = add_source_data;
8945deb0bb6SMatthias Ringwald connection->buffer_offset = 0;
8955deb0bb6SMatthias Ringwald connection->data_size = bass_client_receive_state_len(add_source_data);
8965deb0bb6SMatthias Ringwald
8975deb0bb6SMatthias Ringwald bass_client_run_for_connection(connection);
8985deb0bb6SMatthias Ringwald return ERROR_CODE_SUCCESS;
8995deb0bb6SMatthias Ringwald }
9005deb0bb6SMatthias Ringwald
broadcast_audio_scan_service_client_modify_source(uint16_t bass_cid,uint8_t source_id,const bass_source_data_t * modify_source_data)9015deb0bb6SMatthias Ringwald uint8_t broadcast_audio_scan_service_client_modify_source(uint16_t bass_cid, uint8_t source_id, const bass_source_data_t * modify_source_data){
90297c78dc6SMilanka Ringwald bass_client_connection_t * connection = bass_client_get_connection_for_cid(bass_cid);
9035deb0bb6SMatthias Ringwald if (connection == NULL){
9045deb0bb6SMatthias Ringwald return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
9055deb0bb6SMatthias Ringwald }
906*59e9e2d1SMatthias Ringwald if (connection->state != BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_STATE_READY){
9075deb0bb6SMatthias Ringwald return ERROR_CODE_COMMAND_DISALLOWED;
9085deb0bb6SMatthias Ringwald }
9095deb0bb6SMatthias Ringwald
9105deb0bb6SMatthias Ringwald connection->state = BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_W2_WRITE_CONTROL_POINT_MODIFY_SOURCE;
9115deb0bb6SMatthias Ringwald connection->control_point_operation_data = modify_source_data;
9125deb0bb6SMatthias Ringwald connection->control_point_operation_source_id = source_id;
9135deb0bb6SMatthias Ringwald connection->buffer_offset = 0;
9145deb0bb6SMatthias Ringwald connection->data_size = bass_client_receive_state_len(modify_source_data);
9155deb0bb6SMatthias Ringwald
9165deb0bb6SMatthias Ringwald bass_client_run_for_connection(connection);
9175deb0bb6SMatthias Ringwald return ERROR_CODE_SUCCESS;
9185deb0bb6SMatthias Ringwald }
9195deb0bb6SMatthias Ringwald
broadcast_audio_scan_service_client_remove_source(uint16_t bass_cid,uint8_t source_id)9205deb0bb6SMatthias Ringwald uint8_t broadcast_audio_scan_service_client_remove_source(uint16_t bass_cid, uint8_t source_id){
92197c78dc6SMilanka Ringwald bass_client_connection_t * connection = bass_client_get_connection_for_cid(bass_cid);
9225deb0bb6SMatthias Ringwald if (connection == NULL){
9235deb0bb6SMatthias Ringwald return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
9245deb0bb6SMatthias Ringwald }
925*59e9e2d1SMatthias Ringwald if (connection->state != BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_STATE_READY){
9265deb0bb6SMatthias Ringwald return ERROR_CODE_COMMAND_DISALLOWED;
9275deb0bb6SMatthias Ringwald }
9285deb0bb6SMatthias Ringwald
9295deb0bb6SMatthias Ringwald connection->state = BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_W2_WRITE_CONTROL_POINT_REMOVE_SOURCE;
9305deb0bb6SMatthias Ringwald connection->control_point_operation_source_id = source_id;
9315deb0bb6SMatthias Ringwald
9325deb0bb6SMatthias Ringwald bass_client_run_for_connection(connection);
9335deb0bb6SMatthias Ringwald return ERROR_CODE_SUCCESS;
9345deb0bb6SMatthias Ringwald }
9355deb0bb6SMatthias Ringwald
broadcast_audio_scan_service_client_set_broadcast_code(uint16_t bass_cid,uint8_t source_id,const uint8_t * broadcast_code)9365deb0bb6SMatthias Ringwald uint8_t broadcast_audio_scan_service_client_set_broadcast_code(uint16_t bass_cid, uint8_t source_id, const uint8_t * broadcast_code){
93797c78dc6SMilanka Ringwald bass_client_connection_t * connection = bass_client_get_connection_for_cid(bass_cid);
9385deb0bb6SMatthias Ringwald if (connection == NULL){
9395deb0bb6SMatthias Ringwald return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
9405deb0bb6SMatthias Ringwald }
941*59e9e2d1SMatthias Ringwald if (connection->state != BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_STATE_READY){
9425deb0bb6SMatthias Ringwald return ERROR_CODE_COMMAND_DISALLOWED;
9435deb0bb6SMatthias Ringwald }
9445deb0bb6SMatthias Ringwald
9455deb0bb6SMatthias Ringwald connection->state = BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_W2_WRITE_CONTROL_POINT_SET_BROADCAST_CODE;
9465deb0bb6SMatthias Ringwald connection->control_point_operation_source_id = source_id;
9475deb0bb6SMatthias Ringwald connection->broadcast_code = broadcast_code;
9485deb0bb6SMatthias Ringwald
9495deb0bb6SMatthias Ringwald bass_client_run_for_connection(connection);
9505deb0bb6SMatthias Ringwald return ERROR_CODE_SUCCESS;
9515deb0bb6SMatthias Ringwald }
9525deb0bb6SMatthias Ringwald
broadcast_audio_scan_service_client_get_source_data(uint16_t bass_cid,uint8_t source_id)9535deb0bb6SMatthias Ringwald const bass_source_data_t * broadcast_audio_scan_service_client_get_source_data(uint16_t bass_cid, uint8_t source_id){
95497c78dc6SMilanka Ringwald bass_client_connection_t * connection = bass_client_get_connection_for_cid(bass_cid);
9555deb0bb6SMatthias Ringwald if (connection == NULL){
9565deb0bb6SMatthias Ringwald return NULL;
9575deb0bb6SMatthias Ringwald }
958*59e9e2d1SMatthias Ringwald if (connection->state != BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_STATE_READY){
9595deb0bb6SMatthias Ringwald return NULL;
9605deb0bb6SMatthias Ringwald }
96197c78dc6SMilanka Ringwald return (const bass_source_data_t *) &bass_client_get_source_for_source_id(connection, source_id)->data;
9625deb0bb6SMatthias Ringwald }
9635deb0bb6SMatthias Ringwald
broadcast_audio_scan_service_client_get_encryption_state(uint16_t bass_cid,uint8_t source_id,le_audio_big_encryption_t * out_big_encryption,uint8_t * out_bad_code)9645deb0bb6SMatthias Ringwald uint8_t broadcast_audio_scan_service_client_get_encryption_state(uint16_t bass_cid, uint8_t source_id,
9655deb0bb6SMatthias Ringwald le_audio_big_encryption_t * out_big_encryption, uint8_t * out_bad_code){
9665deb0bb6SMatthias Ringwald btstack_assert(out_big_encryption != NULL);
9675deb0bb6SMatthias Ringwald btstack_assert(out_bad_code != NULL);
9685deb0bb6SMatthias Ringwald
96997c78dc6SMilanka Ringwald bass_client_connection_t * connection = bass_client_get_connection_for_cid(bass_cid);
9705deb0bb6SMatthias Ringwald if (connection == NULL){
9715deb0bb6SMatthias Ringwald return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
9725deb0bb6SMatthias Ringwald }
973*59e9e2d1SMatthias Ringwald if (connection->state != BROADCAST_AUDIO_SCAN_SERVICE_CLIENT_STATE_READY){
9745deb0bb6SMatthias Ringwald return ERROR_CODE_COMMAND_DISALLOWED;
9755deb0bb6SMatthias Ringwald }
97697c78dc6SMilanka Ringwald bass_client_source_t * source = bass_client_get_source_for_source_id(connection, source_id);
9775deb0bb6SMatthias Ringwald if (source == NULL){
9785deb0bb6SMatthias Ringwald return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
9795deb0bb6SMatthias Ringwald }
9805deb0bb6SMatthias Ringwald *out_big_encryption = source->big_encryption;
9815deb0bb6SMatthias Ringwald memcpy(out_bad_code, source->bad_code, 16);
9825deb0bb6SMatthias Ringwald return ERROR_CODE_SUCCESS;
9835deb0bb6SMatthias Ringwald }
9845deb0bb6SMatthias Ringwald
broadcast_audio_scan_service_client_deinit(uint16_t bass_cid)9855deb0bb6SMatthias Ringwald void broadcast_audio_scan_service_client_deinit(uint16_t bass_cid){
98697c78dc6SMilanka Ringwald bass_client_event_callback = NULL;
98797c78dc6SMilanka Ringwald bass_client_connection_t * connection = bass_client_get_connection_for_cid(bass_cid);
9885deb0bb6SMatthias Ringwald if (connection == NULL){
9895deb0bb6SMatthias Ringwald return;
9905deb0bb6SMatthias Ringwald }
9915deb0bb6SMatthias Ringwald // finalize connections
9925deb0bb6SMatthias Ringwald bass_client_finalize_connection(connection);
9935deb0bb6SMatthias Ringwald }
9945deb0bb6SMatthias Ringwald
995