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