xref: /btstack/src/classic/avrcp_controller.c (revision 6983e65e8fc2d4d8998d4365fc3aeee49461fe5d)
1*6983e65eSMilanka Ringwald /*
2*6983e65eSMilanka Ringwald  * Copyright (C) 2016 BlueKitchen GmbH
3*6983e65eSMilanka Ringwald  *
4*6983e65eSMilanka Ringwald  * Redistribution and use in source and binary forms, with or without
5*6983e65eSMilanka Ringwald  * modification, are permitted provided that the following conditions
6*6983e65eSMilanka Ringwald  * are met:
7*6983e65eSMilanka Ringwald  *
8*6983e65eSMilanka Ringwald  * 1. Redistributions of source code must retain the above copyright
9*6983e65eSMilanka Ringwald  *    notice, this list of conditions and the following disclaimer.
10*6983e65eSMilanka Ringwald  * 2. Redistributions in binary form must reproduce the above copyright
11*6983e65eSMilanka Ringwald  *    notice, this list of conditions and the following disclaimer in the
12*6983e65eSMilanka Ringwald  *    documentation and/or other materials provided with the distribution.
13*6983e65eSMilanka Ringwald  * 3. Neither the name of the copyright holders nor the names of
14*6983e65eSMilanka Ringwald  *    contributors may be used to endorse or promote products derived
15*6983e65eSMilanka Ringwald  *    from this software without specific prior written permission.
16*6983e65eSMilanka Ringwald  * 4. Any redistribution, use, or modification is done solely for
17*6983e65eSMilanka Ringwald  *    personal benefit and not for any commercial purpose or for
18*6983e65eSMilanka Ringwald  *    monetary gain.
19*6983e65eSMilanka Ringwald  *
20*6983e65eSMilanka Ringwald  * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
21*6983e65eSMilanka Ringwald  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22*6983e65eSMilanka Ringwald  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23*6983e65eSMilanka Ringwald  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
24*6983e65eSMilanka Ringwald  * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25*6983e65eSMilanka Ringwald  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26*6983e65eSMilanka Ringwald  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
27*6983e65eSMilanka Ringwald  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
28*6983e65eSMilanka Ringwald  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29*6983e65eSMilanka Ringwald  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
30*6983e65eSMilanka Ringwald  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31*6983e65eSMilanka Ringwald  * SUCH DAMAGE.
32*6983e65eSMilanka Ringwald  *
33*6983e65eSMilanka Ringwald  * Please inquire about commercial licensing options at
34*6983e65eSMilanka Ringwald  * [email protected]
35*6983e65eSMilanka Ringwald  *
36*6983e65eSMilanka Ringwald  */
37*6983e65eSMilanka Ringwald 
38*6983e65eSMilanka Ringwald #define __BTSTACK_FILE__ "avrcp.c"
39*6983e65eSMilanka Ringwald 
40*6983e65eSMilanka Ringwald #include <stdint.h>
41*6983e65eSMilanka Ringwald #include <stdio.h>
42*6983e65eSMilanka Ringwald #include <stdlib.h>
43*6983e65eSMilanka Ringwald #include <string.h>
44*6983e65eSMilanka Ringwald 
45*6983e65eSMilanka Ringwald #include "btstack.h"
46*6983e65eSMilanka Ringwald #include "classic/avrcp.h"
47*6983e65eSMilanka Ringwald #include "classic/avrcp_controller.h"
48*6983e65eSMilanka Ringwald 
49*6983e65eSMilanka Ringwald 
50*6983e65eSMilanka Ringwald /*
51*6983e65eSMilanka Ringwald Category 1: Player/Recorder
52*6983e65eSMilanka Ringwald Category 2: Monitor/Amplifier
53*6983e65eSMilanka Ringwald Category 3: Tuner
54*6983e65eSMilanka Ringwald Category 4: Menu
55*6983e65eSMilanka Ringwald */
56*6983e65eSMilanka Ringwald 
57*6983e65eSMilanka Ringwald /* controller supported features
58*6983e65eSMilanka Ringwald Bit 0 = Category 1
59*6983e65eSMilanka Ringwald Bit 1 = Category 2
60*6983e65eSMilanka Ringwald Bit 2 = Category 3
61*6983e65eSMilanka Ringwald Bit 3 = Category 4
62*6983e65eSMilanka Ringwald Bit 4-5 = RFA
63*6983e65eSMilanka Ringwald Bit 6 = Supports browsing
64*6983e65eSMilanka Ringwald Bit 7-15 = RFA
65*6983e65eSMilanka Ringwald The bits for supported categories are set to 1. Others are set to 0.
66*6983e65eSMilanka Ringwald */
67*6983e65eSMilanka Ringwald 
68*6983e65eSMilanka Ringwald /* target supported features
69*6983e65eSMilanka Ringwald Bit 0 = Category 1
70*6983e65eSMilanka Ringwald Bit 1 = Category 2
71*6983e65eSMilanka Ringwald Bit 2 = Category 3
72*6983e65eSMilanka Ringwald Bit 3 = Category 4
73*6983e65eSMilanka Ringwald Bit 4 = Player Application Settings. Bit 0 should be set for this bit to be set.
74*6983e65eSMilanka Ringwald Bit 5 = Group Navigation. Bit 0 should be set for this bit to be set.
75*6983e65eSMilanka Ringwald Bit 6 = Supports browsing*4
76*6983e65eSMilanka Ringwald Bit 7 = Supports multiple media player applications
77*6983e65eSMilanka Ringwald Bit 8-15 = RFA
78*6983e65eSMilanka Ringwald The bits for supported categories are set to 1. Others are set to 0.
79*6983e65eSMilanka Ringwald */
80*6983e65eSMilanka Ringwald 
81*6983e65eSMilanka Ringwald static avrcp_context_t avrcp_controller_context;
82*6983e65eSMilanka Ringwald 
83*6983e65eSMilanka 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){
84*6983e65eSMilanka Ringwald     avrcp_create_sdp_record(1, service, service_record_handle, browsing, supported_features, service_name, service_provider_name);
85*6983e65eSMilanka Ringwald }
86*6983e65eSMilanka Ringwald 
87*6983e65eSMilanka 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){
88*6983e65eSMilanka Ringwald     if (!callback) return;
89*6983e65eSMilanka Ringwald     uint8_t event[8];
90*6983e65eSMilanka Ringwald     int pos = 0;
91*6983e65eSMilanka Ringwald     event[pos++] = HCI_EVENT_AVRCP_META;
92*6983e65eSMilanka Ringwald     event[pos++] = sizeof(event) - 2;
93*6983e65eSMilanka Ringwald     event[pos++] = AVRCP_SUBEVENT_SHUFFLE_AND_REPEAT_MODE;
94*6983e65eSMilanka Ringwald     little_endian_store_16(event, pos, avrcp_cid);
95*6983e65eSMilanka Ringwald     pos += 2;
96*6983e65eSMilanka Ringwald     event[pos++] = ctype;
97*6983e65eSMilanka Ringwald     event[pos++] = repeat_mode;
98*6983e65eSMilanka Ringwald     event[pos++] = shuffle_mode;
99*6983e65eSMilanka Ringwald     (*callback)(HCI_EVENT_PACKET, 0, event, sizeof(event));
100*6983e65eSMilanka Ringwald }
101*6983e65eSMilanka Ringwald 
102*6983e65eSMilanka 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){
103*6983e65eSMilanka Ringwald     if (!callback) return;
104*6983e65eSMilanka Ringwald     uint8_t event[7];
105*6983e65eSMilanka Ringwald     int pos = 0;
106*6983e65eSMilanka Ringwald     event[pos++] = HCI_EVENT_AVRCP_META;
107*6983e65eSMilanka Ringwald     event[pos++] = sizeof(event) - 2;
108*6983e65eSMilanka Ringwald     event[pos++] = subevent;
109*6983e65eSMilanka Ringwald     little_endian_store_16(event, pos, avrcp_cid);
110*6983e65eSMilanka Ringwald     pos += 2;
111*6983e65eSMilanka Ringwald     event[pos++] = ctype;
112*6983e65eSMilanka Ringwald     event[pos++] = operation_id;
113*6983e65eSMilanka Ringwald     (*callback)(HCI_EVENT_PACKET, 0, event, sizeof(event));
114*6983e65eSMilanka Ringwald }
115*6983e65eSMilanka Ringwald 
116*6983e65eSMilanka Ringwald static void avrcp_press_and_hold_timeout_handler(btstack_timer_source_t * timer){
117*6983e65eSMilanka Ringwald     UNUSED(timer);
118*6983e65eSMilanka Ringwald     avrcp_connection_t * connection = btstack_run_loop_get_timer_context(timer);
119*6983e65eSMilanka Ringwald     btstack_run_loop_set_timer(&connection->press_and_hold_cmd_timer, 2000); // 2 seconds timeout
120*6983e65eSMilanka Ringwald     btstack_run_loop_add_timer(&connection->press_and_hold_cmd_timer);
121*6983e65eSMilanka Ringwald     connection->state = AVCTP_W2_SEND_PRESS_COMMAND;
122*6983e65eSMilanka Ringwald     avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid);
123*6983e65eSMilanka Ringwald }
124*6983e65eSMilanka Ringwald 
125*6983e65eSMilanka Ringwald static void avrcp_press_and_hold_timer_start(avrcp_connection_t * connection){
126*6983e65eSMilanka Ringwald     btstack_run_loop_remove_timer(&connection->press_and_hold_cmd_timer);
127*6983e65eSMilanka Ringwald     btstack_run_loop_set_timer_handler(&connection->press_and_hold_cmd_timer, avrcp_press_and_hold_timeout_handler);
128*6983e65eSMilanka Ringwald     btstack_run_loop_set_timer_context(&connection->press_and_hold_cmd_timer, connection);
129*6983e65eSMilanka Ringwald     btstack_run_loop_set_timer(&connection->press_and_hold_cmd_timer, 2000); // 2 seconds timeout
130*6983e65eSMilanka Ringwald     btstack_run_loop_add_timer(&connection->press_and_hold_cmd_timer);
131*6983e65eSMilanka Ringwald }
132*6983e65eSMilanka Ringwald 
133*6983e65eSMilanka Ringwald static void avrcp_press_and_hold_timer_stop(avrcp_connection_t * connection){
134*6983e65eSMilanka Ringwald     connection->continuous_fast_forward_cmd = 0;
135*6983e65eSMilanka Ringwald     btstack_run_loop_remove_timer(&connection->press_and_hold_cmd_timer);
136*6983e65eSMilanka Ringwald }
137*6983e65eSMilanka Ringwald 
138*6983e65eSMilanka Ringwald static uint8_t request_pass_through_release_control_cmd(avrcp_connection_t * connection){
139*6983e65eSMilanka Ringwald     connection->state = AVCTP_W2_SEND_RELEASE_COMMAND;
140*6983e65eSMilanka Ringwald     if (connection->continuous_fast_forward_cmd){
141*6983e65eSMilanka Ringwald         avrcp_press_and_hold_timer_stop(connection);
142*6983e65eSMilanka Ringwald     }
143*6983e65eSMilanka Ringwald     connection->cmd_operands[0] = 0x80 | connection->cmd_operands[0];
144*6983e65eSMilanka Ringwald     connection->transaction_label++;
145*6983e65eSMilanka Ringwald     avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid);
146*6983e65eSMilanka Ringwald     return ERROR_CODE_SUCCESS;
147*6983e65eSMilanka Ringwald }
148*6983e65eSMilanka Ringwald 
149*6983e65eSMilanka 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){
150*6983e65eSMilanka Ringwald     avrcp_connection_t * connection = get_avrcp_connection_for_avrcp_cid(avrcp_cid, context);
151*6983e65eSMilanka Ringwald     if (!connection){
152*6983e65eSMilanka Ringwald         log_error("avrcp: could not find a connection.");
153*6983e65eSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
154*6983e65eSMilanka Ringwald     }
155*6983e65eSMilanka Ringwald     if (connection->state != AVCTP_CONNECTION_OPENED) return ERROR_CODE_COMMAND_DISALLOWED;
156*6983e65eSMilanka Ringwald     connection->state = AVCTP_W2_SEND_PRESS_COMMAND;
157*6983e65eSMilanka Ringwald     connection->command_opcode =  AVRCP_CMD_OPCODE_PASS_THROUGH;
158*6983e65eSMilanka Ringwald     connection->command_type = AVRCP_CTYPE_CONTROL;
159*6983e65eSMilanka Ringwald     connection->subunit_type = AVRCP_SUBUNIT_TYPE_PANEL;
160*6983e65eSMilanka Ringwald     connection->subunit_id =   AVRCP_SUBUNIT_ID;
161*6983e65eSMilanka Ringwald     connection->cmd_operands_length = 0;
162*6983e65eSMilanka Ringwald 
163*6983e65eSMilanka Ringwald     connection->continuous_fast_forward_cmd = continuous_fast_forward_cmd;
164*6983e65eSMilanka Ringwald     connection->cmd_operands_length = 2;
165*6983e65eSMilanka Ringwald     connection->cmd_operands[0] = opid;
166*6983e65eSMilanka Ringwald     if (playback_speed > 0){
167*6983e65eSMilanka Ringwald         connection->cmd_operands[2] = playback_speed;
168*6983e65eSMilanka Ringwald         connection->cmd_operands_length++;
169*6983e65eSMilanka Ringwald     }
170*6983e65eSMilanka Ringwald     connection->cmd_operands[1] = connection->cmd_operands_length - 2;
171*6983e65eSMilanka Ringwald 
172*6983e65eSMilanka Ringwald     if (connection->continuous_fast_forward_cmd){
173*6983e65eSMilanka Ringwald         avrcp_press_and_hold_timer_start(connection);
174*6983e65eSMilanka Ringwald     }
175*6983e65eSMilanka Ringwald 
176*6983e65eSMilanka Ringwald     connection->transaction_label++;
177*6983e65eSMilanka Ringwald     avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid);
178*6983e65eSMilanka Ringwald     return ERROR_CODE_SUCCESS;
179*6983e65eSMilanka Ringwald }
180*6983e65eSMilanka Ringwald 
181*6983e65eSMilanka Ringwald static uint8_t request_single_pass_through_press_control_cmd(uint16_t avrcp_cid, avrcp_operation_id_t opid, uint16_t playback_speed){
182*6983e65eSMilanka Ringwald     return request_pass_through_press_control_cmd(avrcp_cid, opid, playback_speed, 0, &avrcp_controller_context);
183*6983e65eSMilanka Ringwald }
184*6983e65eSMilanka Ringwald 
185*6983e65eSMilanka Ringwald static uint8_t request_continuous_pass_through_press_control_cmd(uint16_t avrcp_cid, avrcp_operation_id_t opid, uint16_t playback_speed){
186*6983e65eSMilanka Ringwald     return request_pass_through_press_control_cmd(avrcp_cid, opid, playback_speed, 1, &avrcp_controller_context);
187*6983e65eSMilanka Ringwald }
188*6983e65eSMilanka Ringwald 
189*6983e65eSMilanka Ringwald static int avrcp_send_cmd(uint16_t cid, avrcp_connection_t * connection){
190*6983e65eSMilanka Ringwald     uint8_t command[30];
191*6983e65eSMilanka Ringwald     int pos = 0;
192*6983e65eSMilanka Ringwald     // transport header
193*6983e65eSMilanka Ringwald     // Transaction label | Packet_type | C/R | IPID (1 == invalid profile identifier)
194*6983e65eSMilanka Ringwald     command[pos++] = (connection->transaction_label << 4) | (AVRCP_SINGLE_PACKET << 2) | (AVRCP_COMMAND_FRAME << 1) | 0;
195*6983e65eSMilanka Ringwald     // Profile IDentifier (PID)
196*6983e65eSMilanka Ringwald     command[pos++] = BLUETOOTH_SERVICE_CLASS_AV_REMOTE_CONTROL >> 8;
197*6983e65eSMilanka Ringwald     command[pos++] = BLUETOOTH_SERVICE_CLASS_AV_REMOTE_CONTROL & 0x00FF;
198*6983e65eSMilanka Ringwald 
199*6983e65eSMilanka Ringwald     // command_type
200*6983e65eSMilanka Ringwald     command[pos++] = connection->command_type;
201*6983e65eSMilanka Ringwald     // subunit_type | subunit ID
202*6983e65eSMilanka Ringwald     command[pos++] = (connection->subunit_type << 3) | connection->subunit_id;
203*6983e65eSMilanka Ringwald     // opcode
204*6983e65eSMilanka Ringwald     command[pos++] = (uint8_t)connection->command_opcode;
205*6983e65eSMilanka Ringwald     // operands
206*6983e65eSMilanka Ringwald     memcpy(command+pos, connection->cmd_operands, connection->cmd_operands_length);
207*6983e65eSMilanka Ringwald     pos += connection->cmd_operands_length;
208*6983e65eSMilanka Ringwald 
209*6983e65eSMilanka Ringwald     return l2cap_send(cid, command, pos);
210*6983e65eSMilanka Ringwald }
211*6983e65eSMilanka Ringwald 
212*6983e65eSMilanka Ringwald static int avrcp_register_notification(avrcp_connection_t * connection, avrcp_notification_event_id_t event_id){
213*6983e65eSMilanka Ringwald     if (connection->notifications_to_deregister & (1 << event_id)) return 0;
214*6983e65eSMilanka Ringwald     if (connection->notifications_enabled & (1 << event_id)) return 0;
215*6983e65eSMilanka Ringwald     if (connection->notifications_to_register & (1 << event_id)) return 0;
216*6983e65eSMilanka Ringwald     connection->notifications_to_register |= (1 << event_id);
217*6983e65eSMilanka Ringwald     avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid);
218*6983e65eSMilanka Ringwald     return 1;
219*6983e65eSMilanka Ringwald }
220*6983e65eSMilanka Ringwald 
221*6983e65eSMilanka Ringwald static void avrcp_prepare_notification(avrcp_connection_t * connection, avrcp_notification_event_id_t event_id){
222*6983e65eSMilanka Ringwald     connection->transaction_label++;
223*6983e65eSMilanka Ringwald     connection->command_opcode = AVRCP_CMD_OPCODE_VENDOR_DEPENDENT;
224*6983e65eSMilanka Ringwald     connection->command_type = AVRCP_CTYPE_NOTIFY;
225*6983e65eSMilanka Ringwald     connection->subunit_type = AVRCP_SUBUNIT_TYPE_PANEL;
226*6983e65eSMilanka Ringwald     connection->subunit_id = AVRCP_SUBUNIT_ID;
227*6983e65eSMilanka Ringwald     int pos = 0;
228*6983e65eSMilanka Ringwald     big_endian_store_24(connection->cmd_operands, pos, BT_SIG_COMPANY_ID);
229*6983e65eSMilanka Ringwald     pos += 3;
230*6983e65eSMilanka Ringwald     connection->cmd_operands[pos++] = AVRCP_PDU_ID_REGISTER_NOTIFICATION;
231*6983e65eSMilanka Ringwald     connection->cmd_operands[pos++] = 0;                     // reserved(upper 6) | packet_type -> 0
232*6983e65eSMilanka Ringwald     big_endian_store_16(connection->cmd_operands, pos, 5);     // parameter length
233*6983e65eSMilanka Ringwald     pos += 2;
234*6983e65eSMilanka Ringwald     connection->cmd_operands[pos++] = event_id;
235*6983e65eSMilanka Ringwald     big_endian_store_32(connection->cmd_operands, pos, 0);
236*6983e65eSMilanka Ringwald     pos += 4;
237*6983e65eSMilanka Ringwald     connection->cmd_operands_length = pos;
238*6983e65eSMilanka Ringwald     // AVRCP_SPEC_V14.pdf 166
239*6983e65eSMilanka Ringwald     // answer page 61
240*6983e65eSMilanka Ringwald }
241*6983e65eSMilanka Ringwald 
242*6983e65eSMilanka Ringwald static uint8_t avrcp_cmd_opcode(uint8_t *packet, uint16_t size){
243*6983e65eSMilanka Ringwald     uint8_t cmd_opcode_index = 5;
244*6983e65eSMilanka Ringwald     if (cmd_opcode_index > size) return AVRCP_CMD_OPCODE_UNDEFINED;
245*6983e65eSMilanka Ringwald     return packet[cmd_opcode_index];
246*6983e65eSMilanka Ringwald }
247*6983e65eSMilanka Ringwald 
248*6983e65eSMilanka Ringwald 
249*6983e65eSMilanka Ringwald static void avrcp_handle_l2cap_data_packet_for_signaling_connection(avrcp_connection_t * connection, uint8_t *packet, uint16_t size){
250*6983e65eSMilanka Ringwald     uint8_t operands[20];
251*6983e65eSMilanka Ringwald     uint8_t opcode;
252*6983e65eSMilanka Ringwald     int     pos = 3;
253*6983e65eSMilanka Ringwald     // uint8_t transport_header = packet[0];
254*6983e65eSMilanka Ringwald     // uint8_t transaction_label = transport_header >> 4;
255*6983e65eSMilanka Ringwald     // uint8_t packet_type = (transport_header & 0x0F) >> 2;
256*6983e65eSMilanka Ringwald     // uint8_t frame_type = (transport_header & 0x03) >> 1;
257*6983e65eSMilanka Ringwald     // uint8_t ipid = transport_header & 0x01;
258*6983e65eSMilanka Ringwald     // uint8_t byte_value = packet[2];
259*6983e65eSMilanka Ringwald     // uint16_t pid = (byte_value << 8) | packet[2];
260*6983e65eSMilanka Ringwald 
261*6983e65eSMilanka Ringwald     avrcp_command_type_t ctype = (avrcp_command_type_t) packet[pos++];
262*6983e65eSMilanka Ringwald     uint8_t byte_value = packet[pos++];
263*6983e65eSMilanka Ringwald     avrcp_subunit_type_t subunit_type = (avrcp_subunit_type_t) (byte_value >> 3);
264*6983e65eSMilanka Ringwald     avrcp_subunit_type_t subunit_id = (avrcp_subunit_type_t)   (byte_value & 0x07);
265*6983e65eSMilanka Ringwald     opcode = packet[pos++];
266*6983e65eSMilanka Ringwald 
267*6983e65eSMilanka Ringwald     // printf("    Transport header 0x%02x (transaction_label %d, packet_type %d, frame_type %d, ipid %d), pid 0x%4x\n",
268*6983e65eSMilanka Ringwald     //     transport_header, transaction_label, packet_type, frame_type, ipid, pid);
269*6983e65eSMilanka Ringwald     // // printf_hexdump(packet+pos, size-pos);
270*6983e65eSMilanka Ringwald 
271*6983e65eSMilanka Ringwald     uint8_t pdu_id;
272*6983e65eSMilanka Ringwald     uint16_t param_length;
273*6983e65eSMilanka Ringwald     switch (avrcp_cmd_opcode(packet,size)){
274*6983e65eSMilanka Ringwald         case AVRCP_CMD_OPCODE_UNIT_INFO:{
275*6983e65eSMilanka Ringwald             if (connection->state != AVCTP_W2_RECEIVE_RESPONSE) return;
276*6983e65eSMilanka Ringwald             connection->state = AVCTP_CONNECTION_OPENED;
277*6983e65eSMilanka Ringwald 
278*6983e65eSMilanka Ringwald             // operands:
279*6983e65eSMilanka Ringwald             memcpy(operands, packet+pos, 5);
280*6983e65eSMilanka Ringwald             uint8_t unit_type = operands[1] >> 3;
281*6983e65eSMilanka Ringwald             uint8_t unit = operands[1] & 0x07;
282*6983e65eSMilanka Ringwald             uint32_t company_id = operands[2] << 16 | operands[3] << 8 | operands[4];
283*6983e65eSMilanka 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",
284*6983e65eSMilanka Ringwald                 ctype, subunit_type, subunit_id, opcode, unit_type, unit, company_id );
285*6983e65eSMilanka Ringwald             break;
286*6983e65eSMilanka Ringwald         }
287*6983e65eSMilanka Ringwald         case AVRCP_CMD_OPCODE_VENDOR_DEPENDENT:
288*6983e65eSMilanka Ringwald             if (size - pos < 7) {
289*6983e65eSMilanka Ringwald                 log_error("avrcp: wrong packet size");
290*6983e65eSMilanka Ringwald                 return;
291*6983e65eSMilanka Ringwald             };
292*6983e65eSMilanka Ringwald             // operands:
293*6983e65eSMilanka Ringwald             memcpy(operands, packet+pos, 7);
294*6983e65eSMilanka Ringwald             pos += 7;
295*6983e65eSMilanka Ringwald             // uint32_t company_id = operands[0] << 16 | operands[1] << 8 | operands[2];
296*6983e65eSMilanka Ringwald             pdu_id = operands[3];
297*6983e65eSMilanka Ringwald 
298*6983e65eSMilanka Ringwald             if (connection->state != AVCTP_W2_RECEIVE_RESPONSE && pdu_id != AVRCP_PDU_ID_REGISTER_NOTIFICATION){
299*6983e65eSMilanka Ringwald                 log_info("AVRCP_CMD_OPCODE_VENDOR_DEPENDENT state %d", connection->state);
300*6983e65eSMilanka Ringwald                 return;
301*6983e65eSMilanka Ringwald             }
302*6983e65eSMilanka Ringwald             connection->state = AVCTP_CONNECTION_OPENED;
303*6983e65eSMilanka Ringwald 
304*6983e65eSMilanka Ringwald 
305*6983e65eSMilanka Ringwald             // uint8_t unit_type = operands[4] >> 3;
306*6983e65eSMilanka Ringwald             // uint8_t unit = operands[4] & 0x07;
307*6983e65eSMilanka Ringwald             param_length = big_endian_read_16(operands, 5);
308*6983e65eSMilanka Ringwald 
309*6983e65eSMilanka 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",
310*6983e65eSMilanka Ringwald             //     ctype, subunit_type, subunit_id, opcode, unit_type, unit, company_id );
311*6983e65eSMilanka Ringwald 
312*6983e65eSMilanka Ringwald             // if (ctype == AVRCP_CTYPE_RESPONSE_INTERIM) return;
313*6983e65eSMilanka Ringwald             log_info("        VENDOR DEPENDENT response: pdu id 0x%02x, param_length %d, status %s", pdu_id, param_length, avrcp_ctype2str(ctype));
314*6983e65eSMilanka Ringwald             switch (pdu_id){
315*6983e65eSMilanka Ringwald                 case AVRCP_PDU_ID_GetCurrentPlayerApplicationSettingValue:{
316*6983e65eSMilanka Ringwald                     uint8_t num_attributes = packet[pos++];
317*6983e65eSMilanka Ringwald                     int i;
318*6983e65eSMilanka Ringwald                     avrcp_repeat_mode_t  repeat_mode =  AVRCP_REPEAT_MODE_INVALID;
319*6983e65eSMilanka Ringwald                     avrcp_shuffle_mode_t shuffle_mode = AVRCP_SHUFFLE_MODE_INVALID;
320*6983e65eSMilanka Ringwald                     for (i = 0; i < num_attributes; i++){
321*6983e65eSMilanka Ringwald                         uint8_t attribute_id    = packet[pos++];
322*6983e65eSMilanka Ringwald                         uint8_t value = packet[pos++];
323*6983e65eSMilanka Ringwald                         switch (attribute_id){
324*6983e65eSMilanka Ringwald                             case 0x02:
325*6983e65eSMilanka Ringwald                                 repeat_mode = (avrcp_repeat_mode_t) value;
326*6983e65eSMilanka Ringwald                                 break;
327*6983e65eSMilanka Ringwald                             case 0x03:
328*6983e65eSMilanka Ringwald                                 shuffle_mode = (avrcp_shuffle_mode_t) value;
329*6983e65eSMilanka Ringwald                                 break;
330*6983e65eSMilanka Ringwald                             default:
331*6983e65eSMilanka Ringwald                                 break;
332*6983e65eSMilanka Ringwald                         }
333*6983e65eSMilanka Ringwald                     }
334*6983e65eSMilanka Ringwald                     avrcp_emit_repeat_and_shuffle_mode(avrcp_controller_context.avrcp_callback, connection->avrcp_cid, ctype, repeat_mode, shuffle_mode);
335*6983e65eSMilanka Ringwald                     break;
336*6983e65eSMilanka Ringwald                 }
337*6983e65eSMilanka Ringwald                 case AVRCP_PDU_ID_SetPlayerApplicationSettingValue:{
338*6983e65eSMilanka Ringwald                     uint8_t event[6];
339*6983e65eSMilanka Ringwald                     int offset = 0;
340*6983e65eSMilanka Ringwald                     event[offset++] = HCI_EVENT_AVRCP_META;
341*6983e65eSMilanka Ringwald                     event[offset++] = sizeof(event) - 2;
342*6983e65eSMilanka Ringwald                     event[offset++] = AVRCP_SUBEVENT_PLAYER_APPLICATION_VALUE_RESPONSE;
343*6983e65eSMilanka Ringwald                     little_endian_store_16(event, offset, connection->avrcp_cid);
344*6983e65eSMilanka Ringwald                     offset += 2;
345*6983e65eSMilanka Ringwald                     event[offset++] = ctype;
346*6983e65eSMilanka Ringwald                     (*avrcp_controller_context.avrcp_callback)(HCI_EVENT_PACKET, 0, event, sizeof(event));
347*6983e65eSMilanka Ringwald                     break;
348*6983e65eSMilanka Ringwald                 }
349*6983e65eSMilanka Ringwald                 case AVRCP_PDU_ID_SET_ABSOLUTE_VOLUME:{
350*6983e65eSMilanka Ringwald                     uint8_t event[7];
351*6983e65eSMilanka Ringwald                     int offset = 0;
352*6983e65eSMilanka Ringwald                     event[offset++] = HCI_EVENT_AVRCP_META;
353*6983e65eSMilanka Ringwald                     event[offset++] = sizeof(event) - 2;
354*6983e65eSMilanka Ringwald                     event[offset++] = AVRCP_SUBEVENT_SET_ABSOLUTE_VOLUME_RESPONSE;
355*6983e65eSMilanka Ringwald                     little_endian_store_16(event, offset, connection->avrcp_cid);
356*6983e65eSMilanka Ringwald                     offset += 2;
357*6983e65eSMilanka Ringwald                     event[offset++] = ctype;
358*6983e65eSMilanka Ringwald                     event[offset++] = packet[pos++];
359*6983e65eSMilanka Ringwald                     (*avrcp_controller_context.avrcp_callback)(HCI_EVENT_PACKET, 0, event, sizeof(event));
360*6983e65eSMilanka Ringwald                     break;
361*6983e65eSMilanka Ringwald                 }
362*6983e65eSMilanka Ringwald                 case AVRCP_PDU_ID_GET_CAPABILITIES:{
363*6983e65eSMilanka Ringwald                     avrcp_capability_id_t capability_id = (avrcp_capability_id_t) packet[pos++];
364*6983e65eSMilanka Ringwald                     uint8_t capability_count = packet[pos++];
365*6983e65eSMilanka Ringwald                     int i;
366*6983e65eSMilanka Ringwald                     switch (capability_id){
367*6983e65eSMilanka Ringwald                         case AVRCP_CAPABILITY_ID_COMPANY:
368*6983e65eSMilanka Ringwald                             // log_info("Supported companies %d: ", capability_count);
369*6983e65eSMilanka Ringwald                             for (i = 0; i < capability_count; i++){
370*6983e65eSMilanka Ringwald                                 uint32_t company_id = big_endian_read_24(packet, pos);
371*6983e65eSMilanka Ringwald                                 pos += 3;
372*6983e65eSMilanka Ringwald                                 log_info("  0x%06x, ", company_id);
373*6983e65eSMilanka Ringwald                             }
374*6983e65eSMilanka Ringwald                             break;
375*6983e65eSMilanka Ringwald                         case AVRCP_CAPABILITY_ID_EVENT:
376*6983e65eSMilanka Ringwald                             // log_info("Supported events %d: ", capability_count);
377*6983e65eSMilanka Ringwald                             for (i = 0; i < capability_count; i++){
378*6983e65eSMilanka Ringwald                                 uint8_t event_id = packet[pos++];
379*6983e65eSMilanka Ringwald                                 log_info("  0x%02x %s", event_id, avrcp_event2str(event_id));
380*6983e65eSMilanka Ringwald                             }
381*6983e65eSMilanka Ringwald                             break;
382*6983e65eSMilanka Ringwald                     }
383*6983e65eSMilanka Ringwald                     break;
384*6983e65eSMilanka Ringwald                 }
385*6983e65eSMilanka Ringwald                 case AVRCP_PDU_ID_GET_PLAY_STATUS:{
386*6983e65eSMilanka Ringwald                     uint32_t song_length = big_endian_read_32(packet, pos);
387*6983e65eSMilanka Ringwald                     pos += 4;
388*6983e65eSMilanka Ringwald                     uint32_t song_position = big_endian_read_32(packet, pos);
389*6983e65eSMilanka Ringwald                     pos += 4;
390*6983e65eSMilanka Ringwald                     uint8_t play_status = packet[pos];
391*6983e65eSMilanka Ringwald                     // log_info("        GET_PLAY_STATUS length 0x%04X, position 0x%04X, status %s", song_length, song_position, avrcp_play_status2str(play_status));
392*6983e65eSMilanka Ringwald 
393*6983e65eSMilanka Ringwald                     uint8_t event[15];
394*6983e65eSMilanka Ringwald                     int offset = 0;
395*6983e65eSMilanka Ringwald                     event[offset++] = HCI_EVENT_AVRCP_META;
396*6983e65eSMilanka Ringwald                     event[offset++] = sizeof(event) - 2;
397*6983e65eSMilanka Ringwald                     event[offset++] = AVRCP_SUBEVENT_PLAY_STATUS;
398*6983e65eSMilanka Ringwald                     little_endian_store_16(event, offset, connection->avrcp_cid);
399*6983e65eSMilanka Ringwald                     offset += 2;
400*6983e65eSMilanka Ringwald                     event[offset++] = ctype;
401*6983e65eSMilanka Ringwald                     little_endian_store_32(event, offset, song_length);
402*6983e65eSMilanka Ringwald                     offset += 4;
403*6983e65eSMilanka Ringwald                     little_endian_store_32(event, offset, song_position);
404*6983e65eSMilanka Ringwald                     offset += 4;
405*6983e65eSMilanka Ringwald                     event[offset++] = play_status;
406*6983e65eSMilanka Ringwald                     (*avrcp_controller_context.avrcp_callback)(HCI_EVENT_PACKET, 0, event, sizeof(event));
407*6983e65eSMilanka Ringwald                     break;
408*6983e65eSMilanka Ringwald                 }
409*6983e65eSMilanka Ringwald                 case AVRCP_PDU_ID_REGISTER_NOTIFICATION:{
410*6983e65eSMilanka Ringwald                     avrcp_notification_event_id_t  event_id = (avrcp_notification_event_id_t) packet[pos++];
411*6983e65eSMilanka Ringwald                     uint16_t event_mask = (1 << event_id);
412*6983e65eSMilanka Ringwald                     uint16_t reset_event_mask = ~event_mask;
413*6983e65eSMilanka Ringwald                     switch (ctype){
414*6983e65eSMilanka Ringwald                         case AVRCP_CTYPE_RESPONSE_INTERIM:
415*6983e65eSMilanka Ringwald                             // register as enabled
416*6983e65eSMilanka Ringwald                             connection->notifications_enabled |= event_mask;
417*6983e65eSMilanka Ringwald                             // printf("INTERIM notifications_enabled 0x%2x, notifications_to_register 0x%2x\n", connection->notifications_enabled,  connection->notifications_to_register);
418*6983e65eSMilanka Ringwald                             break;
419*6983e65eSMilanka Ringwald                         case AVRCP_CTYPE_RESPONSE_CHANGED_STABLE:
420*6983e65eSMilanka Ringwald                             // received change, event is considered deregistered
421*6983e65eSMilanka Ringwald                             // we are re-enabling it automatically, if it is not
422*6983e65eSMilanka Ringwald                             // explicitly disabled
423*6983e65eSMilanka Ringwald                             connection->notifications_enabled &= reset_event_mask;
424*6983e65eSMilanka Ringwald                             if (! (connection->notifications_to_deregister & event_mask)){
425*6983e65eSMilanka Ringwald                                 avrcp_register_notification(connection, event_id);
426*6983e65eSMilanka Ringwald                                 // printf("CHANGED_STABLE notifications_enabled 0x%2x, notifications_to_register 0x%2x\n", connection->notifications_enabled,  connection->notifications_to_register);
427*6983e65eSMilanka Ringwald                             } else {
428*6983e65eSMilanka Ringwald                                 connection->notifications_to_deregister &= reset_event_mask;
429*6983e65eSMilanka Ringwald                             }
430*6983e65eSMilanka Ringwald                             break;
431*6983e65eSMilanka Ringwald                         default:
432*6983e65eSMilanka Ringwald                             connection->notifications_to_register &= reset_event_mask;
433*6983e65eSMilanka Ringwald                             connection->notifications_enabled &= reset_event_mask;
434*6983e65eSMilanka Ringwald                             connection->notifications_to_deregister &= reset_event_mask;
435*6983e65eSMilanka Ringwald                             break;
436*6983e65eSMilanka Ringwald                     }
437*6983e65eSMilanka Ringwald 
438*6983e65eSMilanka Ringwald                     switch (event_id){
439*6983e65eSMilanka Ringwald                         case AVRCP_NOTIFICATION_EVENT_PLAYBACK_STATUS_CHANGED:{
440*6983e65eSMilanka Ringwald                             uint8_t event[7];
441*6983e65eSMilanka Ringwald                             int offset = 0;
442*6983e65eSMilanka Ringwald                             event[offset++] = HCI_EVENT_AVRCP_META;
443*6983e65eSMilanka Ringwald                             event[offset++] = sizeof(event) - 2;
444*6983e65eSMilanka Ringwald                             event[offset++] = AVRCP_SUBEVENT_NOTIFICATION_PLAYBACK_STATUS_CHANGED;
445*6983e65eSMilanka Ringwald                             little_endian_store_16(event, offset, connection->avrcp_cid);
446*6983e65eSMilanka Ringwald                             offset += 2;
447*6983e65eSMilanka Ringwald                             event[offset++] = ctype;
448*6983e65eSMilanka Ringwald                             event[offset++] = packet[pos];
449*6983e65eSMilanka Ringwald                             (*avrcp_controller_context.avrcp_callback)(HCI_EVENT_PACKET, 0, event, sizeof(event));
450*6983e65eSMilanka Ringwald                             break;
451*6983e65eSMilanka Ringwald                         }
452*6983e65eSMilanka Ringwald                         case AVRCP_NOTIFICATION_EVENT_TRACK_CHANGED:{
453*6983e65eSMilanka Ringwald                             uint8_t event[6];
454*6983e65eSMilanka Ringwald                             int offset = 0;
455*6983e65eSMilanka Ringwald                             event[offset++] = HCI_EVENT_AVRCP_META;
456*6983e65eSMilanka Ringwald                             event[offset++] = sizeof(event) - 2;
457*6983e65eSMilanka Ringwald                             event[offset++] = AVRCP_SUBEVENT_NOTIFICATION_TRACK_CHANGED;
458*6983e65eSMilanka Ringwald                             little_endian_store_16(event, offset, connection->avrcp_cid);
459*6983e65eSMilanka Ringwald                             offset += 2;
460*6983e65eSMilanka Ringwald                             event[offset++] = ctype;
461*6983e65eSMilanka Ringwald                             (*avrcp_controller_context.avrcp_callback)(HCI_EVENT_PACKET, 0, event, sizeof(event));
462*6983e65eSMilanka Ringwald                             break;
463*6983e65eSMilanka Ringwald                         }
464*6983e65eSMilanka Ringwald                         case AVRCP_NOTIFICATION_EVENT_NOW_PLAYING_CONTENT_CHANGED:{
465*6983e65eSMilanka Ringwald                             uint8_t event[6];
466*6983e65eSMilanka Ringwald                             int offset = 0;
467*6983e65eSMilanka Ringwald                             event[offset++] = HCI_EVENT_AVRCP_META;
468*6983e65eSMilanka Ringwald                             event[offset++] = sizeof(event) - 2;
469*6983e65eSMilanka Ringwald                             event[offset++] = AVRCP_SUBEVENT_NOTIFICATION_NOW_PLAYING_CONTENT_CHANGED;
470*6983e65eSMilanka Ringwald                             little_endian_store_16(event, offset, connection->avrcp_cid);
471*6983e65eSMilanka Ringwald                             offset += 2;
472*6983e65eSMilanka Ringwald                             event[offset++] = ctype;
473*6983e65eSMilanka Ringwald                             (*avrcp_controller_context.avrcp_callback)(HCI_EVENT_PACKET, 0, event, sizeof(event));
474*6983e65eSMilanka Ringwald                             break;
475*6983e65eSMilanka Ringwald                         }
476*6983e65eSMilanka Ringwald                         case AVRCP_NOTIFICATION_EVENT_AVAILABLE_PLAYERS_CHANGED:{
477*6983e65eSMilanka Ringwald                             uint8_t event[6];
478*6983e65eSMilanka Ringwald                             int offset = 0;
479*6983e65eSMilanka Ringwald                             event[offset++] = HCI_EVENT_AVRCP_META;
480*6983e65eSMilanka Ringwald                             event[offset++] = sizeof(event) - 2;
481*6983e65eSMilanka Ringwald                             event[offset++] = AVRCP_SUBEVENT_NOTIFICATION_AVAILABLE_PLAYERS_CHANGED;
482*6983e65eSMilanka Ringwald                             little_endian_store_16(event, offset, connection->avrcp_cid);
483*6983e65eSMilanka Ringwald                             offset += 2;
484*6983e65eSMilanka Ringwald                             event[offset++] = ctype;
485*6983e65eSMilanka Ringwald                             (*avrcp_controller_context.avrcp_callback)(HCI_EVENT_PACKET, 0, event, sizeof(event));
486*6983e65eSMilanka Ringwald                             break;
487*6983e65eSMilanka Ringwald                         }
488*6983e65eSMilanka Ringwald                         case AVRCP_NOTIFICATION_EVENT_VOLUME_CHANGED:{
489*6983e65eSMilanka Ringwald                             uint8_t event[7];
490*6983e65eSMilanka Ringwald                             int offset = 0;
491*6983e65eSMilanka Ringwald                             event[offset++] = HCI_EVENT_AVRCP_META;
492*6983e65eSMilanka Ringwald                             event[offset++] = sizeof(event) - 2;
493*6983e65eSMilanka Ringwald                             event[offset++] = AVRCP_SUBEVENT_NOTIFICATION_VOLUME_CHANGED;
494*6983e65eSMilanka Ringwald                             little_endian_store_16(event, offset, connection->avrcp_cid);
495*6983e65eSMilanka Ringwald                             offset += 2;
496*6983e65eSMilanka Ringwald                             event[offset++] = ctype;
497*6983e65eSMilanka Ringwald                             event[offset++] = packet[pos++] & 0x7F;
498*6983e65eSMilanka Ringwald                             (*avrcp_controller_context.avrcp_callback)(HCI_EVENT_PACKET, 0, event, sizeof(event));
499*6983e65eSMilanka Ringwald                             break;
500*6983e65eSMilanka Ringwald                         }
501*6983e65eSMilanka Ringwald                         // case AVRCP_NOTIFICATION_EVENT_PLAYER_APPLICATION_SETTING_CHANGED:{
502*6983e65eSMilanka Ringwald                         //     uint8_t num_PlayerApplicationSettingAttributes = packet[pos++];
503*6983e65eSMilanka Ringwald                         //     int i;
504*6983e65eSMilanka Ringwald                         //     for (i = 0; i < num_PlayerApplicationSettingAttributes; i++){
505*6983e65eSMilanka Ringwald                         //         uint8_t PlayerApplicationSetting_AttributeID = packet[pos++];
506*6983e65eSMilanka Ringwald                         //         uint8_t PlayerApplicationSettingValueID = packet[pos++];
507*6983e65eSMilanka Ringwald                         //     }
508*6983e65eSMilanka Ringwald                         //     break;
509*6983e65eSMilanka Ringwald                         // }
510*6983e65eSMilanka Ringwald                         // case AVRCP_NOTIFICATION_EVENT_ADDRESSED_PLAYER_CHANGED:
511*6983e65eSMilanka Ringwald                         //     uint16_t player_id = big_endian_read_16(packet, pos);
512*6983e65eSMilanka Ringwald                         //     pos += 2;
513*6983e65eSMilanka Ringwald                         //     uint16_t uid_counter = big_endian_read_16(packet, pos);
514*6983e65eSMilanka Ringwald                         //     pos += 2;
515*6983e65eSMilanka Ringwald                         //     break;
516*6983e65eSMilanka Ringwald                         // case AVRCP_NOTIFICATION_EVENT_UIDS_CHANGED:
517*6983e65eSMilanka Ringwald                         //     uint16_t uid_counter = big_endian_read_16(packet, pos);
518*6983e65eSMilanka Ringwald                         //     pos += 2;
519*6983e65eSMilanka Ringwald                         //     break;
520*6983e65eSMilanka Ringwald                         default:
521*6983e65eSMilanka Ringwald                             log_info("avrcp: not implemented");
522*6983e65eSMilanka Ringwald                             break;
523*6983e65eSMilanka Ringwald                     }
524*6983e65eSMilanka Ringwald                     if (connection->notifications_to_register != 0){
525*6983e65eSMilanka Ringwald                         avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid);
526*6983e65eSMilanka Ringwald                     }
527*6983e65eSMilanka Ringwald                     break;
528*6983e65eSMilanka Ringwald                 }
529*6983e65eSMilanka Ringwald 
530*6983e65eSMilanka Ringwald                 case AVRCP_PDU_ID_GET_ELEMENT_ATTRIBUTES:{
531*6983e65eSMilanka Ringwald                     uint8_t num_attributes = packet[pos++];
532*6983e65eSMilanka Ringwald                     int i;
533*6983e65eSMilanka Ringwald                     struct item {
534*6983e65eSMilanka Ringwald                         uint16_t len;
535*6983e65eSMilanka Ringwald                         uint8_t  * value;
536*6983e65eSMilanka Ringwald                     } items[AVRCP_MEDIA_ATTR_COUNT];
537*6983e65eSMilanka Ringwald                     memset(items, 0, sizeof(items));
538*6983e65eSMilanka Ringwald 
539*6983e65eSMilanka Ringwald                     uint16_t string_attributes_len = 0;
540*6983e65eSMilanka Ringwald                     uint8_t  num_string_attributes = 0;
541*6983e65eSMilanka Ringwald                     uint16_t total_event_payload_for_string_attributes = HCI_EVENT_PAYLOAD_SIZE-2;
542*6983e65eSMilanka Ringwald                     uint16_t max_string_attribute_value_len = 0;
543*6983e65eSMilanka Ringwald                     if (ctype == AVRCP_CTYPE_RESPONSE_IMPLEMENTED_STABLE || ctype == AVRCP_CTYPE_RESPONSE_CHANGED_STABLE){
544*6983e65eSMilanka Ringwald                         for (i = 0; i < num_attributes; i++){
545*6983e65eSMilanka Ringwald                             avrcp_media_attribute_id_t attr_id = (avrcp_media_attribute_id_t) big_endian_read_32(packet, pos);
546*6983e65eSMilanka Ringwald                             pos += 4;
547*6983e65eSMilanka Ringwald                             // uint16_t character_set = big_endian_read_16(packet, pos);
548*6983e65eSMilanka Ringwald                             pos += 2;
549*6983e65eSMilanka Ringwald                             uint16_t attr_value_length = big_endian_read_16(packet, pos);
550*6983e65eSMilanka Ringwald                             pos += 2;
551*6983e65eSMilanka Ringwald 
552*6983e65eSMilanka Ringwald                             // debug - to remove later
553*6983e65eSMilanka Ringwald                             uint8_t  value[100];
554*6983e65eSMilanka Ringwald                             uint16_t value_len = sizeof(value) <= attr_value_length? sizeof(value) - 1 : attr_value_length;
555*6983e65eSMilanka Ringwald                             memcpy(value, packet+pos, value_len);
556*6983e65eSMilanka Ringwald                             value[value_len] = 0;
557*6983e65eSMilanka Ringwald                             // printf("Now Playing Info %s: %s \n", attribute2str(attr_id), value);
558*6983e65eSMilanka Ringwald                             // end debug
559*6983e65eSMilanka Ringwald 
560*6983e65eSMilanka Ringwald                             if ((attr_id >= 1) || (attr_id <= AVRCP_MEDIA_ATTR_COUNT)) {
561*6983e65eSMilanka Ringwald                                 items[attr_id-1].len = attr_value_length;
562*6983e65eSMilanka Ringwald                                 items[attr_id-1].value = &packet[pos];
563*6983e65eSMilanka Ringwald                                 switch (attr_id){
564*6983e65eSMilanka Ringwald                                     case AVRCP_MEDIA_ATTR_TITLE:
565*6983e65eSMilanka Ringwald                                     case AVRCP_MEDIA_ATTR_ARTIST:
566*6983e65eSMilanka Ringwald                                     case AVRCP_MEDIA_ATTR_ALBUM:
567*6983e65eSMilanka Ringwald                                     case AVRCP_MEDIA_ATTR_GENRE:
568*6983e65eSMilanka Ringwald                                         num_string_attributes++;
569*6983e65eSMilanka Ringwald                                         string_attributes_len += attr_value_length;
570*6983e65eSMilanka Ringwald                                         if (max_string_attribute_value_len < attr_value_length){
571*6983e65eSMilanka Ringwald                                             max_string_attribute_value_len = attr_value_length;
572*6983e65eSMilanka Ringwald                                         }
573*6983e65eSMilanka Ringwald                                         break;
574*6983e65eSMilanka Ringwald                                     default:
575*6983e65eSMilanka Ringwald                                         break;
576*6983e65eSMilanka Ringwald                                 }
577*6983e65eSMilanka Ringwald                             }
578*6983e65eSMilanka Ringwald                             pos += attr_value_length;
579*6983e65eSMilanka Ringwald                         }
580*6983e65eSMilanka Ringwald                     }
581*6983e65eSMilanka Ringwald 
582*6983e65eSMilanka Ringwald                     // subtract space for fixed fields
583*6983e65eSMilanka Ringwald                     total_event_payload_for_string_attributes -= 14 + 4;    // 4 for '\0'
584*6983e65eSMilanka Ringwald 
585*6983e65eSMilanka Ringwald                     // @TODO optimize space by repeatedly decreasing max_string_attribute_value_len until it fits into buffer instead of crude divion
586*6983e65eSMilanka 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;
587*6983e65eSMilanka 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);
588*6983e65eSMilanka Ringwald 
589*6983e65eSMilanka Ringwald                     const uint8_t attribute_order[] = {
590*6983e65eSMilanka Ringwald                         AVRCP_MEDIA_ATTR_TRACK,
591*6983e65eSMilanka Ringwald                         AVRCP_MEDIA_ATTR_TOTAL_TRACKS,
592*6983e65eSMilanka Ringwald                         AVRCP_MEDIA_ATTR_SONG_LENGTH,
593*6983e65eSMilanka Ringwald                         AVRCP_MEDIA_ATTR_TITLE,
594*6983e65eSMilanka Ringwald                         AVRCP_MEDIA_ATTR_ARTIST,
595*6983e65eSMilanka Ringwald                         AVRCP_MEDIA_ATTR_ALBUM,
596*6983e65eSMilanka Ringwald                         AVRCP_MEDIA_ATTR_GENRE
597*6983e65eSMilanka Ringwald                     };
598*6983e65eSMilanka Ringwald 
599*6983e65eSMilanka Ringwald                     uint8_t event[HCI_EVENT_BUFFER_SIZE];
600*6983e65eSMilanka Ringwald                     event[0] = HCI_EVENT_AVRCP_META;
601*6983e65eSMilanka Ringwald                     pos = 2;
602*6983e65eSMilanka Ringwald                     event[pos++] = AVRCP_SUBEVENT_NOW_PLAYING_INFO;
603*6983e65eSMilanka Ringwald                     little_endian_store_16(event, pos, connection->avrcp_cid);
604*6983e65eSMilanka Ringwald                     pos += 2;
605*6983e65eSMilanka Ringwald                     event[pos++] = ctype;
606*6983e65eSMilanka Ringwald                     for (i = 0; i < sizeof(attribute_order); i++){
607*6983e65eSMilanka Ringwald                         avrcp_media_attribute_id_t attr_id = (avrcp_media_attribute_id_t) attribute_order[i];
608*6983e65eSMilanka Ringwald                         uint16_t value_len = 0;
609*6983e65eSMilanka Ringwald                         switch (attr_id){
610*6983e65eSMilanka Ringwald                             case AVRCP_MEDIA_ATTR_TITLE:
611*6983e65eSMilanka Ringwald                             case AVRCP_MEDIA_ATTR_ARTIST:
612*6983e65eSMilanka Ringwald                             case AVRCP_MEDIA_ATTR_ALBUM:
613*6983e65eSMilanka Ringwald                             case AVRCP_MEDIA_ATTR_GENRE:
614*6983e65eSMilanka Ringwald                                 if (items[attr_id-1].value){
615*6983e65eSMilanka Ringwald                                     value_len = items[attr_id-1].len <= max_value_len ? items[attr_id-1].len : max_value_len;
616*6983e65eSMilanka Ringwald                                 }
617*6983e65eSMilanka Ringwald                                 event[pos++] = value_len + 1;
618*6983e65eSMilanka Ringwald                                 if (value_len){
619*6983e65eSMilanka Ringwald                                     memcpy(event+pos, items[attr_id-1].value, value_len);
620*6983e65eSMilanka Ringwald                                     pos += value_len;
621*6983e65eSMilanka Ringwald                                 }
622*6983e65eSMilanka Ringwald                                 event[pos++] = 0;
623*6983e65eSMilanka Ringwald                                 break;
624*6983e65eSMilanka Ringwald                             case AVRCP_MEDIA_ATTR_SONG_LENGTH:
625*6983e65eSMilanka Ringwald                                 if (items[attr_id-1].value){
626*6983e65eSMilanka Ringwald                                     little_endian_store_32(event, pos, btstack_atoi((char *)items[attr_id-1].value));
627*6983e65eSMilanka Ringwald                                 } else {
628*6983e65eSMilanka Ringwald                                     little_endian_store_32(event, pos, 0);
629*6983e65eSMilanka Ringwald                                 }
630*6983e65eSMilanka Ringwald                                 pos += 4;
631*6983e65eSMilanka Ringwald                                 break;
632*6983e65eSMilanka Ringwald                             case AVRCP_MEDIA_ATTR_TRACK:
633*6983e65eSMilanka Ringwald                             case AVRCP_MEDIA_ATTR_TOTAL_TRACKS:
634*6983e65eSMilanka Ringwald                                 if (items[attr_id-1].value){
635*6983e65eSMilanka Ringwald                                     event[pos++] = btstack_atoi((char *)items[attr_id-1].value);
636*6983e65eSMilanka Ringwald                                 } else {
637*6983e65eSMilanka Ringwald                                     event[pos++] = 0;
638*6983e65eSMilanka Ringwald                                 }
639*6983e65eSMilanka Ringwald                                 break;
640*6983e65eSMilanka Ringwald                         }
641*6983e65eSMilanka Ringwald                     }
642*6983e65eSMilanka Ringwald                     event[1] = pos - 2;
643*6983e65eSMilanka Ringwald                     // printf_hexdump(event, pos);
644*6983e65eSMilanka Ringwald                     (*avrcp_controller_context.avrcp_callback)(HCI_EVENT_PACKET, 0, event, pos);
645*6983e65eSMilanka Ringwald                     break;
646*6983e65eSMilanka Ringwald                 }
647*6983e65eSMilanka Ringwald                 default:
648*6983e65eSMilanka Ringwald                     break;
649*6983e65eSMilanka Ringwald             }
650*6983e65eSMilanka Ringwald             break;
651*6983e65eSMilanka Ringwald         case AVRCP_CMD_OPCODE_PASS_THROUGH:{
652*6983e65eSMilanka Ringwald             // 0x80 | connection->cmd_operands[0]
653*6983e65eSMilanka Ringwald             uint8_t operation_id = packet[pos++];
654*6983e65eSMilanka Ringwald             switch (connection->state){
655*6983e65eSMilanka Ringwald                 case AVCTP_W2_RECEIVE_PRESS_RESPONSE:
656*6983e65eSMilanka Ringwald                     if (connection->continuous_fast_forward_cmd){
657*6983e65eSMilanka Ringwald                         connection->state = AVCTP_W4_STOP;
658*6983e65eSMilanka Ringwald                     } else {
659*6983e65eSMilanka Ringwald                         connection->state = AVCTP_W2_SEND_RELEASE_COMMAND;
660*6983e65eSMilanka Ringwald                     }
661*6983e65eSMilanka Ringwald                     break;
662*6983e65eSMilanka Ringwald                 case AVCTP_W2_RECEIVE_RESPONSE:
663*6983e65eSMilanka Ringwald                     connection->state = AVCTP_CONNECTION_OPENED;
664*6983e65eSMilanka Ringwald                     break;
665*6983e65eSMilanka Ringwald                 default:
666*6983e65eSMilanka Ringwald                     // check for notifications? move state transition down
667*6983e65eSMilanka Ringwald                     // log_info("AVRCP_CMD_OPCODE_PASS_THROUGH state %d\n", connection->state);
668*6983e65eSMilanka Ringwald                     break;
669*6983e65eSMilanka Ringwald             }
670*6983e65eSMilanka Ringwald             if (connection->state == AVCTP_W4_STOP){
671*6983e65eSMilanka Ringwald                 avrcp_emit_operation_status(avrcp_controller_context.avrcp_callback, AVRCP_SUBEVENT_OPERATION_START, connection->avrcp_cid, ctype, operation_id);
672*6983e65eSMilanka Ringwald             }
673*6983e65eSMilanka Ringwald             if (connection->state == AVCTP_CONNECTION_OPENED) {
674*6983e65eSMilanka Ringwald                 // RELEASE response
675*6983e65eSMilanka Ringwald                 operation_id = operation_id & 0x7F;
676*6983e65eSMilanka Ringwald                 avrcp_emit_operation_status(avrcp_controller_context.avrcp_callback, AVRCP_SUBEVENT_OPERATION_COMPLETE, connection->avrcp_cid, ctype, operation_id);
677*6983e65eSMilanka Ringwald             }
678*6983e65eSMilanka Ringwald             if (connection->state == AVCTP_W2_SEND_RELEASE_COMMAND){
679*6983e65eSMilanka Ringwald                 // PRESS response
680*6983e65eSMilanka Ringwald                 request_pass_through_release_control_cmd(connection);
681*6983e65eSMilanka Ringwald             }
682*6983e65eSMilanka Ringwald             break;
683*6983e65eSMilanka Ringwald         }
684*6983e65eSMilanka Ringwald         default:
685*6983e65eSMilanka Ringwald             break;
686*6983e65eSMilanka Ringwald     }
687*6983e65eSMilanka Ringwald }
688*6983e65eSMilanka Ringwald 
689*6983e65eSMilanka Ringwald static void avrcp_controller_handle_can_send_now(avrcp_connection_t * connection){
690*6983e65eSMilanka Ringwald     int i;
691*6983e65eSMilanka Ringwald     switch (connection->state){
692*6983e65eSMilanka Ringwald         case AVCTP_W2_SEND_PRESS_COMMAND:
693*6983e65eSMilanka Ringwald             connection->state = AVCTP_W2_RECEIVE_PRESS_RESPONSE;
694*6983e65eSMilanka Ringwald             avrcp_send_cmd(connection->l2cap_signaling_cid, connection);
695*6983e65eSMilanka Ringwald             break;
696*6983e65eSMilanka Ringwald         case AVCTP_W2_SEND_COMMAND:
697*6983e65eSMilanka Ringwald         case AVCTP_W2_SEND_RELEASE_COMMAND:
698*6983e65eSMilanka Ringwald             connection->state = AVCTP_W2_RECEIVE_RESPONSE;
699*6983e65eSMilanka Ringwald             avrcp_send_cmd(connection->l2cap_signaling_cid, connection);
700*6983e65eSMilanka Ringwald             break;
701*6983e65eSMilanka Ringwald         case AVCTP_CONNECTION_OPENED:
702*6983e65eSMilanka Ringwald             if (connection->notifications_to_register != 0){
703*6983e65eSMilanka Ringwald                 for (i = 1; i <= AVRCP_NOTIFICATION_EVENT_VOLUME_CHANGED; i++){
704*6983e65eSMilanka Ringwald                     if (connection->notifications_to_register & (1<<i)){
705*6983e65eSMilanka Ringwald                         connection->notifications_to_register &= ~ (1 << i);
706*6983e65eSMilanka Ringwald                         avrcp_prepare_notification(connection, (avrcp_notification_event_id_t) i);
707*6983e65eSMilanka Ringwald                         connection->state = AVCTP_W2_RECEIVE_RESPONSE;
708*6983e65eSMilanka Ringwald                         avrcp_send_cmd(connection->l2cap_signaling_cid, connection);
709*6983e65eSMilanka Ringwald                         return;
710*6983e65eSMilanka Ringwald                     }
711*6983e65eSMilanka Ringwald                 }
712*6983e65eSMilanka Ringwald             }
713*6983e65eSMilanka Ringwald             return;
714*6983e65eSMilanka Ringwald         default:
715*6983e65eSMilanka Ringwald             return;
716*6983e65eSMilanka Ringwald     }
717*6983e65eSMilanka Ringwald }
718*6983e65eSMilanka Ringwald 
719*6983e65eSMilanka Ringwald static void avrcp_controller_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
720*6983e65eSMilanka Ringwald     avrcp_connection_t * connection;
721*6983e65eSMilanka Ringwald 
722*6983e65eSMilanka Ringwald     switch (packet_type) {
723*6983e65eSMilanka Ringwald         case L2CAP_DATA_PACKET:
724*6983e65eSMilanka Ringwald             connection = get_avrcp_connection_for_l2cap_signaling_cid(channel, &avrcp_controller_context);
725*6983e65eSMilanka Ringwald             if (!connection) break;
726*6983e65eSMilanka Ringwald             avrcp_handle_l2cap_data_packet_for_signaling_connection(connection, packet, size);
727*6983e65eSMilanka Ringwald             break;
728*6983e65eSMilanka Ringwald         case HCI_EVENT_PACKET:
729*6983e65eSMilanka Ringwald             switch (hci_event_packet_get_type(packet)){
730*6983e65eSMilanka Ringwald                 case L2CAP_EVENT_CAN_SEND_NOW:
731*6983e65eSMilanka Ringwald                     connection = get_avrcp_connection_for_l2cap_signaling_cid(channel, &avrcp_controller_context);
732*6983e65eSMilanka Ringwald                     if (!connection) break;
733*6983e65eSMilanka Ringwald                     avrcp_controller_handle_can_send_now(connection);
734*6983e65eSMilanka Ringwald                     break;
735*6983e65eSMilanka Ringwald             default:
736*6983e65eSMilanka Ringwald                 avrcp_packet_handler(packet_type, channel, packet, size, &avrcp_controller_context);
737*6983e65eSMilanka Ringwald                 break;
738*6983e65eSMilanka Ringwald         }
739*6983e65eSMilanka Ringwald         default:
740*6983e65eSMilanka Ringwald             break;
741*6983e65eSMilanka Ringwald     }
742*6983e65eSMilanka Ringwald }
743*6983e65eSMilanka Ringwald 
744*6983e65eSMilanka Ringwald void avrcp_controller_init(void){
745*6983e65eSMilanka Ringwald     avrcp_controller_context.role = AVRCP_CONTROLLER;
746*6983e65eSMilanka Ringwald     avrcp_controller_context.connections = NULL;
747*6983e65eSMilanka Ringwald     avrcp_controller_context.packet_handler = avrcp_controller_packet_handler;
748*6983e65eSMilanka Ringwald     l2cap_register_service(&avrcp_controller_packet_handler, BLUETOOTH_PROTOCOL_AVCTP, 0xffff, LEVEL_0);
749*6983e65eSMilanka Ringwald }
750*6983e65eSMilanka Ringwald 
751*6983e65eSMilanka Ringwald void avrcp_controller_register_packet_handler(btstack_packet_handler_t callback){
752*6983e65eSMilanka Ringwald     if (callback == NULL){
753*6983e65eSMilanka Ringwald         log_error("avrcp_register_packet_handler called with NULL callback");
754*6983e65eSMilanka Ringwald         return;
755*6983e65eSMilanka Ringwald     }
756*6983e65eSMilanka Ringwald     avrcp_controller_context.avrcp_callback = callback;
757*6983e65eSMilanka Ringwald }
758*6983e65eSMilanka Ringwald 
759*6983e65eSMilanka Ringwald uint8_t avrcp_controller_connect(bd_addr_t bd_addr, uint16_t * avrcp_cid){
760*6983e65eSMilanka Ringwald     return avrcp_connect(bd_addr, &avrcp_controller_context, avrcp_cid);
761*6983e65eSMilanka Ringwald }
762*6983e65eSMilanka Ringwald 
763*6983e65eSMilanka Ringwald uint8_t avrcp_controller_unit_info(uint16_t avrcp_cid){
764*6983e65eSMilanka Ringwald     avrcp_connection_t * connection = get_avrcp_connection_for_avrcp_cid(avrcp_cid, &avrcp_controller_context);
765*6983e65eSMilanka Ringwald     if (!connection){
766*6983e65eSMilanka Ringwald         log_error("avrcp_unit_info: could not find a connection.");
767*6983e65eSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
768*6983e65eSMilanka Ringwald     }
769*6983e65eSMilanka Ringwald     if (connection->state != AVCTP_CONNECTION_OPENED) return ERROR_CODE_COMMAND_DISALLOWED;
770*6983e65eSMilanka Ringwald     connection->state = AVCTP_W2_SEND_COMMAND;
771*6983e65eSMilanka Ringwald 
772*6983e65eSMilanka Ringwald     connection->transaction_label++;
773*6983e65eSMilanka Ringwald     connection->command_opcode = AVRCP_CMD_OPCODE_UNIT_INFO;
774*6983e65eSMilanka Ringwald     connection->command_type = AVRCP_CTYPE_STATUS;
775*6983e65eSMilanka Ringwald     connection->subunit_type = AVRCP_SUBUNIT_TYPE_UNIT; //vendor unique
776*6983e65eSMilanka Ringwald     connection->subunit_id =   AVRCP_SUBUNIT_ID_IGNORE;
777*6983e65eSMilanka Ringwald     memset(connection->cmd_operands, 0xFF, connection->cmd_operands_length);
778*6983e65eSMilanka Ringwald     connection->cmd_operands_length = 5;
779*6983e65eSMilanka Ringwald     avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid);
780*6983e65eSMilanka Ringwald     return ERROR_CODE_SUCCESS;
781*6983e65eSMilanka Ringwald }
782*6983e65eSMilanka Ringwald 
783*6983e65eSMilanka Ringwald static uint8_t avrcp_controller_get_capabilities(uint16_t avrcp_cid, uint8_t capability_id){
784*6983e65eSMilanka Ringwald     avrcp_connection_t * connection = get_avrcp_connection_for_avrcp_cid(avrcp_cid, &avrcp_controller_context);
785*6983e65eSMilanka Ringwald     if (!connection){
786*6983e65eSMilanka Ringwald         log_error("avrcp_get_capabilities: could not find a connection.");
787*6983e65eSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
788*6983e65eSMilanka Ringwald     }
789*6983e65eSMilanka Ringwald     if (connection->state != AVCTP_CONNECTION_OPENED) return ERROR_CODE_COMMAND_DISALLOWED;
790*6983e65eSMilanka Ringwald     connection->state = AVCTP_W2_SEND_COMMAND;
791*6983e65eSMilanka Ringwald 
792*6983e65eSMilanka Ringwald     connection->transaction_label++;
793*6983e65eSMilanka Ringwald     connection->command_opcode = AVRCP_CMD_OPCODE_VENDOR_DEPENDENT;
794*6983e65eSMilanka Ringwald     connection->command_type = AVRCP_CTYPE_STATUS;
795*6983e65eSMilanka Ringwald     connection->subunit_type = AVRCP_SUBUNIT_TYPE_PANEL;
796*6983e65eSMilanka Ringwald     connection->subunit_id = AVRCP_SUBUNIT_ID;
797*6983e65eSMilanka Ringwald     big_endian_store_24(connection->cmd_operands, 0, BT_SIG_COMPANY_ID);
798*6983e65eSMilanka Ringwald     connection->cmd_operands[3] = AVRCP_PDU_ID_GET_CAPABILITIES; // PDU ID
799*6983e65eSMilanka Ringwald     connection->cmd_operands[4] = 0;
800*6983e65eSMilanka Ringwald     big_endian_store_16(connection->cmd_operands, 5, 1); // parameter length
801*6983e65eSMilanka Ringwald     connection->cmd_operands[7] = capability_id;                  // capability ID
802*6983e65eSMilanka Ringwald     connection->cmd_operands_length = 8;
803*6983e65eSMilanka Ringwald     avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid);
804*6983e65eSMilanka Ringwald     return ERROR_CODE_SUCCESS;
805*6983e65eSMilanka Ringwald }
806*6983e65eSMilanka Ringwald 
807*6983e65eSMilanka Ringwald uint8_t avrcp_controller_get_supported_company_ids(uint16_t avrcp_cid){
808*6983e65eSMilanka Ringwald     return avrcp_controller_get_capabilities(avrcp_cid, AVRCP_CAPABILITY_ID_COMPANY);
809*6983e65eSMilanka Ringwald }
810*6983e65eSMilanka Ringwald 
811*6983e65eSMilanka Ringwald uint8_t avrcp_controller_get_supported_events(uint16_t avrcp_cid){
812*6983e65eSMilanka Ringwald     return avrcp_controller_get_capabilities(avrcp_cid, AVRCP_CAPABILITY_ID_EVENT);
813*6983e65eSMilanka Ringwald }
814*6983e65eSMilanka Ringwald 
815*6983e65eSMilanka Ringwald 
816*6983e65eSMilanka Ringwald uint8_t avrcp_controller_play(uint16_t avrcp_cid){
817*6983e65eSMilanka Ringwald     return request_single_pass_through_press_control_cmd(avrcp_cid, AVRCP_OPERATION_ID_PLAY, 0);
818*6983e65eSMilanka Ringwald }
819*6983e65eSMilanka Ringwald 
820*6983e65eSMilanka Ringwald uint8_t avrcp_controller_stop(uint16_t avrcp_cid){
821*6983e65eSMilanka Ringwald     return request_single_pass_through_press_control_cmd(avrcp_cid, AVRCP_OPERATION_ID_STOP, 0);
822*6983e65eSMilanka Ringwald }
823*6983e65eSMilanka Ringwald 
824*6983e65eSMilanka Ringwald uint8_t avrcp_controller_pause(uint16_t avrcp_cid){
825*6983e65eSMilanka Ringwald     return request_single_pass_through_press_control_cmd(avrcp_cid, AVRCP_OPERATION_ID_PAUSE, 0);
826*6983e65eSMilanka Ringwald }
827*6983e65eSMilanka Ringwald 
828*6983e65eSMilanka Ringwald uint8_t avrcp_controller_forward(uint16_t avrcp_cid){
829*6983e65eSMilanka Ringwald     return request_single_pass_through_press_control_cmd(avrcp_cid, AVRCP_OPERATION_ID_FORWARD, 0);
830*6983e65eSMilanka Ringwald }
831*6983e65eSMilanka Ringwald 
832*6983e65eSMilanka Ringwald uint8_t avrcp_controller_backward(uint16_t avrcp_cid){
833*6983e65eSMilanka Ringwald     return request_single_pass_through_press_control_cmd(avrcp_cid, AVRCP_OPERATION_ID_BACKWARD, 0);
834*6983e65eSMilanka Ringwald }
835*6983e65eSMilanka Ringwald 
836*6983e65eSMilanka Ringwald uint8_t avrcp_controller_start_rewind(uint16_t avrcp_cid){
837*6983e65eSMilanka Ringwald     return request_continuous_pass_through_press_control_cmd(avrcp_cid, AVRCP_OPERATION_ID_REWIND, 0);
838*6983e65eSMilanka Ringwald }
839*6983e65eSMilanka Ringwald 
840*6983e65eSMilanka Ringwald uint8_t avrcp_controller_volume_up(uint16_t avrcp_cid){
841*6983e65eSMilanka Ringwald     return request_single_pass_through_press_control_cmd(avrcp_cid, AVRCP_OPERATION_ID_VOLUME_UP, 0);
842*6983e65eSMilanka Ringwald }
843*6983e65eSMilanka Ringwald 
844*6983e65eSMilanka Ringwald uint8_t avrcp_controller_volume_down(uint16_t avrcp_cid){
845*6983e65eSMilanka Ringwald     return request_single_pass_through_press_control_cmd(avrcp_cid, AVRCP_OPERATION_ID_VOLUME_DOWN, 0);
846*6983e65eSMilanka Ringwald }
847*6983e65eSMilanka Ringwald 
848*6983e65eSMilanka Ringwald uint8_t avrcp_controller_mute(uint16_t avrcp_cid){
849*6983e65eSMilanka Ringwald     return request_single_pass_through_press_control_cmd(avrcp_cid, AVRCP_OPERATION_ID_MUTE, 0);
850*6983e65eSMilanka Ringwald }
851*6983e65eSMilanka Ringwald 
852*6983e65eSMilanka Ringwald uint8_t avrcp_controller_skip(uint16_t avrcp_cid){
853*6983e65eSMilanka Ringwald     return request_single_pass_through_press_control_cmd(avrcp_cid, AVRCP_OPERATION_ID_SKIP, 0);
854*6983e65eSMilanka Ringwald }
855*6983e65eSMilanka Ringwald 
856*6983e65eSMilanka Ringwald uint8_t avrcp_controller_stop_rewind(uint16_t avrcp_cid){
857*6983e65eSMilanka Ringwald     avrcp_connection_t * connection = get_avrcp_connection_for_avrcp_cid(avrcp_cid, &avrcp_controller_context);
858*6983e65eSMilanka Ringwald     if (!connection){
859*6983e65eSMilanka Ringwald         log_error("avrcp_stop_rewind: could not find a connection.");
860*6983e65eSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
861*6983e65eSMilanka Ringwald     }
862*6983e65eSMilanka Ringwald     if (connection->state != AVCTP_W4_STOP) return ERROR_CODE_COMMAND_DISALLOWED;
863*6983e65eSMilanka Ringwald     return request_pass_through_release_control_cmd(connection);
864*6983e65eSMilanka Ringwald }
865*6983e65eSMilanka Ringwald 
866*6983e65eSMilanka Ringwald uint8_t avrcp_controller_start_fast_forward(uint16_t avrcp_cid){
867*6983e65eSMilanka Ringwald     return request_continuous_pass_through_press_control_cmd(avrcp_cid, AVRCP_OPERATION_ID_FAST_FORWARD, 0);
868*6983e65eSMilanka Ringwald }
869*6983e65eSMilanka Ringwald 
870*6983e65eSMilanka Ringwald uint8_t avrcp_controller_fast_forward(uint16_t avrcp_cid){
871*6983e65eSMilanka Ringwald     return request_single_pass_through_press_control_cmd(avrcp_cid, AVRCP_OPERATION_ID_FAST_FORWARD, 0);
872*6983e65eSMilanka Ringwald }
873*6983e65eSMilanka Ringwald 
874*6983e65eSMilanka Ringwald uint8_t avrcp_controller_rewind(uint16_t avrcp_cid){
875*6983e65eSMilanka Ringwald     return request_single_pass_through_press_control_cmd(avrcp_cid, AVRCP_OPERATION_ID_REWIND, 0);
876*6983e65eSMilanka Ringwald }
877*6983e65eSMilanka Ringwald 
878*6983e65eSMilanka Ringwald 
879*6983e65eSMilanka Ringwald uint8_t avrcp_controller_stop_fast_forward(uint16_t avrcp_cid){
880*6983e65eSMilanka Ringwald     avrcp_connection_t * connection = get_avrcp_connection_for_avrcp_cid(avrcp_cid, &avrcp_controller_context);
881*6983e65eSMilanka Ringwald     if (!connection){
882*6983e65eSMilanka Ringwald         log_error("avrcp_stop_fast_forward: could not find a connection.");
883*6983e65eSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
884*6983e65eSMilanka Ringwald     }
885*6983e65eSMilanka Ringwald     if (connection->state != AVCTP_W4_STOP) return ERROR_CODE_COMMAND_DISALLOWED;
886*6983e65eSMilanka Ringwald     return request_pass_through_release_control_cmd(connection);
887*6983e65eSMilanka Ringwald }
888*6983e65eSMilanka Ringwald 
889*6983e65eSMilanka Ringwald uint8_t avrcp_controller_get_play_status(uint16_t avrcp_cid){
890*6983e65eSMilanka Ringwald     avrcp_connection_t * connection = get_avrcp_connection_for_avrcp_cid(avrcp_cid, &avrcp_controller_context);
891*6983e65eSMilanka Ringwald     if (!connection){
892*6983e65eSMilanka Ringwald         log_error("avrcp_get_play_status: could not find a connection.");
893*6983e65eSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
894*6983e65eSMilanka Ringwald     }
895*6983e65eSMilanka Ringwald     if (connection->state != AVCTP_CONNECTION_OPENED) return ERROR_CODE_COMMAND_DISALLOWED;
896*6983e65eSMilanka Ringwald     connection->state = AVCTP_W2_SEND_COMMAND;
897*6983e65eSMilanka Ringwald     connection->transaction_label++;
898*6983e65eSMilanka Ringwald     connection->command_opcode = AVRCP_CMD_OPCODE_VENDOR_DEPENDENT;
899*6983e65eSMilanka Ringwald     connection->command_type = AVRCP_CTYPE_STATUS;
900*6983e65eSMilanka Ringwald     connection->subunit_type = AVRCP_SUBUNIT_TYPE_PANEL;
901*6983e65eSMilanka Ringwald     connection->subunit_id = AVRCP_SUBUNIT_ID;
902*6983e65eSMilanka Ringwald     big_endian_store_24(connection->cmd_operands, 0, BT_SIG_COMPANY_ID);
903*6983e65eSMilanka Ringwald     connection->cmd_operands[3] = AVRCP_PDU_ID_GET_PLAY_STATUS;
904*6983e65eSMilanka Ringwald     connection->cmd_operands[4] = 0;                     // reserved(upper 6) | packet_type -> 0
905*6983e65eSMilanka Ringwald     big_endian_store_16(connection->cmd_operands, 5, 0); // parameter length
906*6983e65eSMilanka Ringwald     connection->cmd_operands_length = 7;
907*6983e65eSMilanka Ringwald     avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid);
908*6983e65eSMilanka Ringwald     return ERROR_CODE_SUCCESS;
909*6983e65eSMilanka Ringwald }
910*6983e65eSMilanka Ringwald 
911*6983e65eSMilanka Ringwald uint8_t avrcp_controller_enable_notification(uint16_t avrcp_cid, avrcp_notification_event_id_t event_id){
912*6983e65eSMilanka Ringwald     avrcp_connection_t * connection = get_avrcp_connection_for_avrcp_cid(avrcp_cid, &avrcp_controller_context);
913*6983e65eSMilanka Ringwald     if (!connection){
914*6983e65eSMilanka Ringwald         log_error("avrcp_get_play_status: could not find a connection.");
915*6983e65eSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
916*6983e65eSMilanka Ringwald     }
917*6983e65eSMilanka Ringwald     avrcp_register_notification(connection, event_id);
918*6983e65eSMilanka Ringwald     return ERROR_CODE_SUCCESS;
919*6983e65eSMilanka Ringwald }
920*6983e65eSMilanka Ringwald 
921*6983e65eSMilanka Ringwald uint8_t avrcp_controller_disable_notification(uint16_t avrcp_cid, avrcp_notification_event_id_t event_id){
922*6983e65eSMilanka Ringwald     avrcp_connection_t * connection = get_avrcp_connection_for_avrcp_cid(avrcp_cid, &avrcp_controller_context);
923*6983e65eSMilanka Ringwald     if (!connection){
924*6983e65eSMilanka Ringwald         log_error("avrcp_get_play_status: could not find a connection.");
925*6983e65eSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
926*6983e65eSMilanka Ringwald     }
927*6983e65eSMilanka Ringwald     connection->notifications_to_deregister |= (1 << event_id);
928*6983e65eSMilanka Ringwald     return ERROR_CODE_SUCCESS;
929*6983e65eSMilanka Ringwald }
930*6983e65eSMilanka Ringwald 
931*6983e65eSMilanka Ringwald uint8_t avrcp_controller_get_now_playing_info(uint16_t avrcp_cid){
932*6983e65eSMilanka Ringwald     avrcp_connection_t * connection = get_avrcp_connection_for_avrcp_cid(avrcp_cid, &avrcp_controller_context);
933*6983e65eSMilanka Ringwald     if (!connection){
934*6983e65eSMilanka Ringwald         log_error("avrcp_get_capabilities: could not find a connection.");
935*6983e65eSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
936*6983e65eSMilanka Ringwald     }
937*6983e65eSMilanka Ringwald     if (connection->state != AVCTP_CONNECTION_OPENED) return ERROR_CODE_COMMAND_DISALLOWED;
938*6983e65eSMilanka Ringwald     connection->state = AVCTP_W2_SEND_COMMAND;
939*6983e65eSMilanka Ringwald 
940*6983e65eSMilanka Ringwald     connection->transaction_label++;
941*6983e65eSMilanka Ringwald     connection->command_opcode = AVRCP_CMD_OPCODE_VENDOR_DEPENDENT;
942*6983e65eSMilanka Ringwald     connection->command_type = AVRCP_CTYPE_STATUS;
943*6983e65eSMilanka Ringwald     connection->subunit_type = AVRCP_SUBUNIT_TYPE_PANEL;
944*6983e65eSMilanka Ringwald     connection->subunit_id = AVRCP_SUBUNIT_ID;
945*6983e65eSMilanka Ringwald     int pos = 0;
946*6983e65eSMilanka Ringwald     big_endian_store_24(connection->cmd_operands, pos, BT_SIG_COMPANY_ID);
947*6983e65eSMilanka Ringwald     pos += 3;
948*6983e65eSMilanka Ringwald     connection->cmd_operands[pos++] = AVRCP_PDU_ID_GET_ELEMENT_ATTRIBUTES; // PDU ID
949*6983e65eSMilanka Ringwald     connection->cmd_operands[pos++] = 0;
950*6983e65eSMilanka Ringwald 
951*6983e65eSMilanka Ringwald     // Parameter Length
952*6983e65eSMilanka Ringwald     big_endian_store_16(connection->cmd_operands, pos, 9);
953*6983e65eSMilanka Ringwald     pos += 2;
954*6983e65eSMilanka Ringwald 
955*6983e65eSMilanka Ringwald     // write 8 bytes value
956*6983e65eSMilanka Ringwald     memset(connection->cmd_operands + pos, 0, 8); // identifier: PLAYING
957*6983e65eSMilanka Ringwald     pos += 8;
958*6983e65eSMilanka Ringwald 
959*6983e65eSMilanka Ringwald     connection->cmd_operands[pos++] = 0; // attribute count, if 0 get all attributes
960*6983e65eSMilanka Ringwald     // every attribute is 4 bytes long
961*6983e65eSMilanka Ringwald 
962*6983e65eSMilanka Ringwald     connection->cmd_operands_length = pos;
963*6983e65eSMilanka Ringwald     avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid);
964*6983e65eSMilanka Ringwald     return ERROR_CODE_SUCCESS;
965*6983e65eSMilanka Ringwald }
966*6983e65eSMilanka Ringwald 
967*6983e65eSMilanka Ringwald uint8_t avrcp_controller_set_absolute_volume(uint16_t avrcp_cid, uint8_t volume){
968*6983e65eSMilanka Ringwald      avrcp_connection_t * connection = get_avrcp_connection_for_avrcp_cid(avrcp_cid, &avrcp_controller_context);
969*6983e65eSMilanka Ringwald     if (!connection){
970*6983e65eSMilanka Ringwald         log_error("avrcp_get_capabilities: could not find a connection.");
971*6983e65eSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
972*6983e65eSMilanka Ringwald     }
973*6983e65eSMilanka Ringwald     if (connection->state != AVCTP_CONNECTION_OPENED) return ERROR_CODE_COMMAND_DISALLOWED;
974*6983e65eSMilanka Ringwald     connection->state = AVCTP_W2_SEND_COMMAND;
975*6983e65eSMilanka Ringwald 
976*6983e65eSMilanka Ringwald     connection->transaction_label++;
977*6983e65eSMilanka Ringwald     connection->command_opcode = AVRCP_CMD_OPCODE_VENDOR_DEPENDENT;
978*6983e65eSMilanka Ringwald     connection->command_type = AVRCP_CTYPE_CONTROL;
979*6983e65eSMilanka Ringwald     connection->subunit_type = AVRCP_SUBUNIT_TYPE_PANEL;
980*6983e65eSMilanka Ringwald     connection->subunit_id = AVRCP_SUBUNIT_ID;
981*6983e65eSMilanka Ringwald     int pos = 0;
982*6983e65eSMilanka Ringwald     big_endian_store_24(connection->cmd_operands, pos, BT_SIG_COMPANY_ID);
983*6983e65eSMilanka Ringwald     pos += 3;
984*6983e65eSMilanka Ringwald     connection->cmd_operands[pos++] = AVRCP_PDU_ID_SET_ABSOLUTE_VOLUME; // PDU ID
985*6983e65eSMilanka Ringwald     connection->cmd_operands[pos++] = 0;
986*6983e65eSMilanka Ringwald 
987*6983e65eSMilanka Ringwald     // Parameter Length
988*6983e65eSMilanka Ringwald     big_endian_store_16(connection->cmd_operands, pos, 1);
989*6983e65eSMilanka Ringwald     pos += 2;
990*6983e65eSMilanka Ringwald     connection->cmd_operands[pos++] = volume;
991*6983e65eSMilanka Ringwald 
992*6983e65eSMilanka Ringwald     connection->cmd_operands_length = pos;
993*6983e65eSMilanka Ringwald     avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid);
994*6983e65eSMilanka Ringwald     return ERROR_CODE_SUCCESS;
995*6983e65eSMilanka Ringwald }
996*6983e65eSMilanka Ringwald 
997*6983e65eSMilanka Ringwald uint8_t avrcp_controller_query_shuffle_and_repeat_modes(uint16_t avrcp_cid){
998*6983e65eSMilanka Ringwald     avrcp_connection_t * connection = get_avrcp_connection_for_avrcp_cid(avrcp_cid, &avrcp_controller_context);
999*6983e65eSMilanka Ringwald     if (!connection){
1000*6983e65eSMilanka Ringwald         log_error("avrcp_get_capabilities: could not find a connection.");
1001*6983e65eSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1002*6983e65eSMilanka Ringwald     }
1003*6983e65eSMilanka Ringwald     if (connection->state != AVCTP_CONNECTION_OPENED) return ERROR_CODE_COMMAND_DISALLOWED;
1004*6983e65eSMilanka Ringwald     connection->state = AVCTP_W2_SEND_COMMAND;
1005*6983e65eSMilanka Ringwald 
1006*6983e65eSMilanka Ringwald     connection->transaction_label++;
1007*6983e65eSMilanka Ringwald     connection->command_opcode = AVRCP_CMD_OPCODE_VENDOR_DEPENDENT;
1008*6983e65eSMilanka Ringwald     connection->command_type = AVRCP_CTYPE_STATUS;
1009*6983e65eSMilanka Ringwald     connection->subunit_type = AVRCP_SUBUNIT_TYPE_PANEL;
1010*6983e65eSMilanka Ringwald     connection->subunit_id = AVRCP_SUBUNIT_ID;
1011*6983e65eSMilanka Ringwald     big_endian_store_24(connection->cmd_operands, 0, BT_SIG_COMPANY_ID);
1012*6983e65eSMilanka Ringwald     connection->cmd_operands[3] = AVRCP_PDU_ID_GetCurrentPlayerApplicationSettingValue; // PDU ID
1013*6983e65eSMilanka Ringwald     connection->cmd_operands[4] = 0;
1014*6983e65eSMilanka Ringwald     big_endian_store_16(connection->cmd_operands, 5, 5); // parameter length
1015*6983e65eSMilanka Ringwald     connection->cmd_operands[7] = 4;                     // NumPlayerApplicationSettingAttributeID
1016*6983e65eSMilanka Ringwald     // PlayerApplicationSettingAttributeID1 AVRCP Spec, Appendix F, 133
1017*6983e65eSMilanka Ringwald     connection->cmd_operands[8]  = 0x01;    // equalizer  (1-OFF, 2-ON)
1018*6983e65eSMilanka Ringwald     connection->cmd_operands[9]  = 0x02;    // repeat     (1-off, 2-single track, 3-all tracks, 4-group repeat)
1019*6983e65eSMilanka Ringwald     connection->cmd_operands[10] = 0x03;    // shuffle    (1-off, 2-all tracks, 3-group shuffle)
1020*6983e65eSMilanka Ringwald     connection->cmd_operands[11] = 0x04;    // scan       (1-off, 2-all tracks, 3-group scan)
1021*6983e65eSMilanka Ringwald     connection->cmd_operands_length = 12;
1022*6983e65eSMilanka Ringwald     avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid);
1023*6983e65eSMilanka Ringwald     return ERROR_CODE_SUCCESS;
1024*6983e65eSMilanka Ringwald }
1025*6983e65eSMilanka Ringwald 
1026*6983e65eSMilanka Ringwald static uint8_t avrcp_controller_set_current_player_application_setting_value(uint16_t avrcp_cid, uint8_t attr_id, uint8_t attr_value){
1027*6983e65eSMilanka Ringwald     avrcp_connection_t * connection = get_avrcp_connection_for_avrcp_cid(avrcp_cid, &avrcp_controller_context);
1028*6983e65eSMilanka Ringwald     if (!connection){
1029*6983e65eSMilanka Ringwald         log_error("avrcp_get_capabilities: could not find a connection.");
1030*6983e65eSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1031*6983e65eSMilanka Ringwald     }
1032*6983e65eSMilanka Ringwald     if (connection->state != AVCTP_CONNECTION_OPENED) return ERROR_CODE_COMMAND_DISALLOWED;
1033*6983e65eSMilanka Ringwald     connection->state = AVCTP_W2_SEND_COMMAND;
1034*6983e65eSMilanka Ringwald 
1035*6983e65eSMilanka Ringwald     connection->transaction_label++;
1036*6983e65eSMilanka Ringwald     connection->command_opcode = AVRCP_CMD_OPCODE_VENDOR_DEPENDENT;
1037*6983e65eSMilanka Ringwald     connection->command_type = AVRCP_CTYPE_CONTROL;
1038*6983e65eSMilanka Ringwald     connection->subunit_type = AVRCP_SUBUNIT_TYPE_PANEL;
1039*6983e65eSMilanka Ringwald     connection->subunit_id = AVRCP_SUBUNIT_ID;
1040*6983e65eSMilanka Ringwald     int pos = 0;
1041*6983e65eSMilanka Ringwald     big_endian_store_24(connection->cmd_operands, pos, BT_SIG_COMPANY_ID);
1042*6983e65eSMilanka Ringwald     pos += 3;
1043*6983e65eSMilanka Ringwald     connection->cmd_operands[pos++] = AVRCP_PDU_ID_SetPlayerApplicationSettingValue; // PDU ID
1044*6983e65eSMilanka Ringwald     connection->cmd_operands[pos++] = 0;
1045*6983e65eSMilanka Ringwald     // Parameter Length
1046*6983e65eSMilanka Ringwald     big_endian_store_16(connection->cmd_operands, pos, 3);
1047*6983e65eSMilanka Ringwald     pos += 2;
1048*6983e65eSMilanka Ringwald     connection->cmd_operands[pos++] = 2;
1049*6983e65eSMilanka Ringwald     connection->cmd_operands_length = pos;
1050*6983e65eSMilanka Ringwald     connection->cmd_operands[pos++]  = attr_id;
1051*6983e65eSMilanka Ringwald     connection->cmd_operands[pos++]  = attr_value;
1052*6983e65eSMilanka Ringwald     connection->cmd_operands_length = pos;
1053*6983e65eSMilanka Ringwald     avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid);
1054*6983e65eSMilanka Ringwald     return ERROR_CODE_SUCCESS;
1055*6983e65eSMilanka Ringwald }
1056*6983e65eSMilanka Ringwald 
1057*6983e65eSMilanka Ringwald uint8_t avrcp_controller_set_shuffle_mode(uint16_t avrcp_cid, avrcp_shuffle_mode_t mode){
1058*6983e65eSMilanka Ringwald     if (mode < AVRCP_SHUFFLE_MODE_OFF || mode > AVRCP_SHUFFLE_MODE_GROUP) return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
1059*6983e65eSMilanka Ringwald     return avrcp_controller_set_current_player_application_setting_value(avrcp_cid, 0x03, mode);
1060*6983e65eSMilanka Ringwald }
1061*6983e65eSMilanka Ringwald 
1062*6983e65eSMilanka Ringwald uint8_t avrcp_controller_set_repeat_mode(uint16_t avrcp_cid, avrcp_repeat_mode_t mode){
1063*6983e65eSMilanka Ringwald     if (mode < AVRCP_REPEAT_MODE_OFF || mode > AVRCP_REPEAT_MODE_GROUP) return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
1064*6983e65eSMilanka Ringwald     return avrcp_controller_set_current_player_application_setting_value(avrcp_cid, 0x02, mode);
1065*6983e65eSMilanka Ringwald }
1066*6983e65eSMilanka Ringwald 
1067*6983e65eSMilanka Ringwald uint8_t avrcp_controller_disconnect(uint16_t avrcp_cid){
1068*6983e65eSMilanka Ringwald     avrcp_connection_t * connection = get_avrcp_connection_for_avrcp_cid(avrcp_cid, &avrcp_controller_context);
1069*6983e65eSMilanka Ringwald     if (!connection){
1070*6983e65eSMilanka Ringwald         log_error("avrcp_get_capabilities: could not find a connection.");
1071*6983e65eSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1072*6983e65eSMilanka Ringwald     }
1073*6983e65eSMilanka Ringwald     if (connection->state != AVCTP_CONNECTION_OPENED) return ERROR_CODE_COMMAND_DISALLOWED;
1074*6983e65eSMilanka Ringwald     l2cap_disconnect(connection->l2cap_signaling_cid, 0);
1075*6983e65eSMilanka Ringwald     return ERROR_CODE_SUCCESS;
1076*6983e65eSMilanka Ringwald }