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 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 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 382 void avrcp_browsing_target_deinit(void){ 383 avrcp_controller_context.browsing_packet_handler = NULL; 384 } 385 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 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 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 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 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 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 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 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 }