xref: /btstack/src/classic/avrcp_browsing_target.c (revision d81d696e0d61654dba9ace5b810aca4cdd994489)
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 BLUEKITCHEN
24  * GMBH 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 "classic/avrcp.h"
44 #include "classic/avrcp_browsing.h"
45 #include "classic/avrcp_browsing_target.h"
46 #include "classic/avrcp_target.h"
47 #include "classic/avrcp_controller.h"
48 
49 #include "bluetooth_sdp.h"
50 #include "btstack_debug.h"
51 #include "btstack_event.h"
52 
53 static void avrcp_browsing_target_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
54 
avrcp_browsing_target_handle_can_send_now(avrcp_browsing_connection_t * connection)55 static int avrcp_browsing_target_handle_can_send_now(avrcp_browsing_connection_t * connection){
56     int pos = 0;
57 
58     // l2cap_reserve_packet_buffer();
59     // uint8_t * packet = l2cap_get_outgoing_buffer();
60     uint8_t packet[400];
61     connection->packet_type = AVRCP_SINGLE_PACKET;
62 
63     packet[pos++] = (connection->transaction_label << 4) | (connection->packet_type << 2) | (AVRCP_RESPONSE_FRAME << 1) | 0;
64     // Profile IDentifier (PID)
65     packet[pos++] = BLUETOOTH_SERVICE_CLASS_AV_REMOTE_CONTROL >> 8;
66     packet[pos++] = BLUETOOTH_SERVICE_CLASS_AV_REMOTE_CONTROL & 0x00FF;
67     (void)memcpy(packet + pos, connection->cmd_operands,
68                  connection->cmd_operands_length);
69 
70     pos += connection->cmd_operands_length;
71     connection->wait_to_send = false;
72     // return l2cap_send_prepared(connection->l2cap_browsing_cid, pos);
73     return l2cap_send(connection->l2cap_browsing_cid, packet, pos);
74 }
75 
76 
avrcp_browsing_target_response_general_reject(avrcp_browsing_connection_t * connection,avrcp_status_code_t status)77 static uint8_t avrcp_browsing_target_response_general_reject(avrcp_browsing_connection_t * connection, avrcp_status_code_t status){
78     // AVRCP_CTYPE_RESPONSE_REJECTED
79     int pos = 0;
80     connection->cmd_operands[pos++] = AVRCP_PDU_ID_GENERAL_REJECT;
81     // connection->message_body[pos++] = 0;
82     // param length
83     big_endian_store_16(connection->cmd_operands, pos, 1);
84     pos += 2;
85     connection->cmd_operands[pos++] = status;
86     connection->cmd_operands_length = 4;
87     connection->state = AVCTP_W2_SEND_RESPONSE;
88     avrcp_browsing_request_can_send_now(connection, connection->l2cap_browsing_cid);
89     return ERROR_CODE_SUCCESS;
90 }
91 
avrcp_browsing_target_emit_get_folder_items(btstack_packet_handler_t callback,uint16_t browsing_cid,avrcp_browsing_connection_t * connection)92 static void avrcp_browsing_target_emit_get_folder_items(btstack_packet_handler_t callback, uint16_t browsing_cid, avrcp_browsing_connection_t * connection){
93     btstack_assert(callback != NULL);
94 
95     uint8_t event[18];
96     int pos = 0;
97     event[pos++] = HCI_EVENT_AVRCP_META;
98     event[pos++] = sizeof(event) - 2;
99     event[pos++] = AVRCP_SUBEVENT_BROWSING_GET_FOLDER_ITEMS;
100     little_endian_store_16(event, pos, browsing_cid);
101     pos += 2;
102     event[pos++] = connection->scope;
103     little_endian_store_32(event, pos, connection->start_item);
104     pos += 4;
105     little_endian_store_32(event, pos, connection->end_item);
106     pos += 4;
107     little_endian_store_32(event, pos, connection->attr_bitmap);
108     pos += 4;
109     (*callback)(HCI_EVENT_PACKET, 0, event, pos);
110 }
111 
avrcp_browsing_target_emit_search(btstack_packet_handler_t callback,uint16_t browsing_cid,avrcp_browsing_connection_t * connection)112 static void avrcp_browsing_target_emit_search(btstack_packet_handler_t callback, uint16_t browsing_cid, avrcp_browsing_connection_t * connection){
113     btstack_assert(callback != NULL);
114 
115     uint8_t event[11 + AVRCP_SEARCH_STRING_MAX_LENGTH];
116     int pos = 0;
117     event[pos++] = HCI_EVENT_AVRCP_META;
118     event[pos++] = sizeof(event) - 2;
119     event[pos++] = AVRCP_SUBEVENT_BROWSING_SEARCH;
120     little_endian_store_16(event, pos, browsing_cid);
121     pos += 2;
122     little_endian_store_16(event, pos, connection->target_search_characterset);
123     pos += 2;
124     little_endian_store_16(event, pos, connection->target_search_str_len);
125     pos += 2;
126     uint16_t target_search_str_len = btstack_min(AVRCP_SEARCH_STRING_MAX_LENGTH, strlen(connection->target_search_str));
127     little_endian_store_16(event, pos, target_search_str_len);
128     pos += 2;
129     if (target_search_str_len > 0){
130         memcpy(&event[pos], connection->target_search_str, target_search_str_len);
131         connection->target_search_str[target_search_str_len - 1] = 0;
132         pos += target_search_str_len;
133     }
134     (*callback)(HCI_EVENT_PACKET, 0, event, pos);
135 }
136 
avrcp_browsing_target_emit_get_total_num_items(btstack_packet_handler_t callback,uint16_t browsing_cid,avrcp_browsing_connection_t * connection)137 static void avrcp_browsing_target_emit_get_total_num_items(btstack_packet_handler_t callback, uint16_t browsing_cid, avrcp_browsing_connection_t * connection){
138     btstack_assert(callback != NULL);
139 
140     uint8_t event[10];
141     int pos = 0;
142     event[pos++] = HCI_EVENT_AVRCP_META;
143     event[pos++] = sizeof(event) - 2;
144     event[pos++] = AVRCP_SUBEVENT_BROWSING_GET_TOTAL_NUM_ITEMS;
145     little_endian_store_16(event, pos, browsing_cid);
146     pos += 2;
147     event[pos++] = connection->scope;
148     (*callback)(HCI_EVENT_PACKET, 0, event, pos);
149 }
150 
avrcp_browsing_target_emit_set_browsed_player(btstack_packet_handler_t callback,uint16_t browsing_cid,uint16_t browsed_player_id)151 static void avrcp_browsing_target_emit_set_browsed_player(btstack_packet_handler_t callback, uint16_t browsing_cid, uint16_t browsed_player_id){
152     btstack_assert(callback != NULL);
153 
154     uint8_t event[10];
155     int pos = 0;
156     event[pos++] = HCI_EVENT_AVRCP_META;
157     event[pos++] = sizeof(event) - 2;
158     event[pos++] = AVRCP_SUBEVENT_BROWSING_SET_BROWSED_PLAYER;
159     little_endian_store_16(event, pos, browsing_cid);
160     pos += 2;
161     little_endian_store_16(event, pos, browsed_player_id);
162     pos += 2;
163     (*callback)(HCI_EVENT_PACKET, 0, event, pos);
164 }
165 
avrcp_browsing_target_emit_change_path(btstack_packet_handler_t callback,uint16_t browsing_cid,uint16_t uid_counter,avrcp_browsing_direction_t direction,uint8_t * item_id)166 static void avrcp_browsing_target_emit_change_path(btstack_packet_handler_t callback, uint16_t browsing_cid, uint16_t uid_counter, avrcp_browsing_direction_t direction, uint8_t * item_id){
167     btstack_assert(callback != NULL);
168 
169     uint8_t event[19];
170     int pos = 0;
171     event[pos++] = HCI_EVENT_AVRCP_META;
172     event[pos++] = sizeof(event) - 2;
173     event[pos++] = AVRCP_SUBEVENT_BROWSING_CHANGE_PATH;
174     little_endian_store_16(event, pos, browsing_cid);
175     pos += 2;
176     little_endian_store_16(event, pos, uid_counter);
177     pos += 2;
178     event[pos++] = direction;
179     memcpy(&event[pos], item_id, 8);
180     pos += 8;
181     (*callback)(HCI_EVENT_PACKET, 0, event, pos);
182 }
183 
avrcp_browsing_target_emit_get_item_attributes(btstack_packet_handler_t callback,uint16_t browsing_cid,uint16_t uid_counter,uint8_t scope,uint8_t * item_id,uint8_t attr_num,uint8_t * attr_list)184 static void avrcp_browsing_target_emit_get_item_attributes(btstack_packet_handler_t callback, uint16_t browsing_cid, uint16_t uid_counter, uint8_t scope, uint8_t * item_id, uint8_t attr_num, uint8_t * attr_list){
185     btstack_assert(callback != NULL);
186     btstack_assert(attr_num <= AVRCP_MEDIA_ATTR_NUM);
187 
188     uint8_t event[19 + 4 * AVRCP_MEDIA_ATTR_NUM];
189     int pos = 0;
190     event[pos++] = HCI_EVENT_AVRCP_META;
191     event[pos++] = sizeof(event) - 2;
192     event[pos++] = AVRCP_SUBEVENT_BROWSING_GET_ITEM_ATTRIBUTES;
193     little_endian_store_16(event, pos, browsing_cid);
194     pos += 2;
195     little_endian_store_16(event, pos, uid_counter);
196     pos += 2;
197     event[pos++] = scope;
198     memcpy(&event[pos], item_id, 8);
199     pos += 8;
200     uint16_t attr_len = attr_num * 4;
201     little_endian_store_16(event, pos, attr_len);
202     pos += 2;
203 
204     memcpy(&event[pos], attr_list, attr_len);
205     pos += attr_len;
206     (*callback)(HCI_EVENT_PACKET, 0, event, pos);
207 }
208 
avrcp_browsing_target_packet_handler(uint8_t packet_type,uint16_t channel,uint8_t * packet,uint16_t size)209 static void avrcp_browsing_target_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
210     UNUSED(size);
211     avrcp_browsing_connection_t * browsing_connection;
212 
213     switch (packet_type) {
214         case L2CAP_DATA_PACKET:{
215             browsing_connection = avrcp_get_browsing_connection_for_l2cap_cid_for_role(AVRCP_TARGET, channel);
216             if (!browsing_connection) break;
217             int pos = 0;
218             uint8_t transport_header = packet[pos++];
219             // Transaction label | Packet_type | C/R | IPID (1 == invalid profile identifier)
220             browsing_connection->transaction_label = transport_header >> 4;
221             avctp_packet_type_t avctp_packet_type = (avctp_packet_type_t)((transport_header & 0x0F) >> 2);
222             switch (avctp_packet_type){
223                 case AVCTP_SINGLE_PACKET:
224                 case AVCTP_START_PACKET:
225                     browsing_connection->subunit_type = packet[pos++] >> 2;
226                     browsing_connection->subunit_id = 0;
227                     browsing_connection->command_opcode = packet[pos++];
228                     browsing_connection->num_packets = 1;
229                     if (avctp_packet_type == AVCTP_START_PACKET){
230                         browsing_connection->num_packets = packet[pos++];
231                     }
232                     browsing_connection->pdu_id = packet[pos++];
233                     uint16_t parameter_length = big_endian_read_16(packet, pos);
234                     pos += 2;
235 
236                     switch(browsing_connection->pdu_id){
237                         case AVRCP_PDU_ID_SEARCH:{
238                             if (parameter_length < 4){
239                                 avrcp_browsing_target_response_general_reject(browsing_connection, AVRCP_STATUS_INVALID_COMMAND);
240                                 break;
241                             }
242                             browsing_connection->target_search_characterset = big_endian_read_16(packet, pos);
243                             pos += 2;
244                             browsing_connection->target_search_str_len = big_endian_read_16(packet, pos);
245                             pos += 2;
246                             browsing_connection->target_search_str = (char *) &packet[pos];
247 
248                             if (parameter_length < (4 + browsing_connection->target_search_str_len)){
249                                 avrcp_browsing_target_response_general_reject(browsing_connection, AVRCP_STATUS_INVALID_COMMAND);
250                                 break;
251                             }
252 
253                             uint16_t string_len = strlen(browsing_connection->target_search_str);
254                             if ((browsing_connection->target_search_str_len != string_len) || (browsing_connection->target_search_str_len > (size-pos))){
255                                 avrcp_browsing_target_response_general_reject(browsing_connection, AVRCP_STATUS_INVALID_PARAMETER);
256                                 break;
257                             }
258                             avrcp_browsing_target_emit_search(avrcp_target_context.browsing_avrcp_callback, channel, browsing_connection);
259                             break;
260                         }
261                         case AVRCP_PDU_ID_GET_FOLDER_ITEMS:
262                             if (parameter_length < 10){
263                                 avrcp_browsing_target_response_general_reject(browsing_connection, AVRCP_STATUS_INVALID_COMMAND);
264                                 break;
265                             }
266 
267                             browsing_connection->scope = packet[pos++];
268                             browsing_connection->start_item = big_endian_read_32(packet, pos);
269                             pos += 4;
270                             browsing_connection->end_item = big_endian_read_32(packet, pos);
271                             pos += 4;
272                             uint8_t attr_count = packet[pos++];
273                             browsing_connection->attr_bitmap = 0;
274 
275                             while (attr_count){
276                                 uint32_t attr_id = big_endian_read_32(packet, pos);
277                                 pos += 4;
278                                 browsing_connection->attr_bitmap |= (1 << attr_id);
279                                 attr_count--;
280                             }
281                             avrcp_browsing_target_emit_get_folder_items(avrcp_target_context.browsing_avrcp_callback, channel, browsing_connection);
282                             break;
283 
284                         case AVRCP_PDU_ID_GET_TOTAL_NUMBER_OF_ITEMS:{
285                             // send total num items
286                             if (parameter_length != 1){
287                                 avrcp_browsing_target_response_general_reject(browsing_connection, AVRCP_STATUS_INVALID_SCOPE);
288                                 break;
289                             }
290 
291                             browsing_connection->scope = packet[pos++];
292                             avrcp_browsing_target_emit_get_total_num_items(avrcp_target_context.browsing_avrcp_callback, channel, browsing_connection);
293                             break;
294                         }
295                         case AVRCP_PDU_ID_SET_BROWSED_PLAYER:
296                             // player_id (2)
297                             if (parameter_length != 2){
298                                 avrcp_browsing_target_response_general_reject(browsing_connection, AVRCP_STATUS_INVALID_COMMAND);
299                                 break;
300                             }
301                             if ( (pos + 2) > size ){
302                                 avrcp_browsing_target_response_general_reject(browsing_connection, AVRCP_STATUS_INVALID_PLAYER_ID);
303                                 break;
304                             }
305                             avrcp_browsing_target_emit_set_browsed_player(avrcp_target_context.browsing_avrcp_callback, channel, big_endian_read_16(packet, pos));
306                             break;
307 
308                         case AVRCP_PDU_ID_CHANGE_PATH:
309                             // one level up or down in the virtual filesystem
310                             if (parameter_length != 11){
311                                 avrcp_browsing_target_response_general_reject(browsing_connection, AVRCP_STATUS_INVALID_COMMAND);
312                                 break;
313                             }
314                             browsing_connection->uid_counter = big_endian_read_16(packet, pos);
315                             pos += 2;
316                             browsing_connection->direction = (avrcp_browsing_direction_t)packet[pos++];
317 
318                             if (browsing_connection->direction > AVRCP_BROWSING_DIRECTION_FOLDER_RFU){
319                                 avrcp_browsing_target_response_general_reject(browsing_connection, AVRCP_STATUS_INVALID_DIRECTION);
320                                 break;
321                             }
322                             memcpy(browsing_connection->item_uid, &packet[pos], 8);
323                             avrcp_browsing_target_emit_change_path(avrcp_target_context.browsing_avrcp_callback, channel, browsing_connection->uid_counter, browsing_connection->direction, browsing_connection->item_uid);
324                             break;
325 
326                         case AVRCP_PDU_ID_GET_ITEM_ATTRIBUTES:{
327                             if (parameter_length < 12){
328                                 avrcp_browsing_target_response_general_reject(browsing_connection, AVRCP_STATUS_INVALID_COMMAND);
329                                 break;
330                             }
331 
332                             browsing_connection->scope = packet[pos++];
333                             memcpy(browsing_connection->item_uid, &packet[pos], 8);
334                             pos += 8;
335                             browsing_connection->uid_counter = big_endian_read_16(packet, pos);
336                             pos += 2;
337                             browsing_connection->attr_list_size = packet[pos++];
338                             browsing_connection->attr_list = &packet[pos];
339 
340                             avrcp_browsing_target_emit_get_item_attributes(avrcp_target_context.browsing_avrcp_callback, channel, browsing_connection->uid_counter,
341                                                                            browsing_connection->scope, browsing_connection->item_uid, browsing_connection->attr_list_size, browsing_connection->attr_list);
342                             break;
343                         }
344 
345                         default:
346                             avrcp_browsing_target_response_general_reject(browsing_connection, AVRCP_STATUS_INVALID_COMMAND);
347                             log_info("not parsed pdu ID 0x%02x", browsing_connection->pdu_id);
348                             break;
349                     }
350                     browsing_connection->state = AVCTP_CONNECTION_OPENED;
351                     break;
352 
353                 default:
354                     break;
355             }
356             break;
357         }
358 
359         case HCI_EVENT_PACKET:
360             switch (hci_event_packet_get_type(packet)){
361                 case L2CAP_EVENT_CAN_SEND_NOW:
362                     browsing_connection = avrcp_get_browsing_connection_for_l2cap_cid_for_role(AVRCP_TARGET, channel);
363                     if (browsing_connection->state != AVCTP_W2_SEND_RESPONSE) return;
364                     browsing_connection->state = AVCTP_CONNECTION_OPENED;
365                     avrcp_browsing_target_handle_can_send_now(browsing_connection);
366                     break;
367                 default:
368                     break;
369             }
370             break;
371 
372         default:
373             break;
374     }
375 }
376 
avrcp_browsing_target_init(void)377 void avrcp_browsing_target_init(void){
378     avrcp_target_context.browsing_packet_handler = avrcp_browsing_target_packet_handler;
379     avrcp_browsing_register_target_packet_handler(avrcp_browsing_target_packet_handler);
380 }
381 
avrcp_browsing_target_deinit(void)382 void avrcp_browsing_target_deinit(void){
383     avrcp_controller_context.browsing_packet_handler = NULL;
384 }
385 
avrcp_browsing_target_register_packet_handler(btstack_packet_handler_t callback)386 void avrcp_browsing_target_register_packet_handler(btstack_packet_handler_t callback){
387     btstack_assert(callback != NULL);
388     avrcp_target_context.browsing_avrcp_callback = callback;
389 }
390 
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,uint16_t num_items)391 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, uint16_t num_items){
392     avrcp_connection_t * avrcp_connection = avrcp_get_connection_for_browsing_cid_for_role(AVRCP_TARGET, avrcp_browsing_cid);
393     if (!avrcp_connection){
394         log_error("Could not find an AVRCP Target connection for browsing_cid 0x%02x.", avrcp_browsing_cid);
395         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
396     }
397 
398     avrcp_browsing_connection_t * connection = avrcp_connection->browsing_connection;
399     if (!connection){
400         log_info("Could not find a browsing connection.");
401         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
402     }
403     if (connection->state != AVCTP_CONNECTION_OPENED){
404         return ERROR_CODE_COMMAND_DISALLOWED;
405     }
406 
407     // TODO: handle response to SetAddressedPlayer
408 
409     uint16_t pos = 0;
410     connection->cmd_operands[pos++] = AVRCP_PDU_ID_GET_FOLDER_ITEMS;
411     uint8_t param_length = 5;
412     uint8_t param_length_pos = pos;
413     pos += 2;
414 
415     avrcp_status_code_t status = AVRCP_STATUS_SUCCESS;
416     uint8_t status_pos = pos;
417     pos++;
418 
419     big_endian_store_16(connection->cmd_operands, pos, uid_counter);
420     pos += 2;
421 
422     // TODO: fragmentation
423     if (attr_list_size >  sizeof(connection->cmd_operands)){
424         connection->attr_list = attr_list;
425         connection->attr_list_size = attr_list_size;
426         log_info(" todo: list too big, invoke fragmentation");
427         return 1;
428     }
429 
430     uint16_t items_byte_len = 0;
431     if (connection->start_item < num_items) {
432         if (connection->end_item < num_items) {
433             items_byte_len =  connection->end_item - connection->start_item + 1;
434         } else {
435             items_byte_len = num_items - connection->start_item;
436         }
437 
438     } else {
439         big_endian_store_16(connection->cmd_operands, pos, 0);
440         pos += 2;
441     }
442     big_endian_store_16(connection->cmd_operands, pos, items_byte_len);
443     pos += 2;
444     param_length += items_byte_len;
445 
446     if (items_byte_len > 0){
447         (void)memcpy(&connection->cmd_operands[pos], attr_list, attr_list_size);
448         pos += attr_list_size;
449         connection->cmd_operands_length = pos;
450     } else {
451         status = AVRCP_STATUS_RANGE_OUT_OF_BOUNDS;
452         param_length = 1;
453         connection->cmd_operands_length = status_pos + 1;
454     }
455 
456     big_endian_store_16(connection->cmd_operands, param_length_pos, param_length);
457     connection->cmd_operands[status_pos] = status;
458 
459     btstack_assert(pos <= 400);
460 
461 
462     connection->state = AVCTP_W2_SEND_RESPONSE;
463     avrcp_browsing_request_can_send_now(connection, connection->l2cap_browsing_cid);
464     return ERROR_CODE_SUCCESS;
465 }
466 
avrcp_browsing_target_send_change_path_response(uint16_t avrcp_browsing_cid,avrcp_status_code_t status,uint32_t num_items)467 uint8_t avrcp_browsing_target_send_change_path_response(uint16_t avrcp_browsing_cid, avrcp_status_code_t status, uint32_t num_items){
468     avrcp_connection_t * avrcp_connection = avrcp_get_connection_for_browsing_cid_for_role(AVRCP_TARGET, avrcp_browsing_cid);
469     if (!avrcp_connection){
470         log_error("Could not find an AVRCP Target connection for browsing_cid 0x%02x.", avrcp_browsing_cid);
471         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
472     }
473 
474     avrcp_browsing_connection_t * connection = avrcp_connection->browsing_connection;
475     if (!connection){
476         log_info("Could not find a browsing connection.");
477         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
478     }
479     if (connection->state != AVCTP_CONNECTION_OPENED){
480         return ERROR_CODE_COMMAND_DISALLOWED;
481     }
482 
483     uint16_t pos = 0;
484     uint16_t param_length = (status == AVRCP_STATUS_SUCCESS) ? 5 : 1;
485     connection->cmd_operands[pos++] = AVRCP_PDU_ID_CHANGE_PATH;
486 
487     // Param length
488     big_endian_store_16(connection->cmd_operands, pos, param_length);
489     pos += 2;
490     connection->cmd_operands[pos++] = status;
491 
492     if (status == AVRCP_STATUS_SUCCESS){
493         big_endian_store_32(connection->cmd_operands, pos, num_items);
494         pos += 4;
495     }
496 
497     connection->cmd_operands_length = pos;
498     connection->state = AVCTP_W2_SEND_RESPONSE;
499     avrcp_browsing_request_can_send_now(connection, connection->l2cap_browsing_cid);
500     return ERROR_CODE_SUCCESS;
501 }
502 
avrcp_browsing_target_send_get_item_attributes_response(uint16_t avrcp_browsing_cid,avrcp_status_code_t status,uint8_t * attr_list,uint16_t attr_list_size,uint8_t num_items)503 uint8_t avrcp_browsing_target_send_get_item_attributes_response(uint16_t avrcp_browsing_cid, avrcp_status_code_t status, uint8_t * attr_list, uint16_t attr_list_size, uint8_t num_items){
504     avrcp_connection_t * avrcp_connection = avrcp_get_connection_for_browsing_cid_for_role(AVRCP_TARGET, avrcp_browsing_cid);
505     if (!avrcp_connection){
506         log_error("Could not find an AVRCP Target connection for browsing_cid 0x%02x.", avrcp_browsing_cid);
507         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
508     }
509 
510     avrcp_browsing_connection_t * connection = avrcp_connection->browsing_connection;
511     if (!connection){
512         log_info("Could not find a browsing connection.");
513         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
514     }
515     if (connection->state != AVCTP_CONNECTION_OPENED){
516         return ERROR_CODE_COMMAND_DISALLOWED;
517     }
518 
519     // TODO: fragmentation
520     if (attr_list_size >  (sizeof(connection->cmd_operands) - 5)){
521         connection->attr_list = attr_list;
522         connection->attr_list_size = attr_list_size;
523         log_info(" todo: list too big, invoke fragmentation");
524         return 1;
525     }
526 
527     uint16_t pos = 0;
528     connection->cmd_operands[pos++] = AVRCP_PDU_ID_GET_ITEM_ATTRIBUTES;
529 
530     uint8_t param_length_pos = pos;
531     big_endian_store_16(connection->cmd_operands, pos, 1);
532     pos += 2;
533     connection->cmd_operands[pos++] = status;
534 
535     if (status != AVRCP_STATUS_SUCCESS){
536         connection->cmd_operands_length = pos;
537         connection->state = AVCTP_W2_SEND_RESPONSE;
538         avrcp_browsing_request_can_send_now(connection, connection->l2cap_browsing_cid);
539         return ERROR_CODE_SUCCESS;
540     }
541 
542     connection->cmd_operands[pos++] = num_items;
543     (void)memcpy(&connection->cmd_operands[pos], attr_list, attr_list_size);
544     pos += attr_list_size;
545 
546     big_endian_store_16(connection->cmd_operands, param_length_pos, pos - 3);
547     connection->cmd_operands_length = pos;
548     connection->state = AVCTP_W2_SEND_RESPONSE;
549     avrcp_browsing_request_can_send_now(connection, connection->l2cap_browsing_cid);
550     return ERROR_CODE_SUCCESS;
551 }
552 
avrcp_browsing_target_send_accept_set_browsed_player(uint16_t avrcp_browsing_cid,uint16_t uid_counter,uint16_t browsed_player_id,uint8_t * response,uint16_t response_size)553 uint8_t avrcp_browsing_target_send_accept_set_browsed_player(uint16_t avrcp_browsing_cid, uint16_t uid_counter, uint16_t browsed_player_id, uint8_t * response, uint16_t response_size){
554     avrcp_connection_t * avrcp_connection = avrcp_get_connection_for_browsing_cid_for_role(AVRCP_TARGET, avrcp_browsing_cid);
555     if (!avrcp_connection){
556         log_error("Could not find an AVRCP Target connection for browsing_cid 0x%02x.", avrcp_browsing_cid);
557         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
558     }
559 
560     avrcp_browsing_connection_t * connection = avrcp_connection->browsing_connection;
561     if (!connection){
562         log_info("Could not find a browsing connection.");
563         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
564     }
565 
566     if (connection->state != AVCTP_CONNECTION_OPENED) {
567         log_info("Browsing connection wrong state.");
568         return ERROR_CODE_COMMAND_DISALLOWED;
569     }
570 
571     connection->browsed_player_id = browsed_player_id;
572 
573     uint16_t pos = 0;
574     connection->cmd_operands[pos++] = AVRCP_PDU_ID_SET_BROWSED_PLAYER;
575     big_endian_store_16(connection->cmd_operands, pos, response_size + 2 + 1); // uuid counter + status
576     pos += 2;
577 
578     connection->cmd_operands[pos++] = AVRCP_STATUS_SUCCESS;
579     big_endian_store_16(connection->cmd_operands, pos, uid_counter);
580     pos += 2;
581 
582     // TODO: fragmentation
583     if (response_size >  sizeof(connection->cmd_operands)){
584         connection->attr_list = response;
585         connection->attr_list_size = response_size;
586         log_info(" todo: list too big, invoke fragmentation");
587         return 1;
588     }
589 
590     (void)memcpy(&connection->cmd_operands[pos], response, response_size);
591     pos += response_size;
592 	btstack_assert(pos <= 255);
593     connection->cmd_operands_length = (uint8_t) pos;
594 
595     connection->state = AVCTP_W2_SEND_RESPONSE;
596     avrcp_browsing_request_can_send_now(connection, connection->l2cap_browsing_cid);
597     return ERROR_CODE_SUCCESS;
598 }
599 
avrcp_browsing_target_send_reject_set_browsed_player(uint16_t avrcp_browsing_cid,avrcp_status_code_t status)600 uint8_t avrcp_browsing_target_send_reject_set_browsed_player(uint16_t avrcp_browsing_cid, avrcp_status_code_t status){
601     avrcp_connection_t * avrcp_connection = avrcp_get_connection_for_browsing_cid_for_role(AVRCP_TARGET, avrcp_browsing_cid);
602     if (!avrcp_connection){
603         log_error("Could not find an AVRCP Target connection for browsing_cid 0x%02x.", avrcp_browsing_cid);
604         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
605     }
606 
607     avrcp_browsing_connection_t * connection = avrcp_connection->browsing_connection;
608     if (!connection){
609         log_info("Could not find a browsing connection.");
610         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
611     }
612 
613     if (connection->state != AVCTP_CONNECTION_OPENED) {
614         log_info("Browsing connection wrong state.");
615         return ERROR_CODE_COMMAND_DISALLOWED;
616     }
617 
618     int pos = 0;
619     connection->cmd_operands[pos++] = AVRCP_PDU_ID_SET_BROWSED_PLAYER;
620     big_endian_store_16(connection->cmd_operands, pos, 1);
621     pos += 2;
622     connection->cmd_operands[pos++] = status;
623     connection->cmd_operands_length = pos;
624 
625     connection->state = AVCTP_W2_SEND_RESPONSE;
626     avrcp_browsing_request_can_send_now(connection, connection->l2cap_browsing_cid);
627     return ERROR_CODE_SUCCESS;
628 }
629 
avrcp_browsing_target_send_get_total_num_items_response(uint16_t avrcp_browsing_cid,uint16_t uid_counter,uint32_t total_num_items)630 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){
631     avrcp_connection_t * avrcp_connection = avrcp_get_connection_for_browsing_cid_for_role(AVRCP_TARGET, avrcp_browsing_cid);
632     if (!avrcp_connection){
633         log_error("Could not find an AVRCP Target connection for browsing_cid 0x%02x.", avrcp_browsing_cid);
634         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
635     }
636 
637     avrcp_browsing_connection_t * connection = avrcp_connection->browsing_connection;
638     if (!connection){
639         log_info("Could not find a browsing connection.");
640         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
641     }
642 
643     if (connection->state != AVCTP_CONNECTION_OPENED) {
644         log_info("Browsing connection wrong state.");
645         return ERROR_CODE_COMMAND_DISALLOWED;
646     }
647 
648     int pos = 0;
649     connection->cmd_operands[pos++] = AVRCP_PDU_ID_GET_TOTAL_NUMBER_OF_ITEMS;
650     big_endian_store_16(connection->cmd_operands, pos, 7);
651     pos += 2;
652     connection->cmd_operands[pos++] = AVRCP_STATUS_SUCCESS;
653     big_endian_store_16(connection->cmd_operands, pos, uid_counter);
654     pos += 2;
655     big_endian_store_32(connection->cmd_operands, pos, total_num_items);
656     pos += 4;
657     connection->cmd_operands_length = pos;
658 
659     connection->state = AVCTP_W2_SEND_RESPONSE;
660     avrcp_browsing_request_can_send_now(connection, connection->l2cap_browsing_cid);
661     return ERROR_CODE_SUCCESS;
662 }
663 
avrcp_browsing_target_send_search_response(uint16_t avrcp_browsing_cid,avrcp_status_code_t status,uint16_t uid_counter,uint32_t num_items)664 uint8_t avrcp_browsing_target_send_search_response(uint16_t avrcp_browsing_cid, avrcp_status_code_t status, uint16_t uid_counter, uint32_t num_items){
665     avrcp_connection_t * avrcp_connection = avrcp_get_connection_for_browsing_cid_for_role(AVRCP_TARGET, avrcp_browsing_cid);
666     if (!avrcp_connection){
667         log_error("Could not find an AVRCP Target connection for browsing_cid 0x%02x.", avrcp_browsing_cid);
668         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
669     }
670 
671     avrcp_browsing_connection_t * connection = avrcp_connection->browsing_connection;
672     if (!connection){
673         log_info("Could not find a browsing connection.");
674         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
675     }
676     if (connection->state != AVCTP_CONNECTION_OPENED){
677         return ERROR_CODE_COMMAND_DISALLOWED;
678     }
679 
680     uint16_t pos = 0;
681     connection->cmd_operands[pos++] = AVRCP_PDU_ID_SEARCH;
682     // param len
683     big_endian_store_16(connection->cmd_operands, pos, 7);
684     pos += 2;
685     connection->cmd_operands[pos++] = status;
686     big_endian_store_16(connection->cmd_operands, pos, uid_counter);
687     pos += 2;
688 
689 //    if (status != AVRCP_STATUS_SUCCESS){
690 //        connection->cmd_operands_length = pos;
691 //        connection->state = AVCTP_W2_SEND_RESPONSE;
692 //        avrcp_browsing_request_can_send_now(connection, connection->l2cap_browsing_cid);
693 //        return ERROR_CODE_SUCCESS;
694 //    }
695 
696     big_endian_store_32(connection->cmd_operands, pos, num_items);
697     pos += 4;
698     connection->cmd_operands_length = pos;
699     connection->state = AVCTP_W2_SEND_RESPONSE;
700     avrcp_browsing_request_can_send_now(connection, connection->l2cap_browsing_cid);
701     return ERROR_CODE_SUCCESS;
702 }