From be65baf476b63f90eedfe80667720885958ce4bb Mon Sep 17 00:00:00 2001 From: Milanka Ringwald <mila@ringwald.ch> Date: Fri, 2 Feb 2018 15:06:32 +0100 Subject: [PATCH] avrcp browsing controller: fragmentation for get folder items --- src/btstack_defines.h | 5 +- src/btstack_event.h | 25 ++- src/classic/avrcp.h | 22 ++- src/classic/avrcp_browsing_controller.c | 201 +++++++++++++++--------- src/classic/avrcp_browsing_controller.h | 1 - src/classic/avrcp_controller.c | 37 +++++ src/classic/avrcp_controller.h | 11 ++ 7 files changed, 213 insertions(+), 89 deletions(-) diff --git a/src/btstack_defines.h b/src/btstack_defines.h index 1c78e4d22..5243e9225 100644 --- a/src/btstack_defines.h +++ b/src/btstack_defines.h @@ -1937,13 +1937,14 @@ typedef uint8_t sm_key_t[16]; /** - * @format 1211 + * @format 12211 * @param subevent_code * @param browsing_cid + * @param uid_counter * @param browsing_status * @param bluetooth_status */ -#define AVRCP_SUBEVENT_BROWSING_MEDIA_ITEM_DONE 0x1E +#define AVRCP_SUBEVENT_BROWSING_DONE 0x1E /** diff --git a/src/btstack_event.h b/src/btstack_event.h index cca3a0997..d4f3008f8 100644 --- a/src/btstack_event.h +++ b/src/btstack_event.h @@ -6216,31 +6216,40 @@ static inline uint16_t avrcp_subevent_browsing_connection_released_get_browsing_ } /** - * @brief Get field browsing_cid from event AVRCP_SUBEVENT_BROWSING_MEDIA_ITEM_DONE + * @brief Get field browsing_cid from event AVRCP_SUBEVENT_BROWSING_DONE * @param event packet * @return browsing_cid * @note: btstack_type 2 */ -static inline uint16_t avrcp_subevent_browsing_media_item_done_get_browsing_cid(const uint8_t * event){ +static inline uint16_t avrcp_subevent_browsing_done_get_browsing_cid(const uint8_t * event){ return little_endian_read_16(event, 3); } /** - * @brief Get field browsing_status from event AVRCP_SUBEVENT_BROWSING_MEDIA_ITEM_DONE + * @brief Get field uid_counter from event AVRCP_SUBEVENT_BROWSING_DONE + * @param event packet + * @return uid_counter + * @note: btstack_type 2 + */ +static inline uint16_t avrcp_subevent_browsing_done_get_uid_counter(const uint8_t * event){ + return little_endian_read_16(event, 5); +} +/** + * @brief Get field browsing_status from event AVRCP_SUBEVENT_BROWSING_DONE * @param event packet * @return browsing_status * @note: btstack_type 1 */ -static inline uint8_t avrcp_subevent_browsing_media_item_done_get_browsing_status(const uint8_t * event){ - return event[5]; +static inline uint8_t avrcp_subevent_browsing_done_get_browsing_status(const uint8_t * event){ + return event[7]; } /** - * @brief Get field bluetooth_status from event AVRCP_SUBEVENT_BROWSING_MEDIA_ITEM_DONE + * @brief Get field bluetooth_status from event AVRCP_SUBEVENT_BROWSING_DONE * @param event packet * @return bluetooth_status * @note: btstack_type 1 */ -static inline uint8_t avrcp_subevent_browsing_media_item_done_get_bluetooth_status(const uint8_t * event){ - return event[6]; +static inline uint8_t avrcp_subevent_browsing_done_get_bluetooth_status(const uint8_t * event){ + return event[8]; } /** diff --git a/src/classic/avrcp.h b/src/classic/avrcp.h index 37ec260e5..0e1d819d4 100644 --- a/src/classic/avrcp.h +++ b/src/classic/avrcp.h @@ -57,6 +57,7 @@ extern "C" { #define AVRCP_MEDIA_ATTR_COUNT 7 #define AVRCP_MAX_ATTRIBUTTE_SIZE 100 #define AVRCP_ATTRIBUTE_HEADER_LEN 8 +#define AVRCP_MAX_FOLDER_NAME_SIZE 20 typedef enum { AVRCP_STATUS_INVALID_COMMAND = 0, // sent if TG received a PDU that it did not understand. @@ -129,6 +130,7 @@ typedef enum { AVRCP_PDU_ID_SET_BROWSED_PLAYER = 0x70, AVRCP_PDU_ID_GET_FOLDER_ITEMS = 0x71, AVRCP_PDU_ID_CHANGE_PATH = 0x72, + AVRCP_PDU_ID_PLAY_ITEM = 0x74, AVRCP_PDU_ID_UNDEFINED = 0xFF } avrcp_pdu_id_t; @@ -256,6 +258,13 @@ typedef enum { AVCTP_W2_RECEIVE_RESPONSE } avctp_connection_state_t; +typedef enum { + AVRCP_BROWSING_MEDIA_PLAYER_LIST = 0x00, + AVRCP_BROWSING_MEDIA_PLAYER_VIRTUAL_FILESYSTEM, + AVRCP_BROWSING_SEARCH, + AVRCP_BROWSING_NOW_PLAYING +} avrcp_browsing_scope_t; + typedef struct { uint16_t len; uint8_t * value; @@ -304,7 +313,7 @@ typedef struct { // get folder item uint8_t get_folder_item; - uint8_t scope; + avrcp_browsing_scope_t scope; uint32_t start_item; uint32_t end_item; uint32_t attr_bitmap; @@ -314,6 +323,17 @@ typedef struct { uint8_t direction; uint16_t uid_counter; uint8_t folder_uid[8]; + + // fragmentation + uint8_t fragmented; + avrcp_pdu_id_t fragmented_pdu_id; + uint8_t fragmented_browsing_status; + uint16_t fragmented_uid_counter; + uint16_t num_items; + uint8_t item_type; + uint16_t item_length; + uint16_t fragment_size; + uint8_t fragment[100]; } avrcp_browsing_connection_t; // BROWSING END diff --git a/src/classic/avrcp_browsing_controller.c b/src/classic/avrcp_browsing_controller.c index 47f347266..d9f435260 100644 --- a/src/classic/avrcp_browsing_controller.c +++ b/src/classic/avrcp_browsing_controller.c @@ -383,26 +383,33 @@ static void avrcp_browsing_controller_handle_can_send_now(avrcp_browsing_connect avrcp_browsing_controller_send_change_path_cmd(connection->l2cap_browsing_cid, connection); break; } - + break; default: return; } } -static void avrcp_browsing_controller_emit_done(btstack_packet_handler_t callback, uint16_t browsing_cid, uint8_t browsing_status, uint8_t bluetooth_status){ + +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){ if (!callback) return; - uint8_t event[7]; + uint8_t event[9]; int pos = 0; event[pos++] = HCI_EVENT_AVRCP_META; event[pos++] = sizeof(event) - 2; - event[pos++] = AVRCP_SUBEVENT_BROWSING_MEDIA_ITEM_DONE; + event[pos++] = AVRCP_SUBEVENT_BROWSING_DONE; little_endian_store_16(event, pos, browsing_cid); pos += 2; + little_endian_store_16(event, pos, uid_counter); + pos += 2; event[pos++] = browsing_status; event[pos++] = bluetooth_status; (*callback)(HCI_EVENT_PACKET, 0, event, sizeof(event)); } +static void avrcp_browsing_controller_emit_done(btstack_packet_handler_t callback, uint16_t browsing_cid, uint8_t browsing_status, uint8_t bluetooth_status){ + avrcp_browsing_controller_emit_done_with_uid_counter(callback, browsing_cid, 0, browsing_status, bluetooth_status); +} + static void avrcp_browsing_controller_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ avrcp_browsing_connection_t * browsing_connection; @@ -410,99 +417,136 @@ static void avrcp_browsing_controller_packet_handler(uint8_t packet_type, uint16 case L2CAP_DATA_PACKET:{ browsing_connection = get_avrcp_browsing_connection_for_l2cap_cid(channel, &avrcp_controller_context); if (!browsing_connection) break; - browsing_connection->state = AVCTP_CONNECTION_OPENED; - - // printf_hexdump(packet, size); int pos = 0; uint8_t transport_header = packet[pos++]; // Transaction label | Packet_type | C/R | IPID (1 == invalid profile identifier) // uint8_t transaction_label = transport_header >> 4; avrcp_packet_type_t avctp_packet_type = (transport_header & 0x0F) >> 2; - // uint8_t frame_type = (transport_header & 0x03) >> 1; - // uint8_t ipid = transport_header & 0x01; - pos += 2; - browsing_connection->num_packets = 1; - switch (avctp_packet_type){ - case AVRCP_SINGLE_PACKET: - break; - case AVRCP_START_PACKET: + + avrcp_pdu_id_t pdu_id; + uint8_t browsing_status; + uint16_t uid_counter; + if (!browsing_connection->fragmented){ + browsing_connection->state = AVCTP_CONNECTION_OPENED; + // printf_hexdump(packet, size); + // uint8_t frame_type = (transport_header & 0x03) >> 1; + // uint8_t ipid = transport_header & 0x01; + pos += 2; + browsing_connection->num_packets = 1; + if (avctp_packet_type == AVRCP_START_PACKET){ browsing_connection->num_packets = packet[pos++]; - break; - case AVRCP_CONTINUE_PACKET: - case AVRCP_END_PACKET: - // browsing_connection->num_packets = packet[pos++]; - break; - } + } - if (pos + 4 > size){ - avrcp_browsing_controller_emit_done(avrcp_controller_context.browsing_avrcp_callback, channel, AVRCP_BROWSING_ERROR_CODE_INVALID_COMMAND, ERROR_CODE_SUCCESS); - return; + if (pos + 4 > size){ + avrcp_browsing_controller_emit_done(avrcp_controller_context.browsing_avrcp_callback, channel, AVRCP_BROWSING_ERROR_CODE_INVALID_COMMAND, ERROR_CODE_SUCCESS); + return; + } + pdu_id = packet[pos++]; + // uint16_t length = big_endian_read_16(packet, pos); + pos += 2; + browsing_status = packet[pos++]; + if (browsing_status != AVRCP_BROWSING_ERROR_CODE_SUCCESS){ + avrcp_browsing_controller_emit_done(avrcp_controller_context.browsing_avrcp_callback, channel, browsing_status, ERROR_CODE_SUCCESS); + return; + } + uid_counter = big_endian_read_16(packet, pos); + pos += 2; + } else { + pdu_id = browsing_connection->fragmented_pdu_id; + browsing_status = browsing_connection->fragmented_browsing_status; + uid_counter = browsing_connection->fragmented_uid_counter; } - - avrcp_pdu_id_t pdu_id = packet[pos++]; - uint16_t length = big_endian_read_16(packet, pos); - pos += 2; - UNUSED(length); - // if (browsing_connection->num_packets > 1) - // if (browsing_connection->num_packets == 1 && (pos + length > size)){ - // printf("pos + length > size, %d, %d, %d \n", pos, length, size); - // avrcp_browsing_controller_emit_done(avrcp_controller_context.browsing_avrcp_callback, channel, AVRCP_BROWSING_ERROR_CODE_INVALID_COMMAND, ERROR_CODE_SUCCESS); - // return; - // } - - uint8_t browsing_status = packet[pos++]; - if (browsing_status != AVRCP_BROWSING_ERROR_CODE_SUCCESS){ - printf("browsing_status %d\n", browsing_status); - avrcp_browsing_controller_emit_done(avrcp_controller_context.browsing_avrcp_callback, channel, browsing_status, ERROR_CODE_SUCCESS); - return; - } - + uint32_t i; switch(pdu_id){ case AVRCP_PDU_ID_CHANGE_PATH: printf("AVRCP_PDU_ID_CHANGE_PATH \n"); break; case AVRCP_PDU_ID_SET_ADDRESSED_PLAYER: - printf("AVRCP_PDU_ID_CHANGE_PATH \n"); + printf("AVRCP_PDU_ID_SET_ADDRESSED_PLAYER \n"); break; case AVRCP_PDU_ID_SET_BROWSED_PLAYER:{ - browsing_connection->browsed_player_uid_counter = big_endian_read_16(packet, pos); - pos += 2; - uint32_t num_items = big_endian_read_32(packet, pos); + browsing_connection->browsed_player_uid_counter = uid_counter; + // uint32_t num_items = big_endian_read_32(packet, pos); pos += 4; - printf("AVRCP_PDU_ID_SET_BROWSED_PLAYER uuid counter 0x0%2x, num items %d\n", browsing_connection->browsed_player_uid_counter, num_items); - - for (i = 0; i < num_items; i++){ - uint16_t browsable_item_length = 5 + big_endian_read_16(packet, pos+3); - printf(" pos %d, len %d\n", pos, browsable_item_length); - // reuse byte to put the new type AVRCP_BROWSING_MEDIA_ROOT_FOLDER + // uint16_t charset = big_endian_read_16(packet, pos); + pos += 2; + uint8_t folder_depth = packet[pos++]; + + for (i = 0; i < folder_depth; i++){ + uint16_t folder_name_length = big_endian_read_16(packet, pos); + pos += 2; + // reuse packet and add data type as a header packet[pos-1] = AVRCP_BROWSING_MEDIA_ROOT_FOLDER; - (*avrcp_controller_context.browsing_avrcp_callback)(AVRCP_BROWSING_DATA_PACKET, channel, packet+pos, browsable_item_length+1); - pos += browsable_item_length; + (*avrcp_controller_context.browsing_avrcp_callback)(AVRCP_BROWSING_DATA_PACKET, channel, packet+pos-1, folder_name_length+1); + pos += folder_name_length; + } + if (avctp_packet_type == AVRCP_SINGLE_PACKET || avctp_packet_type == AVRCP_END_PACKET){ + avrcp_browsing_controller_emit_done_with_uid_counter(avrcp_controller_context.browsing_avrcp_callback, channel, browsing_connection->browsed_player_uid_counter, browsing_status, ERROR_CODE_SUCCESS); } break; } case AVRCP_PDU_ID_GET_FOLDER_ITEMS:{ - // uint16_t uid_counter = big_endian_read_16(packet, pos); - pos += 2; - uint16_t num_items = big_endian_read_16(packet, pos); - pos += 2; - for (i = 0; i < num_items; i++){ - uint16_t browsable_item_length = 3 + big_endian_read_16(packet, pos+1); - (*avrcp_controller_context.browsing_avrcp_callback)(AVRCP_BROWSING_DATA_PACKET, channel, packet+pos, browsable_item_length); - pos += browsable_item_length; - } + printf("AVRCP_PDU_ID_GET_FOLDER_ITEMS \n"); + // send UID counter with done + if (!browsing_connection->fragmented){ + browsing_connection->num_items = big_endian_read_16(packet, pos); + pos += 2; + } + while (pos < size){ + uint8_t header_size = 1; + if (browsing_connection->fragment_size == 0){ + browsing_connection->item_type = packet[pos++]; + browsing_connection->item_length = big_endian_read_16(packet, pos); + pos += 2; + // reuse packet and add data type as a header + packet[pos-1] = browsing_connection->item_type; + + if (browsing_connection->item_length + pos > size) { + browsing_connection->fragment_size = size - pos; + browsing_connection->fragment[0] = browsing_connection->item_type; + memcpy(&browsing_connection->fragment[header_size], &packet[pos], browsing_connection->fragment_size); + break; + } + (*avrcp_controller_context.browsing_avrcp_callback)(AVRCP_BROWSING_DATA_PACKET, channel, &packet[pos-header_size], browsing_connection->item_length+header_size); + browsing_connection->num_items--; + pos += browsing_connection->item_length; + } else { + uint16_t remaining_bytes_to_copy = browsing_connection->item_length - browsing_connection->fragment_size; + if (remaining_bytes_to_copy + pos > size) { + memcpy(&browsing_connection->fragment[browsing_connection->fragment_size+1], &packet[pos], size - pos); + browsing_connection->fragment_size += size - pos; + break; + } + memcpy(&browsing_connection->fragment[browsing_connection->fragment_size+1], &packet[pos], remaining_bytes_to_copy); + (*avrcp_controller_context.browsing_avrcp_callback)(AVRCP_BROWSING_DATA_PACKET, channel, browsing_connection->fragment, browsing_connection->item_length+1); + browsing_connection->num_items--; + browsing_connection->fragment_size = 0; + pos += remaining_bytes_to_copy; + } + } break; } default: break; } + switch (avctp_packet_type){ case AVRCP_SINGLE_PACKET: case AVRCP_END_PACKET: - avrcp_browsing_controller_emit_done(avrcp_controller_context.browsing_avrcp_callback, channel, browsing_status, ERROR_CODE_SUCCESS); + browsing_connection->state = AVCTP_CONNECTION_OPENED; + browsing_connection->fragmented = 0; + browsing_connection->num_items = 0; + browsing_connection->fragment_size = 0; + avrcp_browsing_controller_emit_done_with_uid_counter(avrcp_controller_context.browsing_avrcp_callback, channel, uid_counter, browsing_status, ERROR_CODE_SUCCESS); break; - default: + case AVRCP_START_PACKET: + browsing_connection->fragmented = 1; + browsing_connection->fragmented_pdu_id = pdu_id; + browsing_connection->fragmented_browsing_status = browsing_status; + break; + case AVRCP_CONTINUE_PACKET: + browsing_connection->fragmented = 1; break; } break; @@ -600,7 +644,7 @@ uint8_t avrcp_avrcp_browsing_decline_incoming_connection(uint16_t avrcp_browsing * @param attribute_count * @param attribute_list **/ -static uint8_t avrcp_browsing_controller_get_folder_items(uint16_t avrcp_browsing_cid, uint8_t scope, uint32_t start_item, uint32_t end_item, uint32_t attr_bitmap){ +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){ avrcp_connection_t * avrcp_connection = get_avrcp_connection_for_browsing_cid(avrcp_browsing_cid, &avrcp_controller_context); if (!avrcp_connection){ log_error("avrcp_browsing_controller_disconnect: could not find a connection."); @@ -620,21 +664,21 @@ static uint8_t avrcp_browsing_controller_get_folder_items(uint16_t avrcp_browsin } 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){ - return avrcp_browsing_controller_get_folder_items(avrcp_browsing_cid, 0, start_item, end_item, attr_bitmap); + return avrcp_browsing_controller_get_folder_items(avrcp_browsing_cid, AVRCP_BROWSING_MEDIA_PLAYER_LIST, start_item, end_item, attr_bitmap); } 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){ // return avrcp_browsing_controller_get_folder_items(avrcp_browsing_cid, 1, 0, 0xFFFFFFFF, attr_bitmap); - return avrcp_browsing_controller_get_folder_items(avrcp_browsing_cid, 1, start_item, end_item, attr_bitmap); + return avrcp_browsing_controller_get_folder_items(avrcp_browsing_cid, AVRCP_BROWSING_MEDIA_PLAYER_VIRTUAL_FILESYSTEM, start_item, end_item, attr_bitmap); } uint8_t avrcp_browsing_controller_browse_media(uint16_t avrcp_browsing_cid, uint32_t start_item, uint32_t end_item, uint32_t attr_bitmap){ // return avrcp_browsing_controller_get_folder_items(avrcp_browsing_cid, 2, 0, 0xFFFFFFFF, 0, NULL); - return avrcp_browsing_controller_get_folder_items(avrcp_browsing_cid, 2, start_item, end_item, attr_bitmap); + return avrcp_browsing_controller_get_folder_items(avrcp_browsing_cid, AVRCP_BROWSING_SEARCH, start_item, end_item, attr_bitmap); } 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){ - return avrcp_browsing_controller_get_folder_items(avrcp_browsing_cid, 3, start_item, end_item, attr_bitmap); + return avrcp_browsing_controller_get_folder_items(avrcp_browsing_cid, AVRCP_BROWSING_NOW_PLAYING, start_item, end_item, attr_bitmap); } @@ -690,7 +734,7 @@ uint8_t avrcp_browsing_controller_change_path(uint16_t avrcp_browsing_cid, uint8 avrcp_browsing_connection_t * connection = avrcp_connection->browsing_connection; - if (!connection || connection->state != AVCTP_CONNECTION_OPENED || !folder_uid){ + if (!connection || connection->state != AVCTP_CONNECTION_OPENED){ log_error("avrcp_browsing_controller_change_path: connection in wrong state."); return ERROR_CODE_COMMAND_DISALLOWED; } @@ -699,19 +743,22 @@ uint8_t avrcp_browsing_controller_change_path(uint16_t avrcp_browsing_cid, uint8 log_error("avrcp_browsing_controller_change_path: no browsed player set."); return ERROR_CODE_COMMAND_DISALLOWED; } - + printf(" send change path\n"); connection->change_path = 1; connection->direction = direction; - - memcpy(connection->folder_uid, folder_uid, 8); + memset(connection->folder_uid, 0, 8); + if (folder_uid){ + memcpy(connection->folder_uid, folder_uid, 8); + } + avrcp_request_can_send_now(avrcp_connection, connection->l2cap_browsing_cid); return ERROR_CODE_SUCCESS; } uint8_t avrcp_browsing_controller_go_up_one_level(uint16_t avrcp_browsing_cid){ - return avrcp_browsing_controller_change_path(avrcp_browsing_cid, 0, 0); + return avrcp_browsing_controller_change_path(avrcp_browsing_cid, 0, NULL); } uint8_t avrcp_browsing_controller_go_down_one_level(uint16_t avrcp_browsing_cid, uint8_t * folder_uid){ return avrcp_browsing_controller_change_path(avrcp_browsing_cid, 1, folder_uid); -} +} \ No newline at end of file diff --git a/src/classic/avrcp_browsing_controller.h b/src/classic/avrcp_browsing_controller.h index 91561d89b..20275cd8a 100644 --- a/src/classic/avrcp_browsing_controller.h +++ b/src/classic/avrcp_browsing_controller.h @@ -202,7 +202,6 @@ uint8_t avrcp_browsing_controller_set_addressed_player(uint16_t avrcp_browsing_c uint8_t avrcp_browsing_controller_change_path(uint16_t avrcp_browsing_cid, uint8_t direction, uint8_t * folder_uid); uint8_t avrcp_browsing_controller_go_up_one_level(uint16_t avrcp_browsing_cid); uint8_t avrcp_browsing_controller_go_down_one_level(uint16_t avrcp_browsing_cid, uint8_t * folder_uid); - /* API_END */ #if defined __cplusplus diff --git a/src/classic/avrcp_controller.c b/src/classic/avrcp_controller.c index 31c4df338..cb32bcb65 100644 --- a/src/classic/avrcp_controller.c +++ b/src/classic/avrcp_controller.c @@ -1185,3 +1185,40 @@ uint8_t avrcp_controller_disconnect(uint16_t avrcp_cid){ l2cap_disconnect(connection->l2cap_signaling_cid, 0); return ERROR_CODE_SUCCESS; } + +uint8_t avrcp_controller_play_item(uint16_t avrcp_cid, avrcp_browsing_scope_t scope, uint8_t * uid, uint16_t uid_counter){ + avrcp_connection_t * connection = get_avrcp_connection_for_avrcp_cid(avrcp_cid, &avrcp_controller_context); + if (!connection){ + log_error("avrcp_controller_play_item: could not find a connection."); + return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; + } + if (connection->state != AVCTP_CONNECTION_OPENED) return ERROR_CODE_COMMAND_DISALLOWED; + connection->state = AVCTP_W2_SEND_COMMAND; + + connection->transaction_label++; + connection->command_type = AVRCP_CTYPE_CONTROL; + connection->subunit_type = AVRCP_SUBUNIT_TYPE_PANEL; + connection->subunit_id = AVRCP_SUBUNIT_ID; + connection->command_opcode = AVRCP_CMD_OPCODE_VENDOR_DEPENDENT; + int pos = 0; + big_endian_store_24(connection->cmd_operands, pos, BT_SIG_COMPANY_ID); + pos += 3; + connection->cmd_operands[pos++] = AVRCP_PDU_ID_PLAY_ITEM; // PDU ID + // reserved + connection->cmd_operands[pos++] = 0; + // Parameter Length + big_endian_store_16(connection->cmd_operands, pos, 11); + pos += 2; + connection->cmd_operands[pos++] = scope; + memset(&connection->cmd_operands[pos], 0, 8); + if (uid){ + memcpy(&connection->cmd_operands[pos], uid, 8); + } + pos += 8; + big_endian_store_16(connection->cmd_operands, pos, uid_counter); + pos += 2; + connection->cmd_operands_length = pos; + + avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid); + return ERROR_CODE_SUCCESS; +} diff --git a/src/classic/avrcp_controller.h b/src/classic/avrcp_controller.h index 9b97fd4aa..8b68ee40b 100644 --- a/src/classic/avrcp_controller.h +++ b/src/classic/avrcp_controller.h @@ -284,6 +284,17 @@ uint8_t avrcp_controller_set_shuffle_mode(uint16_t avrcp_cid, avrcp_shuffle_mode * @returns status */ uint8_t avrcp_controller_set_repeat_mode(uint16_t avrcp_cid, avrcp_repeat_mode_t mode); + +/** + * @brief The PlayItem command starts playing an item indicated by the UID. It is routed to the Addressed Player. + * @param avrcp_cid + * @param scope + * @param uid + * @param uid_counter + * @return status + **/ +uint8_t avrcp_controller_play_item(uint16_t avrcp_cid, avrcp_browsing_scope_t scope, uint8_t * uid, uint16_t uid_counter); + /* API_END */ // Used by AVRCP controller and AVRCP browsing controller