xref: /btstack/src/classic/avrcp.c (revision 34b22aac0913b061ca6c0da686fd034f9e188df1)
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
23be32e7f1SMilanka Ringwald  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
24be32e7f1SMilanka Ringwald  * RINGWALD 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 
38ab2c6ae4SMatthias Ringwald #define __BTSTACK_FILE__ "avrcp.c"
39ab2c6ae4SMatthias Ringwald 
40be32e7f1SMilanka Ringwald #include <stdint.h>
41be32e7f1SMilanka Ringwald #include <stdio.h>
42be32e7f1SMilanka Ringwald #include <stdlib.h>
43be32e7f1SMilanka Ringwald #include <string.h>
44be32e7f1SMilanka Ringwald 
45be32e7f1SMilanka Ringwald #include "btstack.h"
46be32e7f1SMilanka Ringwald #include "classic/avrcp.h"
47be32e7f1SMilanka Ringwald 
48235946f1SMatthias Ringwald #define PSM_AVCTP                       BLUETOOTH_PROTOCOL_AVCTP
49a0f524f0SMatthias Ringwald #define PSM_AVCTP_BROWSING              0x001b
50be32e7f1SMilanka Ringwald 
51be32e7f1SMilanka Ringwald /*
52be32e7f1SMilanka Ringwald Category 1: Player/Recorder
53be32e7f1SMilanka Ringwald Category 2: Monitor/Amplifier
54be32e7f1SMilanka Ringwald Category 3: Tuner
55be32e7f1SMilanka Ringwald Category 4: Menu
56be32e7f1SMilanka Ringwald */
57be32e7f1SMilanka Ringwald 
58be32e7f1SMilanka Ringwald /* controller supported features
59be32e7f1SMilanka Ringwald Bit 0 = Category 1
60be32e7f1SMilanka Ringwald Bit 1 = Category 2
61be32e7f1SMilanka Ringwald Bit 2 = Category 3
62be32e7f1SMilanka Ringwald Bit 3 = Category 4
63be32e7f1SMilanka Ringwald Bit 4-5 = RFA
64be32e7f1SMilanka Ringwald Bit 6 = Supports browsing
65be32e7f1SMilanka Ringwald Bit 7-15 = RFA
66be32e7f1SMilanka Ringwald The bits for supported categories are set to 1. Others are set to 0.
67be32e7f1SMilanka Ringwald */
68be32e7f1SMilanka Ringwald 
69be32e7f1SMilanka Ringwald /* target supported features
70be32e7f1SMilanka Ringwald Bit 0 = Category 1
71be32e7f1SMilanka Ringwald Bit 1 = Category 2
72be32e7f1SMilanka Ringwald Bit 2 = Category 3
73be32e7f1SMilanka Ringwald Bit 3 = Category 4
74be32e7f1SMilanka Ringwald Bit 4 = Player Application Settings. Bit 0 should be set for this bit to be set.
75be32e7f1SMilanka Ringwald Bit 5 = Group Navigation. Bit 0 should be set for this bit to be set.
76be32e7f1SMilanka Ringwald Bit 6 = Supports browsing*4
77be32e7f1SMilanka Ringwald Bit 7 = Supports multiple media player applications
78be32e7f1SMilanka Ringwald Bit 8-15 = RFA
79be32e7f1SMilanka Ringwald The bits for supported categories are set to 1. Others are set to 0.
80be32e7f1SMilanka Ringwald */
81be32e7f1SMilanka Ringwald 
826086246cSMilanka Ringwald static uint16_t avrcp_cid_counter = 1;
836086246cSMilanka Ringwald 
84be32e7f1SMilanka Ringwald // TODO: merge with avdtp_packet_type_t
85be32e7f1SMilanka Ringwald typedef enum {
86be32e7f1SMilanka Ringwald     AVRCP_SINGLE_PACKET= 0,
87be32e7f1SMilanka Ringwald     AVRCP_START_PACKET    ,
88be32e7f1SMilanka Ringwald     AVRCP_CONTINUE_PACKET ,
89be32e7f1SMilanka Ringwald     AVRCP_END_PACKET
90be32e7f1SMilanka Ringwald } avrcp_packet_type_t;
91be32e7f1SMilanka Ringwald 
92be32e7f1SMilanka Ringwald typedef enum {
93be32e7f1SMilanka Ringwald     AVRCP_COMMAND_FRAME = 0,
94be32e7f1SMilanka Ringwald     AVRCP_RESPONSE_FRAME
95be32e7f1SMilanka Ringwald } avrcp_frame_type_t;
96be32e7f1SMilanka Ringwald 
976086246cSMilanka Ringwald static int record_id = -1;
986086246cSMilanka Ringwald static uint8_t   attribute_value[1000];
996086246cSMilanka Ringwald static const unsigned int attribute_value_buffer_size = sizeof(attribute_value);
1006086246cSMilanka Ringwald 
101be32e7f1SMilanka Ringwald static const char * default_avrcp_controller_service_name = "BTstack AVRCP Controller Service";
102be32e7f1SMilanka Ringwald static const char * default_avrcp_controller_service_provider_name = "BTstack AVRCP Controller Service Provider";
103be32e7f1SMilanka Ringwald static const char * default_avrcp_target_service_name = "BTstack AVRCP Target Service";
104be32e7f1SMilanka Ringwald static const char * default_avrcp_target_service_provider_name = "BTstack AVRCP Target Service Provider";
105be32e7f1SMilanka Ringwald 
106be32e7f1SMilanka Ringwald static btstack_packet_handler_t avrcp_callback;
107be32e7f1SMilanka Ringwald 
1086086246cSMilanka Ringwald static avrcp_context_t avrcp_controller_context;
1096086246cSMilanka Ringwald 
110be32e7f1SMilanka Ringwald static const char * avrcp_subunit_type_name[] = {
111be32e7f1SMilanka Ringwald     "MONITOR", "AUDIO", "PRINTER", "DISC", "TAPE_RECORDER_PLAYER", "TUNER",
112be32e7f1SMilanka Ringwald     "CA", "CAMERA", "RESERVED", "PANEL", "BULLETIN_BOARD", "CAMERA_STORAGE",
113be32e7f1SMilanka Ringwald     "VENDOR_UNIQUE", "RESERVED_FOR_ALL_SUBUNIT_TYPES",
114be32e7f1SMilanka Ringwald     "EXTENDED_TO_NEXT_BYTE", "UNIT", "ERROR"
115be32e7f1SMilanka Ringwald };
116be32e7f1SMilanka Ringwald const char * avrcp_subunit2str(uint16_t index){
117be32e7f1SMilanka Ringwald     if (index <= 11) return avrcp_subunit_type_name[index];
118be32e7f1SMilanka Ringwald     if (index >= 0x1C && index <= 0x1F) return avrcp_subunit_type_name[index - 0x10];
119be32e7f1SMilanka Ringwald     return avrcp_subunit_type_name[16];
120be32e7f1SMilanka Ringwald }
121be32e7f1SMilanka Ringwald 
122be32e7f1SMilanka Ringwald static const char * avrcp_event_name[] = {
123be32e7f1SMilanka Ringwald     "ERROR", "PLAYBACK_STATUS_CHANGED",
124be32e7f1SMilanka Ringwald     "TRACK_CHANGED", "TRACK_REACHED_END", "TRACK_REACHED_START",
125be32e7f1SMilanka Ringwald     "PLAYBACK_POS_CHANGED", "BATT_STATUS_CHANGED", "SYSTEM_STATUS_CHANGED",
126be32e7f1SMilanka Ringwald     "PLAYER_APPLICATION_SETTING_CHANGED", "NOW_PLAYING_CONTENT_CHANGED",
127be32e7f1SMilanka Ringwald     "AVAILABLE_PLAYERS_CHANGED", "ADDRESSED_PLAYER_CHANGED", "UIDS_CHANGED", "VOLUME_CHANGED"
128be32e7f1SMilanka Ringwald };
129be32e7f1SMilanka Ringwald const char * avrcp_event2str(uint16_t index){
130be32e7f1SMilanka Ringwald     if (index <= 0x0d) return avrcp_event_name[index];
131be32e7f1SMilanka Ringwald     return avrcp_event_name[0];
132be32e7f1SMilanka Ringwald }
133be32e7f1SMilanka Ringwald 
134be32e7f1SMilanka Ringwald static const char * avrcp_operation_name[] = {
135be32e7f1SMilanka Ringwald     "NOT SUPPORTED", // 0x3B
136be32e7f1SMilanka Ringwald     "SKIP", "NOT SUPPORTED", "NOT SUPPORTED", "NOT SUPPORTED", "NOT SUPPORTED",
137be32e7f1SMilanka Ringwald     "VOLUME_UP", "VOLUME_DOWN", "MUTE", "PLAY", "STOP", "PAUSE", "NOT SUPPORTED",
138be32e7f1SMilanka Ringwald     "REWIND", "FAST_FORWARD", "NOT SUPPORTED", "FORWARD", "BACKWARD" // 0x4C
139be32e7f1SMilanka Ringwald };
140be32e7f1SMilanka Ringwald const char * avrcp_operation2str(uint8_t index){
141be32e7f1SMilanka Ringwald     if (index >= 0x3B && index <= 0x4C) return avrcp_operation_name[index - 0x3B];
142be32e7f1SMilanka Ringwald     return avrcp_operation_name[0];
143be32e7f1SMilanka Ringwald }
144be32e7f1SMilanka Ringwald 
145be32e7f1SMilanka Ringwald static const char * avrcp_media_attribute_id_name[] = {
146be32e7f1SMilanka Ringwald     "NONE", "TITLE", "ARTIST", "ALBUM", "TRACK", "TOTAL TRACKS", "GENRE", "SONG LENGTH"
147be32e7f1SMilanka Ringwald };
148be32e7f1SMilanka Ringwald const char * avrcp_attribute2str(uint8_t index){
149be32e7f1SMilanka Ringwald     if (index >= 1 && index <= 7) return avrcp_media_attribute_id_name[index];
150be32e7f1SMilanka Ringwald     return avrcp_media_attribute_id_name[0];
151be32e7f1SMilanka Ringwald }
152be32e7f1SMilanka Ringwald 
153be32e7f1SMilanka Ringwald static const char * avrcp_play_status_name[] = {
154be32e7f1SMilanka Ringwald     "STOPPED", "PLAYING", "PAUSED", "FORWARD SEEK", "REVERSE SEEK",
155be32e7f1SMilanka Ringwald     "ERROR" // 0xFF
156be32e7f1SMilanka Ringwald };
157be32e7f1SMilanka Ringwald const char * avrcp_play_status2str(uint8_t index){
158be32e7f1SMilanka Ringwald     if (index >= 1 && index <= 4) return avrcp_play_status_name[index];
159be32e7f1SMilanka Ringwald     return avrcp_play_status_name[5];
160be32e7f1SMilanka Ringwald }
161be32e7f1SMilanka Ringwald 
162be32e7f1SMilanka Ringwald static const char * avrcp_ctype_name[] = {
163be32e7f1SMilanka Ringwald     "CONTROL",
164be32e7f1SMilanka Ringwald     "STATUS",
165be32e7f1SMilanka Ringwald     "SPECIFIC_INQUIRY",
166be32e7f1SMilanka Ringwald     "NOTIFY",
167be32e7f1SMilanka Ringwald     "GENERAL_INQUIRY",
168be32e7f1SMilanka Ringwald     "RESERVED5",
169be32e7f1SMilanka Ringwald     "RESERVED6",
170be32e7f1SMilanka Ringwald     "RESERVED7",
1719cc1f3ceSMilanka Ringwald     "NOT IMPLEMENTED IN REMOTE",
1729cc1f3ceSMilanka Ringwald     "ACCEPTED BY REMOTE",
1739cc1f3ceSMilanka Ringwald     "REJECTED BY REMOTE",
174be32e7f1SMilanka Ringwald     "IN_TRANSITION",
175be32e7f1SMilanka Ringwald     "IMPLEMENTED_STABLE",
176be32e7f1SMilanka Ringwald     "CHANGED_STABLE",
177be32e7f1SMilanka Ringwald     "RESERVED",
178be32e7f1SMilanka Ringwald     "INTERIM"
179be32e7f1SMilanka Ringwald };
180be32e7f1SMilanka Ringwald const char * avrcp_ctype2str(uint8_t index){
1813982eab9SMatthias Ringwald     if (index < sizeof(avrcp_ctype_name)){
182be32e7f1SMilanka Ringwald         return avrcp_ctype_name[index];
183be32e7f1SMilanka Ringwald     }
184be32e7f1SMilanka Ringwald     return "NONE";
185be32e7f1SMilanka Ringwald }
186be32e7f1SMilanka Ringwald 
187be32e7f1SMilanka Ringwald static const char * avrcp_shuffle_mode_name[] = {
188be32e7f1SMilanka Ringwald     "SHUFFLE OFF",
189be32e7f1SMilanka Ringwald     "SHUFFLE ALL TRACKS",
190be32e7f1SMilanka Ringwald     "SHUFFLE GROUP"
191be32e7f1SMilanka Ringwald };
192be32e7f1SMilanka Ringwald 
193be32e7f1SMilanka Ringwald const char * avrcp_shuffle2str(uint8_t index){
194be32e7f1SMilanka Ringwald     if (index >= 1 && index <= 3) return avrcp_shuffle_mode_name[index-1];
195be32e7f1SMilanka Ringwald     return "NONE";
196be32e7f1SMilanka Ringwald }
197be32e7f1SMilanka Ringwald 
198be32e7f1SMilanka Ringwald static const char * avrcp_repeat_mode_name[] = {
199be32e7f1SMilanka Ringwald     "REPEAT OFF",
200be32e7f1SMilanka Ringwald     "REPEAT SINGLE TRACK",
201be32e7f1SMilanka Ringwald     "REPEAT ALL TRACKS",
202be32e7f1SMilanka Ringwald     "REPEAT GROUP"
203be32e7f1SMilanka Ringwald };
204be32e7f1SMilanka Ringwald 
205be32e7f1SMilanka Ringwald const char * avrcp_repeat2str(uint8_t index){
206be32e7f1SMilanka Ringwald     if (index >= 1 && index <= 4) return avrcp_repeat_mode_name[index-1];
207be32e7f1SMilanka Ringwald     return "NONE";
208be32e7f1SMilanka Ringwald }
2096086246cSMilanka Ringwald static void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size, avrcp_context_t * context);
2106086246cSMilanka Ringwald 
2116086246cSMilanka Ringwald static avrcp_sdp_query_context_t sdp_query_context;
2126086246cSMilanka Ringwald static void avrcp_handle_sdp_client_query_result(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
213be32e7f1SMilanka Ringwald 
214be32e7f1SMilanka Ringwald static void avrcp_create_sdp_record(uint8_t 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){
215be32e7f1SMilanka Ringwald     uint8_t* attribute;
216be32e7f1SMilanka Ringwald     de_create_sequence(service);
217be32e7f1SMilanka Ringwald 
218be32e7f1SMilanka Ringwald     // 0x0000 "Service Record Handle"
219235946f1SMatthias Ringwald     de_add_number(service, DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_SERVICE_RECORD_HANDLE);
220be32e7f1SMilanka Ringwald     de_add_number(service, DE_UINT, DE_SIZE_32, service_record_handle);
221be32e7f1SMilanka Ringwald 
222be32e7f1SMilanka Ringwald     // 0x0001 "Service Class ID List"
223235946f1SMatthias Ringwald     de_add_number(service,  DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_SERVICE_CLASS_ID_LIST);
224be32e7f1SMilanka Ringwald     attribute = de_push_sequence(service);
225be32e7f1SMilanka Ringwald     {
226be32e7f1SMilanka Ringwald         if (controller){
2276086246cSMilanka Ringwald             de_add_number(attribute, DE_UUID, DE_SIZE_16, BLUETOOTH_SERVICE_CLASS_AV_REMOTE_CONTROL);
2286086246cSMilanka Ringwald             de_add_number(attribute, DE_UUID, DE_SIZE_16, BLUETOOTH_SERVICE_CLASS_AV_REMOTE_CONTROL_CONTROLLER);
229be32e7f1SMilanka Ringwald         } else {
2306086246cSMilanka Ringwald             de_add_number(attribute, DE_UUID, DE_SIZE_16, BLUETOOTH_SERVICE_CLASS_AV_REMOTE_CONTROL_TARGET);
231be32e7f1SMilanka Ringwald         }
232be32e7f1SMilanka Ringwald     }
233be32e7f1SMilanka Ringwald     de_pop_sequence(service, attribute);
234be32e7f1SMilanka Ringwald 
235be32e7f1SMilanka Ringwald     // 0x0004 "Protocol Descriptor List"
236235946f1SMatthias Ringwald     de_add_number(service,  DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_PROTOCOL_DESCRIPTOR_LIST);
237be32e7f1SMilanka Ringwald     attribute = de_push_sequence(service);
238be32e7f1SMilanka Ringwald     {
239be32e7f1SMilanka Ringwald         uint8_t* l2cpProtocol = de_push_sequence(attribute);
240be32e7f1SMilanka Ringwald         {
241235946f1SMatthias Ringwald             de_add_number(l2cpProtocol,  DE_UUID, DE_SIZE_16, BLUETOOTH_PROTOCOL_L2CAP);
242235946f1SMatthias Ringwald             de_add_number(l2cpProtocol,  DE_UINT, DE_SIZE_16, BLUETOOTH_PROTOCOL_AVCTP);
243be32e7f1SMilanka Ringwald         }
244be32e7f1SMilanka Ringwald         de_pop_sequence(attribute, l2cpProtocol);
245be32e7f1SMilanka Ringwald 
246be32e7f1SMilanka Ringwald         uint8_t* avctpProtocol = de_push_sequence(attribute);
247be32e7f1SMilanka Ringwald         {
248235946f1SMatthias Ringwald             de_add_number(avctpProtocol,  DE_UUID, DE_SIZE_16, BLUETOOTH_PROTOCOL_AVCTP);  // avctpProtocol_service
249be32e7f1SMilanka Ringwald             de_add_number(avctpProtocol,  DE_UINT, DE_SIZE_16,  0x0103);    // version
250be32e7f1SMilanka Ringwald         }
251be32e7f1SMilanka Ringwald         de_pop_sequence(attribute, avctpProtocol);
252be32e7f1SMilanka Ringwald     }
253be32e7f1SMilanka Ringwald     de_pop_sequence(service, attribute);
254be32e7f1SMilanka Ringwald 
255be32e7f1SMilanka Ringwald     // 0x0005 "Public Browse Group"
256235946f1SMatthias Ringwald     de_add_number(service,  DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_BROWSE_GROUP_LIST); // public browse group
257be32e7f1SMilanka Ringwald     attribute = de_push_sequence(service);
258be32e7f1SMilanka Ringwald     {
259235946f1SMatthias Ringwald         de_add_number(attribute,  DE_UUID, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_PUBLIC_BROWSE_ROOT);
260be32e7f1SMilanka Ringwald     }
261be32e7f1SMilanka Ringwald     de_pop_sequence(service, attribute);
262be32e7f1SMilanka Ringwald 
263be32e7f1SMilanka Ringwald     // 0x0009 "Bluetooth Profile Descriptor List"
264235946f1SMatthias Ringwald     de_add_number(service,  DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_BLUETOOTH_PROFILE_DESCRIPTOR_LIST);
265be32e7f1SMilanka Ringwald     attribute = de_push_sequence(service);
266be32e7f1SMilanka Ringwald     {
267be32e7f1SMilanka Ringwald         uint8_t *avrcProfile = de_push_sequence(attribute);
268be32e7f1SMilanka Ringwald         {
2696086246cSMilanka Ringwald             de_add_number(avrcProfile,  DE_UUID, DE_SIZE_16, BLUETOOTH_SERVICE_CLASS_AV_REMOTE_CONTROL);
270be32e7f1SMilanka Ringwald             de_add_number(avrcProfile,  DE_UINT, DE_SIZE_16, 0x0105);
271be32e7f1SMilanka Ringwald         }
272be32e7f1SMilanka Ringwald         de_pop_sequence(attribute, avrcProfile);
273be32e7f1SMilanka Ringwald     }
274be32e7f1SMilanka Ringwald     de_pop_sequence(service, attribute);
275be32e7f1SMilanka Ringwald 
276a0f524f0SMatthias Ringwald     // 0x000d "Additional Bluetooth Profile Descriptor List"
2775c806868SMatthias Ringwald     if (browsing){
278a0f524f0SMatthias Ringwald         de_add_number(service,  DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS);
279a0f524f0SMatthias Ringwald         attribute = de_push_sequence(service);
2805c806868SMatthias Ringwald         {
2815c806868SMatthias Ringwald             uint8_t * des = de_push_sequence(attribute);
2825c806868SMatthias Ringwald             {
2835c806868SMatthias Ringwald                 uint8_t* browsing_l2cpProtocol = de_push_sequence(des);
284a0f524f0SMatthias Ringwald                 {
285a0f524f0SMatthias Ringwald                     de_add_number(browsing_l2cpProtocol,  DE_UUID, DE_SIZE_16, BLUETOOTH_PROTOCOL_L2CAP);
286a0f524f0SMatthias Ringwald                     de_add_number(browsing_l2cpProtocol,  DE_UINT, DE_SIZE_16, PSM_AVCTP_BROWSING);
287a0f524f0SMatthias Ringwald                 }
2885c806868SMatthias Ringwald                 de_pop_sequence(des, browsing_l2cpProtocol);
289a0f524f0SMatthias Ringwald 
2905c806868SMatthias Ringwald                 uint8_t* browsing_avctpProtocol = de_push_sequence(des);
291a0f524f0SMatthias Ringwald                 {
292a0f524f0SMatthias Ringwald                     de_add_number(browsing_avctpProtocol,  DE_UUID, DE_SIZE_16, BLUETOOTH_PROTOCOL_AVCTP);  // browsing_avctpProtocol_service
293a0f524f0SMatthias Ringwald                     de_add_number(browsing_avctpProtocol,  DE_UINT, DE_SIZE_16,  0x0103);    // version
294a0f524f0SMatthias Ringwald                 }
2955c806868SMatthias Ringwald                 de_pop_sequence(des, browsing_avctpProtocol);
2965c806868SMatthias Ringwald             }
2975c806868SMatthias Ringwald             de_pop_sequence(attribute, des);
298a0f524f0SMatthias Ringwald         }
299a0f524f0SMatthias Ringwald         de_pop_sequence(service, attribute);
3005c806868SMatthias Ringwald     }
301a0f524f0SMatthias Ringwald 
302be32e7f1SMilanka Ringwald 
303be32e7f1SMilanka Ringwald     // 0x0100 "Service Name"
304be32e7f1SMilanka Ringwald     de_add_number(service,  DE_UINT, DE_SIZE_16, 0x0100);
305be32e7f1SMilanka Ringwald     if (service_name){
306be32e7f1SMilanka Ringwald         de_add_data(service,  DE_STRING, strlen(service_name), (uint8_t *) service_name);
307be32e7f1SMilanka Ringwald     } else {
308be32e7f1SMilanka Ringwald         if (controller){
309be32e7f1SMilanka Ringwald             de_add_data(service,  DE_STRING, strlen(default_avrcp_controller_service_name), (uint8_t *) default_avrcp_controller_service_name);
310be32e7f1SMilanka Ringwald         } else {
311be32e7f1SMilanka Ringwald             de_add_data(service,  DE_STRING, strlen(default_avrcp_target_service_name), (uint8_t *) default_avrcp_target_service_name);
312be32e7f1SMilanka Ringwald         }
313be32e7f1SMilanka Ringwald     }
314be32e7f1SMilanka Ringwald 
315be32e7f1SMilanka Ringwald     // 0x0100 "Provider Name"
316be32e7f1SMilanka Ringwald     de_add_number(service,  DE_UINT, DE_SIZE_16, 0x0102);
317be32e7f1SMilanka Ringwald     if (service_provider_name){
318be32e7f1SMilanka Ringwald         de_add_data(service,  DE_STRING, strlen(service_provider_name), (uint8_t *) service_provider_name);
319be32e7f1SMilanka Ringwald     } else {
320be32e7f1SMilanka Ringwald         if (controller){
321be32e7f1SMilanka Ringwald             de_add_data(service,  DE_STRING, strlen(default_avrcp_controller_service_provider_name), (uint8_t *) default_avrcp_controller_service_provider_name);
322be32e7f1SMilanka Ringwald         } else {
323be32e7f1SMilanka Ringwald             de_add_data(service,  DE_STRING, strlen(default_avrcp_target_service_provider_name), (uint8_t *) default_avrcp_target_service_provider_name);
324be32e7f1SMilanka Ringwald         }
325be32e7f1SMilanka Ringwald     }
326be32e7f1SMilanka Ringwald 
327be32e7f1SMilanka Ringwald     // 0x0311 "Supported Features"
328be32e7f1SMilanka Ringwald     de_add_number(service, DE_UINT, DE_SIZE_16, 0x0311);
329be32e7f1SMilanka Ringwald     de_add_number(service, DE_UINT, DE_SIZE_16, supported_features);
330be32e7f1SMilanka Ringwald }
331be32e7f1SMilanka Ringwald 
332be32e7f1SMilanka Ringwald void avrcp_controller_create_sdp_record(uint8_t * service, uint32_t service_record_handle, uint8_t browsing, uint16_t supported_features, const char * service_name, const char * service_provider_name){
333be32e7f1SMilanka Ringwald     avrcp_create_sdp_record(1, service, service_record_handle, browsing, supported_features, service_name, service_provider_name);
334be32e7f1SMilanka Ringwald }
335be32e7f1SMilanka Ringwald 
336be32e7f1SMilanka Ringwald void avrcp_target_create_sdp_record(uint8_t * service, uint32_t service_record_handle, uint8_t browsing, uint16_t supported_features, const char * service_name, const char * service_provider_name){
337be32e7f1SMilanka Ringwald     avrcp_create_sdp_record(0, service, service_record_handle, browsing, supported_features, service_name, service_provider_name);
338be32e7f1SMilanka Ringwald }
339be32e7f1SMilanka Ringwald 
3406086246cSMilanka Ringwald static void avrcp_emit_connection_established(btstack_packet_handler_t callback, uint16_t avrcp_cid, bd_addr_t addr, uint8_t status){
341b193c45eSMilanka Ringwald     if (!callback) return;
3426086246cSMilanka Ringwald     uint8_t event[12];
343b193c45eSMilanka Ringwald     int pos = 0;
344b193c45eSMilanka Ringwald     event[pos++] = HCI_EVENT_AVRCP_META;
345b193c45eSMilanka Ringwald     event[pos++] = sizeof(event) - 2;
346b193c45eSMilanka Ringwald     event[pos++] = AVRCP_SUBEVENT_CONNECTION_ESTABLISHED;
3476f43fcd7SMatthias Ringwald     event[pos++] = status;
348b193c45eSMilanka Ringwald     reverse_bd_addr(addr,&event[pos]);
349b193c45eSMilanka Ringwald     pos += 6;
350b193c45eSMilanka Ringwald     little_endian_store_16(event, pos, avrcp_cid);
351b193c45eSMilanka Ringwald     pos += 2;
352b193c45eSMilanka Ringwald     (*callback)(HCI_EVENT_PACKET, 0, event, sizeof(event));
353b193c45eSMilanka Ringwald }
354b193c45eSMilanka Ringwald 
355d3b6a85eSMatthias Ringwald static void avrcp_emit_repeat_and_shuffle_mode(btstack_packet_handler_t callback, uint16_t avrcp_cid, uint8_t ctype, avrcp_repeat_mode_t repeat_mode, avrcp_shuffle_mode_t shuffle_mode){
356be32e7f1SMilanka Ringwald     if (!callback) return;
357be32e7f1SMilanka Ringwald     uint8_t event[8];
358be32e7f1SMilanka Ringwald     int pos = 0;
359be32e7f1SMilanka Ringwald     event[pos++] = HCI_EVENT_AVRCP_META;
360be32e7f1SMilanka Ringwald     event[pos++] = sizeof(event) - 2;
361be32e7f1SMilanka Ringwald     event[pos++] = AVRCP_SUBEVENT_SHUFFLE_AND_REPEAT_MODE;
362b193c45eSMilanka Ringwald     little_endian_store_16(event, pos, avrcp_cid);
363be32e7f1SMilanka Ringwald     pos += 2;
364d3b6a85eSMatthias Ringwald     event[pos++] = ctype;
365be32e7f1SMilanka Ringwald     event[pos++] = repeat_mode;
366be32e7f1SMilanka Ringwald     event[pos++] = shuffle_mode;
367be32e7f1SMilanka Ringwald     (*callback)(HCI_EVENT_PACKET, 0, event, sizeof(event));
368be32e7f1SMilanka Ringwald }
369be32e7f1SMilanka Ringwald 
370d3b6a85eSMatthias Ringwald static void avrcp_emit_operation_status(btstack_packet_handler_t callback, uint8_t subevent, uint16_t avrcp_cid, uint8_t ctype, uint8_t operation_id){
371be32e7f1SMilanka Ringwald     if (!callback) return;
372be32e7f1SMilanka Ringwald     uint8_t event[7];
373be32e7f1SMilanka Ringwald     int pos = 0;
374be32e7f1SMilanka Ringwald     event[pos++] = HCI_EVENT_AVRCP_META;
375be32e7f1SMilanka Ringwald     event[pos++] = sizeof(event) - 2;
376be32e7f1SMilanka Ringwald     event[pos++] = subevent;
377b193c45eSMilanka Ringwald     little_endian_store_16(event, pos, avrcp_cid);
378be32e7f1SMilanka Ringwald     pos += 2;
379d3b6a85eSMatthias Ringwald     event[pos++] = ctype;
380be32e7f1SMilanka Ringwald     event[pos++] = operation_id;
381be32e7f1SMilanka Ringwald     (*callback)(HCI_EVENT_PACKET, 0, event, sizeof(event));
382be32e7f1SMilanka Ringwald }
383be32e7f1SMilanka Ringwald 
384b193c45eSMilanka Ringwald static void avrcp_emit_connection_closed(btstack_packet_handler_t callback, uint16_t avrcp_cid){
385be32e7f1SMilanka Ringwald     if (!callback) return;
386be32e7f1SMilanka Ringwald     uint8_t event[5];
387be32e7f1SMilanka Ringwald     int pos = 0;
388be32e7f1SMilanka Ringwald     event[pos++] = HCI_EVENT_AVRCP_META;
389be32e7f1SMilanka Ringwald     event[pos++] = sizeof(event) - 2;
390be32e7f1SMilanka Ringwald     event[pos++] = AVRCP_SUBEVENT_CONNECTION_RELEASED;
391b193c45eSMilanka Ringwald     little_endian_store_16(event, pos, avrcp_cid);
392be32e7f1SMilanka Ringwald     pos += 2;
393be32e7f1SMilanka Ringwald     (*callback)(HCI_EVENT_PACKET, 0, event, sizeof(event));
394be32e7f1SMilanka Ringwald }
395be32e7f1SMilanka Ringwald 
3966086246cSMilanka Ringwald static avrcp_connection_t * get_avrcp_connection_for_bd_addr(bd_addr_t addr, avrcp_context_t * context){
397be32e7f1SMilanka Ringwald     btstack_linked_list_iterator_t it;
3986086246cSMilanka Ringwald     btstack_linked_list_iterator_init(&it, (btstack_linked_list_t *) &context->connections);
399be32e7f1SMilanka Ringwald     while (btstack_linked_list_iterator_has_next(&it)){
400be32e7f1SMilanka Ringwald         avrcp_connection_t * connection = (avrcp_connection_t *)btstack_linked_list_iterator_next(&it);
401be32e7f1SMilanka Ringwald         if (memcmp(addr, connection->remote_addr, 6) != 0) continue;
402be32e7f1SMilanka Ringwald         return connection;
403be32e7f1SMilanka Ringwald     }
404be32e7f1SMilanka Ringwald     return NULL;
405be32e7f1SMilanka Ringwald }
406be32e7f1SMilanka Ringwald 
4076086246cSMilanka Ringwald static avrcp_connection_t * get_avrcp_connection_for_l2cap_signaling_cid(uint16_t l2cap_cid, avrcp_context_t * context){
408be32e7f1SMilanka Ringwald     btstack_linked_list_iterator_t it;
4096086246cSMilanka Ringwald     btstack_linked_list_iterator_init(&it, (btstack_linked_list_t *)  &context->connections);
410be32e7f1SMilanka Ringwald     while (btstack_linked_list_iterator_has_next(&it)){
411be32e7f1SMilanka Ringwald         avrcp_connection_t * connection = (avrcp_connection_t *)btstack_linked_list_iterator_next(&it);
412be32e7f1SMilanka Ringwald         if (connection->l2cap_signaling_cid != l2cap_cid) continue;
413be32e7f1SMilanka Ringwald         return connection;
414be32e7f1SMilanka Ringwald     }
415be32e7f1SMilanka Ringwald     return NULL;
416be32e7f1SMilanka Ringwald }
417be32e7f1SMilanka Ringwald 
4186086246cSMilanka Ringwald static avrcp_connection_t * get_avrcp_connection_for_avrcp_cid(uint16_t l2cap_cid, avrcp_context_t * context){
4196086246cSMilanka Ringwald     btstack_linked_list_iterator_t it;
4206086246cSMilanka Ringwald     btstack_linked_list_iterator_init(&it, (btstack_linked_list_t *)  &context->connections);
4216086246cSMilanka Ringwald     while (btstack_linked_list_iterator_has_next(&it)){
4226086246cSMilanka Ringwald         avrcp_connection_t * connection = (avrcp_connection_t *)btstack_linked_list_iterator_next(&it);
4236086246cSMilanka Ringwald         if (connection->avrcp_cid != l2cap_cid) continue;
4246086246cSMilanka Ringwald         return connection;
4256086246cSMilanka Ringwald     }
4266086246cSMilanka Ringwald     return NULL;
4276086246cSMilanka Ringwald }
4286086246cSMilanka Ringwald 
429be32e7f1SMilanka Ringwald static void avrcp_request_can_send_now(avrcp_connection_t * connection, uint16_t l2cap_cid){
430be32e7f1SMilanka Ringwald     connection->wait_to_send = 1;
431be32e7f1SMilanka Ringwald     l2cap_request_can_send_now_event(l2cap_cid);
432be32e7f1SMilanka Ringwald }
433be32e7f1SMilanka Ringwald 
434be32e7f1SMilanka Ringwald static void avrcp_press_and_hold_timeout_handler(btstack_timer_source_t * timer){
435be32e7f1SMilanka Ringwald     UNUSED(timer);
436be32e7f1SMilanka Ringwald     avrcp_connection_t * connection = btstack_run_loop_get_timer_context(timer);
437be32e7f1SMilanka Ringwald     btstack_run_loop_set_timer(&connection->press_and_hold_cmd_timer, 2000); // 2 seconds timeout
438be32e7f1SMilanka Ringwald     btstack_run_loop_add_timer(&connection->press_and_hold_cmd_timer);
439be32e7f1SMilanka Ringwald     connection->state = AVCTP_W2_SEND_PRESS_COMMAND;
440be32e7f1SMilanka Ringwald     avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid);
441be32e7f1SMilanka Ringwald }
442be32e7f1SMilanka Ringwald 
443be32e7f1SMilanka Ringwald static void avrcp_press_and_hold_timer_start(avrcp_connection_t * connection){
444be32e7f1SMilanka Ringwald     btstack_run_loop_remove_timer(&connection->press_and_hold_cmd_timer);
445be32e7f1SMilanka Ringwald     btstack_run_loop_set_timer_handler(&connection->press_and_hold_cmd_timer, avrcp_press_and_hold_timeout_handler);
446be32e7f1SMilanka Ringwald     btstack_run_loop_set_timer_context(&connection->press_and_hold_cmd_timer, connection);
447be32e7f1SMilanka Ringwald     btstack_run_loop_set_timer(&connection->press_and_hold_cmd_timer, 2000); // 2 seconds timeout
448be32e7f1SMilanka Ringwald     btstack_run_loop_add_timer(&connection->press_and_hold_cmd_timer);
449be32e7f1SMilanka Ringwald }
450be32e7f1SMilanka Ringwald 
451be32e7f1SMilanka Ringwald static void avrcp_press_and_hold_timer_stop(avrcp_connection_t * connection){
452d1845da3SMilanka Ringwald     connection->continuous_fast_forward_cmd = 0;
453be32e7f1SMilanka Ringwald     btstack_run_loop_remove_timer(&connection->press_and_hold_cmd_timer);
454be32e7f1SMilanka Ringwald }
455be32e7f1SMilanka Ringwald 
45631a5a745SMatthias Ringwald static uint8_t request_pass_through_release_control_cmd(avrcp_connection_t * connection){
457be32e7f1SMilanka Ringwald     connection->state = AVCTP_W2_SEND_RELEASE_COMMAND;
458d1845da3SMilanka Ringwald     if (connection->continuous_fast_forward_cmd){
459be32e7f1SMilanka Ringwald         avrcp_press_and_hold_timer_stop(connection);
460be32e7f1SMilanka Ringwald     }
461be32e7f1SMilanka Ringwald     connection->cmd_operands[0] = 0x80 | connection->cmd_operands[0];
462be32e7f1SMilanka Ringwald     connection->transaction_label++;
463be32e7f1SMilanka Ringwald     avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid);
46431a5a745SMatthias Ringwald     return ERROR_CODE_SUCCESS;
465be32e7f1SMilanka Ringwald }
466be32e7f1SMilanka Ringwald 
4676086246cSMilanka Ringwald static inline uint8_t request_pass_through_press_control_cmd(uint16_t avrcp_cid, avrcp_operation_id_t opid, uint16_t playback_speed, uint8_t continuous_fast_forward_cmd, avrcp_context_t * context){
4686086246cSMilanka Ringwald     avrcp_connection_t * connection = get_avrcp_connection_for_avrcp_cid(avrcp_cid, context);
469be32e7f1SMilanka Ringwald     if (!connection){
470be32e7f1SMilanka Ringwald         log_error("avrcp: could not find a connection.");
47131a5a745SMatthias Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
472be32e7f1SMilanka Ringwald     }
47331a5a745SMatthias Ringwald     if (connection->state != AVCTP_CONNECTION_OPENED) return ERROR_CODE_COMMAND_DISALLOWED;
474be32e7f1SMilanka Ringwald     connection->state = AVCTP_W2_SEND_PRESS_COMMAND;
4759cc1f3ceSMilanka Ringwald     connection->command_opcode =  AVRCP_CMD_OPCODE_PASS_THROUGH;
476be32e7f1SMilanka Ringwald     connection->command_type = AVRCP_CTYPE_CONTROL;
477be32e7f1SMilanka Ringwald     connection->subunit_type = AVRCP_SUBUNIT_TYPE_PANEL;
478d5ff3b26SMatthias Ringwald     connection->subunit_id =   AVRCP_SUBUNIT_ID;
479adcdded9SMilanka Ringwald     connection->cmd_operands_length = 0;
480be32e7f1SMilanka Ringwald 
481d1845da3SMilanka Ringwald     connection->continuous_fast_forward_cmd = continuous_fast_forward_cmd;
482adcdded9SMilanka Ringwald     connection->cmd_operands_length = 2;
483be32e7f1SMilanka Ringwald     connection->cmd_operands[0] = opid;
484be32e7f1SMilanka Ringwald     if (playback_speed > 0){
485be32e7f1SMilanka Ringwald         connection->cmd_operands[2] = playback_speed;
486adcdded9SMilanka Ringwald         connection->cmd_operands_length++;
487be32e7f1SMilanka Ringwald     }
488adcdded9SMilanka Ringwald     connection->cmd_operands[1] = connection->cmd_operands_length - 2;
489be32e7f1SMilanka Ringwald 
490d1845da3SMilanka Ringwald     if (connection->continuous_fast_forward_cmd){
491be32e7f1SMilanka Ringwald         avrcp_press_and_hold_timer_start(connection);
492be32e7f1SMilanka Ringwald     }
493d1845da3SMilanka Ringwald 
494be32e7f1SMilanka Ringwald     connection->transaction_label++;
495be32e7f1SMilanka Ringwald     avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid);
49631a5a745SMatthias Ringwald     return ERROR_CODE_SUCCESS;
497be32e7f1SMilanka Ringwald }
498be32e7f1SMilanka Ringwald 
499d1845da3SMilanka Ringwald static uint8_t request_single_pass_through_press_control_cmd(uint16_t avrcp_cid, avrcp_operation_id_t opid, uint16_t playback_speed){
5006086246cSMilanka Ringwald     return request_pass_through_press_control_cmd(avrcp_cid, opid, playback_speed, 0, &avrcp_controller_context);
501d1845da3SMilanka Ringwald }
502d1845da3SMilanka Ringwald 
503d1845da3SMilanka Ringwald static uint8_t request_continuous_pass_through_press_control_cmd(uint16_t avrcp_cid, avrcp_operation_id_t opid, uint16_t playback_speed){
5046086246cSMilanka Ringwald     return request_pass_through_press_control_cmd(avrcp_cid, opid, playback_speed, 1, &avrcp_controller_context);
505d1845da3SMilanka Ringwald }
506be32e7f1SMilanka Ringwald 
507be32e7f1SMilanka Ringwald static int avrcp_send_cmd(uint16_t cid, avrcp_connection_t * connection){
508adcdded9SMilanka Ringwald     uint8_t command[30];
509be32e7f1SMilanka Ringwald     int pos = 0;
510be32e7f1SMilanka Ringwald     // transport header
511be32e7f1SMilanka Ringwald     // Transaction label | Packet_type | C/R | IPID (1 == invalid profile identifier)
512be32e7f1SMilanka Ringwald     command[pos++] = (connection->transaction_label << 4) | (AVRCP_SINGLE_PACKET << 2) | (AVRCP_COMMAND_FRAME << 1) | 0;
513be32e7f1SMilanka Ringwald     // Profile IDentifier (PID)
5146086246cSMilanka Ringwald     command[pos++] = BLUETOOTH_SERVICE_CLASS_AV_REMOTE_CONTROL >> 8;
5156086246cSMilanka Ringwald     command[pos++] = BLUETOOTH_SERVICE_CLASS_AV_REMOTE_CONTROL & 0x00FF;
516be32e7f1SMilanka Ringwald 
517be32e7f1SMilanka Ringwald     // command_type
518be32e7f1SMilanka Ringwald     command[pos++] = connection->command_type;
519be32e7f1SMilanka Ringwald     // subunit_type | subunit ID
520be32e7f1SMilanka Ringwald     command[pos++] = (connection->subunit_type << 3) | connection->subunit_id;
521be32e7f1SMilanka Ringwald     // opcode
5229cc1f3ceSMilanka Ringwald     command[pos++] = (uint8_t)connection->command_opcode;
523be32e7f1SMilanka Ringwald     // operands
524adcdded9SMilanka Ringwald     memcpy(command+pos, connection->cmd_operands, connection->cmd_operands_length);
525adcdded9SMilanka Ringwald     pos += connection->cmd_operands_length;
526be32e7f1SMilanka Ringwald 
527be32e7f1SMilanka Ringwald     return l2cap_send(cid, command, pos);
528be32e7f1SMilanka Ringwald }
529be32e7f1SMilanka Ringwald 
530be32e7f1SMilanka Ringwald static int avrcp_register_notification(avrcp_connection_t * connection, avrcp_notification_event_id_t event_id){
531be32e7f1SMilanka Ringwald     if (connection->notifications_to_deregister & (1 << event_id)) return 0;
532be32e7f1SMilanka Ringwald     if (connection->notifications_enabled & (1 << event_id)) return 0;
533be32e7f1SMilanka Ringwald     if (connection->notifications_to_register & (1 << event_id)) return 0;
534be32e7f1SMilanka Ringwald     connection->notifications_to_register |= (1 << event_id);
535be32e7f1SMilanka Ringwald     avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid);
536be32e7f1SMilanka Ringwald     return 1;
537be32e7f1SMilanka Ringwald }
538be32e7f1SMilanka Ringwald 
539be32e7f1SMilanka Ringwald static void avrcp_prepare_notification(avrcp_connection_t * connection, avrcp_notification_event_id_t event_id){
540be32e7f1SMilanka Ringwald     connection->transaction_label++;
5419cc1f3ceSMilanka Ringwald     connection->command_opcode = AVRCP_CMD_OPCODE_VENDOR_DEPENDENT;
542be32e7f1SMilanka Ringwald     connection->command_type = AVRCP_CTYPE_NOTIFY;
543be32e7f1SMilanka Ringwald     connection->subunit_type = AVRCP_SUBUNIT_TYPE_PANEL;
544d5ff3b26SMatthias Ringwald     connection->subunit_id = AVRCP_SUBUNIT_ID;
545be32e7f1SMilanka Ringwald     int pos = 0;
546be32e7f1SMilanka Ringwald     big_endian_store_24(connection->cmd_operands, pos, BT_SIG_COMPANY_ID);
547be32e7f1SMilanka Ringwald     pos += 3;
548be32e7f1SMilanka Ringwald     connection->cmd_operands[pos++] = AVRCP_PDU_ID_REGISTER_NOTIFICATION;
549be32e7f1SMilanka Ringwald     connection->cmd_operands[pos++] = 0;                     // reserved(upper 6) | packet_type -> 0
550be32e7f1SMilanka Ringwald     big_endian_store_16(connection->cmd_operands, pos, 5);     // parameter length
551be32e7f1SMilanka Ringwald     pos += 2;
552be32e7f1SMilanka Ringwald     connection->cmd_operands[pos++] = event_id;
553be32e7f1SMilanka Ringwald     big_endian_store_32(connection->cmd_operands, pos, 0);
554be32e7f1SMilanka Ringwald     pos += 4;
555adcdded9SMilanka Ringwald     connection->cmd_operands_length = pos;
556be32e7f1SMilanka Ringwald     // AVRCP_SPEC_V14.pdf 166
557be32e7f1SMilanka Ringwald     // answer page 61
558be32e7f1SMilanka Ringwald }
559be32e7f1SMilanka Ringwald 
5609cc1f3ceSMilanka Ringwald static uint8_t avrcp_cmd_opcode(uint8_t *packet, uint16_t size){
5619cc1f3ceSMilanka Ringwald     uint8_t cmd_opcode_index = 5;
5629cc1f3ceSMilanka Ringwald     if (cmd_opcode_index > size) return AVRCP_CMD_OPCODE_UNDEFINED;
5639cc1f3ceSMilanka Ringwald     return packet[cmd_opcode_index];
564be32e7f1SMilanka Ringwald }
565be32e7f1SMilanka Ringwald 
5666086246cSMilanka Ringwald static void avrcp_handle_sdp_client_query_result(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
5676086246cSMilanka Ringwald     if (!sdp_query_context.connection) return;
5686086246cSMilanka Ringwald     if (sdp_query_context.connection->state != AVCTP_SIGNALING_W4_SDP_QUERY_COMPLETE) return;
5696086246cSMilanka Ringwald     UNUSED(packet_type);
5706086246cSMilanka Ringwald     UNUSED(channel);
5716086246cSMilanka Ringwald     UNUSED(size);
5726086246cSMilanka Ringwald 
5736086246cSMilanka Ringwald     des_iterator_t des_list_it;
5746086246cSMilanka Ringwald     des_iterator_t prot_it;
5756086246cSMilanka Ringwald     // uint32_t avdtp_remote_uuid    = 0;
5766086246cSMilanka Ringwald 
5776086246cSMilanka Ringwald     switch (hci_event_packet_get_type(packet)){
5786086246cSMilanka Ringwald         case SDP_EVENT_QUERY_ATTRIBUTE_VALUE:
5796086246cSMilanka Ringwald             // Handle new SDP record
5806086246cSMilanka Ringwald             if (sdp_event_query_attribute_byte_get_record_id(packet) != record_id) {
5816086246cSMilanka Ringwald                 record_id = sdp_event_query_attribute_byte_get_record_id(packet);
5826086246cSMilanka Ringwald                 // log_info("SDP Record: Nr: %d", record_id);
5836086246cSMilanka Ringwald             }
5846086246cSMilanka Ringwald 
5856086246cSMilanka Ringwald             if (sdp_event_query_attribute_byte_get_attribute_length(packet) <= attribute_value_buffer_size) {
5866086246cSMilanka Ringwald                 attribute_value[sdp_event_query_attribute_byte_get_data_offset(packet)] = sdp_event_query_attribute_byte_get_data(packet);
5876086246cSMilanka Ringwald 
5886086246cSMilanka Ringwald                 if ((uint16_t)(sdp_event_query_attribute_byte_get_data_offset(packet)+1) == sdp_event_query_attribute_byte_get_attribute_length(packet)) {
5896086246cSMilanka Ringwald                     switch(sdp_event_query_attribute_byte_get_attribute_id(packet)) {
5906086246cSMilanka Ringwald                         case BLUETOOTH_ATTRIBUTE_SERVICE_CLASS_ID_LIST:
5916086246cSMilanka Ringwald                             if (de_get_element_type(attribute_value) != DE_DES) break;
5926086246cSMilanka Ringwald                             for (des_iterator_init(&des_list_it, attribute_value); des_iterator_has_more(&des_list_it); des_iterator_next(&des_list_it)) {
5936086246cSMilanka Ringwald                                 uint8_t * element = des_iterator_get_element(&des_list_it);
5946086246cSMilanka Ringwald                                 if (de_get_element_type(element) != DE_UUID) continue;
5956086246cSMilanka Ringwald                                 uint32_t uuid = de_get_uuid32(element);
5966086246cSMilanka Ringwald                                 switch (uuid){
5976086246cSMilanka Ringwald                                     case BLUETOOTH_SERVICE_CLASS_AV_REMOTE_CONTROL_TARGET:
5984b1ac813SMilanka Ringwald                                         if (sdp_query_context.avrcp_context->role == AVRCP_CONTROLLER) {
599df642728SMilanka Ringwald                                             sdp_query_context.role_supported = 1;
6006086246cSMilanka Ringwald                                             break;
6016086246cSMilanka Ringwald                                         }
6026086246cSMilanka Ringwald                                         break;
6036086246cSMilanka Ringwald                                     case BLUETOOTH_SERVICE_CLASS_AV_REMOTE_CONTROL:
604df642728SMilanka Ringwald                                     case BLUETOOTH_SERVICE_CLASS_AV_REMOTE_CONTROL_CONTROLLER:
6054b1ac813SMilanka Ringwald                                         if (sdp_query_context.avrcp_context->role == AVRCP_TARGET) {
606df642728SMilanka Ringwald                                             sdp_query_context.role_supported = 1;
6076086246cSMilanka Ringwald                                             break;
6086086246cSMilanka Ringwald                                         }
6096086246cSMilanka Ringwald                                         break;
6106086246cSMilanka Ringwald                                     default:
6116086246cSMilanka Ringwald                                         break;
6126086246cSMilanka Ringwald                                 }
6136086246cSMilanka Ringwald                             }
6146086246cSMilanka Ringwald                             break;
6156086246cSMilanka Ringwald 
6166086246cSMilanka Ringwald                         case BLUETOOTH_ATTRIBUTE_PROTOCOL_DESCRIPTOR_LIST: {
6176086246cSMilanka Ringwald                                 // log_info("SDP Attribute: 0x%04x", sdp_event_query_attribute_byte_get_attribute_id(packet));
6186086246cSMilanka Ringwald                                 for (des_iterator_init(&des_list_it, attribute_value); des_iterator_has_more(&des_list_it); des_iterator_next(&des_list_it)) {
6196086246cSMilanka Ringwald                                     uint8_t       *des_element;
6206086246cSMilanka Ringwald                                     uint8_t       *element;
6216086246cSMilanka Ringwald                                     uint32_t       uuid;
6226086246cSMilanka Ringwald 
6236086246cSMilanka Ringwald                                     if (des_iterator_get_type(&des_list_it) != DE_DES) continue;
6246086246cSMilanka Ringwald 
6256086246cSMilanka Ringwald                                     des_element = des_iterator_get_element(&des_list_it);
6266086246cSMilanka Ringwald                                     des_iterator_init(&prot_it, des_element);
6276086246cSMilanka Ringwald                                     element = des_iterator_get_element(&prot_it);
6286086246cSMilanka Ringwald 
6296086246cSMilanka Ringwald                                     if (de_get_element_type(element) != DE_UUID) continue;
6306086246cSMilanka Ringwald 
6316086246cSMilanka Ringwald                                     uuid = de_get_uuid32(element);
6326086246cSMilanka Ringwald                                     switch (uuid){
6336086246cSMilanka Ringwald                                         case BLUETOOTH_PROTOCOL_L2CAP:
6346086246cSMilanka Ringwald                                             if (!des_iterator_has_more(&prot_it)) continue;
6356086246cSMilanka Ringwald                                             des_iterator_next(&prot_it);
6366086246cSMilanka Ringwald                                             de_element_get_uint16(des_iterator_get_element(&prot_it), &sdp_query_context.avrcp_l2cap_psm);
6376086246cSMilanka Ringwald                                             break;
6386086246cSMilanka Ringwald                                         case BLUETOOTH_PROTOCOL_AVCTP:
6396086246cSMilanka Ringwald                                             if (!des_iterator_has_more(&prot_it)) continue;
6406086246cSMilanka Ringwald                                             des_iterator_next(&prot_it);
6416086246cSMilanka Ringwald                                             de_element_get_uint16(des_iterator_get_element(&prot_it), &sdp_query_context.avrcp_version);
6426086246cSMilanka Ringwald                                             break;
6436086246cSMilanka Ringwald                                         default:
6446086246cSMilanka Ringwald                                             break;
6456086246cSMilanka Ringwald                                     }
6466086246cSMilanka Ringwald                                 }
6476086246cSMilanka Ringwald                             }
6486086246cSMilanka Ringwald                             break;
649227d16a5SMatthias Ringwald                         case BLUETOOTH_ATTRIBUTE_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS: {
650227d16a5SMatthias Ringwald                                 // log_info("SDP Attribute: 0x%04x", sdp_event_query_attribute_byte_get_attribute_id(packet));
651227d16a5SMatthias Ringwald                                 if (de_get_element_type(attribute_value) != DE_DES) break;
652227d16a5SMatthias Ringwald 
653227d16a5SMatthias Ringwald                                 des_iterator_t des_list_0_it;
654227d16a5SMatthias Ringwald                                 uint8_t       *element_0;
655227d16a5SMatthias Ringwald 
656227d16a5SMatthias Ringwald                                 des_iterator_init(&des_list_0_it, attribute_value);
657227d16a5SMatthias Ringwald                                 element_0 = des_iterator_get_element(&des_list_0_it);
658227d16a5SMatthias Ringwald 
659227d16a5SMatthias Ringwald                                 for (des_iterator_init(&des_list_it, element_0); des_iterator_has_more(&des_list_it); des_iterator_next(&des_list_it)) {
660227d16a5SMatthias Ringwald                                     uint8_t       *des_element;
661227d16a5SMatthias Ringwald                                     uint8_t       *element;
662227d16a5SMatthias Ringwald                                     uint32_t       uuid;
663227d16a5SMatthias Ringwald 
664227d16a5SMatthias Ringwald                                     if (des_iterator_get_type(&des_list_it) != DE_DES) continue;
665227d16a5SMatthias Ringwald 
666227d16a5SMatthias Ringwald                                     des_element = des_iterator_get_element(&des_list_it);
667227d16a5SMatthias Ringwald                                     des_iterator_init(&prot_it, des_element);
668227d16a5SMatthias Ringwald                                     element = des_iterator_get_element(&prot_it);
669227d16a5SMatthias Ringwald 
670227d16a5SMatthias Ringwald                                     if (de_get_element_type(element) != DE_UUID) continue;
671227d16a5SMatthias Ringwald 
672227d16a5SMatthias Ringwald                                     uuid = de_get_uuid32(element);
673227d16a5SMatthias Ringwald                                     switch (uuid){
674227d16a5SMatthias Ringwald                                         case BLUETOOTH_PROTOCOL_L2CAP:
675227d16a5SMatthias Ringwald                                             if (!des_iterator_has_more(&prot_it)) continue;
676227d16a5SMatthias Ringwald                                             des_iterator_next(&prot_it);
677227d16a5SMatthias Ringwald                                             de_element_get_uint16(des_iterator_get_element(&prot_it), &sdp_query_context.avrcp_browsing_l2cap_psm);
678227d16a5SMatthias Ringwald                                             break;
679227d16a5SMatthias Ringwald                                         case BLUETOOTH_PROTOCOL_AVCTP:
680227d16a5SMatthias Ringwald                                             if (!des_iterator_has_more(&prot_it)) continue;
681227d16a5SMatthias Ringwald                                             des_iterator_next(&prot_it);
682227d16a5SMatthias Ringwald                                             de_element_get_uint16(des_iterator_get_element(&prot_it), &sdp_query_context.avrcp_browsing_version);
683227d16a5SMatthias Ringwald                                             break;
684227d16a5SMatthias Ringwald                                         default:
685227d16a5SMatthias Ringwald                                             break;
686227d16a5SMatthias Ringwald                                     }
687227d16a5SMatthias Ringwald                                 }
688227d16a5SMatthias Ringwald                             }
689227d16a5SMatthias Ringwald                             break;
6906086246cSMilanka Ringwald                         default:
6916086246cSMilanka Ringwald                             break;
6926086246cSMilanka Ringwald                     }
6936086246cSMilanka Ringwald                 }
6946086246cSMilanka Ringwald             } else {
6956086246cSMilanka Ringwald                 log_error("SDP attribute value buffer size exceeded: available %d, required %d", attribute_value_buffer_size, sdp_event_query_attribute_byte_get_attribute_length(packet));
6966086246cSMilanka Ringwald             }
6976086246cSMilanka Ringwald             break;
6986086246cSMilanka Ringwald 
6996086246cSMilanka Ringwald         case SDP_EVENT_QUERY_COMPLETE:
7004b1ac813SMilanka Ringwald             log_info("General query done with status %d, role supported %d.\n", sdp_event_query_complete_get_status(packet), sdp_query_context.role_supported);
701df642728SMilanka Ringwald             if (!sdp_query_context.role_supported || !sdp_query_context.avrcp_l2cap_psm){
7026086246cSMilanka Ringwald                 sdp_query_context.connection->state = AVCTP_CONNECTION_IDLE;
703df642728SMilanka Ringwald                 avrcp_emit_connection_established(sdp_query_context.avrcp_context->avrcp_callback, sdp_query_context.connection->avrcp_cid, sdp_query_context.connection->remote_addr, SDP_SERVICE_NOT_FOUND);
7046086246cSMilanka Ringwald                 break;
7056086246cSMilanka Ringwald             }
706227d16a5SMatthias Ringwald             log_info("AVRCP Control PSM 0x%02x, Browsing PSM 0x%02x", sdp_query_context.avrcp_l2cap_psm, sdp_query_context.avrcp_browsing_l2cap_psm);
707df642728SMilanka Ringwald 
7086086246cSMilanka Ringwald             sdp_query_context.connection->state = AVCTP_CONNECTION_W4_L2CAP_CONNECTED;
709*34b22aacSMilanka Ringwald             printf("sdp_query_context.packet_handler %p\n",sdp_query_context.avrcp_context->packet_handler);
710*34b22aacSMilanka Ringwald 
711df642728SMilanka Ringwald             l2cap_create_channel(sdp_query_context.avrcp_context->packet_handler, sdp_query_context.connection->remote_addr, sdp_query_context.avrcp_l2cap_psm, l2cap_max_mtu(), NULL);
7126086246cSMilanka Ringwald             break;
7136086246cSMilanka Ringwald     }
7146086246cSMilanka Ringwald }
7156086246cSMilanka Ringwald 
7169cc1f3ceSMilanka Ringwald static void avrcp_handle_l2cap_data_packet_for_signaling_connection(avrcp_connection_t * connection, uint8_t *packet, uint16_t size){
717be32e7f1SMilanka Ringwald     uint8_t operands[20];
718be32e7f1SMilanka Ringwald     uint8_t opcode;
7198a2c6c7cSMilanka Ringwald     int     pos = 3;
720be32e7f1SMilanka Ringwald     // uint8_t transport_header = packet[0];
721be32e7f1SMilanka Ringwald     // uint8_t transaction_label = transport_header >> 4;
722be32e7f1SMilanka Ringwald     // uint8_t packet_type = (transport_header & 0x0F) >> 2;
723be32e7f1SMilanka Ringwald     // uint8_t frame_type = (transport_header & 0x03) >> 1;
724be32e7f1SMilanka Ringwald     // uint8_t ipid = transport_header & 0x01;
7258a2c6c7cSMilanka Ringwald     // uint8_t byte_value = packet[2];
726be32e7f1SMilanka Ringwald     // uint16_t pid = (byte_value << 8) | packet[2];
7278a2c6c7cSMilanka Ringwald 
728d5ff3b26SMatthias Ringwald     avrcp_command_type_t ctype = (avrcp_command_type_t) packet[pos++];
7298a2c6c7cSMilanka Ringwald     uint8_t byte_value = packet[pos++];
730d5ff3b26SMatthias Ringwald     avrcp_subunit_type_t subunit_type = (avrcp_subunit_type_t) (byte_value >> 3);
731d5ff3b26SMatthias Ringwald     avrcp_subunit_type_t subunit_id = (avrcp_subunit_type_t)   (byte_value & 0x07);
7328a2c6c7cSMilanka Ringwald     opcode = packet[pos++];
733be32e7f1SMilanka Ringwald 
734be32e7f1SMilanka Ringwald     // printf("    Transport header 0x%02x (transaction_label %d, packet_type %d, frame_type %d, ipid %d), pid 0x%4x\n",
735be32e7f1SMilanka Ringwald     //     transport_header, transaction_label, packet_type, frame_type, ipid, pid);
736be32e7f1SMilanka Ringwald     // // printf_hexdump(packet+pos, size-pos);
737d5ff3b26SMatthias Ringwald 
738d5ff3b26SMatthias Ringwald     uint8_t pdu_id;
739d5ff3b26SMatthias Ringwald     uint16_t param_length;
7409cc1f3ceSMilanka Ringwald     switch (avrcp_cmd_opcode(packet,size)){
741be32e7f1SMilanka Ringwald         case AVRCP_CMD_OPCODE_UNIT_INFO:{
742294335f1SMilanka Ringwald             if (connection->state != AVCTP_W2_RECEIVE_RESPONSE) return;
7439cc1f3ceSMilanka Ringwald             connection->state = AVCTP_CONNECTION_OPENED;
744294335f1SMilanka Ringwald 
745be32e7f1SMilanka Ringwald             // operands:
746be32e7f1SMilanka Ringwald             memcpy(operands, packet+pos, 5);
747be32e7f1SMilanka Ringwald             uint8_t unit_type = operands[1] >> 3;
748be32e7f1SMilanka Ringwald             uint8_t unit = operands[1] & 0x07;
749be32e7f1SMilanka Ringwald             uint32_t company_id = operands[2] << 16 | operands[3] << 8 | operands[4];
750be32e7f1SMilanka Ringwald             log_info("    UNIT INFO response: ctype 0x%02x (0C), subunit_type 0x%02x (1F), subunit_id 0x%02x (07), opcode 0x%02x (30), unit_type 0x%02x, unit %d, company_id 0x%06x",
751be32e7f1SMilanka Ringwald                 ctype, subunit_type, subunit_id, opcode, unit_type, unit, company_id );
752be32e7f1SMilanka Ringwald             break;
753be32e7f1SMilanka Ringwald         }
754be32e7f1SMilanka Ringwald         case AVRCP_CMD_OPCODE_VENDOR_DEPENDENT:
755be32e7f1SMilanka Ringwald             if (size - pos < 7) {
756be32e7f1SMilanka Ringwald                 log_error("avrcp: wrong packet size");
757be32e7f1SMilanka Ringwald                 return;
758be32e7f1SMilanka Ringwald             };
759be32e7f1SMilanka Ringwald             // operands:
760be32e7f1SMilanka Ringwald             memcpy(operands, packet+pos, 7);
761be32e7f1SMilanka Ringwald             pos += 7;
762be32e7f1SMilanka Ringwald             // uint32_t company_id = operands[0] << 16 | operands[1] << 8 | operands[2];
763d5ff3b26SMatthias Ringwald             pdu_id = operands[3];
764294335f1SMilanka Ringwald 
765294335f1SMilanka Ringwald             if (connection->state != AVCTP_W2_RECEIVE_RESPONSE && pdu_id != AVRCP_PDU_ID_REGISTER_NOTIFICATION){
766ac98fce4SMilanka Ringwald                 log_info("AVRCP_CMD_OPCODE_VENDOR_DEPENDENT state %d", connection->state);
767294335f1SMilanka Ringwald                 return;
768294335f1SMilanka Ringwald             }
769294335f1SMilanka Ringwald             connection->state = AVCTP_CONNECTION_OPENED;
770294335f1SMilanka Ringwald 
771294335f1SMilanka Ringwald 
772be32e7f1SMilanka Ringwald             // uint8_t unit_type = operands[4] >> 3;
773be32e7f1SMilanka Ringwald             // uint8_t unit = operands[4] & 0x07;
774d5ff3b26SMatthias Ringwald             param_length = big_endian_read_16(operands, 5);
775be32e7f1SMilanka Ringwald 
776be32e7f1SMilanka Ringwald             // printf("    VENDOR DEPENDENT response: ctype 0x%02x (0C), subunit_type 0x%02x (1F), subunit_id 0x%02x (07), opcode 0x%02x (30), unit_type 0x%02x, unit %d, company_id 0x%06x\n",
777be32e7f1SMilanka Ringwald             //     ctype, subunit_type, subunit_id, opcode, unit_type, unit, company_id );
778be32e7f1SMilanka Ringwald 
779be32e7f1SMilanka Ringwald             // if (ctype == AVRCP_CTYPE_RESPONSE_INTERIM) return;
780be32e7f1SMilanka Ringwald             log_info("        VENDOR DEPENDENT response: pdu id 0x%02x, param_length %d, status %s", pdu_id, param_length, avrcp_ctype2str(ctype));
781be32e7f1SMilanka Ringwald             switch (pdu_id){
782be32e7f1SMilanka Ringwald                 case AVRCP_PDU_ID_GetCurrentPlayerApplicationSettingValue:{
783be32e7f1SMilanka Ringwald                     uint8_t num_attributes = packet[pos++];
784be32e7f1SMilanka Ringwald                     int i;
785d5ff3b26SMatthias Ringwald                     avrcp_repeat_mode_t  repeat_mode =  AVRCP_REPEAT_MODE_INVALID;
786d5ff3b26SMatthias Ringwald                     avrcp_shuffle_mode_t shuffle_mode = AVRCP_SHUFFLE_MODE_INVALID;
787be32e7f1SMilanka Ringwald                     for (i = 0; i < num_attributes; i++){
788be32e7f1SMilanka Ringwald                         uint8_t attribute_id    = packet[pos++];
7896086246cSMilanka Ringwald                         uint8_t value = packet[pos++];
790be32e7f1SMilanka Ringwald                         switch (attribute_id){
791be32e7f1SMilanka Ringwald                             case 0x02:
7926086246cSMilanka Ringwald                                 repeat_mode = (avrcp_repeat_mode_t) value;
793be32e7f1SMilanka Ringwald                                 break;
794be32e7f1SMilanka Ringwald                             case 0x03:
7956086246cSMilanka Ringwald                                 shuffle_mode = (avrcp_shuffle_mode_t) value;
796be32e7f1SMilanka Ringwald                                 break;
797be32e7f1SMilanka Ringwald                             default:
798be32e7f1SMilanka Ringwald                                 break;
799be32e7f1SMilanka Ringwald                         }
800be32e7f1SMilanka Ringwald                     }
8016086246cSMilanka Ringwald                     avrcp_emit_repeat_and_shuffle_mode(avrcp_callback, connection->avrcp_cid, ctype, repeat_mode, shuffle_mode);
802be32e7f1SMilanka Ringwald                     break;
803be32e7f1SMilanka Ringwald                 }
804be32e7f1SMilanka Ringwald                 case AVRCP_PDU_ID_SetPlayerApplicationSettingValue:{
805be32e7f1SMilanka Ringwald                     uint8_t event[6];
806be32e7f1SMilanka Ringwald                     int offset = 0;
807be32e7f1SMilanka Ringwald                     event[offset++] = HCI_EVENT_AVRCP_META;
808be32e7f1SMilanka Ringwald                     event[offset++] = sizeof(event) - 2;
809be32e7f1SMilanka Ringwald                     event[offset++] = AVRCP_SUBEVENT_PLAYER_APPLICATION_VALUE_RESPONSE;
8106086246cSMilanka Ringwald                     little_endian_store_16(event, offset, connection->avrcp_cid);
811be32e7f1SMilanka Ringwald                     offset += 2;
812be32e7f1SMilanka Ringwald                     event[offset++] = ctype;
813be32e7f1SMilanka Ringwald                     (*avrcp_callback)(HCI_EVENT_PACKET, 0, event, sizeof(event));
814be32e7f1SMilanka Ringwald                     break;
815be32e7f1SMilanka Ringwald                 }
816be32e7f1SMilanka Ringwald                 case AVRCP_PDU_ID_SET_ABSOLUTE_VOLUME:{
817be32e7f1SMilanka Ringwald                     uint8_t event[7];
818be32e7f1SMilanka Ringwald                     int offset = 0;
819be32e7f1SMilanka Ringwald                     event[offset++] = HCI_EVENT_AVRCP_META;
820be32e7f1SMilanka Ringwald                     event[offset++] = sizeof(event) - 2;
821be32e7f1SMilanka Ringwald                     event[offset++] = AVRCP_SUBEVENT_SET_ABSOLUTE_VOLUME_RESPONSE;
8226086246cSMilanka Ringwald                     little_endian_store_16(event, offset, connection->avrcp_cid);
823be32e7f1SMilanka Ringwald                     offset += 2;
824be32e7f1SMilanka Ringwald                     event[offset++] = ctype;
825be32e7f1SMilanka Ringwald                     event[offset++] = packet[pos++];
826be32e7f1SMilanka Ringwald                     (*avrcp_callback)(HCI_EVENT_PACKET, 0, event, sizeof(event));
827be32e7f1SMilanka Ringwald                     break;
828be32e7f1SMilanka Ringwald                 }
829be32e7f1SMilanka Ringwald                 case AVRCP_PDU_ID_GET_CAPABILITIES:{
830d5ff3b26SMatthias Ringwald                     avrcp_capability_id_t capability_id = (avrcp_capability_id_t) packet[pos++];
831be32e7f1SMilanka Ringwald                     uint8_t capability_count = packet[pos++];
832be32e7f1SMilanka Ringwald                     int i;
833be32e7f1SMilanka Ringwald                     switch (capability_id){
834be32e7f1SMilanka Ringwald                         case AVRCP_CAPABILITY_ID_COMPANY:
835be32e7f1SMilanka Ringwald                             // log_info("Supported companies %d: ", capability_count);
836be32e7f1SMilanka Ringwald                             for (i = 0; i < capability_count; i++){
837be32e7f1SMilanka Ringwald                                 uint32_t company_id = big_endian_read_24(packet, pos);
838be32e7f1SMilanka Ringwald                                 pos += 3;
839be32e7f1SMilanka Ringwald                                 log_info("  0x%06x, ", company_id);
840be32e7f1SMilanka Ringwald                             }
841be32e7f1SMilanka Ringwald                             break;
842be32e7f1SMilanka Ringwald                         case AVRCP_CAPABILITY_ID_EVENT:
843be32e7f1SMilanka Ringwald                             // log_info("Supported events %d: ", capability_count);
844be32e7f1SMilanka Ringwald                             for (i = 0; i < capability_count; i++){
845be32e7f1SMilanka Ringwald                                 uint8_t event_id = packet[pos++];
846be32e7f1SMilanka Ringwald                                 log_info("  0x%02x %s", event_id, avrcp_event2str(event_id));
847be32e7f1SMilanka Ringwald                             }
848be32e7f1SMilanka Ringwald                             break;
849be32e7f1SMilanka Ringwald                     }
850be32e7f1SMilanka Ringwald                     break;
851be32e7f1SMilanka Ringwald                 }
852be32e7f1SMilanka Ringwald                 case AVRCP_PDU_ID_GET_PLAY_STATUS:{
853be32e7f1SMilanka Ringwald                     uint32_t song_length = big_endian_read_32(packet, pos);
854be32e7f1SMilanka Ringwald                     pos += 4;
855be32e7f1SMilanka Ringwald                     uint32_t song_position = big_endian_read_32(packet, pos);
856be32e7f1SMilanka Ringwald                     pos += 4;
857be32e7f1SMilanka Ringwald                     uint8_t play_status = packet[pos];
858be32e7f1SMilanka Ringwald                     // log_info("        GET_PLAY_STATUS length 0x%04X, position 0x%04X, status %s", song_length, song_position, avrcp_play_status2str(play_status));
859be32e7f1SMilanka Ringwald 
860be32e7f1SMilanka Ringwald                     uint8_t event[15];
861be32e7f1SMilanka Ringwald                     int offset = 0;
862be32e7f1SMilanka Ringwald                     event[offset++] = HCI_EVENT_AVRCP_META;
863be32e7f1SMilanka Ringwald                     event[offset++] = sizeof(event) - 2;
864be32e7f1SMilanka Ringwald                     event[offset++] = AVRCP_SUBEVENT_PLAY_STATUS;
8656086246cSMilanka Ringwald                     little_endian_store_16(event, offset, connection->avrcp_cid);
866be32e7f1SMilanka Ringwald                     offset += 2;
867be32e7f1SMilanka Ringwald                     event[offset++] = ctype;
868be32e7f1SMilanka Ringwald                     little_endian_store_32(event, offset, song_length);
869be32e7f1SMilanka Ringwald                     offset += 4;
870be32e7f1SMilanka Ringwald                     little_endian_store_32(event, offset, song_position);
871be32e7f1SMilanka Ringwald                     offset += 4;
872be32e7f1SMilanka Ringwald                     event[offset++] = play_status;
873be32e7f1SMilanka Ringwald                     (*avrcp_callback)(HCI_EVENT_PACKET, 0, event, sizeof(event));
874be32e7f1SMilanka Ringwald                     break;
875be32e7f1SMilanka Ringwald                 }
876be32e7f1SMilanka Ringwald                 case AVRCP_PDU_ID_REGISTER_NOTIFICATION:{
877d5ff3b26SMatthias Ringwald                     avrcp_notification_event_id_t  event_id = (avrcp_notification_event_id_t) packet[pos++];
878be32e7f1SMilanka Ringwald                     uint16_t event_mask = (1 << event_id);
879be32e7f1SMilanka Ringwald                     uint16_t reset_event_mask = ~event_mask;
880be32e7f1SMilanka Ringwald                     switch (ctype){
881be32e7f1SMilanka Ringwald                         case AVRCP_CTYPE_RESPONSE_INTERIM:
882be32e7f1SMilanka Ringwald                             // register as enabled
883be32e7f1SMilanka Ringwald                             connection->notifications_enabled |= event_mask;
884be32e7f1SMilanka Ringwald                             // printf("INTERIM notifications_enabled 0x%2x, notifications_to_register 0x%2x\n", connection->notifications_enabled,  connection->notifications_to_register);
885be32e7f1SMilanka Ringwald                             break;
886be32e7f1SMilanka Ringwald                         case AVRCP_CTYPE_RESPONSE_CHANGED_STABLE:
887be32e7f1SMilanka Ringwald                             // received change, event is considered deregistered
888be32e7f1SMilanka Ringwald                             // we are re-enabling it automatically, if it is not
889be32e7f1SMilanka Ringwald                             // explicitly disabled
890be32e7f1SMilanka Ringwald                             connection->notifications_enabled &= reset_event_mask;
891be32e7f1SMilanka Ringwald                             if (! (connection->notifications_to_deregister & event_mask)){
892be32e7f1SMilanka Ringwald                                 avrcp_register_notification(connection, event_id);
893be32e7f1SMilanka Ringwald                                 // printf("CHANGED_STABLE notifications_enabled 0x%2x, notifications_to_register 0x%2x\n", connection->notifications_enabled,  connection->notifications_to_register);
894be32e7f1SMilanka Ringwald                             } else {
895be32e7f1SMilanka Ringwald                                 connection->notifications_to_deregister &= reset_event_mask;
896be32e7f1SMilanka Ringwald                             }
897be32e7f1SMilanka Ringwald                             break;
898be32e7f1SMilanka Ringwald                         default:
899be32e7f1SMilanka Ringwald                             connection->notifications_to_register &= reset_event_mask;
900be32e7f1SMilanka Ringwald                             connection->notifications_enabled &= reset_event_mask;
901be32e7f1SMilanka Ringwald                             connection->notifications_to_deregister &= reset_event_mask;
902be32e7f1SMilanka Ringwald                             break;
903be32e7f1SMilanka Ringwald                     }
904be32e7f1SMilanka Ringwald 
905be32e7f1SMilanka Ringwald                     switch (event_id){
906be32e7f1SMilanka Ringwald                         case AVRCP_NOTIFICATION_EVENT_PLAYBACK_STATUS_CHANGED:{
907be32e7f1SMilanka Ringwald                             uint8_t event[7];
908be32e7f1SMilanka Ringwald                             int offset = 0;
909be32e7f1SMilanka Ringwald                             event[offset++] = HCI_EVENT_AVRCP_META;
910be32e7f1SMilanka Ringwald                             event[offset++] = sizeof(event) - 2;
911be32e7f1SMilanka Ringwald                             event[offset++] = AVRCP_SUBEVENT_NOTIFICATION_PLAYBACK_STATUS_CHANGED;
9126086246cSMilanka Ringwald                             little_endian_store_16(event, offset, connection->avrcp_cid);
913be32e7f1SMilanka Ringwald                             offset += 2;
914be32e7f1SMilanka Ringwald                             event[offset++] = ctype;
915be32e7f1SMilanka Ringwald                             event[offset++] = packet[pos];
916be32e7f1SMilanka Ringwald                             (*avrcp_callback)(HCI_EVENT_PACKET, 0, event, sizeof(event));
917be32e7f1SMilanka Ringwald                             break;
918be32e7f1SMilanka Ringwald                         }
919be32e7f1SMilanka Ringwald                         case AVRCP_NOTIFICATION_EVENT_TRACK_CHANGED:{
920ee6d17f2SMatthias Ringwald                             uint8_t event[6];
921be32e7f1SMilanka Ringwald                             int offset = 0;
922be32e7f1SMilanka Ringwald                             event[offset++] = HCI_EVENT_AVRCP_META;
923be32e7f1SMilanka Ringwald                             event[offset++] = sizeof(event) - 2;
924be32e7f1SMilanka Ringwald                             event[offset++] = AVRCP_SUBEVENT_NOTIFICATION_TRACK_CHANGED;
9256086246cSMilanka Ringwald                             little_endian_store_16(event, offset, connection->avrcp_cid);
926be32e7f1SMilanka Ringwald                             offset += 2;
927be32e7f1SMilanka Ringwald                             event[offset++] = ctype;
928be32e7f1SMilanka Ringwald                             (*avrcp_callback)(HCI_EVENT_PACKET, 0, event, sizeof(event));
929be32e7f1SMilanka Ringwald                             break;
930be32e7f1SMilanka Ringwald                         }
931be32e7f1SMilanka Ringwald                         case AVRCP_NOTIFICATION_EVENT_NOW_PLAYING_CONTENT_CHANGED:{
932be32e7f1SMilanka Ringwald                             uint8_t event[6];
933be32e7f1SMilanka Ringwald                             int offset = 0;
934be32e7f1SMilanka Ringwald                             event[offset++] = HCI_EVENT_AVRCP_META;
935be32e7f1SMilanka Ringwald                             event[offset++] = sizeof(event) - 2;
936be32e7f1SMilanka Ringwald                             event[offset++] = AVRCP_SUBEVENT_NOTIFICATION_NOW_PLAYING_CONTENT_CHANGED;
9376086246cSMilanka Ringwald                             little_endian_store_16(event, offset, connection->avrcp_cid);
938be32e7f1SMilanka Ringwald                             offset += 2;
939be32e7f1SMilanka Ringwald                             event[offset++] = ctype;
940be32e7f1SMilanka Ringwald                             (*avrcp_callback)(HCI_EVENT_PACKET, 0, event, sizeof(event));
941be32e7f1SMilanka Ringwald                             break;
942be32e7f1SMilanka Ringwald                         }
943be32e7f1SMilanka Ringwald                         case AVRCP_NOTIFICATION_EVENT_AVAILABLE_PLAYERS_CHANGED:{
944be32e7f1SMilanka Ringwald                             uint8_t event[6];
945be32e7f1SMilanka Ringwald                             int offset = 0;
946be32e7f1SMilanka Ringwald                             event[offset++] = HCI_EVENT_AVRCP_META;
947be32e7f1SMilanka Ringwald                             event[offset++] = sizeof(event) - 2;
948be32e7f1SMilanka Ringwald                             event[offset++] = AVRCP_SUBEVENT_NOTIFICATION_AVAILABLE_PLAYERS_CHANGED;
9496086246cSMilanka Ringwald                             little_endian_store_16(event, offset, connection->avrcp_cid);
950be32e7f1SMilanka Ringwald                             offset += 2;
951be32e7f1SMilanka Ringwald                             event[offset++] = ctype;
952be32e7f1SMilanka Ringwald                             (*avrcp_callback)(HCI_EVENT_PACKET, 0, event, sizeof(event));
953be32e7f1SMilanka Ringwald                             break;
954be32e7f1SMilanka Ringwald                         }
955be32e7f1SMilanka Ringwald                         case AVRCP_NOTIFICATION_EVENT_VOLUME_CHANGED:{
956be32e7f1SMilanka Ringwald                             uint8_t event[7];
957be32e7f1SMilanka Ringwald                             int offset = 0;
958be32e7f1SMilanka Ringwald                             event[offset++] = HCI_EVENT_AVRCP_META;
959be32e7f1SMilanka Ringwald                             event[offset++] = sizeof(event) - 2;
960be32e7f1SMilanka Ringwald                             event[offset++] = AVRCP_SUBEVENT_NOTIFICATION_VOLUME_CHANGED;
9616086246cSMilanka Ringwald                             little_endian_store_16(event, offset, connection->avrcp_cid);
962be32e7f1SMilanka Ringwald                             offset += 2;
963be32e7f1SMilanka Ringwald                             event[offset++] = ctype;
964be32e7f1SMilanka Ringwald                             event[offset++] = packet[pos++] & 0x7F;
965be32e7f1SMilanka Ringwald                             (*avrcp_callback)(HCI_EVENT_PACKET, 0, event, sizeof(event));
966be32e7f1SMilanka Ringwald                             break;
967be32e7f1SMilanka Ringwald                         }
968be32e7f1SMilanka Ringwald                         // case AVRCP_NOTIFICATION_EVENT_PLAYER_APPLICATION_SETTING_CHANGED:{
969be32e7f1SMilanka Ringwald                         //     uint8_t num_PlayerApplicationSettingAttributes = packet[pos++];
970be32e7f1SMilanka Ringwald                         //     int i;
971be32e7f1SMilanka Ringwald                         //     for (i = 0; i < num_PlayerApplicationSettingAttributes; i++){
972be32e7f1SMilanka Ringwald                         //         uint8_t PlayerApplicationSetting_AttributeID = packet[pos++];
973be32e7f1SMilanka Ringwald                         //         uint8_t PlayerApplicationSettingValueID = packet[pos++];
974be32e7f1SMilanka Ringwald                         //     }
975be32e7f1SMilanka Ringwald                         //     break;
976be32e7f1SMilanka Ringwald                         // }
977be32e7f1SMilanka Ringwald                         // case AVRCP_NOTIFICATION_EVENT_ADDRESSED_PLAYER_CHANGED:
978be32e7f1SMilanka Ringwald                         //     uint16_t player_id = big_endian_read_16(packet, pos);
979be32e7f1SMilanka Ringwald                         //     pos += 2;
980be32e7f1SMilanka Ringwald                         //     uint16_t uid_counter = big_endian_read_16(packet, pos);
981be32e7f1SMilanka Ringwald                         //     pos += 2;
982be32e7f1SMilanka Ringwald                         //     break;
983be32e7f1SMilanka Ringwald                         // case AVRCP_NOTIFICATION_EVENT_UIDS_CHANGED:
984be32e7f1SMilanka Ringwald                         //     uint16_t uid_counter = big_endian_read_16(packet, pos);
985be32e7f1SMilanka Ringwald                         //     pos += 2;
986be32e7f1SMilanka Ringwald                         //     break;
987be32e7f1SMilanka Ringwald                         default:
988be32e7f1SMilanka Ringwald                             log_info("avrcp: not implemented");
989be32e7f1SMilanka Ringwald                             break;
990be32e7f1SMilanka Ringwald                     }
991be32e7f1SMilanka Ringwald                     if (connection->notifications_to_register != 0){
992be32e7f1SMilanka Ringwald                         avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid);
993be32e7f1SMilanka Ringwald                     }
994be32e7f1SMilanka Ringwald                     break;
995be32e7f1SMilanka Ringwald                 }
996be32e7f1SMilanka Ringwald 
997be32e7f1SMilanka Ringwald                 case AVRCP_PDU_ID_GET_ELEMENT_ATTRIBUTES:{
998be32e7f1SMilanka Ringwald                     uint8_t num_attributes = packet[pos++];
999be32e7f1SMilanka Ringwald                     int i;
1000be32e7f1SMilanka Ringwald                     struct item {
1001be32e7f1SMilanka Ringwald                         uint16_t len;
1002be32e7f1SMilanka Ringwald                         uint8_t  * value;
1003be32e7f1SMilanka Ringwald                     } items[AVRCP_MEDIA_ATTR_COUNT];
1004be32e7f1SMilanka Ringwald                     memset(items, 0, sizeof(items));
1005be32e7f1SMilanka Ringwald 
1006be32e7f1SMilanka Ringwald                     uint16_t string_attributes_len = 0;
1007be32e7f1SMilanka Ringwald                     uint8_t  num_string_attributes = 0;
1008be32e7f1SMilanka Ringwald                     uint16_t total_event_payload_for_string_attributes = HCI_EVENT_PAYLOAD_SIZE-2;
1009be32e7f1SMilanka Ringwald                     uint16_t max_string_attribute_value_len = 0;
10104bb88fd7SMatthias Ringwald                     if (ctype == AVRCP_CTYPE_RESPONSE_IMPLEMENTED_STABLE || ctype == AVRCP_CTYPE_RESPONSE_CHANGED_STABLE){
1011be32e7f1SMilanka Ringwald                         for (i = 0; i < num_attributes; i++){
1012d5ff3b26SMatthias Ringwald                             avrcp_media_attribute_id_t attr_id = (avrcp_media_attribute_id_t) big_endian_read_32(packet, pos);
1013be32e7f1SMilanka Ringwald                             pos += 4;
1014be32e7f1SMilanka Ringwald                             // uint16_t character_set = big_endian_read_16(packet, pos);
1015be32e7f1SMilanka Ringwald                             pos += 2;
1016be32e7f1SMilanka Ringwald                             uint16_t attr_value_length = big_endian_read_16(packet, pos);
1017be32e7f1SMilanka Ringwald                             pos += 2;
1018be32e7f1SMilanka Ringwald 
1019be32e7f1SMilanka Ringwald                             // debug - to remove later
1020be32e7f1SMilanka Ringwald                             uint8_t  value[100];
1021be32e7f1SMilanka Ringwald                             uint16_t value_len = sizeof(value) <= attr_value_length? sizeof(value) - 1 : attr_value_length;
1022be32e7f1SMilanka Ringwald                             memcpy(value, packet+pos, value_len);
1023be32e7f1SMilanka Ringwald                             value[value_len] = 0;
1024be32e7f1SMilanka Ringwald                             // printf("Now Playing Info %s: %s \n", attribute2str(attr_id), value);
1025be32e7f1SMilanka Ringwald                             // end debug
1026be32e7f1SMilanka Ringwald 
1027be32e7f1SMilanka Ringwald                             if ((attr_id >= 1) || (attr_id <= AVRCP_MEDIA_ATTR_COUNT)) {
1028be32e7f1SMilanka Ringwald                                 items[attr_id-1].len = attr_value_length;
1029be32e7f1SMilanka Ringwald                                 items[attr_id-1].value = &packet[pos];
1030be32e7f1SMilanka Ringwald                                 switch (attr_id){
1031be32e7f1SMilanka Ringwald                                     case AVRCP_MEDIA_ATTR_TITLE:
1032be32e7f1SMilanka Ringwald                                     case AVRCP_MEDIA_ATTR_ARTIST:
1033be32e7f1SMilanka Ringwald                                     case AVRCP_MEDIA_ATTR_ALBUM:
1034be32e7f1SMilanka Ringwald                                     case AVRCP_MEDIA_ATTR_GENRE:
1035be32e7f1SMilanka Ringwald                                         num_string_attributes++;
1036be32e7f1SMilanka Ringwald                                         string_attributes_len += attr_value_length;
1037be32e7f1SMilanka Ringwald                                         if (max_string_attribute_value_len < attr_value_length){
1038be32e7f1SMilanka Ringwald                                             max_string_attribute_value_len = attr_value_length;
1039be32e7f1SMilanka Ringwald                                         }
1040be32e7f1SMilanka Ringwald                                         break;
1041be32e7f1SMilanka Ringwald                                     default:
1042be32e7f1SMilanka Ringwald                                         break;
1043be32e7f1SMilanka Ringwald                                 }
1044be32e7f1SMilanka Ringwald                             }
1045be32e7f1SMilanka Ringwald                             pos += attr_value_length;
1046be32e7f1SMilanka Ringwald                         }
10474bb88fd7SMatthias Ringwald                     }
10484bb88fd7SMatthias Ringwald 
1049be32e7f1SMilanka Ringwald                     // subtract space for fixed fields
1050be32e7f1SMilanka Ringwald                     total_event_payload_for_string_attributes -= 14 + 4;    // 4 for '\0'
1051be32e7f1SMilanka Ringwald 
1052be32e7f1SMilanka Ringwald                     // @TODO optimize space by repeatedly decreasing max_string_attribute_value_len until it fits into buffer instead of crude divion
1053be32e7f1SMilanka Ringwald                     uint16_t max_value_len = total_event_payload_for_string_attributes > string_attributes_len? max_string_attribute_value_len : total_event_payload_for_string_attributes/(string_attributes_len+1) - 1;
1054be32e7f1SMilanka Ringwald                     // printf("num_string_attributes %d, string_attributes_len %d, total_event_payload_for_string_attributes %d, max_value_len %d \n", num_string_attributes, string_attributes_len, total_event_payload_for_string_attributes, max_value_len);
1055be32e7f1SMilanka Ringwald 
1056be32e7f1SMilanka Ringwald                     const uint8_t attribute_order[] = {
1057be32e7f1SMilanka Ringwald                         AVRCP_MEDIA_ATTR_TRACK,
1058be32e7f1SMilanka Ringwald                         AVRCP_MEDIA_ATTR_TOTAL_TRACKS,
1059be32e7f1SMilanka Ringwald                         AVRCP_MEDIA_ATTR_SONG_LENGTH,
1060be32e7f1SMilanka Ringwald                         AVRCP_MEDIA_ATTR_TITLE,
1061be32e7f1SMilanka Ringwald                         AVRCP_MEDIA_ATTR_ARTIST,
1062be32e7f1SMilanka Ringwald                         AVRCP_MEDIA_ATTR_ALBUM,
1063be32e7f1SMilanka Ringwald                         AVRCP_MEDIA_ATTR_GENRE
1064be32e7f1SMilanka Ringwald                     };
1065be32e7f1SMilanka Ringwald 
1066be32e7f1SMilanka Ringwald                     uint8_t event[HCI_EVENT_BUFFER_SIZE];
1067be32e7f1SMilanka Ringwald                     event[0] = HCI_EVENT_AVRCP_META;
1068be32e7f1SMilanka Ringwald                     pos = 2;
1069be32e7f1SMilanka Ringwald                     event[pos++] = AVRCP_SUBEVENT_NOW_PLAYING_INFO;
10706086246cSMilanka Ringwald                     little_endian_store_16(event, pos, connection->avrcp_cid);
1071be32e7f1SMilanka Ringwald                     pos += 2;
1072be32e7f1SMilanka Ringwald                     event[pos++] = ctype;
1073be32e7f1SMilanka Ringwald                     for (i = 0; i < sizeof(attribute_order); i++){
1074d5ff3b26SMatthias Ringwald                         avrcp_media_attribute_id_t attr_id = (avrcp_media_attribute_id_t) attribute_order[i];
1075be32e7f1SMilanka Ringwald                         uint16_t value_len = 0;
1076be32e7f1SMilanka Ringwald                         switch (attr_id){
1077be32e7f1SMilanka Ringwald                             case AVRCP_MEDIA_ATTR_TITLE:
1078be32e7f1SMilanka Ringwald                             case AVRCP_MEDIA_ATTR_ARTIST:
1079be32e7f1SMilanka Ringwald                             case AVRCP_MEDIA_ATTR_ALBUM:
1080be32e7f1SMilanka Ringwald                             case AVRCP_MEDIA_ATTR_GENRE:
1081be32e7f1SMilanka Ringwald                                 if (items[attr_id-1].value){
1082be32e7f1SMilanka Ringwald                                     value_len = items[attr_id-1].len <= max_value_len ? items[attr_id-1].len : max_value_len;
1083be32e7f1SMilanka Ringwald                                 }
1084be32e7f1SMilanka Ringwald                                 event[pos++] = value_len + 1;
1085be32e7f1SMilanka Ringwald                                 if (value_len){
1086be32e7f1SMilanka Ringwald                                     memcpy(event+pos, items[attr_id-1].value, value_len);
1087be32e7f1SMilanka Ringwald                                     pos += value_len;
1088be32e7f1SMilanka Ringwald                                 }
1089be32e7f1SMilanka Ringwald                                 event[pos++] = 0;
1090be32e7f1SMilanka Ringwald                                 break;
1091be32e7f1SMilanka Ringwald                             case AVRCP_MEDIA_ATTR_SONG_LENGTH:
1092be32e7f1SMilanka Ringwald                                 if (items[attr_id-1].value){
1093be32e7f1SMilanka Ringwald                                     little_endian_store_32(event, pos, btstack_atoi((char *)items[attr_id-1].value));
1094be32e7f1SMilanka Ringwald                                 } else {
1095be32e7f1SMilanka Ringwald                                     little_endian_store_32(event, pos, 0);
1096be32e7f1SMilanka Ringwald                                 }
1097be32e7f1SMilanka Ringwald                                 pos += 4;
1098be32e7f1SMilanka Ringwald                                 break;
1099be32e7f1SMilanka Ringwald                             case AVRCP_MEDIA_ATTR_TRACK:
1100be32e7f1SMilanka Ringwald                             case AVRCP_MEDIA_ATTR_TOTAL_TRACKS:
1101be32e7f1SMilanka Ringwald                                 if (items[attr_id-1].value){
1102be32e7f1SMilanka Ringwald                                     event[pos++] = btstack_atoi((char *)items[attr_id-1].value);
1103be32e7f1SMilanka Ringwald                                 } else {
1104be32e7f1SMilanka Ringwald                                     event[pos++] = 0;
1105be32e7f1SMilanka Ringwald                                 }
1106be32e7f1SMilanka Ringwald                                 break;
1107be32e7f1SMilanka Ringwald                         }
1108be32e7f1SMilanka Ringwald                     }
1109be32e7f1SMilanka Ringwald                     event[1] = pos - 2;
1110be32e7f1SMilanka Ringwald                     // printf_hexdump(event, pos);
1111be32e7f1SMilanka Ringwald                     (*avrcp_callback)(HCI_EVENT_PACKET, 0, event, pos);
1112be32e7f1SMilanka Ringwald                     break;
1113be32e7f1SMilanka Ringwald                 }
1114be32e7f1SMilanka Ringwald                 default:
1115be32e7f1SMilanka Ringwald                     break;
1116be32e7f1SMilanka Ringwald             }
1117be32e7f1SMilanka Ringwald             break;
1118be32e7f1SMilanka Ringwald         case AVRCP_CMD_OPCODE_PASS_THROUGH:{
1119be32e7f1SMilanka Ringwald             // 0x80 | connection->cmd_operands[0]
1120be32e7f1SMilanka Ringwald             uint8_t operation_id = packet[pos++];
11219cc1f3ceSMilanka Ringwald             switch (connection->state){
11229cc1f3ceSMilanka Ringwald                 case AVCTP_W2_RECEIVE_PRESS_RESPONSE:
1123d1845da3SMilanka Ringwald                     if (connection->continuous_fast_forward_cmd){
11249cc1f3ceSMilanka Ringwald                         connection->state = AVCTP_W4_STOP;
1125d1845da3SMilanka Ringwald                     } else {
11269cc1f3ceSMilanka Ringwald                         connection->state = AVCTP_W2_SEND_RELEASE_COMMAND;
11279cc1f3ceSMilanka Ringwald                     }
11289cc1f3ceSMilanka Ringwald                     break;
11299cc1f3ceSMilanka Ringwald                 case AVCTP_W2_RECEIVE_RESPONSE:
11309cc1f3ceSMilanka Ringwald                     connection->state = AVCTP_CONNECTION_OPENED;
11319cc1f3ceSMilanka Ringwald                     break;
11329cc1f3ceSMilanka Ringwald                 default:
11339cc1f3ceSMilanka Ringwald                     // check for notifications? move state transition down
1134ac98fce4SMilanka Ringwald                     // log_info("AVRCP_CMD_OPCODE_PASS_THROUGH state %d\n", connection->state);
11359cc1f3ceSMilanka Ringwald                     break;
11369cc1f3ceSMilanka Ringwald             }
1137be32e7f1SMilanka Ringwald             if (connection->state == AVCTP_W4_STOP){
11386086246cSMilanka Ringwald                 avrcp_emit_operation_status(avrcp_callback, AVRCP_SUBEVENT_OPERATION_START, connection->avrcp_cid, ctype, operation_id);
1139be32e7f1SMilanka Ringwald             }
1140be32e7f1SMilanka Ringwald             if (connection->state == AVCTP_CONNECTION_OPENED) {
1141be32e7f1SMilanka Ringwald                 // RELEASE response
1142be32e7f1SMilanka Ringwald                 operation_id = operation_id & 0x7F;
11436086246cSMilanka Ringwald                 avrcp_emit_operation_status(avrcp_callback, AVRCP_SUBEVENT_OPERATION_COMPLETE, connection->avrcp_cid, ctype, operation_id);
1144be32e7f1SMilanka Ringwald             }
1145be32e7f1SMilanka Ringwald             if (connection->state == AVCTP_W2_SEND_RELEASE_COMMAND){
1146be32e7f1SMilanka Ringwald                 // PRESS response
1147be32e7f1SMilanka Ringwald                 request_pass_through_release_control_cmd(connection);
1148be32e7f1SMilanka Ringwald             }
1149be32e7f1SMilanka Ringwald             break;
1150be32e7f1SMilanka Ringwald         }
1151be32e7f1SMilanka Ringwald         default:
1152be32e7f1SMilanka Ringwald             break;
1153be32e7f1SMilanka Ringwald     }
1154be32e7f1SMilanka Ringwald }
1155be32e7f1SMilanka Ringwald 
1156be32e7f1SMilanka Ringwald static void avrcp_handle_can_send_now(avrcp_connection_t * connection){
1157be32e7f1SMilanka Ringwald     int i;
1158be32e7f1SMilanka Ringwald     switch (connection->state){
1159be32e7f1SMilanka Ringwald         case AVCTP_W2_SEND_PRESS_COMMAND:
1160be32e7f1SMilanka Ringwald             connection->state = AVCTP_W2_RECEIVE_PRESS_RESPONSE;
1161b193c45eSMilanka Ringwald             avrcp_send_cmd(connection->l2cap_signaling_cid, connection);
1162be32e7f1SMilanka Ringwald             break;
1163be32e7f1SMilanka Ringwald         case AVCTP_W2_SEND_COMMAND:
1164be32e7f1SMilanka Ringwald         case AVCTP_W2_SEND_RELEASE_COMMAND:
1165be32e7f1SMilanka Ringwald             connection->state = AVCTP_W2_RECEIVE_RESPONSE;
1166b193c45eSMilanka Ringwald             avrcp_send_cmd(connection->l2cap_signaling_cid, connection);
1167be32e7f1SMilanka Ringwald             break;
1168be32e7f1SMilanka Ringwald         case AVCTP_CONNECTION_OPENED:
1169be32e7f1SMilanka Ringwald             if (connection->notifications_to_register != 0){
1170d1845da3SMilanka Ringwald                 for (i = 1; i <= AVRCP_NOTIFICATION_EVENT_VOLUME_CHANGED; i++){
1171be32e7f1SMilanka Ringwald                     if (connection->notifications_to_register & (1<<i)){
117273264fe8SMatthias Ringwald                         connection->notifications_to_register &= ~ (1 << i);
1173d5ff3b26SMatthias Ringwald                         avrcp_prepare_notification(connection, (avrcp_notification_event_id_t) i);
1174294335f1SMilanka Ringwald                         connection->state = AVCTP_W2_RECEIVE_RESPONSE;
1175be32e7f1SMilanka Ringwald                         avrcp_send_cmd(connection->l2cap_signaling_cid, connection);
1176be32e7f1SMilanka Ringwald                         return;
1177be32e7f1SMilanka Ringwald                     }
1178be32e7f1SMilanka Ringwald                 }
1179be32e7f1SMilanka Ringwald             }
1180be32e7f1SMilanka Ringwald             return;
1181be32e7f1SMilanka Ringwald         default:
1182be32e7f1SMilanka Ringwald             return;
1183be32e7f1SMilanka Ringwald     }
1184be32e7f1SMilanka Ringwald }
1185be32e7f1SMilanka Ringwald 
11866086246cSMilanka Ringwald static avrcp_connection_t * avrcp_create_connection(bd_addr_t remote_addr, avrcp_context_t * context){
1187be32e7f1SMilanka Ringwald     avrcp_connection_t * connection = btstack_memory_avrcp_connection_get();
1188be32e7f1SMilanka Ringwald     memset(connection, 0, sizeof(avrcp_connection_t));
1189be32e7f1SMilanka Ringwald     connection->state = AVCTP_CONNECTION_IDLE;
1190be32e7f1SMilanka Ringwald     connection->transaction_label = 0xFF;
1191be32e7f1SMilanka Ringwald     memcpy(connection->remote_addr, remote_addr, 6);
11926086246cSMilanka Ringwald     btstack_linked_list_add(&context->connections, (btstack_linked_item_t *) connection);
1193be32e7f1SMilanka Ringwald     return connection;
1194be32e7f1SMilanka Ringwald }
1195be32e7f1SMilanka Ringwald 
11966086246cSMilanka Ringwald static void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size, avrcp_context_t * context){
1197be32e7f1SMilanka Ringwald     bd_addr_t event_addr;
1198be32e7f1SMilanka Ringwald     uint16_t local_cid;
1199be32e7f1SMilanka Ringwald     avrcp_connection_t * connection = NULL;
1200be32e7f1SMilanka Ringwald 
1201be32e7f1SMilanka Ringwald     switch (packet_type) {
1202be32e7f1SMilanka Ringwald         case L2CAP_DATA_PACKET:
12036086246cSMilanka Ringwald             connection = get_avrcp_connection_for_l2cap_signaling_cid(channel, context);
1204be32e7f1SMilanka Ringwald             if (!connection) break;
1205be32e7f1SMilanka Ringwald             avrcp_handle_l2cap_data_packet_for_signaling_connection(connection, packet, size);
1206be32e7f1SMilanka Ringwald             break;
1207be32e7f1SMilanka Ringwald 
1208be32e7f1SMilanka Ringwald         case HCI_EVENT_PACKET:
1209be32e7f1SMilanka Ringwald             switch (hci_event_packet_get_type(packet)) {
1210*34b22aacSMilanka Ringwald                 case HCI_EVENT_DISCONNECTION_COMPLETE:
1211*34b22aacSMilanka Ringwald                     // connection closed -> quit test app
1212*34b22aacSMilanka Ringwald                     // status = hci_event_disconnection_complete_get_status(packet);
1213*34b22aacSMilanka Ringwald                     avrcp_emit_connection_closed(avrcp_callback, 0);
1214*34b22aacSMilanka Ringwald                     break;
1215be32e7f1SMilanka Ringwald                 case L2CAP_EVENT_INCOMING_CONNECTION:
1216be32e7f1SMilanka Ringwald                     l2cap_event_incoming_connection_get_address(packet, event_addr);
1217be32e7f1SMilanka Ringwald                     local_cid = l2cap_event_incoming_connection_get_local_cid(packet);
12186086246cSMilanka Ringwald                     connection = avrcp_create_connection(event_addr, context);
12190f76c2d7SMatthias Ringwald                     if (!connection) {
12200f76c2d7SMatthias Ringwald                         log_error("Failed to alloc connection structure");
12210f76c2d7SMatthias Ringwald                         l2cap_decline_connection(local_cid);
1222be32e7f1SMilanka Ringwald                         break;
1223be32e7f1SMilanka Ringwald                     }
12240f76c2d7SMatthias Ringwald                     connection->state = AVCTP_CONNECTION_W4_L2CAP_CONNECTED;
12250f76c2d7SMatthias Ringwald                     connection->l2cap_signaling_cid = local_cid;
12260f76c2d7SMatthias Ringwald                     l2cap_accept_connection(local_cid);
1227be32e7f1SMilanka Ringwald                     break;
1228be32e7f1SMilanka Ringwald 
1229be32e7f1SMilanka Ringwald                 case L2CAP_EVENT_CHANNEL_OPENED:
1230be32e7f1SMilanka Ringwald                     l2cap_event_channel_opened_get_address(packet, event_addr);
1231be32e7f1SMilanka Ringwald 
12326086246cSMilanka Ringwald                     connection = get_avrcp_connection_for_bd_addr(event_addr, context);
12330f76c2d7SMatthias Ringwald                     local_cid  = l2cap_event_channel_opened_get_local_cid(packet);
12340f76c2d7SMatthias Ringwald 
12356086246cSMilanka Ringwald                     if (!connection){
12360f76c2d7SMatthias Ringwald                         // incoming connection
12376086246cSMilanka Ringwald                         connection = avrcp_create_connection(event_addr, context);
12380f76c2d7SMatthias Ringwald                         if (!connection) {
12390f76c2d7SMatthias Ringwald                             log_error("Failed to alloc connection structure");
12400f76c2d7SMatthias Ringwald                             l2cap_disconnect(local_cid, 0); // reason isn't used
1241b193c45eSMilanka Ringwald                             break;
1242b193c45eSMilanka Ringwald                         }
12436086246cSMilanka Ringwald                         connection->l2cap_signaling_cid = local_cid;
12446086246cSMilanka Ringwald                         connection->avrcp_cid = avrcp_cid_counter++;
12450f76c2d7SMatthias Ringwald                     }
1246b193c45eSMilanka Ringwald 
12476086246cSMilanka Ringwald                     if (l2cap_event_channel_opened_get_status(packet)){
12486086246cSMilanka Ringwald                         log_info("L2CAP connection to connection %s failed. status code 0x%02x",
12496086246cSMilanka Ringwald                             bd_addr_to_str(event_addr), l2cap_event_channel_opened_get_status(packet));
12506086246cSMilanka Ringwald                         if (connection->state == AVCTP_CONNECTION_W4_L2CAP_CONNECTED && connection->l2cap_signaling_cid == local_cid){
12516086246cSMilanka Ringwald                             avrcp_emit_connection_established(avrcp_callback, connection->avrcp_cid, event_addr, l2cap_event_channel_opened_get_status(packet));
12526086246cSMilanka Ringwald                         }
12536086246cSMilanka Ringwald                         // free connection
12546086246cSMilanka Ringwald                         btstack_linked_list_remove(&context->connections, (btstack_linked_item_t*) connection);
12556086246cSMilanka Ringwald                         btstack_memory_avrcp_connection_free(connection);
12566086246cSMilanka Ringwald                         break;
12576086246cSMilanka Ringwald                     }
1258be32e7f1SMilanka Ringwald                     connection->l2cap_signaling_cid = local_cid;
12596086246cSMilanka Ringwald                     log_info("L2CAP_EVENT_CHANNEL_OPENED avrcp_cid 0x%02x, l2cap_signaling_cid 0x%02x",connection->avrcp_cid, connection->l2cap_signaling_cid);
1260be32e7f1SMilanka Ringwald                     connection->state = AVCTP_CONNECTION_OPENED;
12616086246cSMilanka Ringwald                     avrcp_emit_connection_established(avrcp_callback, connection->avrcp_cid, event_addr, ERROR_CODE_SUCCESS);
12625c806868SMatthias Ringwald 
12635c806868SMatthias Ringwald                     // hack
12645c806868SMatthias Ringwald                     // l2cap_create_channel(avrcp_controller_context.packet_handler, connection->remote_addr, 0x001b, l2cap_max_mtu(), NULL);
1265be32e7f1SMilanka Ringwald                     break;
1266be32e7f1SMilanka Ringwald 
1267be32e7f1SMilanka Ringwald                 case L2CAP_EVENT_CAN_SEND_NOW:
12686086246cSMilanka Ringwald                     connection = get_avrcp_connection_for_l2cap_signaling_cid(channel, context);
1269be32e7f1SMilanka Ringwald                     if (!connection) break;
1270be32e7f1SMilanka Ringwald                     avrcp_handle_can_send_now(connection);
1271be32e7f1SMilanka Ringwald                     break;
1272be32e7f1SMilanka Ringwald 
1273be32e7f1SMilanka Ringwald                 case L2CAP_EVENT_CHANNEL_CLOSED:
1274be32e7f1SMilanka Ringwald                     // data: event (8), len(8), channel (16)
1275be32e7f1SMilanka Ringwald                     local_cid = l2cap_event_channel_closed_get_local_cid(packet);
12766086246cSMilanka Ringwald                     connection = get_avrcp_connection_for_l2cap_signaling_cid(local_cid, context);
1277be32e7f1SMilanka Ringwald                     if (connection){
1278*34b22aacSMilanka Ringwald                         avrcp_emit_connection_closed(avrcp_callback, local_cid);
12796f43fcd7SMatthias Ringwald                         // free connection
12806086246cSMilanka Ringwald                         btstack_linked_list_remove(&context->connections, (btstack_linked_item_t*) connection);
12816f43fcd7SMatthias Ringwald                         btstack_memory_avrcp_connection_free(connection);
1282be32e7f1SMilanka Ringwald                         break;
1283be32e7f1SMilanka Ringwald                     }
1284be32e7f1SMilanka Ringwald                     break;
1285be32e7f1SMilanka Ringwald                 default:
1286be32e7f1SMilanka Ringwald                     break;
1287be32e7f1SMilanka Ringwald             }
1288be32e7f1SMilanka Ringwald             break;
1289be32e7f1SMilanka Ringwald         default:
1290be32e7f1SMilanka Ringwald             break;
1291be32e7f1SMilanka Ringwald     }
1292be32e7f1SMilanka Ringwald }
1293be32e7f1SMilanka Ringwald 
12946086246cSMilanka Ringwald static void avrcp_controller_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
12956086246cSMilanka Ringwald     packet_handler(packet_type, channel, packet, size, &avrcp_controller_context);
12966086246cSMilanka Ringwald }
12976086246cSMilanka Ringwald 
12986086246cSMilanka Ringwald void avrcp_controller_init(void){
12996086246cSMilanka Ringwald     avrcp_controller_context.role = AVRCP_CONTROLLER;
13006086246cSMilanka Ringwald     avrcp_controller_context.connections = NULL;
13016086246cSMilanka Ringwald     avrcp_controller_context.avrcp_callback = avrcp_callback;
13026086246cSMilanka Ringwald     avrcp_controller_context.packet_handler = avrcp_controller_packet_handler;
1303a0f524f0SMatthias Ringwald     l2cap_register_service(&avrcp_controller_packet_handler, BLUETOOTH_PROTOCOL_AVCTP, 0xffff, LEVEL_0);
1304be32e7f1SMilanka Ringwald }
1305be32e7f1SMilanka Ringwald 
1306be32e7f1SMilanka Ringwald void avrcp_register_packet_handler(btstack_packet_handler_t callback){
1307be32e7f1SMilanka Ringwald     if (callback == NULL){
1308be32e7f1SMilanka Ringwald         log_error("avrcp_register_packet_handler called with NULL callback");
1309be32e7f1SMilanka Ringwald         return;
1310be32e7f1SMilanka Ringwald     }
1311be32e7f1SMilanka Ringwald     avrcp_callback = callback;
1312df642728SMilanka Ringwald     avrcp_controller_context.avrcp_callback = avrcp_callback;
1313be32e7f1SMilanka Ringwald }
1314be32e7f1SMilanka Ringwald 
13156086246cSMilanka Ringwald uint8_t avrcp_connect(bd_addr_t bd_addr, avrcp_context_t * context, uint16_t * avrcp_cid){
13166086246cSMilanka Ringwald     avrcp_connection_t * connection = get_avrcp_connection_for_bd_addr(bd_addr, context);
13178a2c6c7cSMilanka Ringwald     if (connection){
13188a2c6c7cSMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
13198a2c6c7cSMilanka Ringwald     }
13208a2c6c7cSMilanka Ringwald 
13216086246cSMilanka Ringwald     connection = avrcp_create_connection(bd_addr, context);
1322be32e7f1SMilanka Ringwald     if (!connection){
13238a2c6c7cSMilanka Ringwald         log_error("avrcp: could not allocate connection struct.");
13248a2c6c7cSMilanka Ringwald         return BTSTACK_MEMORY_ALLOC_FAILED;
1325be32e7f1SMilanka Ringwald     }
13268a2c6c7cSMilanka Ringwald 
13276086246cSMilanka Ringwald     if (!avrcp_cid) return L2CAP_LOCAL_CID_DOES_NOT_EXIST;
13286086246cSMilanka Ringwald 
13296086246cSMilanka Ringwald     *avrcp_cid = avrcp_cid_counter++;
13306086246cSMilanka Ringwald     connection->avrcp_cid = *avrcp_cid;
13316086246cSMilanka Ringwald 
13326086246cSMilanka Ringwald     connection->state = AVCTP_SIGNALING_W4_SDP_QUERY_COMPLETE;
1333df642728SMilanka Ringwald     sdp_query_context.avrcp_context = context;
13346086246cSMilanka Ringwald     sdp_query_context.avrcp_l2cap_psm = 0;
13356086246cSMilanka Ringwald     sdp_query_context.avrcp_version = 0;
1336df642728SMilanka Ringwald     sdp_query_context.connection = connection;
13376086246cSMilanka Ringwald 
13386086246cSMilanka Ringwald     sdp_client_query_uuid16(&avrcp_handle_sdp_client_query_result, bd_addr, BLUETOOTH_PROTOCOL_AVCTP);
13398a2c6c7cSMilanka Ringwald     return ERROR_CODE_SUCCESS;
1340be32e7f1SMilanka Ringwald }
1341be32e7f1SMilanka Ringwald 
13426086246cSMilanka Ringwald uint8_t avrcp_controller_connect(bd_addr_t bd_addr, uint16_t * avrcp_cid){
13436086246cSMilanka Ringwald     return avrcp_connect(bd_addr, &avrcp_controller_context, avrcp_cid);
13446086246cSMilanka Ringwald }
13456086246cSMilanka Ringwald 
134631a5a745SMatthias Ringwald uint8_t avrcp_unit_info(uint16_t avrcp_cid){
13476086246cSMilanka Ringwald     avrcp_connection_t * connection = get_avrcp_connection_for_avrcp_cid(avrcp_cid, &avrcp_controller_context);
1348be32e7f1SMilanka Ringwald     if (!connection){
1349be32e7f1SMilanka Ringwald         log_error("avrcp_unit_info: could not find a connection.");
135031a5a745SMatthias Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1351be32e7f1SMilanka Ringwald     }
135231a5a745SMatthias Ringwald     if (connection->state != AVCTP_CONNECTION_OPENED) return ERROR_CODE_COMMAND_DISALLOWED;
1353be32e7f1SMilanka Ringwald     connection->state = AVCTP_W2_SEND_COMMAND;
1354be32e7f1SMilanka Ringwald 
1355be32e7f1SMilanka Ringwald     connection->transaction_label++;
13569cc1f3ceSMilanka Ringwald     connection->command_opcode = AVRCP_CMD_OPCODE_UNIT_INFO;
1357be32e7f1SMilanka Ringwald     connection->command_type = AVRCP_CTYPE_STATUS;
1358be32e7f1SMilanka Ringwald     connection->subunit_type = AVRCP_SUBUNIT_TYPE_UNIT; //vendor unique
1359be32e7f1SMilanka Ringwald     connection->subunit_id =   AVRCP_SUBUNIT_ID_IGNORE;
1360adcdded9SMilanka Ringwald     memset(connection->cmd_operands, 0xFF, connection->cmd_operands_length);
1361adcdded9SMilanka Ringwald     connection->cmd_operands_length = 5;
1362be32e7f1SMilanka Ringwald     avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid);
136331a5a745SMatthias Ringwald     return ERROR_CODE_SUCCESS;
1364be32e7f1SMilanka Ringwald }
1365be32e7f1SMilanka Ringwald 
136631a5a745SMatthias Ringwald static uint8_t avrcp_get_capabilities(uint16_t avrcp_cid, uint8_t capability_id){
13676086246cSMilanka Ringwald     avrcp_connection_t * connection = get_avrcp_connection_for_avrcp_cid(avrcp_cid, &avrcp_controller_context);
1368be32e7f1SMilanka Ringwald     if (!connection){
1369be32e7f1SMilanka Ringwald         log_error("avrcp_get_capabilities: could not find a connection.");
137031a5a745SMatthias Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1371be32e7f1SMilanka Ringwald     }
137231a5a745SMatthias Ringwald     if (connection->state != AVCTP_CONNECTION_OPENED) return ERROR_CODE_COMMAND_DISALLOWED;
1373be32e7f1SMilanka Ringwald     connection->state = AVCTP_W2_SEND_COMMAND;
1374be32e7f1SMilanka Ringwald 
1375be32e7f1SMilanka Ringwald     connection->transaction_label++;
13769cc1f3ceSMilanka Ringwald     connection->command_opcode = AVRCP_CMD_OPCODE_VENDOR_DEPENDENT;
1377be32e7f1SMilanka Ringwald     connection->command_type = AVRCP_CTYPE_STATUS;
1378be32e7f1SMilanka Ringwald     connection->subunit_type = AVRCP_SUBUNIT_TYPE_PANEL;
1379d5ff3b26SMatthias Ringwald     connection->subunit_id = AVRCP_SUBUNIT_ID;
1380be32e7f1SMilanka Ringwald     big_endian_store_24(connection->cmd_operands, 0, BT_SIG_COMPANY_ID);
1381be32e7f1SMilanka Ringwald     connection->cmd_operands[3] = AVRCP_PDU_ID_GET_CAPABILITIES; // PDU ID
1382be32e7f1SMilanka Ringwald     connection->cmd_operands[4] = 0;
1383be32e7f1SMilanka Ringwald     big_endian_store_16(connection->cmd_operands, 5, 1); // parameter length
1384be32e7f1SMilanka Ringwald     connection->cmd_operands[7] = capability_id;                  // capability ID
1385adcdded9SMilanka Ringwald     connection->cmd_operands_length = 8;
1386be32e7f1SMilanka Ringwald     avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid);
138731a5a745SMatthias Ringwald     return ERROR_CODE_SUCCESS;
1388be32e7f1SMilanka Ringwald }
1389be32e7f1SMilanka Ringwald 
139031a5a745SMatthias Ringwald uint8_t avrcp_get_supported_company_ids(uint16_t avrcp_cid){
139131a5a745SMatthias Ringwald     return avrcp_get_capabilities(avrcp_cid, AVRCP_CAPABILITY_ID_COMPANY);
1392be32e7f1SMilanka Ringwald }
1393be32e7f1SMilanka Ringwald 
139431a5a745SMatthias Ringwald uint8_t avrcp_get_supported_events(uint16_t avrcp_cid){
139531a5a745SMatthias Ringwald     return avrcp_get_capabilities(avrcp_cid, AVRCP_CAPABILITY_ID_EVENT);
1396be32e7f1SMilanka Ringwald }
1397be32e7f1SMilanka Ringwald 
1398be32e7f1SMilanka Ringwald 
139931a5a745SMatthias Ringwald uint8_t avrcp_play(uint16_t avrcp_cid){
1400d1845da3SMilanka Ringwald     return request_single_pass_through_press_control_cmd(avrcp_cid, AVRCP_OPERATION_ID_PLAY, 0);
1401be32e7f1SMilanka Ringwald }
1402be32e7f1SMilanka Ringwald 
140331a5a745SMatthias Ringwald uint8_t avrcp_stop(uint16_t avrcp_cid){
1404d1845da3SMilanka Ringwald     return request_single_pass_through_press_control_cmd(avrcp_cid, AVRCP_OPERATION_ID_STOP, 0);
1405be32e7f1SMilanka Ringwald }
1406be32e7f1SMilanka Ringwald 
140731a5a745SMatthias Ringwald uint8_t avrcp_pause(uint16_t avrcp_cid){
1408d1845da3SMilanka Ringwald     return request_single_pass_through_press_control_cmd(avrcp_cid, AVRCP_OPERATION_ID_PAUSE, 0);
1409be32e7f1SMilanka Ringwald }
1410be32e7f1SMilanka Ringwald 
141131a5a745SMatthias Ringwald uint8_t avrcp_forward(uint16_t avrcp_cid){
1412d1845da3SMilanka Ringwald     return request_single_pass_through_press_control_cmd(avrcp_cid, AVRCP_OPERATION_ID_FORWARD, 0);
1413be32e7f1SMilanka Ringwald }
1414be32e7f1SMilanka Ringwald 
141531a5a745SMatthias Ringwald uint8_t avrcp_backward(uint16_t avrcp_cid){
1416d1845da3SMilanka Ringwald     return request_single_pass_through_press_control_cmd(avrcp_cid, AVRCP_OPERATION_ID_BACKWARD, 0);
1417be32e7f1SMilanka Ringwald }
1418be32e7f1SMilanka Ringwald 
141931a5a745SMatthias Ringwald uint8_t avrcp_start_rewind(uint16_t avrcp_cid){
1420d1845da3SMilanka Ringwald     return request_continuous_pass_through_press_control_cmd(avrcp_cid, AVRCP_OPERATION_ID_REWIND, 0);
1421be32e7f1SMilanka Ringwald }
1422be32e7f1SMilanka Ringwald 
142331a5a745SMatthias Ringwald uint8_t avrcp_volume_up(uint16_t avrcp_cid){
1424d1845da3SMilanka Ringwald     return request_single_pass_through_press_control_cmd(avrcp_cid, AVRCP_OPERATION_ID_VOLUME_UP, 0);
1425be32e7f1SMilanka Ringwald }
1426be32e7f1SMilanka Ringwald 
142731a5a745SMatthias Ringwald uint8_t avrcp_volume_down(uint16_t avrcp_cid){
1428d1845da3SMilanka Ringwald     return request_single_pass_through_press_control_cmd(avrcp_cid, AVRCP_OPERATION_ID_VOLUME_DOWN, 0);
1429be32e7f1SMilanka Ringwald }
1430be32e7f1SMilanka Ringwald 
143131a5a745SMatthias Ringwald uint8_t avrcp_mute(uint16_t avrcp_cid){
1432d1845da3SMilanka Ringwald     return request_single_pass_through_press_control_cmd(avrcp_cid, AVRCP_OPERATION_ID_MUTE, 0);
1433be32e7f1SMilanka Ringwald }
1434be32e7f1SMilanka Ringwald 
143531a5a745SMatthias Ringwald uint8_t avrcp_skip(uint16_t avrcp_cid){
1436d1845da3SMilanka Ringwald     return request_single_pass_through_press_control_cmd(avrcp_cid, AVRCP_OPERATION_ID_SKIP, 0);
1437be32e7f1SMilanka Ringwald }
1438be32e7f1SMilanka Ringwald 
143931a5a745SMatthias Ringwald uint8_t avrcp_stop_rewind(uint16_t avrcp_cid){
14406086246cSMilanka Ringwald     avrcp_connection_t * connection = get_avrcp_connection_for_avrcp_cid(avrcp_cid, &avrcp_controller_context);
1441be32e7f1SMilanka Ringwald     if (!connection){
1442be32e7f1SMilanka Ringwald         log_error("avrcp_stop_rewind: could not find a connection.");
144331a5a745SMatthias Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1444be32e7f1SMilanka Ringwald     }
144531a5a745SMatthias Ringwald     if (connection->state != AVCTP_W4_STOP) return ERROR_CODE_COMMAND_DISALLOWED;
144631a5a745SMatthias Ringwald     return request_pass_through_release_control_cmd(connection);
1447be32e7f1SMilanka Ringwald }
1448be32e7f1SMilanka Ringwald 
144931a5a745SMatthias Ringwald uint8_t avrcp_start_fast_forward(uint16_t avrcp_cid){
1450d1845da3SMilanka Ringwald     return request_continuous_pass_through_press_control_cmd(avrcp_cid, AVRCP_OPERATION_ID_FAST_FORWARD, 0);
1451be32e7f1SMilanka Ringwald }
1452be32e7f1SMilanka Ringwald 
1453d1845da3SMilanka Ringwald uint8_t avrcp_fast_forward(uint16_t avrcp_cid){
1454d1845da3SMilanka Ringwald     return request_single_pass_through_press_control_cmd(avrcp_cid, AVRCP_OPERATION_ID_FAST_FORWARD, 0);
1455d1845da3SMilanka Ringwald }
1456d1845da3SMilanka Ringwald 
1457d1845da3SMilanka Ringwald uint8_t avrcp_rewind(uint16_t avrcp_cid){
1458d1845da3SMilanka Ringwald     return request_single_pass_through_press_control_cmd(avrcp_cid, AVRCP_OPERATION_ID_REWIND, 0);
1459d1845da3SMilanka Ringwald }
1460d1845da3SMilanka Ringwald 
1461d1845da3SMilanka Ringwald 
146231a5a745SMatthias Ringwald uint8_t avrcp_stop_fast_forward(uint16_t avrcp_cid){
14636086246cSMilanka Ringwald     avrcp_connection_t * connection = get_avrcp_connection_for_avrcp_cid(avrcp_cid, &avrcp_controller_context);
1464be32e7f1SMilanka Ringwald     if (!connection){
1465be32e7f1SMilanka Ringwald         log_error("avrcp_stop_fast_forward: could not find a connection.");
146631a5a745SMatthias Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1467be32e7f1SMilanka Ringwald     }
146831a5a745SMatthias Ringwald     if (connection->state != AVCTP_W4_STOP) return ERROR_CODE_COMMAND_DISALLOWED;
146931a5a745SMatthias Ringwald     return request_pass_through_release_control_cmd(connection);
1470be32e7f1SMilanka Ringwald }
1471be32e7f1SMilanka Ringwald 
147231a5a745SMatthias Ringwald uint8_t avrcp_get_play_status(uint16_t avrcp_cid){
14736086246cSMilanka Ringwald     avrcp_connection_t * connection = get_avrcp_connection_for_avrcp_cid(avrcp_cid, &avrcp_controller_context);
1474be32e7f1SMilanka Ringwald     if (!connection){
1475be32e7f1SMilanka Ringwald         log_error("avrcp_get_play_status: could not find a connection.");
147631a5a745SMatthias Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1477be32e7f1SMilanka Ringwald     }
147831a5a745SMatthias Ringwald     if (connection->state != AVCTP_CONNECTION_OPENED) return ERROR_CODE_COMMAND_DISALLOWED;
1479be32e7f1SMilanka Ringwald     connection->state = AVCTP_W2_SEND_COMMAND;
1480be32e7f1SMilanka Ringwald     connection->transaction_label++;
14819cc1f3ceSMilanka Ringwald     connection->command_opcode = AVRCP_CMD_OPCODE_VENDOR_DEPENDENT;
1482be32e7f1SMilanka Ringwald     connection->command_type = AVRCP_CTYPE_STATUS;
1483be32e7f1SMilanka Ringwald     connection->subunit_type = AVRCP_SUBUNIT_TYPE_PANEL;
1484d5ff3b26SMatthias Ringwald     connection->subunit_id = AVRCP_SUBUNIT_ID;
1485be32e7f1SMilanka Ringwald     big_endian_store_24(connection->cmd_operands, 0, BT_SIG_COMPANY_ID);
1486be32e7f1SMilanka Ringwald     connection->cmd_operands[3] = AVRCP_PDU_ID_GET_PLAY_STATUS;
1487be32e7f1SMilanka Ringwald     connection->cmd_operands[4] = 0;                     // reserved(upper 6) | packet_type -> 0
1488be32e7f1SMilanka Ringwald     big_endian_store_16(connection->cmd_operands, 5, 0); // parameter length
1489adcdded9SMilanka Ringwald     connection->cmd_operands_length = 7;
1490be32e7f1SMilanka Ringwald     avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid);
149131a5a745SMatthias Ringwald     return ERROR_CODE_SUCCESS;
1492be32e7f1SMilanka Ringwald }
1493be32e7f1SMilanka Ringwald 
149431a5a745SMatthias Ringwald uint8_t avrcp_enable_notification(uint16_t avrcp_cid, avrcp_notification_event_id_t event_id){
14956086246cSMilanka Ringwald     avrcp_connection_t * connection = get_avrcp_connection_for_avrcp_cid(avrcp_cid, &avrcp_controller_context);
1496be32e7f1SMilanka Ringwald     if (!connection){
1497be32e7f1SMilanka Ringwald         log_error("avrcp_get_play_status: could not find a connection.");
149831a5a745SMatthias Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1499be32e7f1SMilanka Ringwald     }
1500be32e7f1SMilanka Ringwald     avrcp_register_notification(connection, event_id);
150131a5a745SMatthias Ringwald     return ERROR_CODE_SUCCESS;
1502be32e7f1SMilanka Ringwald }
1503be32e7f1SMilanka Ringwald 
150431a5a745SMatthias Ringwald uint8_t avrcp_disable_notification(uint16_t avrcp_cid, avrcp_notification_event_id_t event_id){
15056086246cSMilanka Ringwald     avrcp_connection_t * connection = get_avrcp_connection_for_avrcp_cid(avrcp_cid, &avrcp_controller_context);
1506be32e7f1SMilanka Ringwald     if (!connection){
1507be32e7f1SMilanka Ringwald         log_error("avrcp_get_play_status: could not find a connection.");
150831a5a745SMatthias Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1509be32e7f1SMilanka Ringwald     }
1510be32e7f1SMilanka Ringwald     connection->notifications_to_deregister |= (1 << event_id);
151131a5a745SMatthias Ringwald     return ERROR_CODE_SUCCESS;
1512be32e7f1SMilanka Ringwald }
1513be32e7f1SMilanka Ringwald 
151431a5a745SMatthias Ringwald uint8_t avrcp_get_now_playing_info(uint16_t avrcp_cid){
15156086246cSMilanka Ringwald     avrcp_connection_t * connection = get_avrcp_connection_for_avrcp_cid(avrcp_cid, &avrcp_controller_context);
1516be32e7f1SMilanka Ringwald     if (!connection){
1517be32e7f1SMilanka Ringwald         log_error("avrcp_get_capabilities: could not find a connection.");
151831a5a745SMatthias Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1519be32e7f1SMilanka Ringwald     }
152031a5a745SMatthias Ringwald     if (connection->state != AVCTP_CONNECTION_OPENED) return ERROR_CODE_COMMAND_DISALLOWED;
1521be32e7f1SMilanka Ringwald     connection->state = AVCTP_W2_SEND_COMMAND;
1522be32e7f1SMilanka Ringwald 
1523be32e7f1SMilanka Ringwald     connection->transaction_label++;
15249cc1f3ceSMilanka Ringwald     connection->command_opcode = AVRCP_CMD_OPCODE_VENDOR_DEPENDENT;
1525be32e7f1SMilanka Ringwald     connection->command_type = AVRCP_CTYPE_STATUS;
1526be32e7f1SMilanka Ringwald     connection->subunit_type = AVRCP_SUBUNIT_TYPE_PANEL;
1527d5ff3b26SMatthias Ringwald     connection->subunit_id = AVRCP_SUBUNIT_ID;
1528be32e7f1SMilanka Ringwald     int pos = 0;
1529be32e7f1SMilanka Ringwald     big_endian_store_24(connection->cmd_operands, pos, BT_SIG_COMPANY_ID);
1530be32e7f1SMilanka Ringwald     pos += 3;
1531be32e7f1SMilanka Ringwald     connection->cmd_operands[pos++] = AVRCP_PDU_ID_GET_ELEMENT_ATTRIBUTES; // PDU ID
1532be32e7f1SMilanka Ringwald     connection->cmd_operands[pos++] = 0;
1533be32e7f1SMilanka Ringwald 
1534be32e7f1SMilanka Ringwald     // Parameter Length
1535be32e7f1SMilanka Ringwald     big_endian_store_16(connection->cmd_operands, pos, 9);
1536be32e7f1SMilanka Ringwald     pos += 2;
1537be32e7f1SMilanka Ringwald 
1538be32e7f1SMilanka Ringwald     // write 8 bytes value
1539be32e7f1SMilanka Ringwald     memset(connection->cmd_operands + pos, 0, 8); // identifier: PLAYING
1540be32e7f1SMilanka Ringwald     pos += 8;
1541be32e7f1SMilanka Ringwald 
1542be32e7f1SMilanka Ringwald     connection->cmd_operands[pos++] = 0; // attribute count, if 0 get all attributes
1543be32e7f1SMilanka Ringwald     // every attribute is 4 bytes long
1544be32e7f1SMilanka Ringwald 
1545adcdded9SMilanka Ringwald     connection->cmd_operands_length = pos;
1546be32e7f1SMilanka Ringwald     avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid);
154731a5a745SMatthias Ringwald     return ERROR_CODE_SUCCESS;
1548be32e7f1SMilanka Ringwald }
1549be32e7f1SMilanka Ringwald 
155031a5a745SMatthias Ringwald uint8_t avrcp_set_absolute_volume(uint16_t avrcp_cid, uint8_t volume){
15516086246cSMilanka Ringwald      avrcp_connection_t * connection = get_avrcp_connection_for_avrcp_cid(avrcp_cid, &avrcp_controller_context);
1552be32e7f1SMilanka Ringwald     if (!connection){
1553be32e7f1SMilanka Ringwald         log_error("avrcp_get_capabilities: could not find a connection.");
155431a5a745SMatthias Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1555be32e7f1SMilanka Ringwald     }
155631a5a745SMatthias Ringwald     if (connection->state != AVCTP_CONNECTION_OPENED) return ERROR_CODE_COMMAND_DISALLOWED;
1557be32e7f1SMilanka Ringwald     connection->state = AVCTP_W2_SEND_COMMAND;
1558be32e7f1SMilanka Ringwald 
1559be32e7f1SMilanka Ringwald     connection->transaction_label++;
15609cc1f3ceSMilanka Ringwald     connection->command_opcode = AVRCP_CMD_OPCODE_VENDOR_DEPENDENT;
1561be32e7f1SMilanka Ringwald     connection->command_type = AVRCP_CTYPE_CONTROL;
1562be32e7f1SMilanka Ringwald     connection->subunit_type = AVRCP_SUBUNIT_TYPE_PANEL;
1563d5ff3b26SMatthias Ringwald     connection->subunit_id = AVRCP_SUBUNIT_ID;
1564be32e7f1SMilanka Ringwald     int pos = 0;
1565be32e7f1SMilanka Ringwald     big_endian_store_24(connection->cmd_operands, pos, BT_SIG_COMPANY_ID);
1566be32e7f1SMilanka Ringwald     pos += 3;
1567be32e7f1SMilanka Ringwald     connection->cmd_operands[pos++] = AVRCP_PDU_ID_SET_ABSOLUTE_VOLUME; // PDU ID
1568be32e7f1SMilanka Ringwald     connection->cmd_operands[pos++] = 0;
1569be32e7f1SMilanka Ringwald 
1570be32e7f1SMilanka Ringwald     // Parameter Length
1571be32e7f1SMilanka Ringwald     big_endian_store_16(connection->cmd_operands, pos, 1);
1572be32e7f1SMilanka Ringwald     pos += 2;
1573be32e7f1SMilanka Ringwald     connection->cmd_operands[pos++] = volume;
1574be32e7f1SMilanka Ringwald 
1575adcdded9SMilanka Ringwald     connection->cmd_operands_length = pos;
1576be32e7f1SMilanka Ringwald     avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid);
157731a5a745SMatthias Ringwald     return ERROR_CODE_SUCCESS;
1578be32e7f1SMilanka Ringwald }
1579be32e7f1SMilanka Ringwald 
158031a5a745SMatthias Ringwald uint8_t avrcp_query_shuffle_and_repeat_modes(uint16_t avrcp_cid){
15816086246cSMilanka Ringwald     avrcp_connection_t * connection = get_avrcp_connection_for_avrcp_cid(avrcp_cid, &avrcp_controller_context);
1582be32e7f1SMilanka Ringwald     if (!connection){
1583be32e7f1SMilanka Ringwald         log_error("avrcp_get_capabilities: could not find a connection.");
158431a5a745SMatthias Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1585be32e7f1SMilanka Ringwald     }
158631a5a745SMatthias Ringwald     if (connection->state != AVCTP_CONNECTION_OPENED) return ERROR_CODE_COMMAND_DISALLOWED;
1587be32e7f1SMilanka Ringwald     connection->state = AVCTP_W2_SEND_COMMAND;
1588be32e7f1SMilanka Ringwald 
1589be32e7f1SMilanka Ringwald     connection->transaction_label++;
15909cc1f3ceSMilanka Ringwald     connection->command_opcode = AVRCP_CMD_OPCODE_VENDOR_DEPENDENT;
1591be32e7f1SMilanka Ringwald     connection->command_type = AVRCP_CTYPE_STATUS;
1592be32e7f1SMilanka Ringwald     connection->subunit_type = AVRCP_SUBUNIT_TYPE_PANEL;
1593d5ff3b26SMatthias Ringwald     connection->subunit_id = AVRCP_SUBUNIT_ID;
1594be32e7f1SMilanka Ringwald     big_endian_store_24(connection->cmd_operands, 0, BT_SIG_COMPANY_ID);
1595be32e7f1SMilanka Ringwald     connection->cmd_operands[3] = AVRCP_PDU_ID_GetCurrentPlayerApplicationSettingValue; // PDU ID
1596be32e7f1SMilanka Ringwald     connection->cmd_operands[4] = 0;
1597be32e7f1SMilanka Ringwald     big_endian_store_16(connection->cmd_operands, 5, 5); // parameter length
1598be32e7f1SMilanka Ringwald     connection->cmd_operands[7] = 4;                     // NumPlayerApplicationSettingAttributeID
1599be32e7f1SMilanka Ringwald     // PlayerApplicationSettingAttributeID1 AVRCP Spec, Appendix F, 133
1600be32e7f1SMilanka Ringwald     connection->cmd_operands[8]  = 0x01;    // equalizer  (1-OFF, 2-ON)
1601be32e7f1SMilanka Ringwald     connection->cmd_operands[9]  = 0x02;    // repeat     (1-off, 2-single track, 3-all tracks, 4-group repeat)
1602be32e7f1SMilanka Ringwald     connection->cmd_operands[10] = 0x03;    // shuffle    (1-off, 2-all tracks, 3-group shuffle)
1603be32e7f1SMilanka Ringwald     connection->cmd_operands[11] = 0x04;    // scan       (1-off, 2-all tracks, 3-group scan)
1604adcdded9SMilanka Ringwald     connection->cmd_operands_length = 12;
1605be32e7f1SMilanka Ringwald     avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid);
160631a5a745SMatthias Ringwald     return ERROR_CODE_SUCCESS;
1607be32e7f1SMilanka Ringwald }
1608be32e7f1SMilanka Ringwald 
16096086246cSMilanka Ringwald static uint8_t avrcp_set_current_player_application_setting_value(uint16_t avrcp_cid, uint8_t attr_id, uint8_t attr_value){
16106086246cSMilanka Ringwald     avrcp_connection_t * connection = get_avrcp_connection_for_avrcp_cid(avrcp_cid, &avrcp_controller_context);
1611be32e7f1SMilanka Ringwald     if (!connection){
1612be32e7f1SMilanka Ringwald         log_error("avrcp_get_capabilities: could not find a connection.");
161331a5a745SMatthias Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1614be32e7f1SMilanka Ringwald     }
161531a5a745SMatthias Ringwald     if (connection->state != AVCTP_CONNECTION_OPENED) return ERROR_CODE_COMMAND_DISALLOWED;
1616be32e7f1SMilanka Ringwald     connection->state = AVCTP_W2_SEND_COMMAND;
1617be32e7f1SMilanka Ringwald 
1618be32e7f1SMilanka Ringwald     connection->transaction_label++;
16199cc1f3ceSMilanka Ringwald     connection->command_opcode = AVRCP_CMD_OPCODE_VENDOR_DEPENDENT;
1620be32e7f1SMilanka Ringwald     connection->command_type = AVRCP_CTYPE_CONTROL;
1621be32e7f1SMilanka Ringwald     connection->subunit_type = AVRCP_SUBUNIT_TYPE_PANEL;
1622d5ff3b26SMatthias Ringwald     connection->subunit_id = AVRCP_SUBUNIT_ID;
1623be32e7f1SMilanka Ringwald     int pos = 0;
1624be32e7f1SMilanka Ringwald     big_endian_store_24(connection->cmd_operands, pos, BT_SIG_COMPANY_ID);
1625be32e7f1SMilanka Ringwald     pos += 3;
1626be32e7f1SMilanka Ringwald     connection->cmd_operands[pos++] = AVRCP_PDU_ID_SetPlayerApplicationSettingValue; // PDU ID
1627be32e7f1SMilanka Ringwald     connection->cmd_operands[pos++] = 0;
1628be32e7f1SMilanka Ringwald     // Parameter Length
1629be32e7f1SMilanka Ringwald     big_endian_store_16(connection->cmd_operands, pos, 3);
1630be32e7f1SMilanka Ringwald     pos += 2;
1631be32e7f1SMilanka Ringwald     connection->cmd_operands[pos++] = 2;
1632adcdded9SMilanka Ringwald     connection->cmd_operands_length = pos;
16336086246cSMilanka Ringwald     connection->cmd_operands[pos++]  = attr_id;
16346086246cSMilanka Ringwald     connection->cmd_operands[pos++]  = attr_value;
1635adcdded9SMilanka Ringwald     connection->cmd_operands_length = pos;
1636be32e7f1SMilanka Ringwald     avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid);
163731a5a745SMatthias Ringwald     return ERROR_CODE_SUCCESS;
1638be32e7f1SMilanka Ringwald }
1639be32e7f1SMilanka Ringwald 
164031a5a745SMatthias Ringwald uint8_t avrcp_set_shuffle_mode(uint16_t avrcp_cid, avrcp_shuffle_mode_t mode){
164131a5a745SMatthias Ringwald     if (mode < AVRCP_SHUFFLE_MODE_OFF || mode > AVRCP_SHUFFLE_MODE_GROUP) return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
164231a5a745SMatthias Ringwald     return avrcp_set_current_player_application_setting_value(avrcp_cid, 0x03, mode);
1643be32e7f1SMilanka Ringwald }
1644be32e7f1SMilanka Ringwald 
164531a5a745SMatthias Ringwald uint8_t avrcp_set_repeat_mode(uint16_t avrcp_cid, avrcp_repeat_mode_t mode){
164631a5a745SMatthias Ringwald     if (mode < AVRCP_REPEAT_MODE_OFF || mode > AVRCP_REPEAT_MODE_GROUP) return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
164731a5a745SMatthias Ringwald     return avrcp_set_current_player_application_setting_value(avrcp_cid, 0x02, mode);
1648be32e7f1SMilanka Ringwald }
1649be32e7f1SMilanka Ringwald 
165031a5a745SMatthias Ringwald uint8_t avrcp_disconnect(uint16_t avrcp_cid){
16516086246cSMilanka Ringwald     avrcp_connection_t * connection = get_avrcp_connection_for_avrcp_cid(avrcp_cid, &avrcp_controller_context);
1652be32e7f1SMilanka Ringwald     if (!connection){
1653be32e7f1SMilanka Ringwald         log_error("avrcp_get_capabilities: could not find a connection.");
165431a5a745SMatthias Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1655be32e7f1SMilanka Ringwald     }
165631a5a745SMatthias Ringwald     if (connection->state != AVCTP_CONNECTION_OPENED) return ERROR_CODE_COMMAND_DISALLOWED;
1657294335f1SMilanka Ringwald     l2cap_disconnect(connection->l2cap_signaling_cid, 0);
165831a5a745SMatthias Ringwald     return ERROR_CODE_SUCCESS;
1659be32e7f1SMilanka Ringwald }