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