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 }