xref: /btstack/src/classic/avrcp_browsing_target.c (revision 12c4a6eeaf1081a351a6574937759b171cdbc0f2)
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 #define PSM_AVCTP_BROWSING              0x001b
50 
51 static void avrcp_browsing_target_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
52 
53 static void avrcp_browsing_target_request_can_send_now(avrcp_browsing_connection_t * connection, uint16_t l2cap_cid){
54     connection->wait_to_send = 1;
55     l2cap_request_can_send_now_event(l2cap_cid);
56 }
57 
58 static int avrcp_browsing_target_handle_can_send_now(avrcp_browsing_connection_t * connection){
59     int pos = 0;
60     // printf("avrcp_browsing_target_handle_can_send_now, cmd_operands_length %d\n", connection->cmd_operands_length);
61     // printf_hexdump(connection->cmd_operands, connection->cmd_operands_length);
62 
63     // l2cap_reserve_packet_buffer();
64     // uint8_t * packet = l2cap_get_outgoing_buffer();
65     uint8_t packet[300];
66     connection->packet_type = AVRCP_SINGLE_PACKET;
67 
68     packet[pos++] = (connection->transaction_label << 4) | (connection->packet_type << 2) | (AVRCP_RESPONSE_FRAME << 1) | 0;
69     // Profile IDentifier (PID)
70     packet[pos++] = BLUETOOTH_SERVICE_CLASS_AV_REMOTE_CONTROL >> 8;
71     packet[pos++] = BLUETOOTH_SERVICE_CLASS_AV_REMOTE_CONTROL & 0x00FF;
72     (void)memcpy(packet + pos, connection->cmd_operands,
73                  connection->cmd_operands_length);
74 
75     pos += connection->cmd_operands_length;
76     connection->wait_to_send = 0;
77     // return l2cap_send_prepared(connection->l2cap_browsing_cid, pos);
78     return l2cap_send(connection->l2cap_browsing_cid, packet, pos);
79 }
80 
81 
82 static uint8_t avrcp_browsing_target_response_general_reject(avrcp_browsing_connection_t * connection, avrcp_status_code_t status){
83     // AVRCP_CTYPE_RESPONSE_REJECTED
84     int pos = 0;
85     connection->cmd_operands[pos++] = AVRCP_PDU_ID_GENERAL_REJECT;
86     // connection->cmd_operands[pos++] = 0;
87     // param length
88     big_endian_store_16(connection->cmd_operands, pos, 1);
89     pos += 2;
90     connection->cmd_operands[pos++] = status;
91     connection->cmd_operands_length = 4;
92     connection->state = AVCTP_W2_SEND_RESPONSE;
93     avrcp_browsing_target_request_can_send_now(connection, connection->l2cap_browsing_cid);
94     return ERROR_CODE_SUCCESS;
95 }
96 
97 static void avrcp_browsing_target_emit_get_folder_items(btstack_packet_handler_t callback, uint16_t browsing_cid, avrcp_browsing_connection_t * connection){
98     btstack_assert(callback != NULL);
99 
100     uint8_t event[10];
101     int pos = 0;
102     event[pos++] = HCI_EVENT_AVRCP_META;
103     event[pos++] = sizeof(event) - 2;
104     event[pos++] = AVRCP_SUBEVENT_BROWSING_GET_FOLDER_ITEMS;
105     little_endian_store_16(event, pos, browsing_cid);
106     pos += 2;
107     event[pos++] = connection->scope;
108     big_endian_store_32(event, pos, connection->attr_bitmap);
109     pos += 4;
110     (*callback)(HCI_EVENT_PACKET, 0, event, sizeof(event));
111 }
112 
113 static void avrcp_browsing_target_emit_get_total_num_items(btstack_packet_handler_t callback, uint16_t browsing_cid, avrcp_browsing_connection_t * connection){
114     btstack_assert(callback != NULL);
115 
116     uint8_t event[10];
117     int pos = 0;
118     event[pos++] = HCI_EVENT_AVRCP_META;
119     event[pos++] = sizeof(event) - 2;
120     event[pos++] = AVRCP_SUBEVENT_BROWSING_GET_TOTAL_NUM_ITEMS;
121     little_endian_store_16(event, pos, browsing_cid);
122     pos += 2;
123     event[pos++] = connection->scope;
124     (*callback)(HCI_EVENT_PACKET, 0, event, sizeof(event));
125 }
126 
127 
128 static void avrcp_browsing_target_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
129     avrcp_browsing_connection_t * browsing_connection;
130 
131     switch (packet_type) {
132         case L2CAP_DATA_PACKET:{
133             browsing_connection = get_avrcp_browsing_connection_for_l2cap_cid_for_role(AVRCP_TARGET, channel);
134             if (!browsing_connection) break;
135             int pos = 0;
136             uint8_t transport_header = packet[pos++];
137             // Transaction label | Packet_type | C/R | IPID (1 == invalid profile identifier)
138             browsing_connection->transaction_label = transport_header >> 4;
139             avrcp_packet_type_t avctp_packet_type = (transport_header & 0x0F) >> 2;
140             switch (avctp_packet_type){
141                 case AVRCP_SINGLE_PACKET:
142                 case AVRCP_START_PACKET:
143                     browsing_connection->subunit_type = packet[pos++] >> 2;
144                     browsing_connection->subunit_id = 0;
145                     browsing_connection->command_opcode = packet[pos++];
146                     browsing_connection->num_packets = 1;
147                     if (avctp_packet_type == AVRCP_START_PACKET){
148                         browsing_connection->num_packets = packet[pos++];
149                     }
150                     browsing_connection->pdu_id = packet[pos++];
151                     break;
152                 default:
153                     break;
154             }
155             switch (avctp_packet_type){
156                 case AVRCP_SINGLE_PACKET:
157                 case AVRCP_END_PACKET:
158                     switch(browsing_connection->pdu_id){
159                         case AVRCP_PDU_ID_GET_FOLDER_ITEMS:
160                             printf("\n");
161                             browsing_connection->scope = packet[pos++];
162                             browsing_connection->start_item = big_endian_read_32(packet, pos);
163                             pos += 4;
164                             browsing_connection->end_item = big_endian_read_32(packet, pos);
165                             pos += 4;
166                             uint8_t attr_count = packet[pos++];
167 
168                             while (attr_count){
169                                 uint32_t attr_id = big_endian_read_32(packet, pos);
170                                 pos += 4;
171                                 browsing_connection->attr_bitmap |= (1 << attr_id);
172                                 attr_count--;
173                             }
174                             avrcp_browsing_target_emit_get_folder_items(avrcp_target_context.browsing_avrcp_callback, channel, browsing_connection);
175                             break;
176                         case AVRCP_PDU_ID_GET_TOTAL_NUMBER_OF_ITEMS:{
177                             // send total num items
178                             browsing_connection->scope = packet[pos++];
179                             avrcp_browsing_target_emit_get_total_num_items(avrcp_target_context.browsing_avrcp_callback, channel, browsing_connection);
180                             break;
181                         }
182                         default:
183                             avrcp_browsing_target_response_general_reject(browsing_connection, AVRCP_STATUS_INVALID_COMMAND);
184                             log_info(" not parsed pdu ID 0x%02x", browsing_connection->pdu_id);
185                             break;
186                     }
187                     browsing_connection->state = AVCTP_CONNECTION_OPENED;
188                     break;
189                 default:
190                     break;
191             }
192             break;
193         }
194 
195         case HCI_EVENT_PACKET:
196             switch (hci_event_packet_get_type(packet)){
197                 case L2CAP_EVENT_CAN_SEND_NOW:
198                     browsing_connection = get_avrcp_browsing_connection_for_l2cap_cid_for_role(AVRCP_TARGET, channel);
199                     if (!browsing_connection) break;
200                     if (browsing_connection->state != AVCTP_W2_SEND_RESPONSE) return;
201                     browsing_connection->state = AVCTP_CONNECTION_OPENED;
202                     avrcp_browsing_target_handle_can_send_now(browsing_connection);
203                     break;
204                 default:
205                     avrcp_browser_packet_handler(packet_type, channel, packet, size, &avrcp_target_context);
206                     break;
207             }
208             break;
209 
210         default:
211             break;
212     }
213 }
214 
215 void avrcp_browsing_target_init(void){
216     avrcp_target_context.browsing_packet_handler = avrcp_browsing_target_packet_handler;
217     l2cap_register_service(&avrcp_browsing_target_packet_handler, PSM_AVCTP_BROWSING, 0xffff, gap_get_security_level());
218 }
219 
220 void avrcp_browsing_target_register_packet_handler(btstack_packet_handler_t callback){
221     btstack_assert(callback != NULL);
222     avrcp_target_context.browsing_avrcp_callback = callback;
223 }
224 
225 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){
226     return avrcp_browsing_connect(bd_addr, AVRCP_TARGET, avrcp_browsing_target_packet_handler, ertm_buffer, size, ertm_config, avrcp_browsing_cid);
227 }
228 
229 uint8_t avrcp_browsing_target_disconnect(uint16_t avrcp_browsing_cid){
230     return avrcp_browsing_disconnect(avrcp_browsing_cid, AVRCP_TARGET);
231 }
232 
233 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){
234     avrcp_connection_t * avrcp_connection = get_avrcp_connection_for_browsing_cid_for_role(AVRCP_TARGET, avrcp_browsing_cid);
235     if (!avrcp_connection){
236         log_error("avrcp_browsing_decline_incoming_connection: could not find a connection.");
237         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
238     }
239     if (!avrcp_connection->browsing_connection){
240         log_error("avrcp_browsing_decline_incoming_connection: no browsing connection.");
241         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
242     }
243 
244     if (avrcp_connection->browsing_connection->state != AVCTP_CONNECTION_W4_ERTM_CONFIGURATION){
245         log_error("avrcp_browsing_decline_incoming_connection: browsing connection in a wrong state.");
246         return ERROR_CODE_COMMAND_DISALLOWED;
247     }
248 
249     avrcp_connection->browsing_connection->state = AVCTP_CONNECTION_W4_L2CAP_CONNECTED;
250     avrcp_connection->browsing_connection->ertm_buffer = ertm_buffer;
251     avrcp_connection->browsing_connection->ertm_buffer_size = size;
252     (void)memcpy(&avrcp_connection->browsing_connection->ertm_config,
253                  ertm_config, sizeof(l2cap_ertm_config_t));
254     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);
255     return ERROR_CODE_SUCCESS;
256 }
257 
258 uint8_t avrcp_browsing_target_decline_incoming_connection(uint16_t avrcp_browsing_cid){
259     avrcp_connection_t * avrcp_connection = get_avrcp_connection_for_browsing_cid_for_role(AVRCP_TARGET, avrcp_browsing_cid);
260     if (!avrcp_connection){
261         log_error("avrcp_browsing_decline_incoming_connection: could not find a connection.");
262         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
263     }
264     if (!avrcp_connection->browsing_connection) return ERROR_CODE_SUCCESS;
265     if (avrcp_connection->browsing_connection->state > AVCTP_CONNECTION_W4_ERTM_CONFIGURATION) return ERROR_CODE_COMMAND_DISALLOWED;
266 
267     l2cap_decline_connection(avrcp_connection->browsing_connection->l2cap_browsing_cid);
268     // free connection
269     btstack_memory_avrcp_browsing_connection_free(avrcp_connection->browsing_connection);
270     avrcp_connection->browsing_connection = NULL;
271     return ERROR_CODE_SUCCESS;
272 }
273 
274 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){
275     avrcp_connection_t * avrcp_connection = get_avrcp_connection_for_browsing_cid_for_role(AVRCP_TARGET, avrcp_browsing_cid);
276     if (!avrcp_connection){
277         log_error("avrcp_browsing_controller_disconnect: could not find a connection.");
278         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
279     }
280 
281     avrcp_browsing_connection_t * connection = avrcp_connection->browsing_connection;
282     if (!connection){
283         log_info("avrcp_subevent_browsing_get_folder_items_response: could not find a connection.");
284         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
285     }
286     if (connection->state != AVCTP_CONNECTION_OPENED) return ERROR_CODE_COMMAND_DISALLOWED;
287 
288     if (connection->state != AVCTP_CONNECTION_OPENED) {
289         log_info("avrcp_subevent_browsing_get_folder_items_response: wrong state.");
290         return ERROR_CODE_COMMAND_DISALLOWED;
291     }
292     int pos = 0;
293 
294     connection->cmd_operands[pos++] = AVRCP_PDU_ID_GET_FOLDER_ITEMS;
295     big_endian_store_16(connection->cmd_operands, pos, attr_list_size);
296     pos += 2;
297 
298     connection->cmd_operands[pos++] = AVRCP_STATUS_SUCCESS;
299     big_endian_store_16(connection->cmd_operands, pos, uid_counter);
300     pos += 2;
301 
302     // TODO: fragmentation
303     if (attr_list_size >  sizeof(connection->cmd_operands)){
304         connection->attr_list = attr_list;
305         connection->attr_list_size = attr_list_size;
306         log_info(" todo: list too big, invoke fragmentation");
307         return 1;
308     }
309     (void)memcpy(&connection->cmd_operands[pos], attr_list, attr_list_size);
310     pos += attr_list_size;
311     connection->cmd_operands_length = pos;
312     // printf_hexdump(connection->cmd_operands, connection->cmd_operands_length);
313 
314     connection->state = AVCTP_W2_SEND_RESPONSE;
315     avrcp_browsing_target_request_can_send_now(connection, connection->l2cap_browsing_cid);
316     return ERROR_CODE_SUCCESS;
317 }
318 
319 
320 uint8_t avrcp_subevent_browsing_get_total_num_items_response(uint16_t avrcp_browsing_cid, uint16_t uid_counter, uint32_t total_num_items){
321     avrcp_connection_t * avrcp_connection = get_avrcp_connection_for_browsing_cid_for_role(AVRCP_TARGET, avrcp_browsing_cid);
322     if (!avrcp_connection){
323         log_error("avrcp_browsing_controller_disconnect: could not find a connection.");
324         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
325     }
326 
327     avrcp_browsing_connection_t * connection = avrcp_connection->browsing_connection;
328     if (!connection){
329         log_info("avrcp_subevent_browsing_get_folder_items_response: could not find a connection.");
330         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
331     }
332     if (connection->state != AVCTP_CONNECTION_OPENED) return ERROR_CODE_COMMAND_DISALLOWED;
333 
334     if (connection->state != AVCTP_CONNECTION_OPENED) {
335         log_info("avrcp_subevent_browsing_get_folder_items_response: wrong state.");
336         return ERROR_CODE_COMMAND_DISALLOWED;
337     }
338 
339     int pos = 0;
340     connection->cmd_operands[pos++] = AVRCP_PDU_ID_GET_TOTAL_NUMBER_OF_ITEMS;
341     big_endian_store_16(connection->cmd_operands, pos, 7);
342     pos += 2;
343     connection->cmd_operands[pos++] = AVRCP_STATUS_SUCCESS;
344     big_endian_store_16(connection->cmd_operands, pos, uid_counter);
345     pos += 2;
346     big_endian_store_32(connection->cmd_operands, pos, total_num_items);
347     pos += 4;
348     connection->cmd_operands_length = pos;
349 
350     connection->state = AVCTP_W2_SEND_RESPONSE;
351     avrcp_browsing_target_request_can_send_now(connection, connection->l2cap_browsing_cid);
352     return ERROR_CODE_SUCCESS;
353 }
354 
355