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 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 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 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 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 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 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 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 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 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 avrcp_browsing_direction_t direction; 213 214 switch (packet_type) { 215 case L2CAP_DATA_PACKET:{ 216 browsing_connection = avrcp_get_browsing_connection_for_l2cap_cid_for_role(AVRCP_TARGET, channel); 217 if (!browsing_connection) break; 218 int pos = 0; 219 uint8_t transport_header = packet[pos++]; 220 // Transaction label | Packet_type | C/R | IPID (1 == invalid profile identifier) 221 browsing_connection->transaction_label = transport_header >> 4; 222 avctp_packet_type_t avctp_packet_type = (avctp_packet_type_t)((transport_header & 0x0F) >> 2); 223 switch (avctp_packet_type){ 224 case AVCTP_SINGLE_PACKET: 225 case AVCTP_START_PACKET: 226 browsing_connection->subunit_type = packet[pos++] >> 2; 227 browsing_connection->subunit_id = 0; 228 browsing_connection->command_opcode = packet[pos++]; 229 browsing_connection->num_packets = 1; 230 if (avctp_packet_type == AVCTP_START_PACKET){ 231 browsing_connection->num_packets = packet[pos++]; 232 } 233 browsing_connection->pdu_id = packet[pos++]; 234 uint16_t parameter_length = big_endian_read_16(packet, pos); 235 pos += 2; 236 237 switch(browsing_connection->pdu_id){ 238 case AVRCP_PDU_ID_SEARCH:{ 239 if (parameter_length < 4){ 240 avrcp_browsing_target_response_general_reject(browsing_connection, AVRCP_STATUS_INVALID_COMMAND); 241 break; 242 } 243 browsing_connection->target_search_characterset = big_endian_read_16(packet, pos); 244 pos += 2; 245 browsing_connection->target_search_str_len = big_endian_read_16(packet, pos); 246 pos += 2; 247 browsing_connection->target_search_str = (char *) &packet[pos]; 248 249 if (parameter_length < (4 + browsing_connection->target_search_str_len)){ 250 avrcp_browsing_target_response_general_reject(browsing_connection, AVRCP_STATUS_INVALID_COMMAND); 251 break; 252 } 253 254 uint16_t string_len = strlen(browsing_connection->target_search_str); 255 if ((browsing_connection->target_search_str_len != string_len) || (browsing_connection->target_search_str_len > (size-pos))){ 256 avrcp_browsing_target_response_general_reject(browsing_connection, AVRCP_STATUS_INVALID_PARAMETER); 257 break; 258 } 259 avrcp_browsing_target_emit_search(avrcp_target_context.browsing_avrcp_callback, channel, browsing_connection); 260 break; 261 } 262 case AVRCP_PDU_ID_GET_FOLDER_ITEMS: 263 if (parameter_length < 10){ 264 avrcp_browsing_target_response_general_reject(browsing_connection, AVRCP_STATUS_INVALID_COMMAND); 265 break; 266 } 267 268 browsing_connection->scope = packet[pos++]; 269 browsing_connection->start_item = big_endian_read_32(packet, pos); 270 pos += 4; 271 browsing_connection->end_item = big_endian_read_32(packet, pos); 272 pos += 4; 273 uint8_t attr_count = packet[pos++]; 274 browsing_connection->attr_bitmap = 0; 275 276 while (attr_count){ 277 uint32_t attr_id = big_endian_read_32(packet, pos); 278 pos += 4; 279 browsing_connection->attr_bitmap |= (1 << attr_id); 280 attr_count--; 281 } 282 avrcp_browsing_target_emit_get_folder_items(avrcp_target_context.browsing_avrcp_callback, channel, browsing_connection); 283 break; 284 285 case AVRCP_PDU_ID_GET_TOTAL_NUMBER_OF_ITEMS:{ 286 // send total num items 287 if (parameter_length != 1){ 288 avrcp_browsing_target_response_general_reject(browsing_connection, AVRCP_STATUS_INVALID_SCOPE); 289 break; 290 } 291 292 browsing_connection->scope = packet[pos++]; 293 avrcp_browsing_target_emit_get_total_num_items(avrcp_target_context.browsing_avrcp_callback, channel, browsing_connection); 294 break; 295 } 296 case AVRCP_PDU_ID_SET_BROWSED_PLAYER: 297 // player_id (2) 298 if (parameter_length != 2){ 299 avrcp_browsing_target_response_general_reject(browsing_connection, AVRCP_STATUS_INVALID_COMMAND); 300 break; 301 } 302 if ( (pos + 2) > size ){ 303 avrcp_browsing_target_response_general_reject(browsing_connection, AVRCP_STATUS_INVALID_PLAYER_ID); 304 break; 305 } 306 avrcp_browsing_target_emit_set_browsed_player(avrcp_target_context.browsing_avrcp_callback, channel, big_endian_read_16(packet, pos)); 307 break; 308 309 case AVRCP_PDU_ID_CHANGE_PATH: 310 // one level up or down in the virtual filesystem 311 if (parameter_length != 11){ 312 avrcp_browsing_target_response_general_reject(browsing_connection, AVRCP_STATUS_INVALID_COMMAND); 313 break; 314 } 315 browsing_connection->uid_counter = big_endian_read_16(packet, pos); 316 pos += 2; 317 browsing_connection->direction = (avrcp_browsing_direction_t)packet[pos++]; 318 319 if (browsing_connection->direction > AVRCP_BROWSING_DIRECTION_FOLDER_RFU){ 320 avrcp_browsing_target_response_general_reject(browsing_connection, AVRCP_STATUS_INVALID_DIRECTION); 321 break; 322 } 323 memcpy(browsing_connection->item_uid, &packet[pos], 8); 324 avrcp_browsing_target_emit_change_path(avrcp_target_context.browsing_avrcp_callback, channel, browsing_connection->uid_counter, browsing_connection->direction, browsing_connection->item_uid); 325 break; 326 327 case AVRCP_PDU_ID_GET_ITEM_ATTRIBUTES:{ 328 if (parameter_length < 12){ 329 avrcp_browsing_target_response_general_reject(browsing_connection, AVRCP_STATUS_INVALID_COMMAND); 330 break; 331 } 332 333 browsing_connection->scope = packet[pos++]; 334 memcpy(browsing_connection->item_uid, &packet[pos], 8); 335 pos += 8; 336 browsing_connection->uid_counter = big_endian_read_16(packet, pos); 337 pos += 2; 338 browsing_connection->attr_list_size = packet[pos++]; 339 browsing_connection->attr_list = &packet[pos]; 340 341 avrcp_browsing_target_emit_get_item_attributes(avrcp_target_context.browsing_avrcp_callback, channel, browsing_connection->uid_counter, 342 browsing_connection->scope, browsing_connection->item_uid, browsing_connection->attr_list_size, browsing_connection->attr_list); 343 break; 344 } 345 346 default: 347 avrcp_browsing_target_response_general_reject(browsing_connection, AVRCP_STATUS_INVALID_COMMAND); 348 log_info("not parsed pdu ID 0x%02x", browsing_connection->pdu_id); 349 break; 350 } 351 browsing_connection->state = AVCTP_CONNECTION_OPENED; 352 break; 353 354 default: 355 break; 356 } 357 break; 358 } 359 360 case HCI_EVENT_PACKET: 361 switch (hci_event_packet_get_type(packet)){ 362 case L2CAP_EVENT_CAN_SEND_NOW: 363 browsing_connection = avrcp_get_browsing_connection_for_l2cap_cid_for_role(AVRCP_TARGET, channel); 364 if (browsing_connection->state != AVCTP_W2_SEND_RESPONSE) return; 365 browsing_connection->state = AVCTP_CONNECTION_OPENED; 366 avrcp_browsing_target_handle_can_send_now(browsing_connection); 367 break; 368 default: 369 break; 370 } 371 break; 372 373 default: 374 break; 375 } 376 } 377 378 void avrcp_browsing_target_init(void){ 379 avrcp_target_context.browsing_packet_handler = avrcp_browsing_target_packet_handler; 380 avrcp_browsing_register_target_packet_handler(avrcp_browsing_target_packet_handler); 381 } 382 383 void avrcp_browsing_target_deinit(void){ 384 avrcp_controller_context.browsing_packet_handler = NULL; 385 } 386 387 void avrcp_browsing_target_register_packet_handler(btstack_packet_handler_t callback){ 388 btstack_assert(callback != NULL); 389 avrcp_target_context.browsing_avrcp_callback = callback; 390 } 391 392 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){ 393 avrcp_connection_t * avrcp_connection = avrcp_get_connection_for_browsing_cid_for_role(AVRCP_TARGET, avrcp_browsing_cid); 394 if (!avrcp_connection){ 395 log_error("Could not find an AVRCP Target connection for browsing_cid 0x%02x.", avrcp_browsing_cid); 396 return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 397 } 398 399 avrcp_browsing_connection_t * connection = avrcp_connection->browsing_connection; 400 if (!connection){ 401 log_info("Could not find a browsing connection."); 402 return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 403 } 404 if (connection->state != AVCTP_CONNECTION_OPENED){ 405 return ERROR_CODE_COMMAND_DISALLOWED; 406 } 407 408 // TODO: handle response to SetAddressedPlayer 409 410 uint16_t pos = 0; 411 connection->cmd_operands[pos++] = AVRCP_PDU_ID_GET_FOLDER_ITEMS; 412 uint8_t param_length = 5; 413 uint8_t param_length_pos = pos; 414 pos += 2; 415 416 avrcp_status_code_t status = AVRCP_STATUS_SUCCESS; 417 uint8_t status_pos = pos; 418 pos++; 419 420 big_endian_store_16(connection->cmd_operands, pos, uid_counter); 421 pos += 2; 422 423 // TODO: fragmentation 424 if (attr_list_size > sizeof(connection->cmd_operands)){ 425 connection->attr_list = attr_list; 426 connection->attr_list_size = attr_list_size; 427 log_info(" todo: list too big, invoke fragmentation"); 428 return 1; 429 } 430 431 uint16_t items_byte_len = 0; 432 if (connection->start_item < num_items) { 433 if (connection->end_item < num_items) { 434 items_byte_len = connection->end_item - connection->start_item + 1; 435 } else { 436 items_byte_len = num_items - connection->start_item; 437 } 438 439 } else { 440 big_endian_store_16(connection->cmd_operands, pos, 0); 441 pos += 2; 442 } 443 big_endian_store_16(connection->cmd_operands, pos, items_byte_len); 444 pos += 2; 445 param_length += items_byte_len; 446 447 if (items_byte_len > 0){ 448 (void)memcpy(&connection->cmd_operands[pos], attr_list, attr_list_size); 449 pos += attr_list_size; 450 connection->cmd_operands_length = pos; 451 } else { 452 status = AVRCP_STATUS_RANGE_OUT_OF_BOUNDS; 453 param_length = 1; 454 connection->cmd_operands_length = status_pos + 1; 455 } 456 457 big_endian_store_16(connection->cmd_operands, param_length_pos, param_length); 458 connection->cmd_operands[status_pos] = status; 459 460 btstack_assert(pos <= 400); 461 462 463 connection->state = AVCTP_W2_SEND_RESPONSE; 464 avrcp_browsing_request_can_send_now(connection, connection->l2cap_browsing_cid); 465 return ERROR_CODE_SUCCESS; 466 } 467 468 uint8_t avrcp_browsing_target_send_change_path_response(uint16_t avrcp_browsing_cid, uint16_t uid_counter, avrcp_status_code_t status, uint32_t num_items){ 469 avrcp_connection_t * avrcp_connection = avrcp_get_connection_for_browsing_cid_for_role(AVRCP_TARGET, avrcp_browsing_cid); 470 if (!avrcp_connection){ 471 log_error("Could not find an AVRCP Target connection for browsing_cid 0x%02x.", avrcp_browsing_cid); 472 return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 473 } 474 475 avrcp_browsing_connection_t * connection = avrcp_connection->browsing_connection; 476 if (!connection){ 477 log_info("Could not find a browsing connection."); 478 return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 479 } 480 if (connection->state != AVCTP_CONNECTION_OPENED){ 481 return ERROR_CODE_COMMAND_DISALLOWED; 482 } 483 484 uint16_t pos = 0; 485 uint16_t param_length = (status == AVRCP_STATUS_SUCCESS) ? 5 : 1; 486 connection->cmd_operands[pos++] = AVRCP_PDU_ID_CHANGE_PATH; 487 488 // Param length 489 big_endian_store_16(connection->cmd_operands, pos, param_length); 490 pos += 2; 491 connection->cmd_operands[pos++] = status; 492 493 if (status == AVRCP_STATUS_SUCCESS){ 494 big_endian_store_32(connection->cmd_operands, pos, num_items); 495 pos += 4; 496 } 497 498 connection->cmd_operands_length = pos; 499 connection->state = AVCTP_W2_SEND_RESPONSE; 500 avrcp_browsing_request_can_send_now(connection, connection->l2cap_browsing_cid); 501 return ERROR_CODE_SUCCESS; 502 } 503 504 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){ 505 avrcp_connection_t * avrcp_connection = avrcp_get_connection_for_browsing_cid_for_role(AVRCP_TARGET, avrcp_browsing_cid); 506 if (!avrcp_connection){ 507 log_error("Could not find an AVRCP Target connection for browsing_cid 0x%02x.", avrcp_browsing_cid); 508 return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 509 } 510 511 avrcp_browsing_connection_t * connection = avrcp_connection->browsing_connection; 512 if (!connection){ 513 log_info("Could not find a browsing connection."); 514 return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 515 } 516 if (connection->state != AVCTP_CONNECTION_OPENED){ 517 return ERROR_CODE_COMMAND_DISALLOWED; 518 } 519 520 // TODO: fragmentation 521 if (attr_list_size > (sizeof(connection->cmd_operands) - 5)){ 522 connection->attr_list = attr_list; 523 connection->attr_list_size = attr_list_size; 524 log_info(" todo: list too big, invoke fragmentation"); 525 return 1; 526 } 527 528 uint16_t pos = 0; 529 connection->cmd_operands[pos++] = AVRCP_PDU_ID_GET_ITEM_ATTRIBUTES; 530 531 uint8_t param_length_pos = pos; 532 big_endian_store_16(connection->cmd_operands, pos, 1); 533 pos += 2; 534 connection->cmd_operands[pos++] = status; 535 536 if (status != AVRCP_STATUS_SUCCESS){ 537 connection->cmd_operands_length = pos; 538 connection->state = AVCTP_W2_SEND_RESPONSE; 539 avrcp_browsing_request_can_send_now(connection, connection->l2cap_browsing_cid); 540 return ERROR_CODE_SUCCESS; 541 } 542 543 connection->cmd_operands[pos++] = num_items; 544 (void)memcpy(&connection->cmd_operands[pos], attr_list, attr_list_size); 545 pos += attr_list_size; 546 547 big_endian_store_16(connection->cmd_operands, param_length_pos, pos - 3); 548 connection->cmd_operands_length = pos; 549 connection->state = AVCTP_W2_SEND_RESPONSE; 550 avrcp_browsing_request_can_send_now(connection, connection->l2cap_browsing_cid); 551 return ERROR_CODE_SUCCESS; 552 } 553 554 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){ 555 avrcp_connection_t * avrcp_connection = avrcp_get_connection_for_browsing_cid_for_role(AVRCP_TARGET, avrcp_browsing_cid); 556 if (!avrcp_connection){ 557 log_error("Could not find an AVRCP Target connection for browsing_cid 0x%02x.", avrcp_browsing_cid); 558 return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 559 } 560 561 avrcp_browsing_connection_t * connection = avrcp_connection->browsing_connection; 562 if (!connection){ 563 log_info("Could not find a browsing connection."); 564 return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 565 } 566 567 if (connection->state != AVCTP_CONNECTION_OPENED) { 568 log_info("Browsing connection wrong state."); 569 return ERROR_CODE_COMMAND_DISALLOWED; 570 } 571 572 connection->browsed_player_id = browsed_player_id; 573 574 uint16_t pos = 0; 575 connection->cmd_operands[pos++] = AVRCP_PDU_ID_SET_BROWSED_PLAYER; 576 big_endian_store_16(connection->cmd_operands, pos, response_size + 2 + 1); // uuid counter + status 577 pos += 2; 578 579 connection->cmd_operands[pos++] = AVRCP_STATUS_SUCCESS; 580 big_endian_store_16(connection->cmd_operands, pos, uid_counter); 581 pos += 2; 582 583 // TODO: fragmentation 584 if (response_size > sizeof(connection->cmd_operands)){ 585 connection->attr_list = response; 586 connection->attr_list_size = response_size; 587 log_info(" todo: list too big, invoke fragmentation"); 588 return 1; 589 } 590 591 (void)memcpy(&connection->cmd_operands[pos], response, response_size); 592 pos += response_size; 593 btstack_assert(pos <= 255); 594 connection->cmd_operands_length = (uint8_t) pos; 595 596 connection->state = AVCTP_W2_SEND_RESPONSE; 597 avrcp_browsing_request_can_send_now(connection, connection->l2cap_browsing_cid); 598 return ERROR_CODE_SUCCESS; 599 } 600 601 uint8_t avrcp_browsing_target_send_reject_set_browsed_player(uint16_t avrcp_browsing_cid, avrcp_status_code_t status){ 602 avrcp_connection_t * avrcp_connection = avrcp_get_connection_for_browsing_cid_for_role(AVRCP_TARGET, avrcp_browsing_cid); 603 if (!avrcp_connection){ 604 log_error("Could not find an AVRCP Target connection for browsing_cid 0x%02x.", avrcp_browsing_cid); 605 return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 606 } 607 608 avrcp_browsing_connection_t * connection = avrcp_connection->browsing_connection; 609 if (!connection){ 610 log_info("Could not find a browsing connection."); 611 return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 612 } 613 614 if (connection->state != AVCTP_CONNECTION_OPENED) { 615 log_info("Browsing connection wrong state."); 616 return ERROR_CODE_COMMAND_DISALLOWED; 617 } 618 619 int pos = 0; 620 connection->cmd_operands[pos++] = AVRCP_PDU_ID_SET_BROWSED_PLAYER; 621 big_endian_store_16(connection->cmd_operands, pos, 1); 622 pos += 2; 623 connection->cmd_operands[pos++] = status; 624 connection->cmd_operands_length = pos; 625 626 connection->state = AVCTP_W2_SEND_RESPONSE; 627 avrcp_browsing_request_can_send_now(connection, connection->l2cap_browsing_cid); 628 return ERROR_CODE_SUCCESS; 629 } 630 631 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){ 632 avrcp_connection_t * avrcp_connection = avrcp_get_connection_for_browsing_cid_for_role(AVRCP_TARGET, avrcp_browsing_cid); 633 if (!avrcp_connection){ 634 log_error("Could not find an AVRCP Target connection for browsing_cid 0x%02x.", avrcp_browsing_cid); 635 return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 636 } 637 638 avrcp_browsing_connection_t * connection = avrcp_connection->browsing_connection; 639 if (!connection){ 640 log_info("Could not find a browsing connection."); 641 return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 642 } 643 644 if (connection->state != AVCTP_CONNECTION_OPENED) { 645 log_info("Browsing connection wrong state."); 646 return ERROR_CODE_COMMAND_DISALLOWED; 647 } 648 649 int pos = 0; 650 connection->cmd_operands[pos++] = AVRCP_PDU_ID_GET_TOTAL_NUMBER_OF_ITEMS; 651 big_endian_store_16(connection->cmd_operands, pos, 7); 652 pos += 2; 653 connection->cmd_operands[pos++] = AVRCP_STATUS_SUCCESS; 654 big_endian_store_16(connection->cmd_operands, pos, uid_counter); 655 pos += 2; 656 big_endian_store_32(connection->cmd_operands, pos, total_num_items); 657 pos += 4; 658 connection->cmd_operands_length = pos; 659 660 connection->state = AVCTP_W2_SEND_RESPONSE; 661 avrcp_browsing_request_can_send_now(connection, connection->l2cap_browsing_cid); 662 return ERROR_CODE_SUCCESS; 663 } 664 665 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){ 666 avrcp_connection_t * avrcp_connection = avrcp_get_connection_for_browsing_cid_for_role(AVRCP_TARGET, avrcp_browsing_cid); 667 if (!avrcp_connection){ 668 log_error("Could not find an AVRCP Target connection for browsing_cid 0x%02x.", avrcp_browsing_cid); 669 return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 670 } 671 672 avrcp_browsing_connection_t * connection = avrcp_connection->browsing_connection; 673 if (!connection){ 674 log_info("Could not find a browsing connection."); 675 return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 676 } 677 if (connection->state != AVCTP_CONNECTION_OPENED){ 678 return ERROR_CODE_COMMAND_DISALLOWED; 679 } 680 681 uint16_t pos = 0; 682 connection->cmd_operands[pos++] = AVRCP_PDU_ID_SEARCH; 683 // param len 684 big_endian_store_16(connection->cmd_operands, pos, 7); 685 pos += 2; 686 connection->cmd_operands[pos++] = status; 687 big_endian_store_16(connection->cmd_operands, pos, uid_counter); 688 pos += 2; 689 690 // if (status != AVRCP_STATUS_SUCCESS){ 691 // connection->cmd_operands_length = pos; 692 // connection->state = AVCTP_W2_SEND_RESPONSE; 693 // avrcp_browsing_request_can_send_now(connection, connection->l2cap_browsing_cid); 694 // return ERROR_CODE_SUCCESS; 695 // } 696 697 big_endian_store_32(connection->cmd_operands, pos, num_items); 698 pos += 4; 699 connection->cmd_operands_length = pos; 700 connection->state = AVCTP_W2_SEND_RESPONSE; 701 avrcp_browsing_request_can_send_now(connection, connection->l2cap_browsing_cid); 702 return ERROR_CODE_SUCCESS; 703 }