xref: /btstack/src/classic/avrcp_browsing_target.c (revision 4783d25609a5032739e1b6e67d2236f2d80f2100)
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_browsing.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 
54     // l2cap_reserve_packet_buffer();
55     // uint8_t * packet = l2cap_get_outgoing_buffer();
56     uint8_t packet[300];
57     connection->packet_type = AVRCP_SINGLE_PACKET;
58 
59     packet[pos++] = (connection->transaction_label << 4) | (connection->packet_type << 2) | (AVRCP_RESPONSE_FRAME << 1) | 0;
60     // Profile IDentifier (PID)
61     packet[pos++] = BLUETOOTH_SERVICE_CLASS_AV_REMOTE_CONTROL >> 8;
62     packet[pos++] = BLUETOOTH_SERVICE_CLASS_AV_REMOTE_CONTROL & 0x00FF;
63     (void)memcpy(packet + pos, connection->cmd_operands,
64                  connection->cmd_operands_length);
65 
66     pos += connection->cmd_operands_length;
67     connection->wait_to_send = false;
68     // return l2cap_send_prepared(connection->l2cap_browsing_cid, pos);
69     return l2cap_send(connection->l2cap_browsing_cid, packet, pos);
70 }
71 
72 
73 static uint8_t avrcp_browsing_target_response_general_reject(avrcp_browsing_connection_t * connection, avrcp_status_code_t status){
74     // AVRCP_CTYPE_RESPONSE_REJECTED
75     int pos = 0;
76     connection->cmd_operands[pos++] = AVRCP_PDU_ID_GENERAL_REJECT;
77     // connection->cmd_operands[pos++] = 0;
78     // param length
79     big_endian_store_16(connection->cmd_operands, pos, 1);
80     pos += 2;
81     connection->cmd_operands[pos++] = status;
82     connection->cmd_operands_length = 4;
83     connection->state = AVCTP_W2_SEND_RESPONSE;
84     avrcp_browsing_request_can_send_now(connection, connection->l2cap_browsing_cid);
85     return ERROR_CODE_SUCCESS;
86 }
87 
88 static void avrcp_browsing_target_emit_get_folder_items(btstack_packet_handler_t callback, uint16_t browsing_cid, avrcp_browsing_connection_t * connection){
89     btstack_assert(callback != NULL);
90 
91     uint8_t event[10];
92     int pos = 0;
93     event[pos++] = HCI_EVENT_AVRCP_META;
94     event[pos++] = sizeof(event) - 2;
95     event[pos++] = AVRCP_SUBEVENT_BROWSING_GET_FOLDER_ITEMS;
96     little_endian_store_16(event, pos, browsing_cid);
97     pos += 2;
98     event[pos++] = connection->scope;
99     big_endian_store_32(event, pos, connection->attr_bitmap);
100     pos += 4;
101     (*callback)(HCI_EVENT_PACKET, 0, event, sizeof(event));
102 }
103 
104 static void avrcp_browsing_target_emit_get_total_num_items(btstack_packet_handler_t callback, uint16_t browsing_cid, avrcp_browsing_connection_t * connection){
105     btstack_assert(callback != NULL);
106 
107     uint8_t event[10];
108     int pos = 0;
109     event[pos++] = HCI_EVENT_AVRCP_META;
110     event[pos++] = sizeof(event) - 2;
111     event[pos++] = AVRCP_SUBEVENT_BROWSING_GET_TOTAL_NUM_ITEMS;
112     little_endian_store_16(event, pos, browsing_cid);
113     pos += 2;
114     event[pos++] = connection->scope;
115     (*callback)(HCI_EVENT_PACKET, 0, event, sizeof(event));
116 }
117 
118 
119 static void avrcp_browsing_target_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
120     UNUSED(size);
121     avrcp_browsing_connection_t * browsing_connection;
122 
123     switch (packet_type) {
124         case L2CAP_DATA_PACKET:{
125             browsing_connection = get_avrcp_browsing_connection_for_l2cap_cid_for_role(AVRCP_TARGET, channel);
126             if (!browsing_connection) break;
127             int pos = 0;
128             uint8_t transport_header = packet[pos++];
129             // Transaction label | Packet_type | C/R | IPID (1 == invalid profile identifier)
130             browsing_connection->transaction_label = transport_header >> 4;
131             avrcp_packet_type_t avctp_packet_type = (transport_header & 0x0F) >> 2;
132             switch (avctp_packet_type){
133                 case AVRCP_SINGLE_PACKET:
134                 case AVRCP_START_PACKET:
135                     browsing_connection->subunit_type = packet[pos++] >> 2;
136                     browsing_connection->subunit_id = 0;
137                     browsing_connection->command_opcode = packet[pos++];
138                     browsing_connection->num_packets = 1;
139                     if (avctp_packet_type == AVRCP_START_PACKET){
140                         browsing_connection->num_packets = packet[pos++];
141                     }
142                     browsing_connection->pdu_id = packet[pos++];
143                     break;
144                 default:
145                     break;
146             }
147             switch (avctp_packet_type){
148                 case AVRCP_SINGLE_PACKET:
149                 case AVRCP_END_PACKET:
150                     switch(browsing_connection->pdu_id){
151                         case AVRCP_PDU_ID_GET_FOLDER_ITEMS:
152                             printf("\n");
153                             browsing_connection->scope = packet[pos++];
154                             browsing_connection->start_item = big_endian_read_32(packet, pos);
155                             pos += 4;
156                             browsing_connection->end_item = big_endian_read_32(packet, pos);
157                             pos += 4;
158                             uint8_t attr_count = packet[pos++];
159 
160                             while (attr_count){
161                                 uint32_t attr_id = big_endian_read_32(packet, pos);
162                                 pos += 4;
163                                 browsing_connection->attr_bitmap |= (1 << attr_id);
164                                 attr_count--;
165                             }
166                             avrcp_browsing_target_emit_get_folder_items(avrcp_target_context.browsing_avrcp_callback, channel, browsing_connection);
167                             break;
168                         case AVRCP_PDU_ID_GET_TOTAL_NUMBER_OF_ITEMS:{
169                             // send total num items
170                             browsing_connection->scope = packet[pos++];
171                             avrcp_browsing_target_emit_get_total_num_items(avrcp_target_context.browsing_avrcp_callback, channel, browsing_connection);
172                             break;
173                         }
174                         default:
175                             avrcp_browsing_target_response_general_reject(browsing_connection, AVRCP_STATUS_INVALID_COMMAND);
176                             log_info("not parsed pdu ID 0x%02x", browsing_connection->pdu_id);
177                             break;
178                     }
179                     browsing_connection->state = AVCTP_CONNECTION_OPENED;
180                     break;
181                 default:
182                     break;
183             }
184             break;
185         }
186 
187         case HCI_EVENT_PACKET:
188             switch (hci_event_packet_get_type(packet)){
189                 case L2CAP_EVENT_CAN_SEND_NOW:
190                     browsing_connection = get_avrcp_browsing_connection_for_l2cap_cid_for_role(AVRCP_TARGET, channel);
191                     if (browsing_connection->state != AVCTP_W2_SEND_RESPONSE) return;
192                     browsing_connection->state = AVCTP_CONNECTION_OPENED;
193                     avrcp_browsing_target_handle_can_send_now(browsing_connection);
194                     break;
195                 default:
196                     break;
197             }
198             break;
199 
200         default:
201             break;
202     }
203 }
204 
205 void avrcp_browsing_target_init(void){
206     avrcp_target_context.browsing_packet_handler = avrcp_browsing_target_packet_handler;
207     avrcp_browsing_register_target_packet_handler(avrcp_browsing_target_packet_handler);
208 }
209 
210 void avrcp_browsing_target_register_packet_handler(btstack_packet_handler_t callback){
211     btstack_assert(callback != NULL);
212     avrcp_target_context.browsing_avrcp_callback = callback;
213 }
214 
215 uint8_t avrcp_browsing_target_send_get_folder_items_response(uint16_t avrcp_browsing_cid, uint16_t uid_counter, uint8_t * attr_list, uint16_t attr_list_size){
216     avrcp_connection_t * avrcp_connection = get_avrcp_connection_for_browsing_cid_for_role(AVRCP_TARGET, avrcp_browsing_cid);
217     if (!avrcp_connection){
218         log_error("Could not find an AVRCP Target connection for browsing_cid 0x%02x.", avrcp_browsing_cid);
219         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
220     }
221 
222     avrcp_browsing_connection_t * connection = avrcp_connection->browsing_connection;
223     if (!connection){
224         log_info("Could not find a browsing connection.");
225         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
226     }
227     if (connection->state != AVCTP_CONNECTION_OPENED) return ERROR_CODE_COMMAND_DISALLOWED;
228 
229     // TODO: handle response to SetAddressedPlayer
230 
231     uint16_t pos = 0;
232     connection->cmd_operands[pos++] = AVRCP_PDU_ID_GET_FOLDER_ITEMS;
233     big_endian_store_16(connection->cmd_operands, pos, attr_list_size);
234     pos += 2;
235 
236     connection->cmd_operands[pos++] = AVRCP_STATUS_SUCCESS;
237     big_endian_store_16(connection->cmd_operands, pos, uid_counter);
238     pos += 2;
239 
240     // TODO: fragmentation
241     if (attr_list_size >  sizeof(connection->cmd_operands)){
242         connection->attr_list = attr_list;
243         connection->attr_list_size = attr_list_size;
244         log_info(" todo: list too big, invoke fragmentation");
245         return 1;
246     }
247     (void)memcpy(&connection->cmd_operands[pos], attr_list, attr_list_size);
248     pos += attr_list_size;
249     connection->cmd_operands_length = pos;
250     // printf_hexdump(connection->cmd_operands, connection->cmd_operands_length);
251 
252     connection->state = AVCTP_W2_SEND_RESPONSE;
253     avrcp_browsing_request_can_send_now(connection, connection->l2cap_browsing_cid);
254     return ERROR_CODE_SUCCESS;
255 }
256 
257 
258 uint8_t avrcp_browsing_target_send_get_total_num_items_response(uint16_t avrcp_browsing_cid, uint16_t uid_counter, uint32_t total_num_items){
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("Could not find an AVRCP Target connection for browsing_cid 0x%02x.", avrcp_browsing_cid);
262         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
263     }
264 
265     avrcp_browsing_connection_t * connection = avrcp_connection->browsing_connection;
266     if (!connection){
267         log_info("Could not find a browsing connection.");
268         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
269     }
270     if (connection->state != AVCTP_CONNECTION_OPENED) return ERROR_CODE_COMMAND_DISALLOWED;
271 
272     if (connection->state != AVCTP_CONNECTION_OPENED) {
273         log_info("Browsing connection wrong state.");
274         return ERROR_CODE_COMMAND_DISALLOWED;
275     }
276 
277     int pos = 0;
278     connection->cmd_operands[pos++] = AVRCP_PDU_ID_GET_TOTAL_NUMBER_OF_ITEMS;
279     big_endian_store_16(connection->cmd_operands, pos, 7);
280     pos += 2;
281     connection->cmd_operands[pos++] = AVRCP_STATUS_SUCCESS;
282     big_endian_store_16(connection->cmd_operands, pos, uid_counter);
283     pos += 2;
284     big_endian_store_32(connection->cmd_operands, pos, total_num_items);
285     pos += 4;
286     connection->cmd_operands_length = pos;
287 
288     connection->state = AVCTP_W2_SEND_RESPONSE;
289     avrcp_browsing_request_can_send_now(connection, connection->l2cap_browsing_cid);
290     return ERROR_CODE_SUCCESS;
291 }
292 
293