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_controller.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_controller.h" 46 47 48 static void avrcp_browsing_controller_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size); 49 50 static int avrcp_browsing_controller_send_get_folder_items_cmd(uint16_t cid, avrcp_browsing_connection_t * connection){ 51 uint8_t command[100]; 52 int pos = 0; 53 // transport header 54 // Transaction label | Packet_type | C/R | IPID (1 == invalid profile identifier) 55 command[pos++] = (connection->transaction_label << 4) | (AVRCP_SINGLE_PACKET << 2) | (AVRCP_COMMAND_FRAME << 1) | 0; 56 // Profile IDentifier (PID) 57 command[pos++] = BLUETOOTH_SERVICE_CLASS_AV_REMOTE_CONTROL >> 8; 58 command[pos++] = BLUETOOTH_SERVICE_CLASS_AV_REMOTE_CONTROL & 0x00FF; 59 command[pos++] = AVRCP_PDU_ID_GET_FOLDER_ITEMS; 60 61 uint32_t attribute_count = 0; 62 uint32_t attributes_to_copy = 0; 63 64 switch (connection->attr_bitmap){ 65 case AVRCP_MEDIA_ATTR_NONE: 66 attribute_count = AVRCP_MEDIA_ATTR_NONE; // 0xFFFFFFFF 67 break; 68 case AVRCP_MEDIA_ATTR_ALL: 69 attribute_count = AVRCP_MEDIA_ATTR_ALL; // 0 70 break; 71 default: 72 attribute_count = count_set_bits_uint32(connection->attr_bitmap & 0xff); 73 attributes_to_copy = attribute_count; 74 break; 75 } 76 big_endian_store_16(command, pos, 9 + 1 + (attribute_count*4)); 77 pos += 2; 78 79 command[pos++] = connection->scope; 80 big_endian_store_32(command, pos, connection->start_item); 81 pos += 4; 82 big_endian_store_32(command, pos, connection->end_item); 83 pos += 4; 84 command[pos++] = attribute_count; 85 86 int bit_position = 1; 87 while (attributes_to_copy){ 88 if (connection->attr_bitmap & (1 << bit_position)){ 89 big_endian_store_32(command, pos, bit_position); 90 pos += 4; 91 attributes_to_copy--; 92 } 93 bit_position++; 94 } 95 return l2cap_send(cid, command, pos); 96 } 97 98 99 static int avrcp_browsing_controller_send_get_item_attributes_cmd(uint16_t cid, avrcp_browsing_connection_t * connection){ 100 uint8_t command[100]; 101 int pos = 0; 102 // transport header 103 // Transaction label | Packet_type | C/R | IPID (1 == invalid profile identifier) 104 command[pos++] = (connection->transaction_label << 4) | (AVRCP_SINGLE_PACKET << 2) | (AVRCP_COMMAND_FRAME << 1) | 0; 105 // Profile IDentifier (PID) 106 command[pos++] = BLUETOOTH_SERVICE_CLASS_AV_REMOTE_CONTROL >> 8; 107 command[pos++] = BLUETOOTH_SERVICE_CLASS_AV_REMOTE_CONTROL & 0x00FF; 108 command[pos++] = AVRCP_PDU_ID_GET_ITEM_ATTRIBUTES; 109 110 uint32_t attribute_count; 111 uint32_t attributes_to_copy = 0; 112 113 switch (connection->attr_bitmap){ 114 case AVRCP_MEDIA_ATTR_NONE: 115 case AVRCP_MEDIA_ATTR_ALL: 116 attribute_count = 0; 117 break; 118 default: 119 attribute_count = count_set_bits_uint32(connection->attr_bitmap & 0xff); 120 attributes_to_copy = attribute_count; 121 break; 122 } 123 124 big_endian_store_16(command, pos, 12 + (attribute_count*4)); 125 pos += 2; 126 127 command[pos++] = connection->scope; 128 (void)memcpy(command + pos, connection->folder_uid, 8); 129 pos += 8; 130 big_endian_store_16(command, pos, connection->uid_counter); 131 pos += 2; 132 command[pos++] = attribute_count; 133 134 int bit_position = 1; 135 while (attributes_to_copy){ 136 if (connection->attr_bitmap & (1 << bit_position)){ 137 big_endian_store_32(command, pos, bit_position); 138 pos += 4; 139 attributes_to_copy--; 140 } 141 bit_position++; 142 } 143 144 return l2cap_send(cid, command, pos); 145 } 146 147 148 static int avrcp_browsing_controller_send_change_path_cmd(uint16_t cid, avrcp_browsing_connection_t * connection){ 149 uint8_t command[100]; 150 int pos = 0; 151 // transport header 152 // Transaction label | Packet_type | C/R | IPID (1 == invalid profile identifier) 153 command[pos++] = (connection->transaction_label << 4) | (AVRCP_SINGLE_PACKET << 2) | (AVRCP_COMMAND_FRAME << 1) | 0; 154 // Profile IDentifier (PID) 155 command[pos++] = BLUETOOTH_SERVICE_CLASS_AV_REMOTE_CONTROL >> 8; 156 command[pos++] = BLUETOOTH_SERVICE_CLASS_AV_REMOTE_CONTROL & 0x00FF; 157 command[pos++] = AVRCP_PDU_ID_CHANGE_PATH; 158 159 big_endian_store_16(command, pos, 11); 160 pos += 2; 161 pos += 2; 162 command[pos++] = connection->direction; 163 (void)memcpy(command + pos, connection->folder_uid, 8); 164 pos += 8; 165 return l2cap_send(cid, command, pos); 166 } 167 168 static int avrcp_browsing_controller_send_search_cmd(uint16_t cid, avrcp_browsing_connection_t * connection){ 169 uint8_t command[100]; 170 int pos = 0; 171 // transport header 172 // Transaction label | Packet_type | C/R | IPID (1 == invalid profile identifier) 173 command[pos++] = (connection->transaction_label << 4) | (AVRCP_SINGLE_PACKET << 2) | (AVRCP_COMMAND_FRAME << 1) | 0; 174 // Profile IDentifier (PID) 175 command[pos++] = BLUETOOTH_SERVICE_CLASS_AV_REMOTE_CONTROL >> 8; 176 command[pos++] = BLUETOOTH_SERVICE_CLASS_AV_REMOTE_CONTROL & 0x00FF; 177 command[pos++] = AVRCP_PDU_ID_SEARCH; 178 179 big_endian_store_16(command, pos, 4 + connection->search_str_len); 180 pos += 2; 181 182 big_endian_store_16(command, pos, 0x006A); 183 pos += 2; 184 big_endian_store_16(command, pos, connection->search_str_len); 185 pos += 2; 186 187 (void)memcpy(command + pos, connection->search_str, 188 connection->search_str_len); 189 pos += connection->search_str_len; 190 return l2cap_send(cid, command, pos); 191 } 192 193 static int avrcp_browsing_controller_send_set_browsed_player_cmd(uint16_t cid, avrcp_browsing_connection_t * connection){ 194 uint8_t command[100]; 195 int pos = 0; 196 // transport header 197 // Transaction label | Packet_type | C/R | IPID (1 == invalid profile identifier) 198 command[pos++] = (connection->transaction_label << 4) | (AVRCP_SINGLE_PACKET << 2) | (AVRCP_COMMAND_FRAME << 1) | 0; 199 // Profile IDentifier (PID) 200 command[pos++] = BLUETOOTH_SERVICE_CLASS_AV_REMOTE_CONTROL >> 8; 201 command[pos++] = BLUETOOTH_SERVICE_CLASS_AV_REMOTE_CONTROL & 0x00FF; 202 command[pos++] = AVRCP_PDU_ID_SET_BROWSED_PLAYER; 203 204 big_endian_store_16(command, pos, 2); 205 pos += 2; 206 big_endian_store_16(command, pos, connection->browsed_player_id); 207 pos += 2; 208 return l2cap_send(cid, command, pos); 209 } 210 211 static int avrcp_browsing_controller_send_get_total_nr_items_cmd(uint16_t cid, avrcp_browsing_connection_t * connection){ 212 uint8_t command[7]; 213 int pos = 0; 214 // transport header 215 // Transaction label | Packet_type | C/R | IPID (1 == invalid profile identifier) 216 command[pos++] = (connection->transaction_label << 4) | (AVRCP_SINGLE_PACKET << 2) | (AVRCP_COMMAND_FRAME << 1) | 0; 217 // Profile IDentifier (PID) 218 command[pos++] = BLUETOOTH_SERVICE_CLASS_AV_REMOTE_CONTROL >> 8; 219 command[pos++] = BLUETOOTH_SERVICE_CLASS_AV_REMOTE_CONTROL & 0x00FF; 220 command[pos++] = AVRCP_PDU_ID_GET_TOTAL_NUMBER_OF_ITEMS; 221 222 big_endian_store_16(command, pos, 1); 223 pos += 2; 224 command[pos++] = connection->get_total_nr_items_scope; 225 return l2cap_send(cid, command, pos); 226 } 227 228 static void avrcp_browsing_controller_handle_can_send_now(avrcp_browsing_connection_t * connection){ 229 switch (connection->state){ 230 case AVCTP_CONNECTION_OPENED: 231 if (connection->set_browsed_player_id){ 232 connection->state = AVCTP_W2_RECEIVE_RESPONSE; 233 connection->set_browsed_player_id = 0; 234 avrcp_browsing_controller_send_set_browsed_player_cmd(connection->l2cap_browsing_cid, connection); 235 break; 236 } 237 238 if (connection->get_total_nr_items){ 239 connection->state = AVCTP_W2_RECEIVE_RESPONSE; 240 connection->get_total_nr_items = 0; 241 avrcp_browsing_controller_send_get_total_nr_items_cmd(connection->l2cap_browsing_cid, connection); 242 break; 243 } 244 245 if (connection->get_folder_items){ 246 connection->state = AVCTP_W2_RECEIVE_RESPONSE; 247 connection->get_folder_items = 0; 248 avrcp_browsing_controller_send_get_folder_items_cmd(connection->l2cap_browsing_cid, connection); 249 break; 250 } 251 252 if (connection->get_item_attributes){ 253 connection->state = AVCTP_W2_RECEIVE_RESPONSE; 254 connection->get_item_attributes = 0; 255 avrcp_browsing_controller_send_get_item_attributes_cmd(connection->l2cap_browsing_cid, connection); 256 break; 257 } 258 259 if (connection->change_path){ 260 connection->state = AVCTP_W2_RECEIVE_RESPONSE; 261 connection->change_path = 0; 262 avrcp_browsing_controller_send_change_path_cmd(connection->l2cap_browsing_cid, connection); 263 break; 264 } 265 266 if (connection->search){ 267 connection->state = AVCTP_W2_RECEIVE_RESPONSE; 268 connection->search = 0; 269 avrcp_browsing_controller_send_search_cmd(connection->l2cap_browsing_cid, connection); 270 break; 271 } 272 break; 273 default: 274 return; 275 } 276 } 277 278 279 static void avrcp_browsing_controller_emit_done_with_uid_counter(btstack_packet_handler_t callback, uint16_t browsing_cid, uint16_t uid_counter, uint8_t browsing_status, uint8_t bluetooth_status){ 280 btstack_assert(callback != NULL); 281 282 uint8_t event[9]; 283 int pos = 0; 284 event[pos++] = HCI_EVENT_AVRCP_META; 285 event[pos++] = sizeof(event) - 2; 286 event[pos++] = AVRCP_SUBEVENT_BROWSING_DONE; 287 little_endian_store_16(event, pos, browsing_cid); 288 pos += 2; 289 little_endian_store_16(event, pos, uid_counter); 290 pos += 2; 291 event[pos++] = browsing_status; 292 event[pos++] = bluetooth_status; 293 (*callback)(HCI_EVENT_PACKET, 0, event, sizeof(event)); 294 } 295 296 static void avrcp_parser_reset(avrcp_browsing_connection_t * connection){ 297 connection->parser_attribute_header_pos = 0; 298 connection->parsed_attribute_value_offset = 0; 299 connection->parsed_num_attributes = 0; 300 connection->parser_state = AVRCP_PARSER_GET_ATTRIBUTE_HEADER; 301 } 302 303 304 static void avrcp_browsing_parser_process_byte(uint8_t byte, avrcp_browsing_connection_t * connection){ 305 uint8_t prepended_header_size = 1; 306 uint16_t attribute_total_value_len; 307 308 switch(connection->parser_state){ 309 case AVRCP_PARSER_GET_ATTRIBUTE_HEADER: 310 connection->parser_attribute_header[connection->parser_attribute_header_pos++] = byte; 311 if (connection->parser_attribute_header_pos < AVRCP_BROWSING_ITEM_HEADER_LEN) break; 312 313 attribute_total_value_len = big_endian_read_16(connection->parser_attribute_header, 1); 314 connection->parsed_attribute_value[connection->parsed_attribute_value_offset++] = connection->parser_attribute_header[0]; // prepend with item type 315 connection->parsed_attribute_value_len = btstack_min(attribute_total_value_len, AVRCP_MAX_ATTRIBUTTE_SIZE - prepended_header_size); // reduce AVRCP_MAX_ATTRIBUTTE_SIZE for the size ot item type 316 connection->parser_state = AVRCP_PARSER_GET_ATTRIBUTE_VALUE; 317 break; 318 319 case AVRCP_PARSER_GET_ATTRIBUTE_VALUE: 320 connection->parsed_attribute_value[connection->parsed_attribute_value_offset++] = byte; 321 if (connection->parsed_attribute_value_offset < (connection->parsed_attribute_value_len + prepended_header_size)){ 322 break; 323 } 324 if (connection->parsed_attribute_value_offset < big_endian_read_16(connection->parser_attribute_header, 1)){ 325 connection->parser_state = AVRCP_PARSER_IGNORE_REST_OF_ATTRIBUTE_VALUE; 326 break; 327 } 328 connection->parser_state = AVRCP_PARSER_GET_ATTRIBUTE_HEADER; 329 (*avrcp_controller_context.browsing_avrcp_callback)(AVRCP_BROWSING_DATA_PACKET, connection->l2cap_browsing_cid, &connection->parsed_attribute_value[0], connection->parsed_attribute_value_offset); 330 connection->parsed_num_attributes++; 331 connection->parsed_attribute_value_offset = 0; 332 connection->parser_attribute_header_pos = 0; 333 334 if (connection->parsed_num_attributes == connection->num_items){ 335 avrcp_parser_reset(connection); 336 break; 337 } 338 break; 339 340 case AVRCP_PARSER_IGNORE_REST_OF_ATTRIBUTE_VALUE: 341 connection->parsed_attribute_value_offset++; 342 if (connection->parsed_attribute_value_offset < (big_endian_read_16(connection->parser_attribute_header, 1) + prepended_header_size)){ 343 break; 344 } 345 connection->parser_state = AVRCP_PARSER_GET_ATTRIBUTE_HEADER; 346 (*avrcp_controller_context.browsing_avrcp_callback)(AVRCP_BROWSING_DATA_PACKET, connection->l2cap_browsing_cid, &connection->parsed_attribute_value[0], connection->parsed_attribute_value_offset); 347 connection->parsed_num_attributes++; 348 connection->parsed_attribute_value_offset = 0; 349 connection->parser_attribute_header_pos = 0; 350 351 if (connection->parsed_num_attributes == connection->num_items){ 352 avrcp_parser_reset(connection); 353 break; 354 } 355 break; 356 default: 357 break; 358 } 359 } 360 361 static void avrcp_browsing_parse_and_emit_element_attrs(uint8_t * packet, uint16_t num_bytes_to_read, avrcp_browsing_connection_t * connection){ 362 int i; 363 for (i=0;i<num_bytes_to_read;i++){ 364 avrcp_browsing_parser_process_byte(packet[i], connection); 365 } 366 } 367 368 static void avrcp_browsing_controller_emit_failed(btstack_packet_handler_t callback, uint16_t browsing_cid, uint8_t browsing_status, uint8_t bluetooth_status){ 369 avrcp_browsing_controller_emit_done_with_uid_counter(callback, browsing_cid, 0, browsing_status, bluetooth_status); 370 } 371 372 373 static void avrcp_browsing_controller_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ 374 avrcp_browsing_connection_t * browsing_connection; 375 uint8_t transport_header; 376 int pos; 377 switch (packet_type) { 378 case L2CAP_DATA_PACKET: 379 browsing_connection = avrcp_get_browsing_connection_for_l2cap_cid_for_role(AVRCP_CONTROLLER, channel); 380 if (!browsing_connection) break; 381 pos = 0; 382 transport_header = packet[pos++]; 383 // Transaction label | Packet_type | C/R | IPID (1 == invalid profile identifier) 384 browsing_connection->transaction_label = transport_header >> 4; 385 avrcp_packet_type_t avctp_packet_type = (transport_header & 0x0F) >> 2; 386 switch (avctp_packet_type){ 387 case AVRCP_SINGLE_PACKET: 388 case AVRCP_START_PACKET: 389 pos += 2; 390 browsing_connection->num_packets = 1; 391 if (avctp_packet_type == AVRCP_START_PACKET){ 392 browsing_connection->num_packets = packet[pos++]; 393 } 394 if ((pos + 4) > size){ 395 browsing_connection->state = AVCTP_CONNECTION_OPENED; 396 avrcp_browsing_controller_emit_failed(avrcp_controller_context.browsing_avrcp_callback, channel, AVRCP_BROWSING_ERROR_CODE_INVALID_COMMAND, ERROR_CODE_SUCCESS); 397 return; 398 } 399 browsing_connection->pdu_id = packet[pos++]; 400 pos += 2; 401 browsing_connection->browsing_status = packet[pos++]; 402 if (browsing_connection->browsing_status != AVRCP_BROWSING_ERROR_CODE_SUCCESS){ 403 browsing_connection->state = AVCTP_CONNECTION_OPENED; 404 avrcp_browsing_controller_emit_failed(avrcp_controller_context.browsing_avrcp_callback, channel, browsing_connection->browsing_status, ERROR_CODE_SUCCESS); 405 return; 406 } 407 break; 408 default: 409 break; 410 } 411 412 uint32_t i; 413 uint8_t folder_depth; 414 415 switch(browsing_connection->pdu_id){ 416 case AVRCP_PDU_ID_CHANGE_PATH: 417 break; 418 case AVRCP_PDU_ID_SET_ADDRESSED_PLAYER: 419 break; 420 case AVRCP_PDU_ID_GET_TOTAL_NUMBER_OF_ITEMS: 421 break; 422 case AVRCP_PDU_ID_SET_BROWSED_PLAYER: 423 browsing_connection->uid_counter = big_endian_read_16(packet, pos); 424 pos += 2; 425 // num_items 426 pos += 4; 427 // charset 428 pos += 2; 429 folder_depth = packet[pos++]; 430 431 for (i = 0; i < folder_depth; i++){ 432 uint16_t folder_name_length = big_endian_read_16(packet, pos); 433 pos += 2; 434 // reuse packet and add data type as a header 435 packet[pos-1] = AVRCP_BROWSING_MEDIA_ROOT_FOLDER; 436 (*avrcp_controller_context.browsing_avrcp_callback)(AVRCP_BROWSING_DATA_PACKET, channel, packet+pos-1, folder_name_length+1); 437 pos += folder_name_length; 438 } 439 break; 440 441 case AVRCP_PDU_ID_GET_FOLDER_ITEMS:{ 442 switch (avctp_packet_type){ 443 case AVRCP_SINGLE_PACKET: 444 case AVRCP_START_PACKET: 445 avrcp_parser_reset(browsing_connection); 446 browsing_connection->uid_counter = big_endian_read_16(packet, pos); 447 pos += 2; 448 browsing_connection->num_items = big_endian_read_16(packet, pos); //num_items 449 pos += 2; 450 avrcp_browsing_parse_and_emit_element_attrs(packet+pos, size-pos, browsing_connection); 451 break; 452 453 case AVRCP_CONTINUE_PACKET: 454 avrcp_browsing_parse_and_emit_element_attrs(packet+pos, size-pos, browsing_connection); 455 break; 456 457 case AVRCP_END_PACKET: 458 avrcp_browsing_parse_and_emit_element_attrs(packet+pos, size-pos, browsing_connection); 459 avrcp_parser_reset(browsing_connection); 460 break; 461 default: 462 break; 463 } 464 break; 465 } 466 case AVRCP_PDU_ID_SEARCH: 467 browsing_connection->uid_counter = big_endian_read_16(packet, pos); 468 pos += 2; 469 break; 470 case AVRCP_PDU_ID_GET_ITEM_ATTRIBUTES: 471 packet[pos-1] = AVRCP_BROWSING_MEDIA_ELEMENT_ITEM_ATTRIBUTE; 472 (*avrcp_controller_context.browsing_avrcp_callback)(AVRCP_BROWSING_DATA_PACKET, channel, packet+pos-1, size - pos + 1); 473 break; 474 default: 475 log_info(" not parsed pdu ID 0x%02x", browsing_connection->pdu_id); 476 break; 477 } 478 479 switch (avctp_packet_type){ 480 case AVRCP_SINGLE_PACKET: 481 case AVRCP_END_PACKET: 482 browsing_connection->state = AVCTP_CONNECTION_OPENED; 483 avrcp_browsing_controller_emit_done_with_uid_counter(avrcp_controller_context.browsing_avrcp_callback, channel, browsing_connection->uid_counter, browsing_connection->browsing_status, ERROR_CODE_SUCCESS); 484 break; 485 default: 486 break; 487 } 488 break; 489 490 case HCI_EVENT_PACKET: 491 switch (hci_event_packet_get_type(packet)){ 492 case L2CAP_EVENT_CAN_SEND_NOW: 493 browsing_connection = avrcp_get_browsing_connection_for_l2cap_cid_for_role(AVRCP_CONTROLLER,channel); 494 avrcp_browsing_controller_handle_can_send_now(browsing_connection); 495 break; 496 default: 497 break; 498 } 499 break; 500 501 default: 502 break; 503 } 504 } 505 506 void avrcp_browsing_controller_init(void){ 507 avrcp_controller_context.browsing_packet_handler = avrcp_browsing_controller_packet_handler; 508 avrcp_browsing_register_controller_packet_handler(avrcp_browsing_controller_packet_handler); 509 } 510 511 void avrcp_browsing_controller_deinit(void){ 512 avrcp_controller_context.browsing_packet_handler = NULL; 513 } 514 515 void avrcp_browsing_controller_register_packet_handler(btstack_packet_handler_t callback){ 516 btstack_assert(callback != NULL); 517 avrcp_controller_context.browsing_avrcp_callback = callback; 518 } 519 520 uint8_t avrcp_browsing_controller_get_item_attributes_for_scope(uint16_t avrcp_browsing_cid, uint8_t * uid, uint16_t uid_counter, uint32_t attr_bitmap, avrcp_browsing_scope_t scope){ 521 avrcp_connection_t * avrcp_connection = avrcp_get_connection_for_browsing_cid_for_role(AVRCP_CONTROLLER, avrcp_browsing_cid); 522 if (!avrcp_connection){ 523 log_error("avrcp_browsing_controller_get_item_attributes: could not find a connection."); 524 return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 525 } 526 avrcp_browsing_connection_t * connection = avrcp_connection->browsing_connection; 527 if (connection->state != AVCTP_CONNECTION_OPENED){ 528 log_error("avrcp_browsing_controller_get_item_attributes: connection in wrong state %d, expected %d.", connection->state, AVCTP_CONNECTION_OPENED); 529 return ERROR_CODE_COMMAND_DISALLOWED; 530 } 531 532 connection->get_item_attributes = 1; 533 connection->scope = scope; 534 (void)memcpy(connection->folder_uid, uid, 8); 535 connection->uid_counter = uid_counter; 536 connection->attr_bitmap = attr_bitmap; 537 538 avrcp_browsing_request_can_send_now(connection, connection->l2cap_browsing_cid); 539 return ERROR_CODE_SUCCESS; 540 } 541 542 /** 543 * @brief Retrieve a listing of the contents of a folder. 544 * @param scope 0-player list, 1-virtual file system, 2-search, 3-now playing 545 * @param start_item 546 * @param end_item 547 * @param attribute_count 548 * @param attribute_list 549 **/ 550 static uint8_t avrcp_browsing_controller_get_folder_items(uint16_t avrcp_browsing_cid, avrcp_browsing_scope_t scope, uint32_t start_item, uint32_t end_item, uint32_t attr_bitmap){ 551 avrcp_connection_t * avrcp_connection = avrcp_get_connection_for_browsing_cid_for_role(AVRCP_CONTROLLER, avrcp_browsing_cid); 552 if (!avrcp_connection){ 553 log_error("avrcp_browsing_controller_disconnect: could not find a connection."); 554 return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 555 } 556 avrcp_browsing_connection_t * connection = avrcp_connection->browsing_connection; 557 if (connection->state != AVCTP_CONNECTION_OPENED) { 558 log_error("avrcp_browsing_controller_get_folder_items: connection in wrong state %d, expected %d.", connection->state, AVCTP_CONNECTION_OPENED); 559 return ERROR_CODE_COMMAND_DISALLOWED; 560 } 561 562 connection->get_folder_items = 1; 563 connection->scope = scope; 564 connection->start_item = start_item; 565 connection->end_item = end_item; 566 connection->attr_bitmap = attr_bitmap; 567 568 avrcp_browsing_request_can_send_now(connection, connection->l2cap_browsing_cid); 569 return ERROR_CODE_SUCCESS; 570 } 571 572 uint8_t avrcp_browsing_controller_get_media_players(uint16_t avrcp_browsing_cid, uint32_t start_item, uint32_t end_item, uint32_t attr_bitmap){ 573 return avrcp_browsing_controller_get_folder_items(avrcp_browsing_cid, AVRCP_BROWSING_MEDIA_PLAYER_LIST, start_item, end_item, attr_bitmap); 574 } 575 576 uint8_t avrcp_browsing_controller_browse_file_system(uint16_t avrcp_browsing_cid, uint32_t start_item, uint32_t end_item, uint32_t attr_bitmap){ 577 // return avrcp_browsing_controller_get_folder_items(avrcp_browsing_cid, 1, 0, 0xFFFFFFFF, attr_bitmap); 578 return avrcp_browsing_controller_get_folder_items(avrcp_browsing_cid, AVRCP_BROWSING_MEDIA_PLAYER_VIRTUAL_FILESYSTEM, start_item, end_item, attr_bitmap); 579 } 580 581 uint8_t avrcp_browsing_controller_browse_media(uint16_t avrcp_browsing_cid, uint32_t start_item, uint32_t end_item, uint32_t attr_bitmap){ 582 // return avrcp_browsing_controller_get_folder_items(avrcp_browsing_cid, 2, 0, 0xFFFFFFFF, 0, NULL); 583 return avrcp_browsing_controller_get_folder_items(avrcp_browsing_cid, AVRCP_BROWSING_SEARCH, start_item, end_item, attr_bitmap); 584 } 585 586 uint8_t avrcp_browsing_controller_browse_now_playing_list(uint16_t avrcp_browsing_cid, uint32_t start_item, uint32_t end_item, uint32_t attr_bitmap){ 587 return avrcp_browsing_controller_get_folder_items(avrcp_browsing_cid, AVRCP_BROWSING_NOW_PLAYING, start_item, end_item, attr_bitmap); 588 } 589 590 591 uint8_t avrcp_browsing_controller_set_browsed_player(uint16_t avrcp_browsing_cid, uint16_t browsed_player_id){ 592 avrcp_connection_t * avrcp_connection = avrcp_get_connection_for_browsing_cid_for_role(AVRCP_CONTROLLER, avrcp_browsing_cid); 593 if (!avrcp_connection){ 594 log_error("avrcp_browsing_controller_change_path: could not find a connection."); 595 return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 596 } 597 598 avrcp_browsing_connection_t * connection = avrcp_connection->browsing_connection; 599 if (connection->state != AVCTP_CONNECTION_OPENED){ 600 log_error("avrcp_browsing_controller_change_path: connection in wrong state."); 601 return ERROR_CODE_COMMAND_DISALLOWED; 602 } 603 604 connection->set_browsed_player_id = 1; 605 connection->browsed_player_id = browsed_player_id; 606 avrcp_browsing_request_can_send_now(connection, connection->l2cap_browsing_cid); 607 return ERROR_CODE_SUCCESS; 608 } 609 610 /** 611 * @brief Retrieve a listing of the contents of a folder. 612 * @param direction 0-folder up, 1-folder down 613 * @param folder_uid 8 bytes long 614 **/ 615 uint8_t avrcp_browsing_controller_change_path(uint16_t avrcp_browsing_cid, uint8_t direction, uint8_t * folder_uid){ 616 avrcp_connection_t * avrcp_connection = avrcp_get_connection_for_browsing_cid_for_role(AVRCP_CONTROLLER, avrcp_browsing_cid); 617 if (!avrcp_connection){ 618 log_error("avrcp_browsing_controller_change_path: could not find a connection."); 619 return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 620 } 621 622 avrcp_browsing_connection_t * connection = avrcp_connection->browsing_connection; 623 624 if ((connection == NULL) || (connection->state != AVCTP_CONNECTION_OPENED)){ 625 log_error("avrcp_browsing_controller_change_path: connection in wrong state."); 626 return ERROR_CODE_COMMAND_DISALLOWED; 627 } 628 629 if (!connection->browsed_player_id){ 630 log_error("avrcp_browsing_controller_change_path: no browsed player set."); 631 return ERROR_CODE_COMMAND_DISALLOWED; 632 } 633 connection->change_path = 1; 634 connection->direction = direction; 635 memset(connection->folder_uid, 0, 8); 636 if (folder_uid){ 637 (void)memcpy(connection->folder_uid, folder_uid, 8); 638 } 639 640 avrcp_browsing_request_can_send_now(connection, connection->l2cap_browsing_cid); 641 return ERROR_CODE_SUCCESS; 642 } 643 644 uint8_t avrcp_browsing_controller_go_up_one_level(uint16_t avrcp_browsing_cid){ 645 return avrcp_browsing_controller_change_path(avrcp_browsing_cid, 0, NULL); 646 } 647 648 uint8_t avrcp_browsing_controller_go_down_one_level(uint16_t avrcp_browsing_cid, uint8_t * folder_uid){ 649 return avrcp_browsing_controller_change_path(avrcp_browsing_cid, 1, folder_uid); 650 } 651 652 uint8_t avrcp_browsing_controller_search(uint16_t avrcp_browsing_cid, uint16_t search_str_len, char * search_str){ 653 avrcp_connection_t * avrcp_connection = avrcp_get_connection_for_browsing_cid_for_role(AVRCP_CONTROLLER, avrcp_browsing_cid); 654 if (!avrcp_connection){ 655 log_error("avrcp_browsing_controller_change_path: could not find a connection."); 656 return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 657 } 658 659 avrcp_browsing_connection_t * connection = avrcp_connection->browsing_connection; 660 661 if ((connection == NULL) || (connection->state != AVCTP_CONNECTION_OPENED)){ 662 log_error("avrcp_browsing_controller_change_path: connection in wrong state."); 663 return ERROR_CODE_COMMAND_DISALLOWED; 664 } 665 666 if (!connection->browsed_player_id){ 667 log_error("avrcp_browsing_controller_change_path: no browsed player set."); 668 return ERROR_CODE_COMMAND_DISALLOWED; 669 } 670 if (!search_str || (search_str_len == 0)){ 671 return AVRCP_BROWSING_ERROR_CODE_INVALID_COMMAND; 672 } 673 674 connection->search = 1; 675 676 connection->search_str_len = btstack_min(search_str_len, sizeof(connection->search_str)-1); 677 memset(connection->search_str, 0, sizeof(connection->search_str)); 678 (void)memcpy(connection->search_str, search_str, 679 connection->search_str_len); 680 avrcp_browsing_request_can_send_now(connection, connection->l2cap_browsing_cid); 681 return ERROR_CODE_SUCCESS; 682 } 683 684 uint8_t avrcp_browsing_controller_get_total_nr_items_for_scope(uint16_t avrcp_browsing_cid, avrcp_browsing_scope_t scope){ 685 avrcp_connection_t * avrcp_connection = avrcp_get_connection_for_browsing_cid_for_role(AVRCP_CONTROLLER, avrcp_browsing_cid); 686 if (!avrcp_connection){ 687 log_error("avrcp_browsing_controller_change_path: could not find a connection."); 688 return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 689 } 690 691 avrcp_browsing_connection_t * connection = avrcp_connection->browsing_connection; 692 693 if ((connection == NULL) || (connection->state != AVCTP_CONNECTION_OPENED)){ 694 log_error("avrcp_browsing_controller_change_path: connection in wrong state."); 695 return ERROR_CODE_COMMAND_DISALLOWED; 696 } 697 698 if (!connection->browsed_player_id){ 699 log_error("avrcp_browsing_controller_change_path: no browsed player set."); 700 return ERROR_CODE_COMMAND_DISALLOWED; 701 } 702 connection->get_total_nr_items = 1; 703 connection->get_total_nr_items_scope = scope; 704 avrcp_browsing_request_can_send_now(connection, connection->l2cap_browsing_cid); 705 return ERROR_CODE_SUCCESS; 706 } 707