avrcp target: added response for unit and subunit info

This commit is contained in:
Milanka Ringwald 2017-07-25 16:03:13 +02:00
parent 6aa7d7949b
commit 4b338011de
11 changed files with 268 additions and 12 deletions

View File

@ -585,7 +585,7 @@ static void avrcp_controller_packet_handler(uint8_t packet_type, uint16_t channe
switch (packet[2]){
case AVRCP_SUBEVENT_CONNECTION_ESTABLISHED: {
local_cid = avrcp_subevent_connection_established_get_avrcp_cid(packet);
if (avrcp_cid != local_cid) {
if (avrcp_cid != 0 && avrcp_cid != local_cid) {
printf("AVRCP Connection failed, expected 0x%02X l2cap cid, received 0x%02X\n", avrcp_cid, local_cid);
return;
}
@ -596,6 +596,8 @@ static void avrcp_controller_packet_handler(uint8_t packet_type, uint16_t channe
avrcp_cid = 0;
return;
}
avrcp_cid = local_cid;
avrcp_subevent_connection_established_get_bd_addr(packet, event_addr);
printf("Channel successfully opened: %s, avrcp_cid 0x%02x\n", bd_addr_to_str(event_addr), avrcp_cid);

View File

@ -79,6 +79,17 @@ static uint8_t media_sbc_codec_capabilities[] = {
2, 53
};
static const uint8_t subunit_info[] = {
0,0,0,0,
1,1,1,1,
2,2,2,2,
3,3,3,3,
4,4,4,4,
5,5,5,5,
6,6,6,6,
7,7,7,7
};
static const int16_t sine_int16[] = {
0, 2057, 4107, 6140, 8149, 10126, 12062, 13952, 15786, 17557,
19260, 20886, 22431, 23886, 25247, 26509, 27666, 28714, 29648, 30466,
@ -120,6 +131,8 @@ static int hxcmod_initialized;
static modcontext mod_context;
static tracker_buffer_state trkbuf;
static uint32_t company_id = 0x112233;
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;
@ -328,6 +341,15 @@ static void avrcp_target_packet_handler(uint8_t packet_type, uint16_t channel, u
printf("Channel successfully opened: %s, avrcp_cid 0x%02x\n", bd_addr_to_str(event_addr), local_cid);
return;
}
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_CONNECTION_RELEASED:
printf("Channel released: avrcp_cid 0x%02x\n", avrcp_subevent_connection_released_get_avrcp_cid(packet));
avrcp_cid = 0;

View File

@ -1664,6 +1664,21 @@ typedef uint8_t sm_key_t[16];
*/
#define AVRCP_SUBEVENT_PLAYER_APPLICATION_VALUE_RESPONSE 0x0F
/**
* @format 12
* @param subevent_code
* @param avrcp_cid
*/
#define AVRCP_SUBEVENT_UNIT_INFO_QUERY 0x10
/**
* @format 121
* @param subevent_code
* @param avrcp_cid
* @param offset page*4
*/
#define AVRCP_SUBEVENT_SUBUNIT_INFO_QUERY 0x12
/**
* @format 121BH1
* @param subevent_code

View File

@ -4890,6 +4890,62 @@ static inline uint8_t a2dp_subevent_stream_released_get_local_seid(const uint8_t
return event[5];
}
/**
* @brief Get field a2dp_cid from event A2DP_SUBEVENT_COMMAND_ACCEPTED
* @param event packet
* @return a2dp_cid
* @note: btstack_type 2
*/
static inline uint16_t a2dp_subevent_command_accepted_get_a2dp_cid(const uint8_t * event){
return little_endian_read_16(event, 3);
}
/**
* @brief Get field local_seid from event A2DP_SUBEVENT_COMMAND_ACCEPTED
* @param event packet
* @return local_seid
* @note: btstack_type 1
*/
static inline uint8_t a2dp_subevent_command_accepted_get_local_seid(const uint8_t * event){
return event[5];
}
/**
* @brief Get field signal_identifier from event A2DP_SUBEVENT_COMMAND_ACCEPTED
* @param event packet
* @return signal_identifier
* @note: btstack_type 1
*/
static inline uint8_t a2dp_subevent_command_accepted_get_signal_identifier(const uint8_t * event){
return event[6];
}
/**
* @brief Get field a2dp_cid from event A2DP_SUBEVENT_COMMAND_REJECTED
* @param event packet
* @return a2dp_cid
* @note: btstack_type 2
*/
static inline uint16_t a2dp_subevent_command_rejected_get_a2dp_cid(const uint8_t * event){
return little_endian_read_16(event, 3);
}
/**
* @brief Get field local_seid from event A2DP_SUBEVENT_COMMAND_REJECTED
* @param event packet
* @return local_seid
* @note: btstack_type 1
*/
static inline uint8_t a2dp_subevent_command_rejected_get_local_seid(const uint8_t * event){
return event[5];
}
/**
* @brief Get field signal_identifier from event A2DP_SUBEVENT_COMMAND_REJECTED
* @param event packet
* @return signal_identifier
* @note: btstack_type 1
*/
static inline uint8_t a2dp_subevent_command_rejected_get_signal_identifier(const uint8_t * event){
return event[6];
}
/**
* @brief Get field status from event AVRCP_SUBEVENT_CONNECTION_ESTABLISHED
* @param event packet
@ -5373,6 +5429,35 @@ static inline uint8_t avrcp_subevent_player_application_value_response_get_comma
return event[5];
}
/**
* @brief Get field avrcp_cid from event AVRCP_SUBEVENT_UNIT_INFO_QUERY
* @param event packet
* @return avrcp_cid
* @note: btstack_type 2
*/
static inline uint16_t avrcp_subevent_unit_info_query_get_avrcp_cid(const uint8_t * event){
return little_endian_read_16(event, 3);
}
/**
* @brief Get field avrcp_cid from event AVRCP_SUBEVENT_SUBUNIT_INFO_QUERY
* @param event packet
* @return avrcp_cid
* @note: btstack_type 2
*/
static inline uint16_t avrcp_subevent_subunit_info_query_get_avrcp_cid(const uint8_t * event){
return little_endian_read_16(event, 3);
}
/**
* @brief Get field offset from event AVRCP_SUBEVENT_SUBUNIT_INFO_QUERY
* @param event packet
* @return offset
* @note: btstack_type 1
*/
static inline uint8_t avrcp_subevent_subunit_info_query_get_offset(const uint8_t * event){
return event[5];
}
/**
* @brief Get field goep_cid from event GOEP_SUBEVENT_CONNECTION_OPENED
* @param event packet

View File

@ -195,6 +195,12 @@ const char * avrcp_repeat2str(uint8_t index){
return "NONE";
}
uint8_t avrcp_cmd_opcode(uint8_t *packet, uint16_t size){
uint8_t cmd_opcode_index = 5;
if (cmd_opcode_index > size) return AVRCP_CMD_OPCODE_UNDEFINED;
return packet[cmd_opcode_index];
}
void avrcp_create_sdp_record(uint8_t controller, uint8_t * service, uint32_t service_record_handle, uint8_t browsing, uint16_t supported_features, const char * service_name, const char * service_provider_name){
uint8_t* attribute;
de_create_sequence(service);

View File

@ -162,7 +162,7 @@ typedef enum {
AVRCP_CMD_OPCODE_VENDOR_DEPENDENT = 0x00,
// AVRCP_CMD_OPCODE_RESERVE = 0x01,
AVRCP_CMD_OPCODE_UNIT_INFO = 0x30,
// AVRCP_CMD_OPCODE_SUBUNIT_INFO = 0x31,
AVRCP_CMD_OPCODE_SUBUNIT_INFO = 0x31,
AVRCP_CMD_OPCODE_PASS_THROUGH = 0x7C,
// AVRCP_CMD_OPCODE_VERSION = 0xB0,
// AVRCP_CMD_OPCODE_POWER = 0xB2,
@ -194,6 +194,7 @@ typedef enum {
AVCTP_W2_SEND_RELEASE_COMMAND,
AVCTP_W4_STOP,
AVCTP_W2_SEND_COMMAND,
AVCTP_W2_SEND_RESPONSE,
AVCTP_W2_RECEIVE_PRESS_RESPONSE,
AVCTP_W2_RECEIVE_RESPONSE
} avctp_connection_state_t;
@ -282,6 +283,7 @@ uint8_t avrcp_connect(bd_addr_t bd_addr, avrcp_context_t * context, uint16_t * a
void avrcp_emit_connection_established(btstack_packet_handler_t callback, uint16_t avrcp_cid, bd_addr_t addr, uint8_t status);
void avrcp_emit_connection_closed(btstack_packet_handler_t callback, uint16_t avrcp_cid);
uint8_t avrcp_cmd_opcode(uint8_t *packet, uint16_t size);
avrcp_connection_t * get_avrcp_connection_for_l2cap_signaling_cid(uint16_t l2cap_cid, avrcp_context_t * context);
avrcp_connection_t * get_avrcp_connection_for_avrcp_cid(uint16_t avrcp_cid, avrcp_context_t * context);
void avrcp_request_can_send_now(avrcp_connection_t * connection, uint16_t l2cap_cid);

View File

@ -207,13 +207,6 @@ static void avrcp_prepare_notification(avrcp_connection_t * connection, avrcp_no
// answer page 61
}
static uint8_t avrcp_cmd_opcode(uint8_t *packet, uint16_t size){
uint8_t cmd_opcode_index = 5;
if (cmd_opcode_index > size) return AVRCP_CMD_OPCODE_UNDEFINED;
return packet[cmd_opcode_index];
}
static void avrcp_handle_l2cap_data_packet_for_signaling_connection(avrcp_connection_t * connection, uint8_t *packet, uint16_t size){
uint8_t operands[20];
uint8_t opcode;

View File

@ -51,10 +51,138 @@ void avrcp_target_create_sdp_record(uint8_t * service, uint32_t service_record_h
avrcp_create_sdp_record(0, service, service_record_handle, browsing, supported_features, service_name, service_provider_name);
}
static void avrcp_target_emit_respond_query(btstack_packet_handler_t callback, uint16_t avrcp_cid, uint8_t subeventID){
if (!callback) return;
uint8_t event[5];
int pos = 0;
event[pos++] = HCI_EVENT_AVRCP_META;
event[pos++] = sizeof(event) - 2;
event[pos++] = subeventID;
little_endian_store_16(event, pos, avrcp_cid);
pos += 2;
(*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){
if (!callback) return;
uint8_t event[6];
int pos = 0;
event[pos++] = HCI_EVENT_AVRCP_META;
event[pos++] = sizeof(event) - 2;
event[pos++] = AVRCP_SUBEVENT_SUBUNIT_INFO_QUERY;
little_endian_store_16(event, pos, avrcp_cid);
pos += 2;
event[pos++] = page;
(*callback)(HCI_EVENT_PACKET, 0, event, sizeof(event));
}
static int avrcp_send_response(uint16_t cid, avrcp_connection_t * connection){
uint8_t command[30];
int pos = 0;
// transport header
// Transaction label | Packet_type | C/R | IPID (1 == invalid profile identifier)
command[pos++] = (connection->transaction_label << 4) | (AVRCP_SINGLE_PACKET << 2) | (AVRCP_RESPONSE_FRAME << 1) | 0;
// Profile IDentifier (PID)
command[pos++] = BLUETOOTH_SERVICE_CLASS_AV_REMOTE_CONTROL >> 8;
command[pos++] = BLUETOOTH_SERVICE_CLASS_AV_REMOTE_CONTROL & 0x00FF;
// command_type
command[pos++] = connection->command_type;
// subunit_type | subunit ID
command[pos++] = (connection->subunit_type << 3) | connection->subunit_id;
// opcode
command[pos++] = (uint8_t)connection->command_opcode;
// operands
memcpy(command+pos, connection->cmd_operands, connection->cmd_operands_length);
pos += connection->cmd_operands_length;
return l2cap_send(cid, command, pos);
}
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){
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;
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->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);
return avrcp_send_response(connection->l2cap_signaling_cid, connection);
}
uint8_t avrcp_target_subunit_info(uint16_t avrcp_cid, avrcp_subunit_type_t subunit_type, uint8_t offset, uint8_t * subunit_info_data){
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_SUBUNIT_INFO;
connection->command_type = AVRCP_CTYPE_RESPONSE_IMPLEMENTED_STABLE;
connection->subunit_type = subunit_type; //vendor unique
connection->subunit_id = AVRCP_SUBUNIT_ID_IGNORE;
uint8_t page = offset / 4;
uint8_t extension_code = 7;
connection->cmd_operands_length = 5;
connection->cmd_operands[0] = (page << 4) | extension_code;
memcpy(connection->cmd_operands+1, subunit_info_data + offset, 4);
return avrcp_send_response(connection->l2cap_signaling_cid, connection);
}
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;
// uint8_t frame_type = (transport_header & 0x03) >> 1;
// uint8_t ipid = transport_header & 0x01;
// 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);
// 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)){
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));
break;
default:
printf("AVRCP source: opcode 0x%02x not implemented\n", avrcp_cmd_opcode(packet,size));
break;
}
}
static void avrcp_target_handle_can_send_now(avrcp_connection_t * connection){

View File

@ -74,6 +74,9 @@ uint8_t avrcp_target_connect(bd_addr_t bd_addr, uint16_t * avrcp_cid);
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);
/* API_END */
#if defined __cplusplus

Binary file not shown.

View File

@ -51,9 +51,9 @@
#ifdef HAVE_BTSTACK_STDIN
// mac 2011: static const char * device_addr_string = "04:0C:CE:E4:85:D3";
// pts:
static const char * device_addr_string = "00:1B:DC:08:0A:A5";
// mac 2013: static const char * device_addr_string = "84:38:35:65:d1:15";
// pts: static const char * device_addr_string = "00:1B:DC:08:0A:A5";
// mac 2013:
static const char * device_addr_string = "84:38:35:65:d1:15";
// phone 2013: static const char * device_addr_string = "D8:BB:2C:DF:F0:F2";
// minijambox: static const char * device_addr_string = "00:21:3C:AC:F7:38";
// head phones: static const char * device_addr_string = "00:18:09:28:50:18";