avrcp target: add get capabilities

This commit is contained in:
Milanka Ringwald 2017-07-26 16:04:43 +02:00
parent 4b338011de
commit e0bbf3ed25
6 changed files with 222 additions and 21 deletions

View File

@ -133,6 +133,28 @@ static tracker_buffer_state trkbuf;
static uint32_t company_id = 0x112233;
static uint8_t companies_num = 1;
static uint8_t companies[] = {
0x00, 0x19, 0x58 //BT SIG registered CompanyID
};
static uint8_t events_num = 13;
static uint8_t events[] = {
AVRCP_NOTIFICATION_EVENT_PLAYBACK_STATUS_CHANGED,
AVRCP_NOTIFICATION_EVENT_TRACK_CHANGED,
AVRCP_NOTIFICATION_EVENT_TRACK_REACHED_END,
AVRCP_NOTIFICATION_EVENT_TRACK_REACHED_START,
AVRCP_NOTIFICATION_EVENT_PLAYBACK_POS_CHANGED,
AVRCP_NOTIFICATION_EVENT_BATT_STATUS_CHANGED,
AVRCP_NOTIFICATION_EVENT_SYSTEM_STATUS_CHANGED,
AVRCP_NOTIFICATION_EVENT_PLAYER_APPLICATION_SETTING_CHANGED,
AVRCP_NOTIFICATION_EVENT_NOW_PLAYING_CONTENT_CHANGED,
AVRCP_NOTIFICATION_EVENT_AVAILABLE_PLAYERS_CHANGED,
AVRCP_NOTIFICATION_EVENT_ADDRESSED_PLAYER_CHANGED,
AVRCP_NOTIFICATION_EVENT_UIDS_CHANGED,
AVRCP_NOTIFICATION_EVENT_VOLUME_CHANGED
};
static void a2dp_demo_send_media_packet(void){
int num_bytes_in_frame = btstack_sbc_encoder_sbc_buffer_length();
int bytes_in_storage = media_tracker.sbc_storage_count;
@ -345,10 +367,16 @@ static void avrcp_target_packet_handler(uint8_t packet_type, uint16_t channel, u
case AVRCP_SUBEVENT_UNIT_INFO_QUERY:
avrcp_target_unit_info(avrcp_cid, AVRCP_SUBUNIT_TYPE_AUDIO, company_id);
break;
case AVRCP_SUBEVENT_SUBUNIT_INFO_QUERY:
avrcp_target_subunit_info(avrcp_cid, AVRCP_SUBUNIT_TYPE_UNIT, avrcp_subevent_subunit_info_query_get_offset(packet), (uint8_t *)subunit_info);
break;
case AVRCP_SUBEVENT_EVENT_IDS_QUERY:
avrcp_target_supported_events(avrcp_cid, events_num, events, sizeof(events));
break;
case AVRCP_SUBEVENT_COMPANY_IDS_QUERY:
avrcp_target_supported_companies(avrcp_cid, companies_num, companies, sizeof(companies));
break;
case AVRCP_SUBEVENT_CONNECTION_RELEASED:
printf("Channel released: avrcp_cid 0x%02x\n", avrcp_subevent_connection_released_get_avrcp_cid(packet));

View File

@ -1677,7 +1677,21 @@ typedef uint8_t sm_key_t[16];
* @param avrcp_cid
* @param offset page*4
*/
#define AVRCP_SUBEVENT_SUBUNIT_INFO_QUERY 0x12
#define AVRCP_SUBEVENT_SUBUNIT_INFO_QUERY 0x11
/**
* @format 12
* @param subevent_code
* @param avrcp_cid
*/
#define AVRCP_SUBEVENT_COMPANY_IDS_QUERY 0x12
/**
* @format 12
* @param subevent_code
* @param avrcp_cid
*/
#define AVRCP_SUBEVENT_EVENT_IDS_QUERY 0x13
/**
* @format 121BH1

View File

@ -5458,6 +5458,26 @@ static inline uint8_t avrcp_subevent_subunit_info_query_get_offset(const uint8_t
return event[5];
}
/**
* @brief Get field avrcp_cid from event AVRCP_SUBEVENT_COMPANY_IDS_QUERY
* @param event packet
* @return avrcp_cid
* @note: btstack_type 2
*/
static inline uint16_t avrcp_subevent_company_ids_query_get_avrcp_cid(const uint8_t * event){
return little_endian_read_16(event, 3);
}
/**
* @brief Get field avrcp_cid from event AVRCP_SUBEVENT_EVENT_IDS_QUERY
* @param event packet
* @return avrcp_cid
* @note: btstack_type 2
*/
static inline uint16_t avrcp_subevent_event_ids_query_get_avrcp_cid(const uint8_t * event){
return little_endian_read_16(event, 3);
}
/**
* @brief Get field goep_cid from event GOEP_SUBEVENT_CONNECTION_OPENED
* @param event packet

View File

@ -55,6 +55,33 @@ extern "C" {
#define BT_SIG_COMPANY_ID 0x001958
typedef enum {
AVRCP_STATUS_INVALID_COMMAND = 0, // sent if TG received a PDU that it did not understand.
AVRCP_STATUS_INVALID_PARAMETER, // Sent if the TG received a PDU with a parameter ID that it did not understand, or, if there is only one parameter ID in the PDU.
AVRCP_STATUS_SPECIFIED_PARAMETER_NOT_FOUND, // sent if the parameter ID is understood, but content is wrong or corrupted.
AVRCP_STATUS_INTERNAL_ERROR, // sent if there are error conditions not covered by a more specific error code.
AVRCP_STATUS_SUCCESS, // sent if the operation was successful.
AVRCP_STATUS_UID_CHANGED, // sent if the UIDs on the device have changed.
AVRCP_STATUS_RESERVED_6,
AVRCP_STATUS_INVALID_DIRECTION, // The Direction parameter is invalid. Valid for command: Change Path
AVRCP_STATUS_NOT_A_DIRECTORY, // The UID provided does not refer to a folder item. Valid for command: Change Path
AVRCP_STATUS_DOES_NOT_EXIST, // The UID provided does not refer to any currently valid. Valid for command: Change Path, PlayItem, AddToNowPlaying, GetItemAttributes
AVRCP_STATUS_INVALID_SCOPE, // The scope parameter is invalid. Valid for command: GetFolderItems, PlayItem, AddToNowPlayer, GetItemAttributes,
AVRCP_STATUS_RANGE_OUT_OF_BOUNDS, // The start of range provided is not valid. Valid for command: GetFolderItems
AVRCP_STATUS_UID_IS_A_DIRECTORY, // The UID provided refers to a directory, which cannot be handled by this media player. Valid for command: PlayItem, AddToNowPlaying
AVRCP_STATUS_MEDIA_IN_USE, // The media is not able to be used for this operation at this time. Valid for command: PlayItem, AddToNowPlaying
AVRCP_STATUS_NOW_PLAYING_LIST_FULL, // No more items can be added to the Now Playing List. Valid for command: AddToNowPlaying
AVRCP_STATUS_SEARCH_NOT_SUPPORTED, // The Browsed Media Player does not support search. Valid for command: Search
AVRCP_STATUS_SEARCH_IN_PROGRESS, // A search operation is already in progress. Valid for command: Search
AVRCP_STATUS_INVALID_PLAYER_ID, // The specified Player Id does not refer to a valid player. Valid for command: SetAddressedPlayer, SetBrowsedPlayer
AVRCP_STATUS_PLAYER_NOT_BROWSABLE, // The Player Id supplied refers to a Media Player which does not support browsing. Valid for command: SetBrowsedPlayer
AVRCP_STATUS_PLAYER_NOT_ADDRESSED, // The Player Id supplied refers to a player which is not currently addressed, and the command is not able to be performed if the player is not set as addressed. Valid for command: Search SetBrowsedPlayer
AVRCP_STATUS_NO_VALID_SEARCH_RESULTS, // The Search result list does not contain valid entries, e.g. after being invalidated due to change of browsed player. Valid for command: GetFolderItems
AVRCP_STATUS_NO_AVAILABLE_PLAYERS,
AVRCP_STATUS_ADDRESSED_PLAYER_CHANGED, // Valid for command: Register Notification
AVRCP_STATUS_RESERVED // 0x17 - 0xFF
} avrcp_status_code_t;
typedef enum {
AVRCP_SINGLE_PACKET= 0,
AVRCP_START_PACKET ,

View File

@ -63,7 +63,7 @@ static void avrcp_target_emit_respond_query(btstack_packet_handler_t callback, u
(*callback)(HCI_EVENT_PACKET, 0, event, sizeof(event));
}
static void avrcp_target_emit_respond_subunit_info_query(btstack_packet_handler_t callback, uint16_t avrcp_cid, uint8_t page){
static void avrcp_target_emit_respond_subunit_info_query(btstack_packet_handler_t callback, uint16_t avrcp_cid, uint8_t offset){
if (!callback) return;
uint8_t event[6];
int pos = 0;
@ -72,7 +72,20 @@ static void avrcp_target_emit_respond_subunit_info_query(btstack_packet_handler_
event[pos++] = AVRCP_SUBEVENT_SUBUNIT_INFO_QUERY;
little_endian_store_16(event, pos, avrcp_cid);
pos += 2;
event[pos++] = page;
event[pos++] = offset;
(*callback)(HCI_EVENT_PACKET, 0, event, sizeof(event));
}
static void avrcp_target_emit_respond_get_capabilities(btstack_packet_handler_t callback, uint16_t avrcp_cid, uint8_t subevent_id, uint8_t * company_id){
if (!callback) return;
uint8_t event[8];
int pos = 0;
event[pos++] = HCI_EVENT_AVRCP_META;
event[pos++] = sizeof(event) - 2;
event[pos++] = subevent_id;
little_endian_store_16(event, pos, avrcp_cid);
pos += 2;
memcpy(event+pos, company_id, 3);
(*callback)(HCI_EVENT_PACKET, 0, event, sizeof(event));
}
@ -95,10 +108,28 @@ static int avrcp_send_response(uint16_t cid, avrcp_connection_t * connection){
// operands
memcpy(command+pos, connection->cmd_operands, connection->cmd_operands_length);
pos += connection->cmd_operands_length;
return l2cap_send(cid, command, pos);
}
static uint8_t avrcp_target_response_reject(avrcp_connection_t * connection, avrcp_subunit_type_t subunit_type, avrcp_subunit_id_t subunit_id, avrcp_command_opcode_t opcode, avrcp_pdu_id_t pdu_id, avrcp_status_code_t status){
// AVRCP_CTYPE_RESPONSE_REJECTED
connection->command_type = AVRCP_CTYPE_RESPONSE_REJECTED;
connection->subunit_type = subunit_type;
connection->subunit_id = subunit_id;
connection->command_opcode = opcode;
// company id is 3 bytes long
int pos = connection->cmd_operands_length;
connection->cmd_operands[pos++] = pdu_id;
connection->cmd_operands[pos++] = 0;
// param length
big_endian_store_32(connection->cmd_operands, pos, 1);
pos += 2;
connection->cmd_operands[pos++] = status;
connection->cmd_operands_length = pos;
return avrcp_send_response(connection->l2cap_signaling_cid, connection);
}
uint8_t avrcp_target_unit_info(uint16_t avrcp_cid, avrcp_subunit_type_t unit_type, uint32_t company_id){
avrcp_connection_t * connection = get_avrcp_connection_for_avrcp_cid(avrcp_cid, &avrcp_target_context);
if (!connection){
@ -108,16 +139,16 @@ uint8_t avrcp_target_unit_info(uint16_t avrcp_cid, avrcp_subunit_type_t unit_typ
if (connection->state != AVCTP_CONNECTION_OPENED) return ERROR_CODE_COMMAND_DISALLOWED;
uint8_t unit = 0;
connection->command_opcode = AVRCP_CMD_OPCODE_UNIT_INFO;
connection->command_type = AVRCP_CTYPE_RESPONSE_IMPLEMENTED_STABLE;
connection->subunit_type = AVRCP_SUBUNIT_TYPE_UNIT; //vendor unique
connection->subunit_id = AVRCP_SUBUNIT_ID_IGNORE;
connection->command_opcode = AVRCP_CMD_OPCODE_UNIT_INFO;
connection->cmd_operands_length = 5;
connection->cmd_operands[0] = 0x07;
connection->cmd_operands[1] = (unit_type << 4) | unit;
// company id is 3 bytes long
little_endian_store_32(connection->cmd_operands, 2, company_id);
big_endian_store_32(connection->cmd_operands, 2, company_id);
return avrcp_send_response(connection->l2cap_signaling_cid, connection);
}
@ -142,13 +173,61 @@ uint8_t avrcp_target_subunit_info(uint16_t avrcp_cid, avrcp_subunit_type_t subun
return avrcp_send_response(connection->l2cap_signaling_cid, connection);
}
static uint8_t avrcp_target_capability(uint16_t avrcp_cid, avrcp_capability_id_t capability_id, uint8_t capabilities_num, uint8_t * capabilities, uint8_t size){
avrcp_connection_t * connection = get_avrcp_connection_for_avrcp_cid(avrcp_cid, &avrcp_target_context);
if (!connection){
log_error("avrcp_unit_info: could not find a connection.");
return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
}
if (connection->state != AVCTP_CONNECTION_OPENED) return ERROR_CODE_COMMAND_DISALLOWED;
connection->command_opcode = AVRCP_CMD_OPCODE_VENDOR_DEPENDENT;
connection->command_type = AVRCP_CTYPE_RESPONSE_IMPLEMENTED_STABLE;
connection->subunit_type = AVRCP_SUBUNIT_TYPE_PANEL; //AVRCP_SUBUNIT_TYPE_UNIT;
connection->subunit_id = AVRCP_SUBUNIT_ID;
int pos = connection->cmd_operands_length;
connection->cmd_operands[pos++] = AVRCP_PDU_ID_GET_CAPABILITIES;
connection->cmd_operands[pos++] = 0;
// param length
big_endian_store_16(connection->cmd_operands, pos, 2+size);
pos += 2;
connection->cmd_operands[pos++] = capability_id;
connection->cmd_operands[pos++] = capabilities_num;
memcpy(connection->cmd_operands+pos, capabilities, size);
pos += size;
connection->cmd_operands_length = pos;
return avrcp_send_response(connection->l2cap_signaling_cid, connection);
}
uint8_t avrcp_target_supported_companies(uint16_t avrcp_cid, uint8_t capabilities_num, uint8_t * capabilities, uint8_t size ){
return avrcp_target_capability(avrcp_cid, AVRCP_CAPABILITY_ID_COMPANY, capabilities_num, capabilities, size);
}
uint8_t avrcp_target_supported_events(uint16_t avrcp_cid, uint8_t capabilities_num, uint8_t * capabilities, uint8_t size){
return avrcp_target_capability(avrcp_cid, AVRCP_CAPABILITY_ID_EVENT, capabilities_num, capabilities, size);
}
static uint8_t * avrcp_get_company_id(uint8_t *packet, uint16_t size){
UNUSED(size);
return packet + 6;
}
static uint8_t * avrcp_get_pdu(uint8_t *packet, uint16_t size){
UNUSED(size);
return packet + 9;
}
static void avrcp_handle_l2cap_data_packet_for_signaling_connection(avrcp_connection_t * connection, uint8_t *packet, uint16_t size){
UNUSED(connection);
UNUSED(packet);
UNUSED(size);
// uint8_t opcode;
// int pos = 3;
uint8_t transport_header = packet[0];
connection->transaction_label = transport_header >> 4;
// uint8_t packet_type = (transport_header & 0x0F) >> 2;
@ -157,27 +236,58 @@ static void avrcp_handle_l2cap_data_packet_for_signaling_connection(avrcp_connec
// uint8_t byte_value = packet[2];
// uint16_t pid = (byte_value << 8) | packet[2];
// avrcp_command_type_t ctype = (avrcp_command_type_t) packet[pos++];
// byte_value = packet[pos++];
// avrcp_subunit_type_t subunit_type = (avrcp_subunit_type_t) (byte_value >> 3);
// avrcp_subunit_type_t subunit_id = (avrcp_subunit_type_t) (byte_value & 0x07);
// avrcp_command_type_t ctype = (avrcp_command_type_t) packet[3];
// uint8_t byte_value = packet[4];
avrcp_subunit_type_t subunit_type = (avrcp_subunit_type_t) (packet[4] >> 3);
avrcp_subunit_id_t subunit_id = (avrcp_subunit_id_t) (packet[4] & 0x07);
// opcode = packet[pos++];
// printf(" Transport header 0x%02x (transaction_label %d, packet_type %d, frame_type %d, ipid %d), pid 0x%4x\n",
// transport_header, transaction_label, packet_type, frame_type, ipid, pid);
// printf_hexdump(packet+pos, size-pos);
// uint8_t pdu_id;
// uint16_t param_length;
uint8_t * operands = packet + 6;
switch (avrcp_cmd_opcode(packet,size)){
avrcp_command_opcode_t opcode = avrcp_cmd_opcode(packet,size);
// uint16_t param_length = big_endian_read_16(operands, 5);
uint8_t * company_id = avrcp_get_company_id(packet, size);
uint8_t * pdu = avrcp_get_pdu(packet, size);
uint8_t pdu_id = pdu[0];
connection->cmd_operands_length = 0;
switch (opcode){
case AVRCP_CMD_OPCODE_UNIT_INFO:
avrcp_target_emit_respond_query(avrcp_target_context.avrcp_callback, connection->avrcp_cid, AVRCP_SUBEVENT_UNIT_INFO_QUERY);
break;
case AVRCP_CMD_OPCODE_SUBUNIT_INFO:
// printf("operands 0x%02x\n", );
// printf_hexdump(packet, size)
avrcp_target_emit_respond_subunit_info_query(avrcp_target_context.avrcp_callback, connection->avrcp_cid, 4 * (operands[0]>>4));
case AVRCP_CMD_OPCODE_SUBUNIT_INFO:{
uint8_t offset = 4 * (packet[6]>>4);
avrcp_target_emit_respond_subunit_info_query(avrcp_target_context.avrcp_callback, connection->avrcp_cid, offset);
break;
}
case AVRCP_CMD_OPCODE_VENDOR_DEPENDENT:
pdu_id = pdu[0];
// 1 - reserved
// 2-3 param length,
switch (pdu_id){
case AVRCP_PDU_ID_GET_CAPABILITIES:{
avrcp_capability_id_t capability_id = (avrcp_capability_id_t) pdu[4];
memcpy(connection->cmd_operands, company_id, 3);
connection->cmd_operands_length = 3;
switch (capability_id){
case AVRCP_CAPABILITY_ID_EVENT:
avrcp_target_emit_respond_get_capabilities(avrcp_target_context.avrcp_callback, connection->avrcp_cid, AVRCP_SUBEVENT_EVENT_IDS_QUERY, company_id);
break;
case AVRCP_CAPABILITY_ID_COMPANY:
avrcp_target_emit_respond_get_capabilities(avrcp_target_context.avrcp_callback, connection->avrcp_cid, AVRCP_SUBEVENT_COMPANY_IDS_QUERY, company_id);
break;
default:
avrcp_target_response_reject(connection, subunit_type, subunit_id, opcode, pdu_id, AVRCP_STATUS_INVALID_PARAMETER);
break;
}
break;
}
default:
break;
}
break;
default:
printf("AVRCP source: opcode 0x%02x not implemented\n", avrcp_cmd_opcode(packet,size));

View File

@ -77,6 +77,8 @@ uint8_t avrcp_target_disconnect(uint16_t avrcp_cid);
uint8_t avrcp_target_unit_info(uint16_t avrcp_cid, avrcp_subunit_type_t unit_type, uint32_t company_id);
uint8_t avrcp_target_subunit_info(uint16_t avrcp_cid, avrcp_subunit_type_t subunit_type, uint8_t offset, uint8_t * subunit_info_data);
uint8_t avrcp_target_supported_companies(uint16_t avrcp_cid, uint8_t capabilities_length, uint8_t * capabilities, uint8_t size);
uint8_t avrcp_target_supported_events(uint16_t avrcp_cid, uint8_t capabilities_length, uint8_t * capabilities, uint8_t size);
/* API_END */
#if defined __cplusplus