xref: /btstack/src/classic/avrcp_browsing_target.c (revision 0eebc13233163025db15628cd1db9602b65e0e8e)
1 /*
2  * Copyright (C) 2016 BlueKitchen GmbH
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. Neither the name of the copyright holders nor the names of
14  *    contributors may be used to endorse or promote products derived
15  *    from this software without specific prior written permission.
16  * 4. Any redistribution, use, or modification is done solely for
17  *    personal benefit and not for any commercial purpose or for
18  *    monetary gain.
19  *
20  * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
21  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
24  * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
27  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
28  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
30  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  *
33  * Please inquire about commercial licensing options at
34  * [email protected]
35  *
36  */
37 
38 #define BTSTACK_FILE__ "avrcp_browsing_target.c"
39 
40 #include <stdint.h>
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <string.h>
44 #include <inttypes.h>
45 #include "btstack.h"
46 #include "classic/avrcp.h"
47 #include "classic/avrcp_browsing_target.h"
48 
49 static void avrcp_browsing_target_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
50 
51 static int avrcp_browsing_target_handle_can_send_now(avrcp_browsing_connection_t * connection){
52     int pos = 0;
53     // printf("avrcp_browsing_target_handle_can_send_now, cmd_operands_length %d\n", connection->cmd_operands_length);
54     // printf_hexdump(connection->cmd_operands, connection->cmd_operands_length);
55 
56     // l2cap_reserve_packet_buffer();
57     // uint8_t * packet = l2cap_get_outgoing_buffer();
58     uint8_t packet[300];
59     connection->packet_type = AVRCP_SINGLE_PACKET;
60 
61     packet[pos++] = (connection->transaction_label << 4) | (connection->packet_type << 2) | (AVRCP_RESPONSE_FRAME << 1) | 0;
62     // Profile IDentifier (PID)
63     packet[pos++] = BLUETOOTH_SERVICE_CLASS_AV_REMOTE_CONTROL >> 8;
64     packet[pos++] = BLUETOOTH_SERVICE_CLASS_AV_REMOTE_CONTROL & 0x00FF;
65     (void)memcpy(packet + pos, connection->cmd_operands,
66                  connection->cmd_operands_length);
67 
68     pos += connection->cmd_operands_length;
69     connection->wait_to_send = 0;
70     // return l2cap_send_prepared(connection->l2cap_browsing_cid, pos);
71     return l2cap_send(connection->l2cap_browsing_cid, packet, pos);
72 }
73 
74 
75 static uint8_t avrcp_browsing_target_response_general_reject(avrcp_browsing_connection_t * connection, avrcp_status_code_t status){
76     // AVRCP_CTYPE_RESPONSE_REJECTED
77     int pos = 0;
78     connection->cmd_operands[pos++] = AVRCP_PDU_ID_GENERAL_REJECT;
79     // connection->cmd_operands[pos++] = 0;
80     // param length
81     big_endian_store_16(connection->cmd_operands, pos, 1);
82     pos += 2;
83     connection->cmd_operands[pos++] = status;
84     connection->cmd_operands_length = 4;
85     connection->state = AVCTP_W2_SEND_RESPONSE;
86     avrcp_browsing_request_can_send_now(connection, connection->l2cap_browsing_cid);
87     return ERROR_CODE_SUCCESS;
88 }
89 
90 static void avrcp_browsing_target_emit_get_folder_items(btstack_packet_handler_t callback, uint16_t browsing_cid, avrcp_browsing_connection_t * connection){
91     btstack_assert(callback != NULL);
92 
93     uint8_t event[10];
94     int pos = 0;
95     event[pos++] = HCI_EVENT_AVRCP_META;
96     event[pos++] = sizeof(event) - 2;
97     event[pos++] = AVRCP_SUBEVENT_BROWSING_GET_FOLDER_ITEMS;
98     little_endian_store_16(event, pos, browsing_cid);
99     pos += 2;
100     event[pos++] = connection->scope;
101     big_endian_store_32(event, pos, connection->attr_bitmap);
102     pos += 4;
103     (*callback)(HCI_EVENT_PACKET, 0, event, sizeof(event));
104 }
105 
106 static void avrcp_browsing_target_emit_get_total_num_items(btstack_packet_handler_t callback, uint16_t browsing_cid, avrcp_browsing_connection_t * connection){
107     btstack_assert(callback != NULL);
108 
109     uint8_t event[10];
110     int pos = 0;
111     event[pos++] = HCI_EVENT_AVRCP_META;
112     event[pos++] = sizeof(event) - 2;
113     event[pos++] = AVRCP_SUBEVENT_BROWSING_GET_TOTAL_NUM_ITEMS;
114     little_endian_store_16(event, pos, browsing_cid);
115     pos += 2;
116     event[pos++] = connection->scope;
117     (*callback)(HCI_EVENT_PACKET, 0, event, sizeof(event));
118 }
119 
120 
121 static void avrcp_browsing_target_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
122     UNUSED(size);
123     avrcp_browsing_connection_t * browsing_connection;
124 
125     switch (packet_type) {
126         case L2CAP_DATA_PACKET:{
127             browsing_connection = get_avrcp_browsing_connection_for_l2cap_cid_for_role(AVRCP_TARGET, channel);
128             if (!browsing_connection) break;
129             int pos = 0;
130             uint8_t transport_header = packet[pos++];
131             // Transaction label | Packet_type | C/R | IPID (1 == invalid profile identifier)
132             browsing_connection->transaction_label = transport_header >> 4;
133             avrcp_packet_type_t avctp_packet_type = (transport_header & 0x0F) >> 2;
134             switch (avctp_packet_type){
135                 case AVRCP_SINGLE_PACKET:
136                 case AVRCP_START_PACKET:
137                     browsing_connection->subunit_type = packet[pos++] >> 2;
138                     browsing_connection->subunit_id = 0;
139                     browsing_connection->command_opcode = packet[pos++];
140                     browsing_connection->num_packets = 1;
141                     if (avctp_packet_type == AVRCP_START_PACKET){
142                         browsing_connection->num_packets = packet[pos++];
143                     }
144                     browsing_connection->pdu_id = packet[pos++];
145                     break;
146                 default:
147                     break;
148             }
149             switch (avctp_packet_type){
150                 case AVRCP_SINGLE_PACKET:
151                 case AVRCP_END_PACKET:
152                     switch(browsing_connection->pdu_id){
153                         case AVRCP_PDU_ID_GET_FOLDER_ITEMS:
154                             printf("\n");
155                             browsing_connection->scope = packet[pos++];
156                             browsing_connection->start_item = big_endian_read_32(packet, pos);
157                             pos += 4;
158                             browsing_connection->end_item = big_endian_read_32(packet, pos);
159                             pos += 4;
160                             uint8_t attr_count = packet[pos++];
161 
162                             while (attr_count){
163                                 uint32_t attr_id = big_endian_read_32(packet, pos);
164                                 pos += 4;
165                                 browsing_connection->attr_bitmap |= (1 << attr_id);
166                                 attr_count--;
167                             }
168                             avrcp_browsing_target_emit_get_folder_items(avrcp_target_context.browsing_avrcp_callback, channel, browsing_connection);
169                             break;
170                         case AVRCP_PDU_ID_GET_TOTAL_NUMBER_OF_ITEMS:{
171                             // send total num items
172                             browsing_connection->scope = packet[pos++];
173                             avrcp_browsing_target_emit_get_total_num_items(avrcp_target_context.browsing_avrcp_callback, channel, browsing_connection);
174                             break;
175                         }
176                         default:
177                             avrcp_browsing_target_response_general_reject(browsing_connection, AVRCP_STATUS_INVALID_COMMAND);
178                             log_info(" not parsed pdu ID 0x%02x", browsing_connection->pdu_id);
179                             break;
180                     }
181                     browsing_connection->state = AVCTP_CONNECTION_OPENED;
182                     break;
183                 default:
184                     break;
185             }
186             break;
187         }
188 
189         case HCI_EVENT_PACKET:
190             switch (hci_event_packet_get_type(packet)){
191                 case L2CAP_EVENT_CAN_SEND_NOW:
192                     browsing_connection = get_avrcp_browsing_connection_for_l2cap_cid_for_role(AVRCP_TARGET, channel);
193                     if (!browsing_connection) break;
194                     if (browsing_connection->state != AVCTP_W2_SEND_RESPONSE) return;
195                     browsing_connection->state = AVCTP_CONNECTION_OPENED;
196                     avrcp_browsing_target_handle_can_send_now(browsing_connection);
197                     break;
198                 default:
199                     break;
200             }
201             break;
202 
203         default:
204             break;
205     }
206 }
207 
208 void avrcp_browsing_target_init(void){
209     avrcp_target_context.browsing_packet_handler = avrcp_browsing_target_packet_handler;
210     avrcp_browsing_register_target_packet_handler(avrcp_browsing_target_packet_handler);
211 }
212 
213 void avrcp_browsing_target_register_packet_handler(btstack_packet_handler_t callback){
214     btstack_assert(callback != NULL);
215     avrcp_target_context.browsing_avrcp_callback = callback;
216 }
217 
218 uint8_t avrcp_browsing_target_connect(bd_addr_t bd_addr, uint8_t * ertm_buffer, uint32_t size, l2cap_ertm_config_t * ertm_config, uint16_t * avrcp_browsing_cid){
219     return avrcp_browsing_connect(bd_addr, AVRCP_TARGET, ertm_buffer, size, ertm_config, avrcp_browsing_cid);
220 }
221 
222 uint8_t avrcp_browsing_target_disconnect(uint16_t avrcp_browsing_cid){
223     return avrcp_browsing_disconnect(avrcp_browsing_cid, AVRCP_TARGET);
224 }
225 
226 uint8_t avrcp_browsing_target_configure_incoming_connection(uint16_t avrcp_browsing_cid, uint8_t * ertm_buffer, uint32_t size, l2cap_ertm_config_t * ertm_config){
227     avrcp_connection_t * avrcp_connection = get_avrcp_connection_for_browsing_cid_for_role(AVRCP_TARGET, avrcp_browsing_cid);
228     if (!avrcp_connection){
229         log_error("avrcp_browsing_decline_incoming_connection: could not find a connection.");
230         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
231     }
232     if (!avrcp_connection->browsing_connection){
233         log_error("avrcp_browsing_decline_incoming_connection: no browsing connection.");
234         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
235     }
236 
237     if (avrcp_connection->browsing_connection->state != AVCTP_CONNECTION_W4_ERTM_CONFIGURATION){
238         log_error("avrcp_browsing_decline_incoming_connection: browsing connection in a wrong state.");
239         return ERROR_CODE_COMMAND_DISALLOWED;
240     }
241 
242     avrcp_connection->browsing_connection->state = AVCTP_CONNECTION_W4_L2CAP_CONNECTED;
243     avrcp_connection->browsing_connection->ertm_buffer = ertm_buffer;
244     avrcp_connection->browsing_connection->ertm_buffer_size = size;
245     (void)memcpy(&avrcp_connection->browsing_connection->ertm_config,
246                  ertm_config, sizeof(l2cap_ertm_config_t));
247     l2cap_accept_ertm_connection(avrcp_connection->browsing_connection->l2cap_browsing_cid, &avrcp_connection->browsing_connection->ertm_config, avrcp_connection->browsing_connection->ertm_buffer, avrcp_connection->browsing_connection->ertm_buffer_size);
248     return ERROR_CODE_SUCCESS;
249 }
250 
251 uint8_t avrcp_browsing_target_decline_incoming_connection(uint16_t avrcp_browsing_cid){
252     avrcp_connection_t * avrcp_connection = get_avrcp_connection_for_browsing_cid_for_role(AVRCP_TARGET, avrcp_browsing_cid);
253     if (!avrcp_connection){
254         log_error("avrcp_browsing_decline_incoming_connection: could not find a connection.");
255         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
256     }
257     if (!avrcp_connection->browsing_connection) return ERROR_CODE_SUCCESS;
258     if (avrcp_connection->browsing_connection->state > AVCTP_CONNECTION_W4_ERTM_CONFIGURATION) return ERROR_CODE_COMMAND_DISALLOWED;
259 
260     l2cap_decline_connection(avrcp_connection->browsing_connection->l2cap_browsing_cid);
261     // free connection
262     btstack_memory_avrcp_browsing_connection_free(avrcp_connection->browsing_connection);
263     avrcp_connection->browsing_connection = NULL;
264     return ERROR_CODE_SUCCESS;
265 }
266 
267 uint8_t avrcp_subevent_browsing_get_folder_items_response(uint16_t avrcp_browsing_cid, uint16_t uid_counter, uint8_t * attr_list, uint16_t attr_list_size){
268     avrcp_connection_t * avrcp_connection = get_avrcp_connection_for_browsing_cid_for_role(AVRCP_TARGET, avrcp_browsing_cid);
269     if (!avrcp_connection){
270         log_error("avrcp_browsing_controller_disconnect: could not find a connection.");
271         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
272     }
273 
274     avrcp_browsing_connection_t * connection = avrcp_connection->browsing_connection;
275     if (!connection){
276         log_info("avrcp_subevent_browsing_get_folder_items_response: could not find a connection.");
277         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
278     }
279     if (connection->state != AVCTP_CONNECTION_OPENED) return ERROR_CODE_COMMAND_DISALLOWED;
280 
281     if (connection->state != AVCTP_CONNECTION_OPENED) {
282         log_info("avrcp_subevent_browsing_get_folder_items_response: wrong state.");
283         return ERROR_CODE_COMMAND_DISALLOWED;
284     }
285     int pos = 0;
286 
287     connection->cmd_operands[pos++] = AVRCP_PDU_ID_GET_FOLDER_ITEMS;
288     big_endian_store_16(connection->cmd_operands, pos, attr_list_size);
289     pos += 2;
290 
291     connection->cmd_operands[pos++] = AVRCP_STATUS_SUCCESS;
292     big_endian_store_16(connection->cmd_operands, pos, uid_counter);
293     pos += 2;
294 
295     // TODO: fragmentation
296     if (attr_list_size >  sizeof(connection->cmd_operands)){
297         connection->attr_list = attr_list;
298         connection->attr_list_size = attr_list_size;
299         log_info(" todo: list too big, invoke fragmentation");
300         return 1;
301     }
302     (void)memcpy(&connection->cmd_operands[pos], attr_list, attr_list_size);
303     pos += attr_list_size;
304     connection->cmd_operands_length = pos;
305     // printf_hexdump(connection->cmd_operands, connection->cmd_operands_length);
306 
307     connection->state = AVCTP_W2_SEND_RESPONSE;
308     avrcp_browsing_request_can_send_now(connection, connection->l2cap_browsing_cid);
309     return ERROR_CODE_SUCCESS;
310 }
311 
312 
313 uint8_t avrcp_subevent_browsing_get_total_num_items_response(uint16_t avrcp_browsing_cid, uint16_t uid_counter, uint32_t total_num_items){
314     avrcp_connection_t * avrcp_connection = get_avrcp_connection_for_browsing_cid_for_role(AVRCP_TARGET, avrcp_browsing_cid);
315     if (!avrcp_connection){
316         log_error("avrcp_browsing_controller_disconnect: could not find a connection.");
317         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
318     }
319 
320     avrcp_browsing_connection_t * connection = avrcp_connection->browsing_connection;
321     if (!connection){
322         log_info("avrcp_subevent_browsing_get_folder_items_response: could not find a connection.");
323         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
324     }
325     if (connection->state != AVCTP_CONNECTION_OPENED) return ERROR_CODE_COMMAND_DISALLOWED;
326 
327     if (connection->state != AVCTP_CONNECTION_OPENED) {
328         log_info("avrcp_subevent_browsing_get_folder_items_response: wrong state.");
329         return ERROR_CODE_COMMAND_DISALLOWED;
330     }
331 
332     int pos = 0;
333     connection->cmd_operands[pos++] = AVRCP_PDU_ID_GET_TOTAL_NUMBER_OF_ITEMS;
334     big_endian_store_16(connection->cmd_operands, pos, 7);
335     pos += 2;
336     connection->cmd_operands[pos++] = AVRCP_STATUS_SUCCESS;
337     big_endian_store_16(connection->cmd_operands, pos, uid_counter);
338     pos += 2;
339     big_endian_store_32(connection->cmd_operands, pos, total_num_items);
340     pos += 4;
341     connection->cmd_operands_length = pos;
342 
343     connection->state = AVCTP_W2_SEND_RESPONSE;
344     avrcp_browsing_request_can_send_now(connection, connection->l2cap_browsing_cid);
345     return ERROR_CODE_SUCCESS;
346 }
347 
348