1be32e7f1SMilanka Ringwald /*
2be32e7f1SMilanka Ringwald * Copyright (C) 2016 BlueKitchen GmbH
3be32e7f1SMilanka Ringwald *
4be32e7f1SMilanka Ringwald * Redistribution and use in source and binary forms, with or without
5be32e7f1SMilanka Ringwald * modification, are permitted provided that the following conditions
6be32e7f1SMilanka Ringwald * are met:
7be32e7f1SMilanka Ringwald *
8be32e7f1SMilanka Ringwald * 1. Redistributions of source code must retain the above copyright
9be32e7f1SMilanka Ringwald * notice, this list of conditions and the following disclaimer.
10be32e7f1SMilanka Ringwald * 2. Redistributions in binary form must reproduce the above copyright
11be32e7f1SMilanka Ringwald * notice, this list of conditions and the following disclaimer in the
12be32e7f1SMilanka Ringwald * documentation and/or other materials provided with the distribution.
13be32e7f1SMilanka Ringwald * 3. Neither the name of the copyright holders nor the names of
14be32e7f1SMilanka Ringwald * contributors may be used to endorse or promote products derived
15be32e7f1SMilanka Ringwald * from this software without specific prior written permission.
16be32e7f1SMilanka Ringwald * 4. Any redistribution, use, or modification is done solely for
17be32e7f1SMilanka Ringwald * personal benefit and not for any commercial purpose or for
18be32e7f1SMilanka Ringwald * monetary gain.
19be32e7f1SMilanka Ringwald *
20be32e7f1SMilanka Ringwald * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
21be32e7f1SMilanka Ringwald * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22be32e7f1SMilanka Ringwald * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
232fca4dadSMilanka Ringwald * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BLUEKITCHEN
242fca4dadSMilanka Ringwald * GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25be32e7f1SMilanka Ringwald * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26be32e7f1SMilanka Ringwald * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
27be32e7f1SMilanka Ringwald * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
28be32e7f1SMilanka Ringwald * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29be32e7f1SMilanka Ringwald * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
30be32e7f1SMilanka Ringwald * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31be32e7f1SMilanka Ringwald * SUCH DAMAGE.
32be32e7f1SMilanka Ringwald *
33be32e7f1SMilanka Ringwald * Please inquire about commercial licensing options at
34be32e7f1SMilanka Ringwald * [email protected]
35be32e7f1SMilanka Ringwald *
36be32e7f1SMilanka Ringwald */
37be32e7f1SMilanka Ringwald
38e501bae0SMatthias Ringwald #define BTSTACK_FILE__ "avrcp.c"
39ab2c6ae4SMatthias Ringwald
40be32e7f1SMilanka Ringwald #include <stdint.h>
41be32e7f1SMilanka Ringwald #include <string.h>
42be32e7f1SMilanka Ringwald
4384e3541eSMilanka Ringwald #include "bluetooth_psm.h"
4484e3541eSMilanka Ringwald #include "bluetooth_sdp.h"
4584e3541eSMilanka Ringwald #include "btstack_debug.h"
4684e3541eSMilanka Ringwald #include "btstack_event.h"
4784e3541eSMilanka Ringwald #include "btstack_memory.h"
48c7831204SMatthias Ringwald #include "classic/avrcp.h"
4984e3541eSMilanka Ringwald #include "classic/sdp_client.h"
5084e3541eSMilanka Ringwald #include "classic/sdp_util.h"
510eebc132SMilanka Ringwald
5264a27ec5SMilanka Ringwald
5364a27ec5SMilanka Ringwald typedef struct {
5464a27ec5SMilanka Ringwald uint8_t parse_sdp_record;
5564a27ec5SMilanka Ringwald uint32_t record_id;
5664a27ec5SMilanka Ringwald uint16_t avrcp_cid;
5764a27ec5SMilanka Ringwald uint16_t avrcp_l2cap_psm;
5864a27ec5SMilanka Ringwald uint16_t avrcp_version;
5964a27ec5SMilanka Ringwald
6064a27ec5SMilanka Ringwald uint16_t browsing_l2cap_psm;
6164a27ec5SMilanka Ringwald uint16_t browsing_version;
627e7a3a6eSMatthias Ringwald uint16_t cover_art_l2cap_psm;
6364a27ec5SMilanka Ringwald } avrcp_sdp_query_context_t;
6464a27ec5SMilanka Ringwald
650036e267SMilanka Ringwald static void avrcp_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
667a3e6573SMatthias Ringwald static void avrcp_start_next_sdp_query(void);
670036e267SMilanka Ringwald
68be32e7f1SMilanka Ringwald static const char * avrcp_subunit_type_name[] = {
69be32e7f1SMilanka Ringwald "MONITOR", "AUDIO", "PRINTER", "DISC", "TAPE_RECORDER_PLAYER", "TUNER",
70be32e7f1SMilanka Ringwald "CA", "CAMERA", "RESERVED", "PANEL", "BULLETIN_BOARD", "CAMERA_STORAGE",
71be32e7f1SMilanka Ringwald "VENDOR_UNIQUE", "RESERVED_FOR_ALL_SUBUNIT_TYPES",
72be32e7f1SMilanka Ringwald "EXTENDED_TO_NEXT_BYTE", "UNIT", "ERROR"
73be32e7f1SMilanka Ringwald };
746983e65eSMilanka Ringwald
7565bd7af5SMatthias Ringwald // default subunit info: single PANEL subunit
7665bd7af5SMatthias Ringwald static const uint8_t avrcp_default_subunit_info[] = { AVRCP_SUBUNIT_TYPE_PANEL << 3};
7765bd7af5SMatthias Ringwald
7865bd7af5SMatthias Ringwald // globals
7965bd7af5SMatthias Ringwald static bool avrcp_l2cap_service_registered = false;
8065bd7af5SMatthias Ringwald
8165bd7af5SMatthias Ringwald // connections
8265bd7af5SMatthias Ringwald static uint16_t avrcp_cid_counter;
8365bd7af5SMatthias Ringwald static btstack_linked_list_t avrcp_connections;
8465bd7af5SMatthias Ringwald
8565bd7af5SMatthias Ringwald // higher layer callbacks
8665bd7af5SMatthias Ringwald static btstack_packet_handler_t avrcp_callback;
8765bd7af5SMatthias Ringwald static btstack_packet_handler_t avrcp_controller_packet_handler;
8865bd7af5SMatthias Ringwald static btstack_packet_handler_t avrcp_target_packet_handler;
8965bd7af5SMatthias Ringwald
9065bd7af5SMatthias Ringwald // sdp query
9165bd7af5SMatthias Ringwald static btstack_context_callback_registration_t avrcp_sdp_query_registration;
9265bd7af5SMatthias Ringwald static avrcp_sdp_query_context_t avrcp_sdp_query_context;
9365bd7af5SMatthias Ringwald static uint8_t avrcp_sdp_query_attribute_value[45];
9465bd7af5SMatthias Ringwald static const unsigned int avrcp_sdp_query_attribute_value_buffer_size = sizeof(avrcp_sdp_query_attribute_value);
9565bd7af5SMatthias Ringwald
9611256635SMatthias Ringwald static void (*avrcp_browsing_sdp_query_complete_handler)(avrcp_connection_t * connection, uint8_t status);
97f430bcdeSMatthias Ringwald #ifdef ENABLE_AVRCP_COVER_ART
98f430bcdeSMatthias Ringwald static void (*avrcp_cover_art_sdp_query_complete_handler)(avrcp_connection_t * connection, uint8_t status);
99f430bcdeSMatthias Ringwald #endif
10065bd7af5SMatthias Ringwald
avrcp_subunit2str(uint16_t index)101be32e7f1SMilanka Ringwald const char * avrcp_subunit2str(uint16_t index){
102be32e7f1SMilanka Ringwald if (index <= 11) return avrcp_subunit_type_name[index];
1030e588213SMatthias Ringwald if ((index >= 0x1C) && (index <= 0x1F)) return avrcp_subunit_type_name[index - 0x10];
104be32e7f1SMilanka Ringwald return avrcp_subunit_type_name[16];
105be32e7f1SMilanka Ringwald }
106be32e7f1SMilanka Ringwald
107be32e7f1SMilanka Ringwald static const char * avrcp_event_name[] = {
108be32e7f1SMilanka Ringwald "ERROR", "PLAYBACK_STATUS_CHANGED",
109be32e7f1SMilanka Ringwald "TRACK_CHANGED", "TRACK_REACHED_END", "TRACK_REACHED_START",
110be32e7f1SMilanka Ringwald "PLAYBACK_POS_CHANGED", "BATT_STATUS_CHANGED", "SYSTEM_STATUS_CHANGED",
111be32e7f1SMilanka Ringwald "PLAYER_APPLICATION_SETTING_CHANGED", "NOW_PLAYING_CONTENT_CHANGED",
112be32e7f1SMilanka Ringwald "AVAILABLE_PLAYERS_CHANGED", "ADDRESSED_PLAYER_CHANGED", "UIDS_CHANGED", "VOLUME_CHANGED"
113be32e7f1SMilanka Ringwald };
avrcp_event2str(uint16_t index)114be32e7f1SMilanka Ringwald const char * avrcp_event2str(uint16_t index){
115be32e7f1SMilanka Ringwald if (index <= 0x0d) return avrcp_event_name[index];
116be32e7f1SMilanka Ringwald return avrcp_event_name[0];
117be32e7f1SMilanka Ringwald }
118be32e7f1SMilanka Ringwald
119be32e7f1SMilanka Ringwald static const char * avrcp_operation_name[] = {
120ce66cc7aSMilanka Ringwald "SKIP", NULL, NULL, NULL, NULL,
121ce66cc7aSMilanka Ringwald "VOLUME_UP", "VOLUME_DOWN", "MUTE", "PLAY", "STOP", "PAUSE", NULL,
122ce66cc7aSMilanka Ringwald "REWIND", "FAST_FORWARD", NULL, "FORWARD", "BACKWARD" // 0x4C
123be32e7f1SMilanka Ringwald };
1240b578d06SMilanka Ringwald
avrcp_operation2str(uint8_t operation_id)1250b578d06SMilanka Ringwald const char * avrcp_operation2str(uint8_t operation_id){
126ce66cc7aSMilanka Ringwald char * name = NULL;
1270b578d06SMilanka Ringwald if ((operation_id >= AVRCP_OPERATION_ID_SKIP) && (operation_id <= AVRCP_OPERATION_ID_BACKWARD)){
1280b578d06SMilanka Ringwald name = (char *)avrcp_operation_name[operation_id - AVRCP_OPERATION_ID_SKIP];
129ce66cc7aSMilanka Ringwald }
130ce66cc7aSMilanka Ringwald if (name == NULL){
131ce66cc7aSMilanka Ringwald static char buffer[13];
132*9c84d04fSMilanka Ringwald btstack_snprintf_assert_complete(buffer, sizeof(buffer), "ID 0x%02x", operation_id);
133ce66cc7aSMilanka Ringwald buffer[sizeof(buffer)-1] = 0;
134ce66cc7aSMilanka Ringwald return buffer;
135ce66cc7aSMilanka Ringwald } else {
136ce66cc7aSMilanka Ringwald return name;
137ce66cc7aSMilanka Ringwald }
138be32e7f1SMilanka Ringwald }
139be32e7f1SMilanka Ringwald
140be32e7f1SMilanka Ringwald static const char * avrcp_media_attribute_id_name[] = {
141be32e7f1SMilanka Ringwald "NONE", "TITLE", "ARTIST", "ALBUM", "TRACK", "TOTAL TRACKS", "GENRE", "SONG LENGTH"
142be32e7f1SMilanka Ringwald };
avrcp_attribute2str(uint8_t index)143be32e7f1SMilanka Ringwald const char * avrcp_attribute2str(uint8_t index){
1444c7a1d3aSMatthias Ringwald if (index > 7){
1454c7a1d3aSMatthias Ringwald index = 0;
1464c7a1d3aSMatthias Ringwald }
147be32e7f1SMilanka Ringwald return avrcp_media_attribute_id_name[0];
148be32e7f1SMilanka Ringwald }
149be32e7f1SMilanka Ringwald
150be32e7f1SMilanka Ringwald static const char * avrcp_play_status_name[] = {
151be32e7f1SMilanka Ringwald "STOPPED", "PLAYING", "PAUSED", "FORWARD SEEK", "REVERSE SEEK",
152be32e7f1SMilanka Ringwald "ERROR" // 0xFF
153be32e7f1SMilanka Ringwald };
avrcp_play_status2str(uint8_t index)154be32e7f1SMilanka Ringwald const char * avrcp_play_status2str(uint8_t index){
15512448779SMatthias Ringwald if (index > 4){
1564c7a1d3aSMatthias Ringwald index = 5;
157be32e7f1SMilanka Ringwald }
15812448779SMatthias Ringwald return avrcp_play_status_name[index];
15912448779SMatthias Ringwald }
160be32e7f1SMilanka Ringwald
161be32e7f1SMilanka Ringwald static const char * avrcp_ctype_name[] = {
162be32e7f1SMilanka Ringwald "CONTROL",
163be32e7f1SMilanka Ringwald "STATUS",
164be32e7f1SMilanka Ringwald "SPECIFIC_INQUIRY",
165be32e7f1SMilanka Ringwald "NOTIFY",
166be32e7f1SMilanka Ringwald "GENERAL_INQUIRY",
167be32e7f1SMilanka Ringwald "RESERVED5",
168be32e7f1SMilanka Ringwald "RESERVED6",
169be32e7f1SMilanka Ringwald "RESERVED7",
1709cc1f3ceSMilanka Ringwald "NOT IMPLEMENTED IN REMOTE",
1719cc1f3ceSMilanka Ringwald "ACCEPTED BY REMOTE",
1729cc1f3ceSMilanka Ringwald "REJECTED BY REMOTE",
173be32e7f1SMilanka Ringwald "IN_TRANSITION",
174be32e7f1SMilanka Ringwald "IMPLEMENTED_STABLE",
175be32e7f1SMilanka Ringwald "CHANGED_STABLE",
176be32e7f1SMilanka Ringwald "RESERVED",
177be32e7f1SMilanka Ringwald "INTERIM"
178be32e7f1SMilanka Ringwald };
1797d1b72e5SMilanka Ringwald static const uint16_t avrcp_ctype_name_num = 16;
180447150e4SMilanka Ringwald
avrcp_ctype2str(uint8_t index)181be32e7f1SMilanka Ringwald const char * avrcp_ctype2str(uint8_t index){
182447150e4SMilanka Ringwald if (index < avrcp_ctype_name_num){
183be32e7f1SMilanka Ringwald return avrcp_ctype_name[index];
184be32e7f1SMilanka Ringwald }
185be32e7f1SMilanka Ringwald return "NONE";
186be32e7f1SMilanka Ringwald }
187be32e7f1SMilanka Ringwald
188be32e7f1SMilanka Ringwald static const char * avrcp_shuffle_mode_name[] = {
189be32e7f1SMilanka Ringwald "SHUFFLE OFF",
190be32e7f1SMilanka Ringwald "SHUFFLE ALL TRACKS",
191be32e7f1SMilanka Ringwald "SHUFFLE GROUP"
192be32e7f1SMilanka Ringwald };
193be32e7f1SMilanka Ringwald
avrcp_shuffle2str(uint8_t index)194be32e7f1SMilanka Ringwald const char * avrcp_shuffle2str(uint8_t index){
195c1ab6cc1SMatthias Ringwald if ((index >= 1) && (index <= 3)) return avrcp_shuffle_mode_name[index-1];
196be32e7f1SMilanka Ringwald return "NONE";
197be32e7f1SMilanka Ringwald }
198be32e7f1SMilanka Ringwald
199be32e7f1SMilanka Ringwald static const char * avrcp_repeat_mode_name[] = {
200be32e7f1SMilanka Ringwald "REPEAT OFF",
201be32e7f1SMilanka Ringwald "REPEAT SINGLE TRACK",
202be32e7f1SMilanka Ringwald "REPEAT ALL TRACKS",
203be32e7f1SMilanka Ringwald "REPEAT GROUP"
204be32e7f1SMilanka Ringwald };
205be32e7f1SMilanka Ringwald
avrcp_repeat2str(uint8_t index)206be32e7f1SMilanka Ringwald const char * avrcp_repeat2str(uint8_t index){
207c1ab6cc1SMatthias Ringwald if ((index >= 1) && (index <= 4)) return avrcp_repeat_mode_name[index-1];
208be32e7f1SMilanka Ringwald return "NONE";
209be32e7f1SMilanka Ringwald }
2106086246cSMilanka Ringwald
2116fb6f866SMilanka Ringwald static const char * notification_name[] = {
2126fb6f866SMilanka Ringwald "INVALID_INDEX",
2136fb6f866SMilanka Ringwald "PLAYBACK_STATUS_CHANGED",
2146fb6f866SMilanka Ringwald "TRACK_CHANGED",
2156fb6f866SMilanka Ringwald "TRACK_REACHED_END",
2166fb6f866SMilanka Ringwald "TRACK_REACHED_START",
2176fb6f866SMilanka Ringwald "PLAYBACK_POS_CHANGED",
2186fb6f866SMilanka Ringwald "BATT_STATUS_CHANGED",
2196fb6f866SMilanka Ringwald "SYSTEM_STATUS_CHANGED",
2206fb6f866SMilanka Ringwald "PLAYER_APPLICATION_SETTING_CHANGED",
2216fb6f866SMilanka Ringwald "NOW_PLAYING_CONTENT_CHANGED",
2226fb6f866SMilanka Ringwald "AVAILABLE_PLAYERS_CHANGED",
2236fb6f866SMilanka Ringwald "ADDRESSED_PLAYER_CHANGED",
2246fb6f866SMilanka Ringwald "UIDS_CHANGED",
2256fb6f866SMilanka Ringwald "VOLUME_CHANGED",
2266fb6f866SMilanka Ringwald "MAX_VALUE"
2276fb6f866SMilanka Ringwald };
2286fb6f866SMilanka Ringwald
avrcp_notification2str(avrcp_notification_event_id_t index)2296fb6f866SMilanka Ringwald const char * avrcp_notification2str(avrcp_notification_event_id_t index){
2306fb6f866SMilanka Ringwald if ((index >= AVRCP_NOTIFICATION_EVENT_FIRST_INDEX) && (index <= AVRCP_NOTIFICATION_EVENT_LAST_INDEX)){
2316fb6f866SMilanka Ringwald return notification_name[index];
2326fb6f866SMilanka Ringwald }
2336fb6f866SMilanka Ringwald return notification_name[0];
2346fb6f866SMilanka Ringwald }
2356fb6f866SMilanka Ringwald
avrcp_get_connections(void)23664a27ec5SMilanka Ringwald btstack_linked_list_t avrcp_get_connections(void){
23765bd7af5SMatthias Ringwald return avrcp_connections;
23864a27ec5SMilanka Ringwald }
23964a27ec5SMilanka Ringwald
avrcp_cmd_opcode(uint8_t * packet,uint16_t size)2404b338011SMilanka Ringwald uint8_t avrcp_cmd_opcode(uint8_t *packet, uint16_t size){
2414b338011SMilanka Ringwald uint8_t cmd_opcode_index = 5;
2424b338011SMilanka Ringwald if (cmd_opcode_index > size) return AVRCP_CMD_OPCODE_UNDEFINED;
2434b338011SMilanka Ringwald return packet[cmd_opcode_index];
2444b338011SMilanka Ringwald }
2454b338011SMilanka Ringwald
avrcp_create_sdp_record(bool controller,uint8_t * service,uint32_t service_record_handle,uint8_t browsing,uint16_t supported_features,const char * service_name,const char * service_provider_name)246c7831204SMatthias Ringwald void avrcp_create_sdp_record(bool controller, uint8_t * service, uint32_t service_record_handle, uint8_t browsing, uint16_t supported_features,
247654724deSMilanka Ringwald const char * service_name, const char * service_provider_name){
248be32e7f1SMilanka Ringwald uint8_t* attribute;
249be32e7f1SMilanka Ringwald de_create_sequence(service);
250be32e7f1SMilanka Ringwald
251be32e7f1SMilanka Ringwald // 0x0000 "Service Record Handle"
252235946f1SMatthias Ringwald de_add_number(service, DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_SERVICE_RECORD_HANDLE);
253be32e7f1SMilanka Ringwald de_add_number(service, DE_UINT, DE_SIZE_32, service_record_handle);
254be32e7f1SMilanka Ringwald
255be32e7f1SMilanka Ringwald // 0x0001 "Service Class ID List"
256235946f1SMatthias Ringwald de_add_number(service, DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_SERVICE_CLASS_ID_LIST);
257be32e7f1SMilanka Ringwald attribute = de_push_sequence(service);
258be32e7f1SMilanka Ringwald {
259be32e7f1SMilanka Ringwald if (controller){
2606086246cSMilanka Ringwald de_add_number(attribute, DE_UUID, DE_SIZE_16, BLUETOOTH_SERVICE_CLASS_AV_REMOTE_CONTROL);
2616086246cSMilanka Ringwald de_add_number(attribute, DE_UUID, DE_SIZE_16, BLUETOOTH_SERVICE_CLASS_AV_REMOTE_CONTROL_CONTROLLER);
262be32e7f1SMilanka Ringwald } else {
2636086246cSMilanka Ringwald de_add_number(attribute, DE_UUID, DE_SIZE_16, BLUETOOTH_SERVICE_CLASS_AV_REMOTE_CONTROL_TARGET);
264be32e7f1SMilanka Ringwald }
265be32e7f1SMilanka Ringwald }
266be32e7f1SMilanka Ringwald de_pop_sequence(service, attribute);
267be32e7f1SMilanka Ringwald
268be32e7f1SMilanka Ringwald // 0x0004 "Protocol Descriptor List"
269235946f1SMatthias Ringwald de_add_number(service, DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_PROTOCOL_DESCRIPTOR_LIST);
270be32e7f1SMilanka Ringwald attribute = de_push_sequence(service);
271be32e7f1SMilanka Ringwald {
272be32e7f1SMilanka Ringwald uint8_t* l2cpProtocol = de_push_sequence(attribute);
273be32e7f1SMilanka Ringwald {
274235946f1SMatthias Ringwald de_add_number(l2cpProtocol, DE_UUID, DE_SIZE_16, BLUETOOTH_PROTOCOL_L2CAP);
27584e3541eSMilanka Ringwald de_add_number(l2cpProtocol, DE_UINT, DE_SIZE_16, BLUETOOTH_PSM_AVCTP);
276be32e7f1SMilanka Ringwald }
277be32e7f1SMilanka Ringwald de_pop_sequence(attribute, l2cpProtocol);
278be32e7f1SMilanka Ringwald
279be32e7f1SMilanka Ringwald uint8_t* avctpProtocol = de_push_sequence(attribute);
280be32e7f1SMilanka Ringwald {
281235946f1SMatthias Ringwald de_add_number(avctpProtocol, DE_UUID, DE_SIZE_16, BLUETOOTH_PROTOCOL_AVCTP); // avctpProtocol_service
2820b322400SMilanka Ringwald de_add_number(avctpProtocol, DE_UINT, DE_SIZE_16, 0x0104); // version
283be32e7f1SMilanka Ringwald }
284be32e7f1SMilanka Ringwald de_pop_sequence(attribute, avctpProtocol);
285be32e7f1SMilanka Ringwald }
286be32e7f1SMilanka Ringwald de_pop_sequence(service, attribute);
287be32e7f1SMilanka Ringwald
288be32e7f1SMilanka Ringwald // 0x0005 "Public Browse Group"
289235946f1SMatthias Ringwald de_add_number(service, DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_BROWSE_GROUP_LIST); // public browse group
290be32e7f1SMilanka Ringwald attribute = de_push_sequence(service);
291be32e7f1SMilanka Ringwald {
292235946f1SMatthias Ringwald de_add_number(attribute, DE_UUID, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_PUBLIC_BROWSE_ROOT);
293be32e7f1SMilanka Ringwald }
294be32e7f1SMilanka Ringwald de_pop_sequence(service, attribute);
295be32e7f1SMilanka Ringwald
296be32e7f1SMilanka Ringwald // 0x0009 "Bluetooth Profile Descriptor List"
297235946f1SMatthias Ringwald de_add_number(service, DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_BLUETOOTH_PROFILE_DESCRIPTOR_LIST);
298be32e7f1SMilanka Ringwald attribute = de_push_sequence(service);
299be32e7f1SMilanka Ringwald {
300be32e7f1SMilanka Ringwald uint8_t *avrcProfile = de_push_sequence(attribute);
301be32e7f1SMilanka Ringwald {
3026086246cSMilanka Ringwald de_add_number(avrcProfile, DE_UUID, DE_SIZE_16, BLUETOOTH_SERVICE_CLASS_AV_REMOTE_CONTROL);
3030b322400SMilanka Ringwald de_add_number(avrcProfile, DE_UINT, DE_SIZE_16, 0x0106);
304be32e7f1SMilanka Ringwald }
305be32e7f1SMilanka Ringwald de_pop_sequence(attribute, avrcProfile);
306be32e7f1SMilanka Ringwald }
307be32e7f1SMilanka Ringwald de_pop_sequence(service, attribute);
308be32e7f1SMilanka Ringwald
309a0f524f0SMatthias Ringwald // 0x000d "Additional Bluetooth Profile Descriptor List"
3105c806868SMatthias Ringwald if (browsing){
311a0f524f0SMatthias Ringwald de_add_number(service, DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS);
312a0f524f0SMatthias Ringwald attribute = de_push_sequence(service);
3135c806868SMatthias Ringwald {
3145c806868SMatthias Ringwald uint8_t * des = de_push_sequence(attribute);
3155c806868SMatthias Ringwald {
3165c806868SMatthias Ringwald uint8_t* browsing_l2cpProtocol = de_push_sequence(des);
317a0f524f0SMatthias Ringwald {
318a0f524f0SMatthias Ringwald de_add_number(browsing_l2cpProtocol, DE_UUID, DE_SIZE_16, BLUETOOTH_PROTOCOL_L2CAP);
31984e3541eSMilanka Ringwald de_add_number(browsing_l2cpProtocol, DE_UINT, DE_SIZE_16, BLUETOOTH_PSM_AVCTP_BROWSING);
320a0f524f0SMatthias Ringwald }
3215c806868SMatthias Ringwald de_pop_sequence(des, browsing_l2cpProtocol);
322a0f524f0SMatthias Ringwald
3235c806868SMatthias Ringwald uint8_t* browsing_avctpProtocol = de_push_sequence(des);
324a0f524f0SMatthias Ringwald {
325a0f524f0SMatthias Ringwald de_add_number(browsing_avctpProtocol, DE_UUID, DE_SIZE_16, BLUETOOTH_PROTOCOL_AVCTP); // browsing_avctpProtocol_service
3260b322400SMilanka Ringwald de_add_number(browsing_avctpProtocol, DE_UINT, DE_SIZE_16, 0x0104); // version
327a0f524f0SMatthias Ringwald }
3285c806868SMatthias Ringwald de_pop_sequence(des, browsing_avctpProtocol);
3295c806868SMatthias Ringwald }
3305c806868SMatthias Ringwald de_pop_sequence(attribute, des);
331a0f524f0SMatthias Ringwald }
332a0f524f0SMatthias Ringwald de_pop_sequence(service, attribute);
3335c806868SMatthias Ringwald }
334a0f524f0SMatthias Ringwald
335be32e7f1SMilanka Ringwald
336be32e7f1SMilanka Ringwald // 0x0100 "Service Name"
337141dff40SMatthias Ringwald if (strlen(service_name) > 0){
338be32e7f1SMilanka Ringwald de_add_number(service, DE_UINT, DE_SIZE_16, 0x0100);
339ab2445a0SMatthias Ringwald de_add_data(service, DE_STRING, (uint16_t) strlen(service_name), (uint8_t *) service_name);
340be32e7f1SMilanka Ringwald }
341be32e7f1SMilanka Ringwald
342be32e7f1SMilanka Ringwald // 0x0100 "Provider Name"
343141dff40SMatthias Ringwald if (strlen(service_provider_name) > 0){
344be32e7f1SMilanka Ringwald de_add_number(service, DE_UINT, DE_SIZE_16, 0x0102);
345ab2445a0SMatthias Ringwald de_add_data(service, DE_STRING, (uint16_t) strlen(service_provider_name), (uint8_t *) service_provider_name);
346be32e7f1SMilanka Ringwald }
347be32e7f1SMilanka Ringwald
348be32e7f1SMilanka Ringwald // 0x0311 "Supported Features"
349c7831204SMatthias Ringwald de_add_number(service, DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_SUPPORTED_FEATURES);
350be32e7f1SMilanka Ringwald de_add_number(service, DE_UINT, DE_SIZE_16, supported_features);
351be32e7f1SMilanka Ringwald }
352be32e7f1SMilanka Ringwald
avctp_get_num_bytes_for_header(avctp_packet_type_t avctp_packet_type)353aeb99916SMilanka Ringwald uint16_t avctp_get_num_bytes_for_header(avctp_packet_type_t avctp_packet_type) {
35466b242bbSMilanka Ringwald switch (avctp_packet_type){
35566b242bbSMilanka Ringwald case AVCTP_SINGLE_PACKET:
356aeb99916SMilanka Ringwald // AVCTP message: transport header (1), pid (2)
357aeb99916SMilanka Ringwald return 3;
35866b242bbSMilanka Ringwald case AVCTP_START_PACKET:
359aeb99916SMilanka Ringwald // AVCTP message: transport header (1), num_packets (1), pid (2)
360aeb99916SMilanka Ringwald return 4;
36166b242bbSMilanka Ringwald default:
362aeb99916SMilanka Ringwald // AVCTP message: transport header (1)
363aeb99916SMilanka Ringwald return 1;
36466b242bbSMilanka Ringwald }
36566b242bbSMilanka Ringwald }
36666b242bbSMilanka Ringwald
avrcp_get_num_bytes_for_header(avrcp_command_opcode_t command_opcode,avctp_packet_type_t avctp_packet_type)367aeb99916SMilanka Ringwald uint16_t avrcp_get_num_bytes_for_header(avrcp_command_opcode_t command_opcode, avctp_packet_type_t avctp_packet_type) {
3684d7cf390SMilanka Ringwald switch (avctp_packet_type){
3694d7cf390SMilanka Ringwald case AVCTP_SINGLE_PACKET:
3704d7cf390SMilanka Ringwald case AVCTP_START_PACKET:
3714d7cf390SMilanka Ringwald break;
3724d7cf390SMilanka Ringwald default:
3734d7cf390SMilanka Ringwald return 0;
3744d7cf390SMilanka Ringwald }
3754d7cf390SMilanka Ringwald
3764d7cf390SMilanka Ringwald uint16_t offset = 3; // AVRCP message: cmd type (1), subunit (1), opcode (1)
3774d7cf390SMilanka Ringwald switch (command_opcode){
3784d7cf390SMilanka Ringwald case AVRCP_CMD_OPCODE_VENDOR_DEPENDENT:
3794d7cf390SMilanka Ringwald offset += 7; // AVRCP message: company (3), pdu id(1), AVRCP packet type (1), param_len (2)
3804d7cf390SMilanka Ringwald break;
3814d7cf390SMilanka Ringwald case AVRCP_CMD_OPCODE_PASS_THROUGH:
3824d7cf390SMilanka Ringwald offset += 3; // AVRCP message: operation id (1), param_len (2)
3834d7cf390SMilanka Ringwald break;
3844d7cf390SMilanka Ringwald default:
3854d7cf390SMilanka Ringwald break;
3864d7cf390SMilanka Ringwald }
3874d7cf390SMilanka Ringwald return offset;
3884d7cf390SMilanka Ringwald }
3894d7cf390SMilanka Ringwald
avrcp_get_num_free_bytes_for_payload(uint16_t l2cap_mtu,avrcp_command_opcode_t command_opcode,avctp_packet_type_t avctp_packet_type)390c3d92d4cSMilanka Ringwald static uint16_t avrcp_get_num_free_bytes_for_payload(uint16_t l2cap_mtu, avrcp_command_opcode_t command_opcode, avctp_packet_type_t avctp_packet_type){
391c3d92d4cSMilanka Ringwald uint16_t max_frame_size = btstack_min(l2cap_mtu, AVRCP_MAX_AV_C_MESSAGE_FRAME_SIZE);
392aeb99916SMilanka Ringwald uint16_t payload_offset = avctp_get_num_bytes_for_header(avctp_packet_type) +
393aeb99916SMilanka Ringwald avrcp_get_num_bytes_for_header(command_opcode, avctp_packet_type);
3944d7cf390SMilanka Ringwald
395aeb99916SMilanka Ringwald btstack_assert(max_frame_size >= payload_offset);
396aeb99916SMilanka Ringwald return (max_frame_size - payload_offset);
3974d7cf390SMilanka Ringwald }
3984d7cf390SMilanka Ringwald
3994d7cf390SMilanka Ringwald
avctp_get_packet_type(avrcp_connection_t * connection,uint16_t * max_payload_size)4004d7cf390SMilanka Ringwald avctp_packet_type_t avctp_get_packet_type(avrcp_connection_t * connection, uint16_t * max_payload_size){
4017ff388bdSMilanka Ringwald if (connection->l2cap_mtu >= AVRCP_MAX_AV_C_MESSAGE_FRAME_SIZE){
4027ff388bdSMilanka Ringwald return AVCTP_SINGLE_PACKET;
4037ff388bdSMilanka Ringwald }
4047ff388bdSMilanka Ringwald
40566b242bbSMilanka Ringwald if (connection->data_offset == 0){
406c3d92d4cSMilanka Ringwald uint16_t max_payload_size_for_single_packet = avrcp_get_num_free_bytes_for_payload(connection->l2cap_mtu,
407aeb99916SMilanka Ringwald connection->command_opcode,
408aeb99916SMilanka Ringwald AVCTP_SINGLE_PACKET);
40996e5f69aSMilanka Ringwald if (max_payload_size_for_single_packet >= connection->data_len){
41096e5f69aSMilanka Ringwald *max_payload_size = max_payload_size_for_single_packet;
41166b242bbSMilanka Ringwald return AVCTP_SINGLE_PACKET;
41266b242bbSMilanka Ringwald } else {
41396e5f69aSMilanka Ringwald uint16_t max_payload_size_for_start_packet = max_payload_size_for_single_packet - 1;
41496e5f69aSMilanka Ringwald *max_payload_size = max_payload_size_for_start_packet;
41566b242bbSMilanka Ringwald return AVCTP_START_PACKET;
41666b242bbSMilanka Ringwald }
41766b242bbSMilanka Ringwald } else {
41896e5f69aSMilanka Ringwald // both packet types have the same single byte AVCTP header
419c3d92d4cSMilanka Ringwald *max_payload_size = avrcp_get_num_free_bytes_for_payload(connection->l2cap_mtu,
420aeb99916SMilanka Ringwald connection->command_opcode,
421aeb99916SMilanka Ringwald AVCTP_CONTINUE_PACKET);
4224d7cf390SMilanka Ringwald if ((connection->data_len - connection->data_offset) > *max_payload_size){
42366b242bbSMilanka Ringwald return AVCTP_CONTINUE_PACKET;
42466b242bbSMilanka Ringwald } else {
42566b242bbSMilanka Ringwald return AVCTP_END_PACKET;
42666b242bbSMilanka Ringwald }
42766b242bbSMilanka Ringwald }
42866b242bbSMilanka Ringwald }
42966b242bbSMilanka Ringwald
avrcp_get_packet_type(avrcp_connection_t * connection)430aeb99916SMilanka Ringwald avrcp_packet_type_t avrcp_get_packet_type(avrcp_connection_t * connection){
431aeb99916SMilanka Ringwald switch (connection->avctp_packet_type) {
432aeb99916SMilanka Ringwald case AVCTP_SINGLE_PACKET:
433aeb99916SMilanka Ringwald case AVCTP_START_PACKET:
434aeb99916SMilanka Ringwald break;
435aeb99916SMilanka Ringwald default:
436393f5724SMilanka Ringwald return connection->avrcp_packet_type;
437aeb99916SMilanka Ringwald }
438aeb99916SMilanka Ringwald
439d6c2d4bcSMilanka Ringwald uint16_t payload_offset = avctp_get_num_bytes_for_header(connection->avctp_packet_type) +
440d6c2d4bcSMilanka Ringwald avrcp_get_num_bytes_for_header(connection->command_opcode, connection->avctp_packet_type);
441d6c2d4bcSMilanka Ringwald uint16_t bytes_to_send = (connection->data_len - connection->data_offset) + payload_offset;
442d6c2d4bcSMilanka Ringwald
443aeb99916SMilanka Ringwald if (connection->data_offset == 0){
444d6c2d4bcSMilanka Ringwald if (bytes_to_send <= AVRCP_MAX_AV_C_MESSAGE_FRAME_SIZE){
445aeb99916SMilanka Ringwald return AVRCP_SINGLE_PACKET;
446aeb99916SMilanka Ringwald } else {
447aeb99916SMilanka Ringwald return AVRCP_START_PACKET;
448aeb99916SMilanka Ringwald }
449aeb99916SMilanka Ringwald } else {
450d6c2d4bcSMilanka Ringwald if (bytes_to_send > AVRCP_MAX_AV_C_MESSAGE_FRAME_SIZE){
451aeb99916SMilanka Ringwald return AVRCP_CONTINUE_PACKET;
452aeb99916SMilanka Ringwald } else {
453aeb99916SMilanka Ringwald return AVRCP_END_PACKET;
454aeb99916SMilanka Ringwald }
455aeb99916SMilanka Ringwald }
456aeb99916SMilanka Ringwald }
457aeb99916SMilanka Ringwald
avrcp_get_connection_for_bd_addr_for_role(avrcp_role_t role,bd_addr_t addr)4581945fe3eSMilanka Ringwald avrcp_connection_t * avrcp_get_connection_for_bd_addr_for_role(avrcp_role_t role, bd_addr_t addr){
4596983e65eSMilanka Ringwald btstack_linked_list_iterator_t it;
46065bd7af5SMatthias Ringwald btstack_linked_list_iterator_init(&it, (btstack_linked_list_t *) &avrcp_connections);
4616983e65eSMilanka Ringwald while (btstack_linked_list_iterator_has_next(&it)){
4626983e65eSMilanka Ringwald avrcp_connection_t * connection = (avrcp_connection_t *)btstack_linked_list_iterator_next(&it);
46394d9400dSMilanka Ringwald if (connection->role != role) continue;
4646983e65eSMilanka Ringwald if (memcmp(addr, connection->remote_addr, 6) != 0) continue;
4656983e65eSMilanka Ringwald return connection;
4666983e65eSMilanka Ringwald }
4676983e65eSMilanka Ringwald return NULL;
468be32e7f1SMilanka Ringwald }
469be32e7f1SMilanka Ringwald
avrcp_get_connection_for_l2cap_signaling_cid_for_role(avrcp_role_t role,uint16_t l2cap_cid)4701945fe3eSMilanka Ringwald avrcp_connection_t * avrcp_get_connection_for_l2cap_signaling_cid_for_role(avrcp_role_t role, uint16_t l2cap_cid){
4716983e65eSMilanka Ringwald btstack_linked_list_iterator_t it;
47265bd7af5SMatthias Ringwald btstack_linked_list_iterator_init(&it, (btstack_linked_list_t *) &avrcp_connections);
4736983e65eSMilanka Ringwald while (btstack_linked_list_iterator_has_next(&it)){
4746983e65eSMilanka Ringwald avrcp_connection_t * connection = (avrcp_connection_t *)btstack_linked_list_iterator_next(&it);
47594d9400dSMilanka Ringwald if (connection->role != role) continue;
4766983e65eSMilanka Ringwald if (connection->l2cap_signaling_cid != l2cap_cid) continue;
4776983e65eSMilanka Ringwald return connection;
4786983e65eSMilanka Ringwald }
4796983e65eSMilanka Ringwald return NULL;
480be32e7f1SMilanka Ringwald }
481be32e7f1SMilanka Ringwald
avrcp_get_connection_for_avrcp_cid_for_role(avrcp_role_t role,uint16_t avrcp_cid)4821945fe3eSMilanka Ringwald avrcp_connection_t * avrcp_get_connection_for_avrcp_cid_for_role(avrcp_role_t role, uint16_t avrcp_cid){
4836983e65eSMilanka Ringwald btstack_linked_list_iterator_t it;
48465bd7af5SMatthias Ringwald btstack_linked_list_iterator_init(&it, (btstack_linked_list_t *) &avrcp_connections);
4856983e65eSMilanka Ringwald while (btstack_linked_list_iterator_has_next(&it)){
4866983e65eSMilanka Ringwald avrcp_connection_t * connection = (avrcp_connection_t *)btstack_linked_list_iterator_next(&it);
48794d9400dSMilanka Ringwald if (connection->role != role) continue;
48801dc6e35SMilanka Ringwald if (connection->avrcp_cid != avrcp_cid) continue;
4896983e65eSMilanka Ringwald return connection;
4906983e65eSMilanka Ringwald }
4916983e65eSMilanka Ringwald return NULL;
4926983e65eSMilanka Ringwald }
4936983e65eSMilanka Ringwald
avrcp_get_connection_for_browsing_cid_for_role(avrcp_role_t role,uint16_t browsing_cid)4941945fe3eSMilanka Ringwald avrcp_connection_t * avrcp_get_connection_for_browsing_cid_for_role(avrcp_role_t role, uint16_t browsing_cid){
49503a72c8eSMatthias Ringwald btstack_linked_list_iterator_t it;
49665bd7af5SMatthias Ringwald btstack_linked_list_iterator_init(&it, (btstack_linked_list_t *) &avrcp_connections);
49703a72c8eSMatthias Ringwald while (btstack_linked_list_iterator_has_next(&it)){
49803a72c8eSMatthias Ringwald avrcp_connection_t * connection = (avrcp_connection_t *)btstack_linked_list_iterator_next(&it);
49903a72c8eSMatthias Ringwald if (connection->role != role) continue;
50003a72c8eSMatthias Ringwald if (connection->avrcp_browsing_cid != browsing_cid) continue;
50103a72c8eSMatthias Ringwald return connection;
50203a72c8eSMatthias Ringwald }
50303a72c8eSMatthias Ringwald return NULL;
50403a72c8eSMatthias Ringwald }
50503a72c8eSMatthias Ringwald
avrcp_get_connection_for_browsing_l2cap_cid_for_role(avrcp_role_t role,uint16_t browsing_l2cap_cid)5061945fe3eSMilanka Ringwald avrcp_connection_t * avrcp_get_connection_for_browsing_l2cap_cid_for_role(avrcp_role_t role, uint16_t browsing_l2cap_cid){
50703a72c8eSMatthias Ringwald btstack_linked_list_iterator_t it;
50865bd7af5SMatthias Ringwald btstack_linked_list_iterator_init(&it, (btstack_linked_list_t *) &avrcp_connections);
50903a72c8eSMatthias Ringwald while (btstack_linked_list_iterator_has_next(&it)){
51003a72c8eSMatthias Ringwald avrcp_connection_t * connection = (avrcp_connection_t *)btstack_linked_list_iterator_next(&it);
51103a72c8eSMatthias Ringwald if (connection->role != role) continue;
51203a72c8eSMatthias Ringwald if (connection->browsing_connection && (connection->browsing_connection->l2cap_browsing_cid != browsing_l2cap_cid)) continue;
51303a72c8eSMatthias Ringwald return connection;
51403a72c8eSMatthias Ringwald }
51503a72c8eSMatthias Ringwald return NULL;
51603a72c8eSMatthias Ringwald }
51703a72c8eSMatthias Ringwald
avrcp_get_browsing_connection_for_l2cap_cid_for_role(avrcp_role_t role,uint16_t l2cap_cid)5181945fe3eSMilanka Ringwald avrcp_browsing_connection_t * avrcp_get_browsing_connection_for_l2cap_cid_for_role(avrcp_role_t role, uint16_t l2cap_cid){
51903a72c8eSMatthias Ringwald btstack_linked_list_iterator_t it;
52065bd7af5SMatthias Ringwald btstack_linked_list_iterator_init(&it, (btstack_linked_list_t *) &avrcp_connections);
52103a72c8eSMatthias Ringwald while (btstack_linked_list_iterator_has_next(&it)){
52203a72c8eSMatthias Ringwald avrcp_connection_t * connection = (avrcp_connection_t *)btstack_linked_list_iterator_next(&it);
52303a72c8eSMatthias Ringwald if (connection->role != role) continue;
52403a72c8eSMatthias Ringwald if (connection->browsing_connection && (connection->browsing_connection->l2cap_browsing_cid != l2cap_cid)) continue;
52503a72c8eSMatthias Ringwald return connection->browsing_connection;
52603a72c8eSMatthias Ringwald }
52703a72c8eSMatthias Ringwald return NULL;
52803a72c8eSMatthias Ringwald }
52903a72c8eSMatthias Ringwald
avrcp_request_can_send_now(avrcp_connection_t * connection,uint16_t l2cap_cid)5306983e65eSMilanka Ringwald void avrcp_request_can_send_now(avrcp_connection_t * connection, uint16_t l2cap_cid){
531cf36dea0SMilanka Ringwald connection->wait_to_send = true;
532*9c84d04fSMilanka Ringwald (void)l2cap_request_can_send_now_event(l2cap_cid);
5336983e65eSMilanka Ringwald }
5346983e65eSMilanka Ringwald
avrcp_get_next_cid(avrcp_role_t role)535298aeecdSMilanka Ringwald uint16_t avrcp_get_next_cid(avrcp_role_t role){
536298aeecdSMilanka Ringwald do {
537298aeecdSMilanka Ringwald if (avrcp_cid_counter == 0xffff) {
5386983e65eSMilanka Ringwald avrcp_cid_counter = 1;
539298aeecdSMilanka Ringwald } else {
540298aeecdSMilanka Ringwald avrcp_cid_counter++;
5416983e65eSMilanka Ringwald }
5421945fe3eSMilanka Ringwald } while (avrcp_get_connection_for_avrcp_cid_for_role(role, avrcp_cid_counter) != NULL) ;
5436983e65eSMilanka Ringwald return avrcp_cid_counter;
5446983e65eSMilanka Ringwald }
5456983e65eSMilanka Ringwald
avrcp_create_connection(avrcp_role_t role,bd_addr_t remote_addr)546638481deSMilanka Ringwald static avrcp_connection_t * avrcp_create_connection(avrcp_role_t role, bd_addr_t remote_addr){
5476983e65eSMilanka Ringwald avrcp_connection_t * connection = btstack_memory_avrcp_connection_get();
5484567cc17SMilanka Ringwald if (!connection){
5490ec79bd0SMilanka Ringwald log_error("Not enough memory to create connection for role %d", role);
5504567cc17SMilanka Ringwald return NULL;
5514567cc17SMilanka Ringwald }
5520036e267SMilanka Ringwald
5536983e65eSMilanka Ringwald connection->state = AVCTP_CONNECTION_IDLE;
5540036e267SMilanka Ringwald connection->role = role;
555022b77fcSMilanka Ringwald
556022b77fcSMilanka Ringwald connection->transaction_id = 0xFF;
55723773c45SMilanka Ringwald connection->transaction_id_counter = 0;
558022b77fcSMilanka Ringwald
5593d73cc81SMilanka Ringwald connection->controller_max_num_fragments = 0xFF;
560f0af2234SMatthias Ringwald
561f0af2234SMatthias Ringwald // setup default unit / subunit info
562f0af2234SMatthias Ringwald connection->company_id = 0xffffff;
563393f5724SMilanka Ringwald connection->target_unit_type = AVRCP_SUBUNIT_TYPE_PANEL;
564393f5724SMilanka Ringwald connection->target_subunit_info_data_size = sizeof(avrcp_default_subunit_info);
565393f5724SMilanka Ringwald connection->target_subunit_info_data = avrcp_default_subunit_info;
566f0af2234SMatthias Ringwald
567c91f9817SMilanka Ringwald log_info("avrcp_create_connection, role %d", role);
5686535961aSMatthias Ringwald (void)memcpy(connection->remote_addr, remote_addr, 6);
569d3a84258SMatthias Ringwald btstack_linked_list_add_tail(&avrcp_connections, (btstack_linked_item_t *) connection);
5706983e65eSMilanka Ringwald return connection;
5716983e65eSMilanka Ringwald }
5726983e65eSMilanka Ringwald
avrcp_finalize_connection(avrcp_connection_t * connection)573638481deSMilanka Ringwald static void avrcp_finalize_connection(avrcp_connection_t * connection){
57437fae987SMilanka Ringwald btstack_run_loop_remove_timer(&connection->retry_timer);
575cf78f005SMilanka Ringwald btstack_run_loop_remove_timer(&connection->controller_press_and_hold_cmd_timer);
57665bd7af5SMatthias Ringwald btstack_linked_list_remove(&avrcp_connections, (btstack_linked_item_t*) connection);
57755e8029cSMatthias Ringwald btstack_memory_avrcp_connection_free(connection);
57855e8029cSMatthias Ringwald }
5796983e65eSMilanka Ringwald
avrcp_emit_connection_established(uint16_t avrcp_cid,bd_addr_t addr,hci_con_handle_t con_handle,uint8_t status)5807dbc6cb8SMilanka Ringwald static void avrcp_emit_connection_established(uint16_t avrcp_cid, bd_addr_t addr, hci_con_handle_t con_handle, uint8_t status){
581cee0e5b6SMilanka Ringwald btstack_assert(avrcp_callback != NULL);
582cee0e5b6SMilanka Ringwald
5837dbc6cb8SMilanka Ringwald uint8_t event[14];
584b193c45eSMilanka Ringwald int pos = 0;
585b193c45eSMilanka Ringwald event[pos++] = HCI_EVENT_AVRCP_META;
586b193c45eSMilanka Ringwald event[pos++] = sizeof(event) - 2;
587b193c45eSMilanka Ringwald event[pos++] = AVRCP_SUBEVENT_CONNECTION_ESTABLISHED;
5886f43fcd7SMatthias Ringwald event[pos++] = status;
5897dbc6cb8SMilanka Ringwald little_endian_store_16(event, pos, avrcp_cid);
5907dbc6cb8SMilanka Ringwald pos += 2;
591b193c45eSMilanka Ringwald reverse_bd_addr(addr,&event[pos]);
592b193c45eSMilanka Ringwald pos += 6;
5937dbc6cb8SMilanka Ringwald little_endian_store_16(event, pos, con_handle);
594b193c45eSMilanka Ringwald pos += 2;
595cee0e5b6SMilanka Ringwald (*avrcp_callback)(HCI_EVENT_PACKET, 0, event, sizeof(event));
596b193c45eSMilanka Ringwald }
597b193c45eSMilanka Ringwald
avrcp_emit_connection_closed(uint16_t avrcp_cid)598cee0e5b6SMilanka Ringwald static void avrcp_emit_connection_closed(uint16_t avrcp_cid){
599cee0e5b6SMilanka Ringwald btstack_assert(avrcp_callback != NULL);
600cee0e5b6SMilanka Ringwald
601be32e7f1SMilanka Ringwald uint8_t event[5];
602be32e7f1SMilanka Ringwald int pos = 0;
603be32e7f1SMilanka Ringwald event[pos++] = HCI_EVENT_AVRCP_META;
604be32e7f1SMilanka Ringwald event[pos++] = sizeof(event) - 2;
605be32e7f1SMilanka Ringwald event[pos++] = AVRCP_SUBEVENT_CONNECTION_RELEASED;
606b193c45eSMilanka Ringwald little_endian_store_16(event, pos, avrcp_cid);
607be32e7f1SMilanka Ringwald pos += 2;
608cee0e5b6SMilanka Ringwald (*avrcp_callback)(HCI_EVENT_PACKET, 0, event, sizeof(event));
609be32e7f1SMilanka Ringwald }
610be32e7f1SMilanka Ringwald
avrcp_sdp_query_browsing_l2cap_psm(void)6116d8f569dSMilanka Ringwald uint16_t avrcp_sdp_query_browsing_l2cap_psm(void){
61265bd7af5SMatthias Ringwald return avrcp_sdp_query_context.browsing_l2cap_psm;
6134dc95c12SMatthias Ringwald }
6144dc95c12SMatthias Ringwald
avrcp_handle_sdp_client_query_attribute_value(uint8_t * packet)6154dc95c12SMatthias Ringwald void avrcp_handle_sdp_client_query_attribute_value(uint8_t *packet){
6166086246cSMilanka Ringwald des_iterator_t des_list_it;
6177e7a3a6eSMatthias Ringwald
6187e7a3a6eSMatthias Ringwald des_iterator_t additional_protocol_descriptor_list_it;
6197e7a3a6eSMatthias Ringwald des_iterator_t protocol_descriptor_list_it;
6207e7a3a6eSMatthias Ringwald des_iterator_t protocol_it;
6217e7a3a6eSMatthias Ringwald uint8_t protocol_descriptor_id;
6226086246cSMilanka Ringwald
6236086246cSMilanka Ringwald // Handle new SDP record
62465bd7af5SMatthias Ringwald if (sdp_event_query_attribute_byte_get_record_id(packet) != avrcp_sdp_query_context.record_id) {
62565bd7af5SMatthias Ringwald avrcp_sdp_query_context.record_id = sdp_event_query_attribute_byte_get_record_id(packet);
62665bd7af5SMatthias Ringwald avrcp_sdp_query_context.parse_sdp_record = 0;
6276086246cSMilanka Ringwald // log_info("SDP Record: Nr: %d", record_id);
6286086246cSMilanka Ringwald }
6296086246cSMilanka Ringwald
63065bd7af5SMatthias Ringwald if (sdp_event_query_attribute_byte_get_attribute_length(packet) <= avrcp_sdp_query_attribute_value_buffer_size) {
63165bd7af5SMatthias Ringwald avrcp_sdp_query_attribute_value[sdp_event_query_attribute_byte_get_data_offset(packet)] = sdp_event_query_attribute_byte_get_data(packet);
6326086246cSMilanka Ringwald
6336086246cSMilanka Ringwald if ((uint16_t)(sdp_event_query_attribute_byte_get_data_offset(packet)+1) == sdp_event_query_attribute_byte_get_attribute_length(packet)) {
6346086246cSMilanka Ringwald switch(sdp_event_query_attribute_byte_get_attribute_id(packet)) {
6356086246cSMilanka Ringwald case BLUETOOTH_ATTRIBUTE_SERVICE_CLASS_ID_LIST:
63665bd7af5SMatthias Ringwald if (de_get_element_type(avrcp_sdp_query_attribute_value) != DE_DES) break;
63765bd7af5SMatthias Ringwald for (des_iterator_init(&des_list_it, avrcp_sdp_query_attribute_value); des_iterator_has_more(&des_list_it); des_iterator_next(&des_list_it)) {
6386086246cSMilanka Ringwald uint8_t * element = des_iterator_get_element(&des_list_it);
6396086246cSMilanka Ringwald if (de_get_element_type(element) != DE_UUID) continue;
6406086246cSMilanka Ringwald uint32_t uuid = de_get_uuid32(element);
6416086246cSMilanka Ringwald switch (uuid){
6426086246cSMilanka Ringwald case BLUETOOTH_SERVICE_CLASS_AV_REMOTE_CONTROL_TARGET:
6436086246cSMilanka Ringwald case BLUETOOTH_SERVICE_CLASS_AV_REMOTE_CONTROL:
644df642728SMilanka Ringwald case BLUETOOTH_SERVICE_CLASS_AV_REMOTE_CONTROL_CONTROLLER:
64565bd7af5SMatthias Ringwald avrcp_sdp_query_context.parse_sdp_record = 1;
6466086246cSMilanka Ringwald break;
6476086246cSMilanka Ringwald default:
6486086246cSMilanka Ringwald break;
6496086246cSMilanka Ringwald }
6506086246cSMilanka Ringwald }
6516086246cSMilanka Ringwald break;
6526086246cSMilanka Ringwald
6537e7a3a6eSMatthias Ringwald case BLUETOOTH_ATTRIBUTE_PROTOCOL_DESCRIPTOR_LIST:
65465bd7af5SMatthias Ringwald if (!avrcp_sdp_query_context.parse_sdp_record) break;
6556086246cSMilanka Ringwald
6567e7a3a6eSMatthias Ringwald for (des_iterator_init(&protocol_descriptor_list_it, avrcp_sdp_query_attribute_value); des_iterator_has_more(&protocol_descriptor_list_it); des_iterator_next(&protocol_descriptor_list_it)) {
6576086246cSMilanka Ringwald
6587e7a3a6eSMatthias Ringwald if (des_iterator_get_type(&protocol_descriptor_list_it) != DE_DES) continue;
6597e7a3a6eSMatthias Ringwald uint8_t * protocol_descriptor_list_element = des_iterator_get_element(&protocol_descriptor_list_it);
6606086246cSMilanka Ringwald
6617e7a3a6eSMatthias Ringwald des_iterator_init(&protocol_it, protocol_descriptor_list_element);
6627e7a3a6eSMatthias Ringwald uint8_t * protocol_element = des_iterator_get_element(&protocol_it);
6636086246cSMilanka Ringwald
6647e7a3a6eSMatthias Ringwald if (de_get_element_type(protocol_element) != DE_UUID) continue;
6657e7a3a6eSMatthias Ringwald
6667e7a3a6eSMatthias Ringwald uint32_t uuid = de_get_uuid32(protocol_element);
6677e7a3a6eSMatthias Ringwald des_iterator_next(&protocol_it);
6686086246cSMilanka Ringwald switch (uuid){
6696086246cSMilanka Ringwald case BLUETOOTH_PROTOCOL_L2CAP:
6707e7a3a6eSMatthias Ringwald if (!des_iterator_has_more(&protocol_it)) continue;
6717e7a3a6eSMatthias Ringwald de_element_get_uint16(des_iterator_get_element(&protocol_it), &avrcp_sdp_query_context.avrcp_l2cap_psm);
6726086246cSMilanka Ringwald break;
6736086246cSMilanka Ringwald case BLUETOOTH_PROTOCOL_AVCTP:
6747e7a3a6eSMatthias Ringwald if (!des_iterator_has_more(&protocol_it)) continue;
6757e7a3a6eSMatthias Ringwald de_element_get_uint16(des_iterator_get_element(&protocol_it), &avrcp_sdp_query_context.avrcp_version);
6766086246cSMilanka Ringwald break;
6776086246cSMilanka Ringwald default:
6786086246cSMilanka Ringwald break;
6796086246cSMilanka Ringwald }
6806086246cSMilanka Ringwald }
6816086246cSMilanka Ringwald break;
6827e7a3a6eSMatthias Ringwald
6837e7a3a6eSMatthias Ringwald case BLUETOOTH_ATTRIBUTE_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS:
68465bd7af5SMatthias Ringwald if (!avrcp_sdp_query_context.parse_sdp_record) break;
685227d16a5SMatthias Ringwald
6867e7a3a6eSMatthias Ringwald protocol_descriptor_id = 0;
687227d16a5SMatthias Ringwald
6887e7a3a6eSMatthias Ringwald for ( des_iterator_init(&additional_protocol_descriptor_list_it, avrcp_sdp_query_attribute_value);
6897e7a3a6eSMatthias Ringwald des_iterator_has_more(&additional_protocol_descriptor_list_it);
6907e7a3a6eSMatthias Ringwald des_iterator_next(&additional_protocol_descriptor_list_it)) {
691227d16a5SMatthias Ringwald
6927e7a3a6eSMatthias Ringwald if (des_iterator_get_type(&additional_protocol_descriptor_list_it) != DE_DES) continue;
6937e7a3a6eSMatthias Ringwald uint8_t *additional_protocol_descriptor_element = des_iterator_get_element(&additional_protocol_descriptor_list_it);
694227d16a5SMatthias Ringwald
6957e7a3a6eSMatthias Ringwald for ( des_iterator_init(&protocol_descriptor_list_it,additional_protocol_descriptor_element);
6967e7a3a6eSMatthias Ringwald des_iterator_has_more(&protocol_descriptor_list_it);
6977e7a3a6eSMatthias Ringwald des_iterator_next(&protocol_descriptor_list_it)) {
698227d16a5SMatthias Ringwald
6997e7a3a6eSMatthias Ringwald if (des_iterator_get_type(&protocol_descriptor_list_it) != DE_DES) continue;
700227d16a5SMatthias Ringwald
7017e7a3a6eSMatthias Ringwald uint8_t * protocol_descriptor_list_element = des_iterator_get_element(&protocol_descriptor_list_it);
702227d16a5SMatthias Ringwald
7037e7a3a6eSMatthias Ringwald des_iterator_init(&protocol_it, protocol_descriptor_list_element);
7047e7a3a6eSMatthias Ringwald uint8_t * protocol_element = des_iterator_get_element(&protocol_it);
7057e7a3a6eSMatthias Ringwald
7067e7a3a6eSMatthias Ringwald if (de_get_element_type(protocol_element) != DE_UUID) continue;
7077e7a3a6eSMatthias Ringwald
7087e7a3a6eSMatthias Ringwald uint32_t uuid = de_get_uuid32(protocol_element);
7097e7a3a6eSMatthias Ringwald des_iterator_next(&protocol_it);
710227d16a5SMatthias Ringwald switch (uuid) {
711227d16a5SMatthias Ringwald case BLUETOOTH_PROTOCOL_L2CAP:
7127e7a3a6eSMatthias Ringwald if (!des_iterator_has_more(&protocol_it)) continue;
7137e7a3a6eSMatthias Ringwald switch (protocol_descriptor_id) {
7147e7a3a6eSMatthias Ringwald case 0:
7157e7a3a6eSMatthias Ringwald de_element_get_uint16(des_iterator_get_element(&protocol_it),
7167e7a3a6eSMatthias Ringwald &avrcp_sdp_query_context.browsing_l2cap_psm);
7177e7a3a6eSMatthias Ringwald break;
7187e7a3a6eSMatthias Ringwald case 1:
7197e7a3a6eSMatthias Ringwald de_element_get_uint16(des_iterator_get_element(&protocol_it),
7207e7a3a6eSMatthias Ringwald &avrcp_sdp_query_context.cover_art_l2cap_psm);
7217e7a3a6eSMatthias Ringwald break;
7227e7a3a6eSMatthias Ringwald default:
7237e7a3a6eSMatthias Ringwald break;
7247e7a3a6eSMatthias Ringwald }
725227d16a5SMatthias Ringwald break;
726227d16a5SMatthias Ringwald case BLUETOOTH_PROTOCOL_AVCTP:
7277e7a3a6eSMatthias Ringwald if (!des_iterator_has_more(&protocol_it)) continue;
7287e7a3a6eSMatthias Ringwald de_element_get_uint16(des_iterator_get_element(&protocol_it),
7297e7a3a6eSMatthias Ringwald &avrcp_sdp_query_context.browsing_version);
730227d16a5SMatthias Ringwald break;
731227d16a5SMatthias Ringwald default:
732227d16a5SMatthias Ringwald break;
733227d16a5SMatthias Ringwald }
734227d16a5SMatthias Ringwald }
7357e7a3a6eSMatthias Ringwald protocol_descriptor_id++;
736227d16a5SMatthias Ringwald }
737227d16a5SMatthias Ringwald break;
7387e7a3a6eSMatthias Ringwald
7396086246cSMilanka Ringwald default:
7406086246cSMilanka Ringwald break;
7416086246cSMilanka Ringwald }
7426086246cSMilanka Ringwald }
7436086246cSMilanka Ringwald } else {
74465bd7af5SMatthias Ringwald log_error("SDP attribute value buffer size exceeded: available %d, required %d", avrcp_sdp_query_attribute_value_buffer_size, sdp_event_query_attribute_byte_get_attribute_length(packet));
7456086246cSMilanka Ringwald }
7467b81669aSMatthias Ringwald }
7477b81669aSMatthias Ringwald
avrcp_signaling_handle_sdp_query_complete(avrcp_connection_t * connection,uint8_t status)74811256635SMatthias Ringwald static void avrcp_signaling_handle_sdp_query_complete(avrcp_connection_t * connection, uint8_t status){
74934d5ddfaSMatthias Ringwald
75034d5ddfaSMatthias Ringwald // l2cap available?
75147749e04SMatthias Ringwald if (status == ERROR_CODE_SUCCESS){
75234d5ddfaSMatthias Ringwald if (avrcp_sdp_query_context.avrcp_l2cap_psm == 0){
75334d5ddfaSMatthias Ringwald status = SDP_SERVICE_NOT_FOUND;
75434d5ddfaSMatthias Ringwald }
75534d5ddfaSMatthias Ringwald }
75634d5ddfaSMatthias Ringwald
75734d5ddfaSMatthias Ringwald if (status == ERROR_CODE_SUCCESS){
75834d5ddfaSMatthias Ringwald // ready to connect
75934d5ddfaSMatthias Ringwald connection->state = AVCTP_CONNECTION_W2_L2CAP_CONNECT;
76034d5ddfaSMatthias Ringwald
76134d5ddfaSMatthias Ringwald // check if both events have been handled
76234d5ddfaSMatthias Ringwald avrcp_connection_t * connection_with_opposite_role;
76334d5ddfaSMatthias Ringwald switch (connection->role){
76434d5ddfaSMatthias Ringwald case AVRCP_CONTROLLER:
76534d5ddfaSMatthias Ringwald connection_with_opposite_role = avrcp_get_connection_for_avrcp_cid_for_role(AVRCP_TARGET, connection->avrcp_cid);
76634d5ddfaSMatthias Ringwald break;
76734d5ddfaSMatthias Ringwald case AVRCP_TARGET:
76834d5ddfaSMatthias Ringwald connection_with_opposite_role = avrcp_get_connection_for_avrcp_cid_for_role(AVRCP_CONTROLLER, connection->avrcp_cid);
76934d5ddfaSMatthias Ringwald break;
77034d5ddfaSMatthias Ringwald default:
77134d5ddfaSMatthias Ringwald btstack_assert(false);
77234d5ddfaSMatthias Ringwald return;
77334d5ddfaSMatthias Ringwald }
77423ddf1d7SMatthias Ringwald if (connection_with_opposite_role->state == AVCTP_CONNECTION_W2_L2CAP_CONNECT){
77534d5ddfaSMatthias Ringwald connection->state = AVCTP_CONNECTION_W4_L2CAP_CONNECTED;
77634d5ddfaSMatthias Ringwald connection_with_opposite_role->state = AVCTP_CONNECTION_W4_L2CAP_CONNECTED;
77734d5ddfaSMatthias Ringwald l2cap_create_channel(&avrcp_packet_handler, connection->remote_addr, connection->avrcp_l2cap_psm, l2cap_max_mtu(), NULL);
77834d5ddfaSMatthias Ringwald }
77947749e04SMatthias Ringwald } else {
78047749e04SMatthias Ringwald log_info("AVRCP: SDP query failed with status 0x%02x.", status);
78147749e04SMatthias Ringwald avrcp_emit_connection_established(connection->avrcp_cid, connection->remote_addr, connection->con_handle, status);
78247749e04SMatthias Ringwald avrcp_finalize_connection(connection);
78347749e04SMatthias Ringwald }
784463f41baSMilanka Ringwald }
785463f41baSMilanka Ringwald
avrcp_handle_sdp_query_completed(avrcp_connection_t * connection,uint8_t status)78611256635SMatthias Ringwald static void avrcp_handle_sdp_query_completed(avrcp_connection_t * connection, uint8_t status){
78711256635SMatthias Ringwald btstack_assert(connection != NULL);
788ef9778eeSMatthias Ringwald
789ef9778eeSMatthias Ringwald // cache SDP result on success
790ef9778eeSMatthias Ringwald if (status == ERROR_CODE_SUCCESS){
791ef9778eeSMatthias Ringwald connection->avrcp_l2cap_psm = avrcp_sdp_query_context.avrcp_l2cap_psm;
792ef9778eeSMatthias Ringwald connection->browsing_version = avrcp_sdp_query_context.browsing_version;
793ef9778eeSMatthias Ringwald connection->browsing_l2cap_psm = avrcp_sdp_query_context.browsing_l2cap_psm;
794dc8ca179SMatthias Ringwald #ifdef ENABLE_AVRCP_COVER_ART
795dc8ca179SMatthias Ringwald connection->cover_art_psm = avrcp_sdp_query_context.cover_art_l2cap_psm;
796dc8ca179SMatthias Ringwald #endif
797ef9778eeSMatthias Ringwald }
798ef9778eeSMatthias Ringwald
79911256635SMatthias Ringwald // SDP Signaling Query?
80011256635SMatthias Ringwald if (connection->state == AVCTP_CONNECTION_W4_SDP_QUERY_COMPLETE){
80111256635SMatthias Ringwald avrcp_signaling_handle_sdp_query_complete(connection, status);
80211256635SMatthias Ringwald return;
80311256635SMatthias Ringwald }
804f430bcdeSMatthias Ringwald // Browsing SDP <- Browsing Connection <- Existing AVRCP Connection => it wasn't an SDP query for signaling
80511256635SMatthias Ringwald if (avrcp_browsing_sdp_query_complete_handler != NULL){
80611256635SMatthias Ringwald (*avrcp_browsing_sdp_query_complete_handler)(connection, status);
80711256635SMatthias Ringwald }
808f430bcdeSMatthias Ringwald #ifdef ENABLE_AVRCP_COVER_ART
809f430bcdeSMatthias Ringwald // Cover Art SDP <- Cover Art Connection <- Existing AVRCP Connection => it wasn't an SDP query for signaling
810f430bcdeSMatthias Ringwald if (avrcp_cover_art_sdp_query_complete_handler != NULL){
811f430bcdeSMatthias Ringwald (*avrcp_cover_art_sdp_query_complete_handler)(connection, status);
812f430bcdeSMatthias Ringwald }
813f430bcdeSMatthias Ringwald #endif
81411256635SMatthias Ringwald }
81511256635SMatthias Ringwald
avrcp_handle_sdp_client_query_result(uint8_t packet_type,uint16_t channel,uint8_t * packet,uint16_t size)816a062fcddSMilanka Ringwald static void avrcp_handle_sdp_client_query_result(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
81750fc39c5SMilanka Ringwald UNUSED(packet_type);
81850fc39c5SMilanka Ringwald UNUSED(channel);
81950fc39c5SMilanka Ringwald UNUSED(size);
82050fc39c5SMilanka Ringwald
82165bd7af5SMatthias Ringwald avrcp_connection_t * avrcp_target_connection = avrcp_get_connection_for_avrcp_cid_for_role(AVRCP_TARGET, avrcp_sdp_query_context.avrcp_cid);
82265bd7af5SMatthias Ringwald avrcp_connection_t * avrcp_controller_connection = avrcp_get_connection_for_avrcp_cid_for_role(AVRCP_CONTROLLER, avrcp_sdp_query_context.avrcp_cid);
82311256635SMatthias Ringwald bool state_ok = (avrcp_target_connection != NULL) && (avrcp_controller_connection != NULL);
82411256635SMatthias Ringwald
8252423506bSMilanka Ringwald if (!state_ok){
8262423506bSMilanka Ringwald // something wrong, nevertheless, start next sdp query if this one is complete
8272423506bSMilanka Ringwald if (hci_event_packet_get_type(packet) == SDP_EVENT_QUERY_COMPLETE){
82834d5ddfaSMatthias Ringwald avrcp_sdp_query_context.avrcp_cid = 0;
8297a3e6573SMatthias Ringwald avrcp_start_next_sdp_query();
8302423506bSMilanka Ringwald }
83164a27ec5SMilanka Ringwald return;
83264a27ec5SMilanka Ringwald }
8337b81669aSMatthias Ringwald
8347b81669aSMatthias Ringwald uint8_t status;
8357b81669aSMatthias Ringwald
8367b81669aSMatthias Ringwald switch (hci_event_packet_get_type(packet)){
8377b81669aSMatthias Ringwald case SDP_EVENT_QUERY_ATTRIBUTE_VALUE:
83850fc39c5SMilanka Ringwald avrcp_handle_sdp_client_query_attribute_value(packet);
8392423506bSMilanka Ringwald return;
8406086246cSMilanka Ringwald
84150fc39c5SMilanka Ringwald case SDP_EVENT_QUERY_COMPLETE:
84234d5ddfaSMatthias Ringwald // handle result
8435448c259SMilanka Ringwald status = sdp_event_query_complete_get_status(packet);
84447749e04SMatthias Ringwald avrcp_handle_sdp_query_completed(avrcp_controller_connection, status);
84547749e04SMatthias Ringwald avrcp_handle_sdp_query_completed(avrcp_target_connection, status);
8465448c259SMilanka Ringwald
84734d5ddfaSMatthias Ringwald // query done, start next one
84834d5ddfaSMatthias Ringwald avrcp_sdp_query_context.avrcp_cid = 0;
84934d5ddfaSMatthias Ringwald avrcp_start_next_sdp_query();
850463f41baSMilanka Ringwald break;
85150fc39c5SMilanka Ringwald
85250fc39c5SMilanka Ringwald default:
8532423506bSMilanka Ringwald return;
8540036e267SMilanka Ringwald }
85564a27ec5SMilanka Ringwald
8567a3e6573SMatthias Ringwald }
8577a3e6573SMatthias Ringwald
avrcp_handle_start_sdp_client_query(void * context)8587a3e6573SMatthias Ringwald static void avrcp_handle_start_sdp_client_query(void * context){
8597a3e6573SMatthias Ringwald UNUSED(context);
8607a3e6573SMatthias Ringwald
86134d5ddfaSMatthias Ringwald avrcp_connection_t * avrcp_target_connection = avrcp_get_connection_for_avrcp_cid_for_role(AVRCP_TARGET, avrcp_sdp_query_context.avrcp_cid);
86234d5ddfaSMatthias Ringwald avrcp_connection_t * avrcp_controller_connection = avrcp_get_connection_for_avrcp_cid_for_role(AVRCP_CONTROLLER, avrcp_sdp_query_context.avrcp_cid);
86334d5ddfaSMatthias Ringwald bool state_ok = (avrcp_target_connection != NULL) && (avrcp_controller_connection != NULL);
86434d5ddfaSMatthias Ringwald if (state_ok == false){
86534d5ddfaSMatthias Ringwald // connection seems to got finalized in the meantime, just trigger next query
86634d5ddfaSMatthias Ringwald avrcp_start_next_sdp_query();
86734d5ddfaSMatthias Ringwald return;
86834d5ddfaSMatthias Ringwald }
86934d5ddfaSMatthias Ringwald
87034d5ddfaSMatthias Ringwald // prevent triggering SDP query twice (for each role once)
87134d5ddfaSMatthias Ringwald avrcp_target_connection->trigger_sdp_query = false;
87234d5ddfaSMatthias Ringwald avrcp_controller_connection->trigger_sdp_query = false;
87334d5ddfaSMatthias Ringwald
87434d5ddfaSMatthias Ringwald sdp_client_query_uuid16(&avrcp_handle_sdp_client_query_result, avrcp_target_connection->remote_addr, BLUETOOTH_PROTOCOL_AVCTP);
87534d5ddfaSMatthias Ringwald }
87634d5ddfaSMatthias Ringwald
avrcp_start_next_sdp_query(void)87734d5ddfaSMatthias Ringwald static void avrcp_start_next_sdp_query(void) {
87834d5ddfaSMatthias Ringwald if (avrcp_sdp_query_context.avrcp_cid != 0) {
87934d5ddfaSMatthias Ringwald return;
88034d5ddfaSMatthias Ringwald }
8817a3e6573SMatthias Ringwald btstack_linked_list_iterator_t it;
8827a3e6573SMatthias Ringwald btstack_linked_list_iterator_init(&it, &avrcp_connections);
8837a3e6573SMatthias Ringwald while (btstack_linked_list_iterator_has_next(&it)){
8847a3e6573SMatthias Ringwald avrcp_connection_t * connection = (avrcp_connection_t *)btstack_linked_list_iterator_next(&it);
885a8fcc0f5SMatthias Ringwald if (connection->trigger_sdp_query == false) continue;
8867a3e6573SMatthias Ringwald
88734d5ddfaSMatthias Ringwald // we're ready => setup avrcp_sdp_query_context and request sdp query
88834d5ddfaSMatthias Ringwald avrcp_sdp_query_context.avrcp_cid = connection->avrcp_cid;
8897a3e6573SMatthias Ringwald avrcp_sdp_query_context.avrcp_l2cap_psm = 0;
8907a3e6573SMatthias Ringwald avrcp_sdp_query_context.avrcp_version = 0;
8917a3e6573SMatthias Ringwald avrcp_sdp_query_registration.callback = &avrcp_handle_start_sdp_client_query;
89234d5ddfaSMatthias Ringwald uint8_t status = sdp_client_register_query_callback(&avrcp_sdp_query_registration);
89334d5ddfaSMatthias Ringwald btstack_assert(status == ERROR_CODE_SUCCESS);
894b84b7ce1SMatthias Ringwald UNUSED(status);
89534d5ddfaSMatthias Ringwald break;
89634d5ddfaSMatthias Ringwald }
897463f41baSMilanka Ringwald }
89850fc39c5SMilanka Ringwald
avrcp_handle_incoming_connection_for_role(avrcp_role_t role,avrcp_connection_t * connection,bd_addr_t event_addr,hci_con_handle_t con_handle,uint16_t local_cid,uint16_t avrcp_cid)8997dbc6cb8SMilanka Ringwald static avrcp_connection_t * avrcp_handle_incoming_connection_for_role(avrcp_role_t role, avrcp_connection_t * connection, bd_addr_t event_addr, hci_con_handle_t con_handle, uint16_t local_cid, uint16_t avrcp_cid){
900a062fcddSMilanka Ringwald if (connection == NULL){
901a062fcddSMilanka Ringwald connection = avrcp_create_connection(role, event_addr);
902be32e7f1SMilanka Ringwald }
903a062fcddSMilanka Ringwald if (connection) {
9040f76c2d7SMatthias Ringwald connection->state = AVCTP_CONNECTION_W4_L2CAP_CONNECTED;
9050f76c2d7SMatthias Ringwald connection->l2cap_signaling_cid = local_cid;
9067403cbbaSMilanka Ringwald connection->avrcp_cid = avrcp_cid;
9077dbc6cb8SMilanka Ringwald connection->con_handle = con_handle;
90837fae987SMilanka Ringwald btstack_run_loop_remove_timer(&connection->retry_timer);
909a062fcddSMilanka Ringwald }
910a062fcddSMilanka Ringwald return connection;
911654724deSMilanka Ringwald }
912be32e7f1SMilanka Ringwald
avrcp_handle_open_connection(avrcp_connection_t * connection,hci_con_handle_t con_handle,uint16_t local_cid,uint16_t l2cap_mtu)9137dbc6cb8SMilanka Ringwald static void avrcp_handle_open_connection(avrcp_connection_t * connection, hci_con_handle_t con_handle, uint16_t local_cid, uint16_t l2cap_mtu){
914be32e7f1SMilanka Ringwald connection->l2cap_signaling_cid = local_cid;
9150036e267SMilanka Ringwald connection->l2cap_mtu = l2cap_mtu;
9167dbc6cb8SMilanka Ringwald connection->con_handle = con_handle;
917558ceb4aSMilanka Ringwald connection->incoming_declined = false;
918393f5724SMilanka Ringwald connection->target_song_length_ms = 0xFFFFFFFF;
919393f5724SMilanka Ringwald connection->target_song_position_ms = 0xFFFFFFFF;
920f28ce84eSMilanka Ringwald memset(connection->target_track_id, 0xFF, 8);
921f28ce84eSMilanka Ringwald connection->target_track_selected = false;
9227d1b72e5SMilanka Ringwald connection->target_track_changed = false;
923393f5724SMilanka Ringwald connection->target_playback_status = AVRCP_PLAYBACK_STATUS_STOPPED;
924cee0e5b6SMilanka Ringwald connection->state = AVCTP_CONNECTION_OPENED;
925d1207cd8SMilanka Ringwald
92664a27ec5SMilanka Ringwald log_info("L2CAP_EVENT_CHANNEL_OPENED avrcp_cid 0x%02x, l2cap_signaling_cid 0x%02x, role %d, state %d", connection->avrcp_cid, connection->l2cap_signaling_cid, connection->role, connection->state);
9270036e267SMilanka Ringwald }
9280036e267SMilanka Ringwald
avrcp_retry_timer_timeout_handler(btstack_timer_source_t * timer)92937fae987SMilanka Ringwald static void avrcp_retry_timer_timeout_handler(btstack_timer_source_t * timer){
930a062fcddSMilanka Ringwald uint16_t avrcp_cid = (uint16_t)(uintptr_t) btstack_run_loop_get_timer_context(timer);
9311945fe3eSMilanka Ringwald avrcp_connection_t * connection_controller = avrcp_get_connection_for_avrcp_cid_for_role(AVRCP_CONTROLLER, avrcp_cid);
93214c8559dSMilanka Ringwald if (connection_controller == NULL) return;
9331945fe3eSMilanka Ringwald avrcp_connection_t * connection_target = avrcp_get_connection_for_avrcp_cid_for_role(AVRCP_TARGET, avrcp_cid);
93414c8559dSMilanka Ringwald if (connection_target == NULL) return;
935a062fcddSMilanka Ringwald
93637fae987SMilanka Ringwald if (connection_controller->state == AVCTP_CONNECTION_W2_L2CAP_RETRY){
93714c8559dSMilanka Ringwald connection_controller->state = AVCTP_CONNECTION_W4_L2CAP_CONNECTED;
93814c8559dSMilanka Ringwald connection_target->state = AVCTP_CONNECTION_W4_L2CAP_CONNECTED;
93914c8559dSMilanka Ringwald l2cap_create_channel(&avrcp_packet_handler, connection_controller->remote_addr, connection_controller->avrcp_l2cap_psm, l2cap_max_mtu(), NULL);
94014c8559dSMilanka Ringwald }
941a062fcddSMilanka Ringwald }
942a062fcddSMilanka Ringwald
avrcp_retry_timer_start(avrcp_connection_t * connection)94337fae987SMilanka Ringwald static void avrcp_retry_timer_start(avrcp_connection_t * connection){
94437fae987SMilanka Ringwald btstack_run_loop_set_timer_handler(&connection->retry_timer, avrcp_retry_timer_timeout_handler);
94537fae987SMilanka Ringwald btstack_run_loop_set_timer_context(&connection->retry_timer, (void *)(uintptr_t)connection->avrcp_cid);
946a062fcddSMilanka Ringwald
947a062fcddSMilanka Ringwald // add some jitter/randomness to reconnect delay
948a062fcddSMilanka Ringwald uint32_t timeout = 100 + (btstack_run_loop_get_time_ms() & 0x7F);
94937fae987SMilanka Ringwald btstack_run_loop_set_timer(&connection->retry_timer, timeout);
950a062fcddSMilanka Ringwald
95137fae987SMilanka Ringwald btstack_run_loop_add_timer(&connection->retry_timer);
952a062fcddSMilanka Ringwald }
953a062fcddSMilanka Ringwald
avrcp_get_frame_type(uint8_t header)954b8081399SMilanka Ringwald static avrcp_frame_type_t avrcp_get_frame_type(uint8_t header){
955b8081399SMilanka Ringwald return (avrcp_frame_type_t)((header & 0x02) >> 1);
956b8081399SMilanka Ringwald }
957b8081399SMilanka Ringwald
avrcp_packet_handler(uint8_t packet_type,uint16_t channel,uint8_t * packet,uint16_t size)9580036e267SMilanka Ringwald static void avrcp_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
9590036e267SMilanka Ringwald UNUSED(channel);
9600036e267SMilanka Ringwald UNUSED(size);
9610036e267SMilanka Ringwald bd_addr_t event_addr;
9620036e267SMilanka Ringwald uint16_t local_cid;
9630036e267SMilanka Ringwald uint16_t l2cap_mtu;
9640036e267SMilanka Ringwald uint8_t status;
9659b1b0ebdSMatthias Ringwald bool decline_connection;
9669b1b0ebdSMatthias Ringwald bool outoing_active;
967e39127d3SMatthias Ringwald bool connection_already_established;
9687dbc6cb8SMilanka Ringwald hci_con_handle_t con_handle;
9690036e267SMilanka Ringwald
970cee0e5b6SMilanka Ringwald avrcp_connection_t * connection_controller;
971cee0e5b6SMilanka Ringwald avrcp_connection_t * connection_target;
972bf67b2dbSMatthias Ringwald bool can_send;
973cee0e5b6SMilanka Ringwald
9740036e267SMilanka Ringwald switch (packet_type) {
9750036e267SMilanka Ringwald case HCI_EVENT_PACKET:
9760036e267SMilanka Ringwald switch (hci_event_packet_get_type(packet)) {
9770036e267SMilanka Ringwald
9780036e267SMilanka Ringwald case L2CAP_EVENT_INCOMING_CONNECTION:
979e3d57ee2SMilanka Ringwald btstack_assert(avrcp_controller_packet_handler != NULL);
980e3d57ee2SMilanka Ringwald btstack_assert(avrcp_target_packet_handler != NULL);
981e3d57ee2SMilanka Ringwald
9820036e267SMilanka Ringwald l2cap_event_incoming_connection_get_address(packet, event_addr);
9830036e267SMilanka Ringwald local_cid = l2cap_event_incoming_connection_get_local_cid(packet);
9847dbc6cb8SMilanka Ringwald con_handle = l2cap_event_incoming_connection_get_handle(packet);
985cee0e5b6SMilanka Ringwald
9867dbc6cb8SMilanka Ringwald outoing_active = false;
987e39127d3SMatthias Ringwald connection_already_established = false;
988e39127d3SMatthias Ringwald
9891945fe3eSMilanka Ringwald connection_target = avrcp_get_connection_for_bd_addr_for_role(AVRCP_TARGET, event_addr);
990cee0e5b6SMilanka Ringwald if (connection_target != NULL){
991a062fcddSMilanka Ringwald if (connection_target->state == AVCTP_CONNECTION_W4_L2CAP_CONNECTED){
9929b1b0ebdSMatthias Ringwald outoing_active = true;
993cee0e5b6SMilanka Ringwald connection_target->incoming_declined = true;
9949b1b0ebdSMatthias Ringwald }
995e39127d3SMatthias Ringwald if (connection_target->state >= AVCTP_CONNECTION_OPENED){
996e39127d3SMatthias Ringwald connection_already_established = true;
997e39127d3SMatthias Ringwald }
998a062fcddSMilanka Ringwald }
999558ceb4aSMilanka Ringwald
10001945fe3eSMilanka Ringwald connection_controller = avrcp_get_connection_for_bd_addr_for_role(AVRCP_CONTROLLER, event_addr);
1001cee0e5b6SMilanka Ringwald if (connection_controller != NULL){
1002a062fcddSMilanka Ringwald if (connection_controller->state == AVCTP_CONNECTION_W4_L2CAP_CONNECTED) {
10039b1b0ebdSMatthias Ringwald outoing_active = true;
1004cee0e5b6SMilanka Ringwald connection_controller->incoming_declined = true;
10059b1b0ebdSMatthias Ringwald }
1006e39127d3SMatthias Ringwald if (connection_controller->state >= AVCTP_CONNECTION_OPENED){
1007e39127d3SMatthias Ringwald connection_already_established = true;
1008e39127d3SMatthias Ringwald }
1009a062fcddSMilanka Ringwald }
1010558ceb4aSMilanka Ringwald
1011e39127d3SMatthias Ringwald decline_connection = outoing_active || connection_already_established;
1012a062fcddSMilanka Ringwald if (decline_connection == false){
1013a062fcddSMilanka Ringwald uint16_t avrcp_cid;
1014a062fcddSMilanka Ringwald if ((connection_controller == NULL) || (connection_target == NULL)){
1015a062fcddSMilanka Ringwald avrcp_cid = avrcp_get_next_cid(AVRCP_CONTROLLER);
1016a062fcddSMilanka Ringwald } else {
1017a062fcddSMilanka Ringwald avrcp_cid = connection_controller->avrcp_cid;
1018a062fcddSMilanka Ringwald }
10190036e267SMilanka Ringwald // create two connection objects (both)
10207dbc6cb8SMilanka Ringwald connection_target = avrcp_handle_incoming_connection_for_role(AVRCP_TARGET, connection_target, event_addr, con_handle, local_cid, avrcp_cid);
10217dbc6cb8SMilanka Ringwald connection_controller = avrcp_handle_incoming_connection_for_role(AVRCP_CONTROLLER, connection_controller, event_addr, con_handle, local_cid, avrcp_cid);
1022a062fcddSMilanka Ringwald if ((connection_target == NULL) || (connection_controller == NULL)){
10239b1b0ebdSMatthias Ringwald decline_connection = true;
1024a062fcddSMilanka Ringwald if (connection_target) {
1025a062fcddSMilanka Ringwald avrcp_finalize_connection(connection_target);
1026a062fcddSMilanka Ringwald }
1027a062fcddSMilanka Ringwald if (connection_controller) {
1028a062fcddSMilanka Ringwald avrcp_finalize_connection(connection_controller);
1029a062fcddSMilanka Ringwald }
10300036e267SMilanka Ringwald }
10319b1b0ebdSMatthias Ringwald }
10329b1b0ebdSMatthias Ringwald if (decline_connection){
1033e39127d3SMatthias Ringwald log_info("Decline connection 0x%04x: outgoing active %u, connection already established: %u", local_cid, outoing_active, connection_already_established);
10349b1b0ebdSMatthias Ringwald l2cap_decline_connection(local_cid);
10359b1b0ebdSMatthias Ringwald } else {
1036e39127d3SMatthias Ringwald log_info("AVRCP: L2CAP_EVENT_INCOMING_CONNECTION local cid 0x%04x, state %d", local_cid, connection_controller->state);
10370036e267SMilanka Ringwald l2cap_accept_connection(local_cid);
10389b1b0ebdSMatthias Ringwald }
10390036e267SMilanka Ringwald break;
10400036e267SMilanka Ringwald
10410036e267SMilanka Ringwald case L2CAP_EVENT_CHANNEL_OPENED:
10420036e267SMilanka Ringwald l2cap_event_channel_opened_get_address(packet, event_addr);
10430036e267SMilanka Ringwald status = l2cap_event_channel_opened_get_status(packet);
10440036e267SMilanka Ringwald local_cid = l2cap_event_channel_opened_get_local_cid(packet);
10450036e267SMilanka Ringwald l2cap_mtu = l2cap_event_channel_opened_get_remote_mtu(packet);
10467dbc6cb8SMilanka Ringwald con_handle = l2cap_event_channel_opened_get_handle(packet);
10470036e267SMilanka Ringwald
10481945fe3eSMilanka Ringwald connection_controller = avrcp_get_connection_for_bd_addr_for_role(AVRCP_CONTROLLER, event_addr);
10491945fe3eSMilanka Ringwald connection_target = avrcp_get_connection_for_bd_addr_for_role(AVRCP_TARGET, event_addr);
1050a062fcddSMilanka Ringwald
1051a062fcddSMilanka Ringwald // incoming: structs are already created in L2CAP_EVENT_INCOMING_CONNECTION
1052a062fcddSMilanka Ringwald // outgoing: structs are cteated in avrcp_connect()
1053cee0e5b6SMilanka Ringwald if ((connection_controller == NULL) || (connection_target == NULL)) {
1054cee0e5b6SMilanka Ringwald break;
1055cee0e5b6SMilanka Ringwald }
1056cee0e5b6SMilanka Ringwald
1057a062fcddSMilanka Ringwald switch (status){
1058a062fcddSMilanka Ringwald case ERROR_CODE_SUCCESS:
10597dbc6cb8SMilanka Ringwald avrcp_handle_open_connection(connection_target, con_handle, local_cid, l2cap_mtu);
10607dbc6cb8SMilanka Ringwald avrcp_handle_open_connection(connection_controller, con_handle, local_cid, l2cap_mtu);
10617dbc6cb8SMilanka Ringwald avrcp_emit_connection_established(connection_controller->avrcp_cid, event_addr, con_handle, status);
1062a062fcddSMilanka Ringwald return;
1063a062fcddSMilanka Ringwald case L2CAP_CONNECTION_RESPONSE_RESULT_REFUSED_RESOURCES:
1064a062fcddSMilanka Ringwald if (connection_controller->incoming_declined == true){
1065a062fcddSMilanka Ringwald log_info("Incoming connection was declined, and the outgoing failed");
106637fae987SMilanka Ringwald connection_controller->state = AVCTP_CONNECTION_W2_L2CAP_RETRY;
1067a062fcddSMilanka Ringwald connection_controller->incoming_declined = false;
106837fae987SMilanka Ringwald connection_target->state = AVCTP_CONNECTION_W2_L2CAP_RETRY;
1069a062fcddSMilanka Ringwald connection_target->incoming_declined = false;
107037fae987SMilanka Ringwald avrcp_retry_timer_start(connection_controller);
1071a062fcddSMilanka Ringwald return;
1072a062fcddSMilanka Ringwald }
1073a062fcddSMilanka Ringwald break;
1074a062fcddSMilanka Ringwald default:
1075a062fcddSMilanka Ringwald break;
1076a062fcddSMilanka Ringwald }
1077cee0e5b6SMilanka Ringwald log_info("L2CAP connection to connection %s failed. status code 0x%02x", bd_addr_to_str(event_addr), status);
10787dbc6cb8SMilanka Ringwald avrcp_emit_connection_established(connection_controller->avrcp_cid, event_addr, con_handle, status);
1079cee0e5b6SMilanka Ringwald avrcp_finalize_connection(connection_controller);
1080cee0e5b6SMilanka Ringwald avrcp_finalize_connection(connection_target);
1081cee0e5b6SMilanka Ringwald
1082be32e7f1SMilanka Ringwald break;
1083be32e7f1SMilanka Ringwald
1084be32e7f1SMilanka Ringwald case L2CAP_EVENT_CHANNEL_CLOSED:
1085be32e7f1SMilanka Ringwald local_cid = l2cap_event_channel_closed_get_local_cid(packet);
1086cee0e5b6SMilanka Ringwald
10871945fe3eSMilanka Ringwald connection_controller = avrcp_get_connection_for_l2cap_signaling_cid_for_role(AVRCP_CONTROLLER, local_cid);
10881945fe3eSMilanka Ringwald connection_target = avrcp_get_connection_for_l2cap_signaling_cid_for_role(AVRCP_TARGET, local_cid);
1089cee0e5b6SMilanka Ringwald if ((connection_controller == NULL) || (connection_target == NULL)) {
1090cee0e5b6SMilanka Ringwald break;
1091cee0e5b6SMilanka Ringwald }
1092cee0e5b6SMilanka Ringwald avrcp_emit_connection_closed(connection_controller->avrcp_cid);
1093cee0e5b6SMilanka Ringwald avrcp_finalize_connection(connection_controller);
1094cee0e5b6SMilanka Ringwald avrcp_finalize_connection(connection_target);
10950036e267SMilanka Ringwald break;
10960036e267SMilanka Ringwald
10970036e267SMilanka Ringwald case L2CAP_EVENT_CAN_SEND_NOW:
10980036e267SMilanka Ringwald local_cid = l2cap_event_can_send_now_get_local_cid(packet);
1099bf67b2dbSMatthias Ringwald can_send = true;
1100e3d57ee2SMilanka Ringwald
11011945fe3eSMilanka Ringwald connection_target = avrcp_get_connection_for_l2cap_signaling_cid_for_role(AVRCP_TARGET, local_cid);
1102cf36dea0SMilanka Ringwald if ((connection_target != NULL) && connection_target->wait_to_send){
1103cf36dea0SMilanka Ringwald connection_target->wait_to_send = false;
11040036e267SMilanka Ringwald (*avrcp_target_packet_handler)(HCI_EVENT_PACKET, channel, packet, size);
1105bf67b2dbSMatthias Ringwald can_send = false;
11060036e267SMilanka Ringwald }
11070036e267SMilanka Ringwald
11081945fe3eSMilanka Ringwald connection_controller = avrcp_get_connection_for_l2cap_signaling_cid_for_role(AVRCP_CONTROLLER, local_cid);
1109cf36dea0SMilanka Ringwald if ((connection_controller != NULL) && connection_controller->wait_to_send){
1110bf67b2dbSMatthias Ringwald if (can_send){
1111cf36dea0SMilanka Ringwald connection_controller->wait_to_send = false;
11120036e267SMilanka Ringwald (*avrcp_controller_packet_handler)(HCI_EVENT_PACKET, channel, packet, size);
1113bf67b2dbSMatthias Ringwald } else {
1114bf67b2dbSMatthias Ringwald l2cap_request_can_send_now_event(local_cid);
1115bf67b2dbSMatthias Ringwald }
1116be32e7f1SMilanka Ringwald }
1117be32e7f1SMilanka Ringwald break;
11180036e267SMilanka Ringwald
1119be32e7f1SMilanka Ringwald default:
1120be32e7f1SMilanka Ringwald break;
1121be32e7f1SMilanka Ringwald }
1122b106f728SMilanka Ringwald break;
1123e3d57ee2SMilanka Ringwald
1124e3d57ee2SMilanka Ringwald case L2CAP_DATA_PACKET:
1125b8081399SMilanka Ringwald switch (avrcp_get_frame_type(packet[0])){
1126e3d57ee2SMilanka Ringwald case AVRCP_RESPONSE_FRAME:
1127e3d57ee2SMilanka Ringwald (*avrcp_controller_packet_handler)(packet_type, channel, packet, size);
1128e3d57ee2SMilanka Ringwald break;
1129e3d57ee2SMilanka Ringwald case AVRCP_COMMAND_FRAME:
1130e3d57ee2SMilanka Ringwald default: // make compiler happy
1131e3d57ee2SMilanka Ringwald (*avrcp_target_packet_handler)(packet_type, channel, packet, size);
1132e3d57ee2SMilanka Ringwald break;
1133e3d57ee2SMilanka Ringwald }
1134e3d57ee2SMilanka Ringwald break;
1135e3d57ee2SMilanka Ringwald
1136b106f728SMilanka Ringwald default:
1137b106f728SMilanka Ringwald break;
1138b106f728SMilanka Ringwald }
1139be32e7f1SMilanka Ringwald }
1140be32e7f1SMilanka Ringwald
avrcp_init(void)1141bd9a32e4SMatthias Ringwald void avrcp_init(void){
1142bd9a32e4SMatthias Ringwald avrcp_connections = NULL;
1143bd9a32e4SMatthias Ringwald if (avrcp_l2cap_service_registered) return;
1144bd9a32e4SMatthias Ringwald
1145bd9a32e4SMatthias Ringwald int status = l2cap_register_service(&avrcp_packet_handler, BLUETOOTH_PSM_AVCTP, 0xffff, gap_get_security_level());
1146bd9a32e4SMatthias Ringwald if (status != ERROR_CODE_SUCCESS) return;
1147bd9a32e4SMatthias Ringwald avrcp_l2cap_service_registered = true;
1148bd9a32e4SMatthias Ringwald }
1149bd9a32e4SMatthias Ringwald
avrcp_register_controller_packet_handler(btstack_packet_handler_t callback)1150bd9a32e4SMatthias Ringwald void avrcp_register_controller_packet_handler(btstack_packet_handler_t callback){
1151bd9a32e4SMatthias Ringwald // note: called by avrcp_controller_init
1152bd9a32e4SMatthias Ringwald avrcp_controller_packet_handler = callback;
1153bd9a32e4SMatthias Ringwald }
1154bd9a32e4SMatthias Ringwald
avrcp_register_target_packet_handler(btstack_packet_handler_t callback)1155bd9a32e4SMatthias Ringwald void avrcp_register_target_packet_handler(btstack_packet_handler_t callback){
1156bd9a32e4SMatthias Ringwald // note: called by avrcp_target_init
1157bd9a32e4SMatthias Ringwald avrcp_target_packet_handler = callback;
1158bd9a32e4SMatthias Ringwald }
1159bd9a32e4SMatthias Ringwald
avrcp_register_packet_handler(btstack_packet_handler_t callback)1160bd9a32e4SMatthias Ringwald void avrcp_register_packet_handler(btstack_packet_handler_t callback){
1161bd9a32e4SMatthias Ringwald btstack_assert(callback != NULL);
1162bd9a32e4SMatthias Ringwald avrcp_callback = callback;
1163bd9a32e4SMatthias Ringwald }
1164bd9a32e4SMatthias Ringwald
avrcp_register_browsing_sdp_query_complete_handler(void (* callback)(avrcp_connection_t * connection,uint8_t status))1165bd9a32e4SMatthias Ringwald void avrcp_register_browsing_sdp_query_complete_handler(void (*callback)(avrcp_connection_t * connection, uint8_t status)){
1166bd9a32e4SMatthias Ringwald btstack_assert(callback != NULL);
1167bd9a32e4SMatthias Ringwald avrcp_browsing_sdp_query_complete_handler = callback;
1168bd9a32e4SMatthias Ringwald }
1169bd9a32e4SMatthias Ringwald
1170f430bcdeSMatthias Ringwald #ifdef ENABLE_AVRCP_COVER_ART
avrcp_register_cover_art_sdp_query_complete_handler(void (* callback)(avrcp_connection_t * connection,uint8_t status))1171f430bcdeSMatthias Ringwald void avrcp_register_cover_art_sdp_query_complete_handler(void (*callback)(avrcp_connection_t * connection, uint8_t status)){
1172f430bcdeSMatthias Ringwald btstack_assert(callback != NULL);
1173f430bcdeSMatthias Ringwald avrcp_cover_art_sdp_query_complete_handler = callback;
1174f430bcdeSMatthias Ringwald }
1175f430bcdeSMatthias Ringwald #endif
1176bd9a32e4SMatthias Ringwald
avrcp_trigger_sdp_query(avrcp_connection_t * connection_controller,avrcp_connection_t * connection_target)11776eafac07SMatthias Ringwald void avrcp_trigger_sdp_query(avrcp_connection_t *connection_controller, avrcp_connection_t *connection_target) {
11786eafac07SMatthias Ringwald connection_controller->trigger_sdp_query = true;
11796eafac07SMatthias Ringwald connection_target->trigger_sdp_query = true;
11806eafac07SMatthias Ringwald
11816eafac07SMatthias Ringwald avrcp_start_next_sdp_query();
11826eafac07SMatthias Ringwald }
11836eafac07SMatthias Ringwald
avrcp_connect(bd_addr_t remote_addr,uint16_t * avrcp_cid)1184fe10780bSMilanka Ringwald uint8_t avrcp_connect(bd_addr_t remote_addr, uint16_t * avrcp_cid){
1185e3d57ee2SMilanka Ringwald btstack_assert(avrcp_controller_packet_handler != NULL);
1186e3d57ee2SMilanka Ringwald btstack_assert(avrcp_target_packet_handler != NULL);
1187f9ef80eaSMilanka Ringwald
11881945fe3eSMilanka Ringwald avrcp_connection_t * connection_controller = avrcp_get_connection_for_bd_addr_for_role(AVRCP_CONTROLLER, remote_addr);
11897b7bd7d7SMatthias Ringwald bool setup_active = false;
1190bd66227aSMilanka Ringwald if (connection_controller){
11917b7bd7d7SMatthias Ringwald // allow to call avrcp_connect after signaling connection was triggered remotely
11927b7bd7d7SMatthias Ringwald // @note this also allows to call avrcp_connect again before SLC is complete
11937b7bd7d7SMatthias Ringwald if (connection_controller->state < AVCTP_CONNECTION_OPENED){
11947b7bd7d7SMatthias Ringwald setup_active = true;
11957b7bd7d7SMatthias Ringwald } else {
1196bd66227aSMilanka Ringwald return ERROR_CODE_COMMAND_DISALLOWED;
1197bd66227aSMilanka Ringwald }
11987b7bd7d7SMatthias Ringwald }
11991945fe3eSMilanka Ringwald avrcp_connection_t * connection_target = avrcp_get_connection_for_bd_addr_for_role(AVRCP_TARGET, remote_addr);
1200bd66227aSMilanka Ringwald if (connection_target){
12017b7bd7d7SMatthias Ringwald if (connection_target->state < AVCTP_CONNECTION_OPENED){
12027b7bd7d7SMatthias Ringwald setup_active = true;
12037b7bd7d7SMatthias Ringwald } else {
1204bd66227aSMilanka Ringwald return ERROR_CODE_COMMAND_DISALLOWED;
1205bd66227aSMilanka Ringwald }
12067b7bd7d7SMatthias Ringwald }
12077b7bd7d7SMatthias Ringwald if (setup_active){
12087b7bd7d7SMatthias Ringwald return ERROR_CODE_SUCCESS;
12097b7bd7d7SMatthias Ringwald }
1210bd66227aSMilanka Ringwald
12117403cbbaSMilanka Ringwald uint16_t cid = avrcp_get_next_cid(AVRCP_CONTROLLER);
12127403cbbaSMilanka Ringwald
1213bd66227aSMilanka Ringwald connection_controller = avrcp_create_connection(AVRCP_CONTROLLER, remote_addr);
1214bd66227aSMilanka Ringwald if (!connection_controller) return BTSTACK_MEMORY_ALLOC_FAILED;
1215bd66227aSMilanka Ringwald
1216bd66227aSMilanka Ringwald connection_target = avrcp_create_connection(AVRCP_TARGET, remote_addr);
1217bd66227aSMilanka Ringwald if (!connection_target){
1218bd66227aSMilanka Ringwald avrcp_finalize_connection(connection_controller);
1219bd66227aSMilanka Ringwald return BTSTACK_MEMORY_ALLOC_FAILED;
1220bd66227aSMilanka Ringwald }
1221bd66227aSMilanka Ringwald
1222bd66227aSMilanka Ringwald if (avrcp_cid != NULL){
1223bd66227aSMilanka Ringwald *avrcp_cid = cid;
1224bd66227aSMilanka Ringwald }
1225bd66227aSMilanka Ringwald
12267403cbbaSMilanka Ringwald connection_controller->avrcp_cid = cid;
12277403cbbaSMilanka Ringwald connection_target->avrcp_cid = cid;
12285dd5e7e3SMilanka Ringwald
1229a8fcc0f5SMatthias Ringwald connection_controller->state = AVCTP_CONNECTION_W4_SDP_QUERY_COMPLETE;
1230a8fcc0f5SMatthias Ringwald connection_target->state = AVCTP_CONNECTION_W4_SDP_QUERY_COMPLETE;
1231a8fcc0f5SMatthias Ringwald
12326eafac07SMatthias Ringwald avrcp_trigger_sdp_query(connection_controller, connection_target);
1233a8fcc0f5SMatthias Ringwald
123464a27ec5SMilanka Ringwald return ERROR_CODE_SUCCESS;
1235be32e7f1SMilanka Ringwald }
1236638481deSMilanka Ringwald
avrcp_disconnect(uint16_t avrcp_cid)1237bd9a32e4SMatthias Ringwald uint8_t avrcp_disconnect(uint16_t avrcp_cid){
1238bd9a32e4SMatthias Ringwald avrcp_connection_t * connection_controller = avrcp_get_connection_for_avrcp_cid_for_role(AVRCP_CONTROLLER, avrcp_cid);
1239bd9a32e4SMatthias Ringwald if (!connection_controller){
1240bd9a32e4SMatthias Ringwald return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1241bd9a32e4SMatthias Ringwald }
1242bd9a32e4SMatthias Ringwald avrcp_connection_t * connection_target = avrcp_get_connection_for_avrcp_cid_for_role(AVRCP_TARGET, avrcp_cid);
1243bd9a32e4SMatthias Ringwald if (!connection_target){
1244bd9a32e4SMatthias Ringwald return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1245bd9a32e4SMatthias Ringwald }
1246bd9a32e4SMatthias Ringwald if (connection_controller->browsing_connection){
1247bd9a32e4SMatthias Ringwald l2cap_disconnect(connection_controller->browsing_connection->l2cap_browsing_cid);
1248bd9a32e4SMatthias Ringwald }
1249bd9a32e4SMatthias Ringwald l2cap_disconnect(connection_controller->l2cap_signaling_cid);
1250bd9a32e4SMatthias Ringwald return ERROR_CODE_SUCCESS;
1251638481deSMilanka Ringwald }
1252b106f728SMilanka Ringwald
avrcp_deinit(void)1253680af5dcSMatthias Ringwald void avrcp_deinit(void){
125465bd7af5SMatthias Ringwald avrcp_l2cap_service_registered = false;
1255680af5dcSMatthias Ringwald
125665bd7af5SMatthias Ringwald avrcp_cid_counter = 0;
125765bd7af5SMatthias Ringwald avrcp_connections = NULL;
1258680af5dcSMatthias Ringwald
1259680af5dcSMatthias Ringwald avrcp_callback = NULL;
1260680af5dcSMatthias Ringwald avrcp_controller_packet_handler = NULL;
1261680af5dcSMatthias Ringwald avrcp_target_packet_handler = NULL;
126265bd7af5SMatthias Ringwald
126365bd7af5SMatthias Ringwald (void) memset(&avrcp_sdp_query_registration, 0, sizeof(avrcp_sdp_query_registration));
126465bd7af5SMatthias Ringwald (void) memset(&avrcp_sdp_query_context, 0, sizeof(avrcp_sdp_query_context_t));
126565bd7af5SMatthias Ringwald (void) memset(avrcp_sdp_query_attribute_value, 0, sizeof(avrcp_sdp_query_attribute_value));
1266680af5dcSMatthias Ringwald }
1267697b823eSMatthias Ringwald #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
1268697b823eSMatthias Ringwald #define FUZZ_CID 0x44
12693cd982f4SMatthias Ringwald #define FUZZ_CON_HANDLE 0x0001
1270697b823eSMatthias Ringwald static bd_addr_t remote_addr = { 0x33, 0x33, 0x33, 0x33, 0x33, 0x33 };
avrcp_init_fuzz(void)1271697b823eSMatthias Ringwald void avrcp_init_fuzz(void){
1272697b823eSMatthias Ringwald // setup avrcp connections for cid
1273697b823eSMatthias Ringwald avrcp_connection_t * connection_controller = avrcp_create_connection(AVRCP_CONTROLLER, remote_addr);
1274697b823eSMatthias Ringwald avrcp_connection_t * connection_target = avrcp_create_connection(AVRCP_TARGET, remote_addr);
12753cd982f4SMatthias Ringwald avrcp_handle_open_connection(connection_controller, FUZZ_CON_HANDLE, FUZZ_CID, 999);
12763cd982f4SMatthias Ringwald avrcp_handle_open_connection(connection_target, FUZZ_CON_HANDLE, FUZZ_CID, 999);
1277697b823eSMatthias Ringwald }
avrcp_packet_handler_fuzz(uint8_t * packet,uint16_t size)1278697b823eSMatthias Ringwald void avrcp_packet_handler_fuzz(uint8_t *packet, uint16_t size){
1279697b823eSMatthias Ringwald avrcp_packet_handler(L2CAP_DATA_PACKET, FUZZ_CID, packet, size);
1280697b823eSMatthias Ringwald }
1281697b823eSMatthias Ringwald #endif
1282