diff --git a/src/classic/avrcp_browsing_target.c b/src/classic/avrcp_browsing_target.c index e74e0f17a..611342c26 100644 --- a/src/classic/avrcp_browsing_target.c +++ b/src/classic/avrcp_browsing_target.c @@ -119,6 +119,21 @@ static void avrcp_browsing_target_emit_get_total_num_items(btstack_packet_handle (*callback)(HCI_EVENT_PACKET, 0, event, sizeof(event)); } +static void avrcp_browsing_target_emit_set_browsed_player(btstack_packet_handler_t callback, uint16_t browsing_cid, uint16_t browsed_player_id){ + btstack_assert(callback != NULL); + + uint8_t event[10]; + int pos = 0; + event[pos++] = HCI_EVENT_AVRCP_META; + event[pos++] = sizeof(event) - 2; + event[pos++] = AVRCP_SUBEVENT_BROWSING_SET_BROWSED_PLAYER; + little_endian_store_16(event, pos, browsing_cid); + pos += 2; + little_endian_store_16(event, pos, browsed_player_id); + pos += 2; + (*callback)(HCI_EVENT_PACKET, 0, event, sizeof(event)); +} + static void avrcp_browsing_target_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ UNUSED(size); @@ -174,6 +189,14 @@ static void avrcp_browsing_target_packet_handler(uint8_t packet_type, uint16_t c avrcp_browsing_target_emit_get_total_num_items(avrcp_target_context.browsing_avrcp_callback, channel, browsing_connection); break; } + case AVRCP_PDU_ID_SET_BROWSED_PLAYER: + // param length (2), player_id (2) + if (big_endian_read_16(packet, pos) != 2){ + avrcp_browsing_target_response_general_reject(browsing_connection, AVRCP_STATUS_INVALID_COMMAND); + break; + } + avrcp_browsing_target_emit_set_browsed_player(avrcp_target_context.browsing_avrcp_callback, channel, big_endian_read_16(packet, pos+2)); + break; default: avrcp_browsing_target_response_general_reject(browsing_connection, AVRCP_STATUS_INVALID_COMMAND); log_info("not parsed pdu ID 0x%02x", browsing_connection->pdu_id); @@ -260,6 +283,80 @@ uint8_t avrcp_browsing_target_send_get_folder_items_response(uint16_t avrcp_brow return ERROR_CODE_SUCCESS; } +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){ + avrcp_connection_t * avrcp_connection = avrcp_get_connection_for_browsing_cid_for_role(AVRCP_TARGET, avrcp_browsing_cid); + if (!avrcp_connection){ + log_error("Could not find an AVRCP Target connection for browsing_cid 0x%02x.", avrcp_browsing_cid); + return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; + } + + avrcp_browsing_connection_t * connection = avrcp_connection->browsing_connection; + if (!connection){ + log_info("Could not find a browsing connection."); + return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; + } + + if (connection->state != AVCTP_CONNECTION_OPENED) { + log_info("Browsing connection wrong state."); + return ERROR_CODE_COMMAND_DISALLOWED; + } + + connection->browsed_player_id = browsed_player_id; + + uint16_t pos = 0; + connection->cmd_operands[pos++] = AVRCP_PDU_ID_SET_BROWSED_PLAYER; + big_endian_store_16(connection->cmd_operands, pos, response_size + 2 + 1); // uuid counter + status + pos += 2; + + connection->cmd_operands[pos++] = AVRCP_STATUS_SUCCESS; + big_endian_store_16(connection->cmd_operands, pos, uid_counter); + pos += 2; + + // TODO: fragmentation + if (response_size > sizeof(connection->cmd_operands)){ + connection->attr_list = response; + connection->attr_list_size = response_size; + log_info(" todo: list too big, invoke fragmentation"); + return 1; + } + + (void)memcpy(&connection->cmd_operands[pos], response, response_size); + pos += response_size; + connection->cmd_operands_length = pos; + + connection->state = AVCTP_W2_SEND_RESPONSE; + avrcp_browsing_request_can_send_now(connection, connection->l2cap_browsing_cid); + return ERROR_CODE_SUCCESS; +} + +uint8_t avrcp_browsing_target_send_reject_set_browsed_player(uint16_t avrcp_browsing_cid, avrcp_status_code_t status){ + avrcp_connection_t * avrcp_connection = avrcp_get_connection_for_browsing_cid_for_role(AVRCP_TARGET, avrcp_browsing_cid); + if (!avrcp_connection){ + log_error("Could not find an AVRCP Target connection for browsing_cid 0x%02x.", avrcp_browsing_cid); + return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; + } + + avrcp_browsing_connection_t * connection = avrcp_connection->browsing_connection; + if (!connection){ + log_info("Could not find a browsing connection."); + return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; + } + + if (connection->state != AVCTP_CONNECTION_OPENED) { + log_info("Browsing connection wrong state."); + return ERROR_CODE_COMMAND_DISALLOWED; + } + + int pos = 0; + connection->cmd_operands[pos++] = AVRCP_PDU_ID_SET_BROWSED_PLAYER; + big_endian_store_16(connection->cmd_operands, pos, 1); + connection->cmd_operands[pos++] = status; + connection->cmd_operands_length = pos; + + connection->state = AVCTP_W2_SEND_RESPONSE; + avrcp_browsing_request_can_send_now(connection, connection->l2cap_browsing_cid); + return ERROR_CODE_SUCCESS; +} 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){ avrcp_connection_t * avrcp_connection = avrcp_get_connection_for_browsing_cid_for_role(AVRCP_TARGET, avrcp_browsing_cid); @@ -273,7 +370,6 @@ uint8_t avrcp_browsing_target_send_get_total_num_items_response(uint16_t avrcp_b log_info("Could not find a browsing connection."); return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; } - if (connection->state != AVCTP_CONNECTION_OPENED) return ERROR_CODE_COMMAND_DISALLOWED; if (connection->state != AVCTP_CONNECTION_OPENED) { log_info("Browsing connection wrong state."); diff --git a/src/classic/avrcp_browsing_target.h b/src/classic/avrcp_browsing_target.h index 356263062..ac130c34f 100644 --- a/src/classic/avrcp_browsing_target.h +++ b/src/classic/avrcp_browsing_target.h @@ -66,6 +66,23 @@ void avrcp_browsing_target_init(void); */ void avrcp_browsing_target_register_packet_handler(btstack_packet_handler_t callback); +/** + * @brief Accept set browsed player + * @param browsing_cid + * @param uid_counter + * @param browsed_player_id + * @param response + * @param response_size + */ +uint8_t avrcp_browsing_target_send_accept_set_browsed_player(uint16_t browsing_cid, uint16_t uid_counter, uint16_t browsed_player_id, uint8_t * response, uint16_t response_len); + +/** + * @brief Reject set browsed player + * @param browsing_cid + * @param status + */ +uint8_t avrcp_browsing_target_send_reject_set_browsed_player(uint16_t browsing_cid, avrcp_status_code_t status); + /** * @brief Send answer to get folder items query on event AVRCP_SUBEVENT_BROWSING_GET_FOLDER_ITEMS. The first byte of this event defines the scope of the query, see avrcp_browsing_scope_t. * @param browsing_cid diff --git a/test/pts/avrcp_test.c b/test/pts/avrcp_test.c index 423ae2910..57f5ab08b 100644 --- a/test/pts/avrcp_test.c +++ b/test/pts/avrcp_test.c @@ -284,7 +284,28 @@ static uint8_t media_player_list[] = { 0x00, 0x10, // Displayable Name 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x64, 0x20, 0x50, 0x6C, 0x61, 0x79, 0x65, 0x72}; - + + +static uint8_t browsed_player_data[]= { + // number of items (3) + 0x00, 0x00, 0x04, + // Character Set Id (2B): 0x006A (UTF-8) + 0x00, 0x6A, + // Folder depth + 0x03, + // Folder Name Length (2) + 0x00, 0x01, + // FOlder Name + 0x41, + // Folder Name Length (2) + 0x00, 0x01, + // FOlder Name + 0x42, + // Folder Name Length (2) + 0x00, 0x01, + // FOlder Name + 0x43 +}; static uint8_t virtual_filesystem_list[] ={ 0x00, 0x07, 0x02, 0x00, 0x14, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x6A, 0x00, 0x06, 0x41, 0x6C, 0x62, 0x75, 0x6D, 0x73, 0x02, 0x00, 0x15, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x03, 0x00, 0x00, 0x6A, 0x00, 0x07, 0x41, 0x72, 0x74, 0x69, 0x73, 0x74, 0x73, 0x02, 0x00, 0x14, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x04, 0x00, 0x00, 0x6A, 0x00, 0x06, 0x47, 0x65, 0x6E, 0x72, 0x65, 0x73, 0x02, 0x00, 0x17, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x05, 0x00, 0x00, 0x6A, 0x00, 0x09, 0x50, 0x6C, 0x61, 0x79, 0x6C, 0x69, 0x73, 0x74, 0x73, 0x02, 0x00, 0x13, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x05, 0x01, 0x01, 0x00, 0x6A, 0x00, 0x05, 0x53, 0x6F, 0x6E, 0x67, 0x73, 0x02, 0x00, 0x13, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x06, 0x00, 0x00}; // 0x71, 0x00, 0xA7, 0x04, 0x5A, 0x73, static uint8_t search_list[] = {}; @@ -914,6 +935,15 @@ static void avrcp_browsing_target_packet_handler(uint8_t packet_type, uint16_t c if (hci_event_packet_get_type(packet) != HCI_EVENT_AVRCP_META) return; switch (packet[2]){ + case AVRCP_SUBEVENT_BROWSING_SET_BROWSED_PLAYER:{ + uint16_t browsed_player_id = avrcp_subevent_browsing_set_browsed_player_get_player_id(packet); + if (browsed_player_id == 1){ + avrcp_status = avrcp_browsing_target_send_accept_set_browsed_player(browsing_cid, uid_counter, browsed_player_id, browsed_player_data, sizeof(browsed_player_data)); + } else { + avrcp_status = avrcp_browsing_target_send_reject_set_browsed_player(browsing_cid, AVRCP_STATUS_PLAYER_NOT_BROWSABLE); + } + break; + } case AVRCP_SUBEVENT_BROWSING_GET_FOLDER_ITEMS:{ avrcp_browsing_scope_t scope = avrcp_subevent_browsing_get_folder_items_get_scope(packet); switch (scope){