mirror of
https://github.com/bluekitchen/btstack.git
synced 2025-04-10 15:44:32 +00:00
avrcp target: added response for unit and subunit info
This commit is contained in:
parent
6aa7d7949b
commit
4b338011de
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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){
|
||||
|
@ -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.
@ -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";
|
||||
|
Loading…
x
Reference in New Issue
Block a user