avrcp target: add get play status query

This commit is contained in:
Milanka Ringwald 2017-07-26 17:28:06 +02:00
parent d13869281f
commit c045af9951
6 changed files with 109 additions and 37 deletions

View File

@ -79,17 +79,6 @@ static uint8_t media_sbc_codec_capabilities[] = {
2, 53 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[] = { static const int16_t sine_int16[] = {
0, 2057, 4107, 6140, 8149, 10126, 12062, 13952, 15786, 17557, 0, 2057, 4107, 6140, 8149, 10126, 12062, 13952, 15786, 17557,
19260, 20886, 22431, 23886, 25247, 26509, 27666, 28714, 29648, 30466, 19260, 20886, 22431, 23886, 25247, 26509, 27666, 28714, 29648, 30466,
@ -131,6 +120,19 @@ static int hxcmod_initialized;
static modcontext mod_context; static modcontext mod_context;
static tracker_buffer_state trkbuf; static tracker_buffer_state trkbuf;
/* AVRCP Target context START */
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 uint32_t company_id = 0x112233; static uint32_t company_id = 0x112233;
static uint8_t companies_num = 1; static uint8_t companies_num = 1;
@ -155,6 +157,16 @@ static uint8_t events[] = {
AVRCP_NOTIFICATION_EVENT_VOLUME_CHANGED AVRCP_NOTIFICATION_EVENT_VOLUME_CHANGED
}; };
typedef struct {
avrcp_play_status_t status;
uint32_t song_length_ms; // 0xFFFFFFFF if not supported
uint32_t song_position_ms; // 0xFFFFFFFF if not supported
} avrcp_play_status_info_t;
avrcp_play_status_info_t play_info;
/* AVRCP Target context END */
static void a2dp_demo_send_media_packet(void){ static void a2dp_demo_send_media_packet(void){
int num_bytes_in_frame = btstack_sbc_encoder_sbc_buffer_length(); int num_bytes_in_frame = btstack_sbc_encoder_sbc_buffer_length();
int bytes_in_storage = media_tracker.sbc_storage_count; int bytes_in_storage = media_tracker.sbc_storage_count;
@ -303,6 +315,7 @@ static void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packe
break; break;
case A2DP_SUBEVENT_STREAM_STARTED: case A2DP_SUBEVENT_STREAM_STARTED:
play_info.status = AVRCP_PLAY_STATUS_PLAYING;
a2dp_demo_timer_start(&media_tracker); a2dp_demo_timer_start(&media_tracker);
printf("Stream started.\n"); printf("Stream started.\n");
break; break;
@ -312,11 +325,13 @@ static void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packe
break; break;
case A2DP_SUBEVENT_STREAM_SUSPENDED: case A2DP_SUBEVENT_STREAM_SUSPENDED:
play_info.status = AVRCP_PLAY_STATUS_PAUSED;
printf("Stream paused.\n"); printf("Stream paused.\n");
a2dp_demo_timer_pause(&media_tracker); a2dp_demo_timer_pause(&media_tracker);
break; break;
case A2DP_SUBEVENT_STREAM_RELEASED: case A2DP_SUBEVENT_STREAM_RELEASED:
play_info.status = AVRCP_PLAY_STATUS_STOPPED;
printf("Stream released.\n"); printf("Stream released.\n");
a2dp_demo_timer_stop(&media_tracker); a2dp_demo_timer_stop(&media_tracker);
break; break;
@ -359,6 +374,10 @@ static void avrcp_target_packet_handler(uint8_t packet_type, uint16_t channel, u
return; return;
} }
avrcp_cid = local_cid; avrcp_cid = local_cid;
play_info.song_length_ms = 0xFFFFFFFF;
play_info.song_position_ms = 0xFFFFFFFF;
play_info.status = AVRCP_PLAY_STATUS_ERROR;
avrcp_subevent_connection_established_get_bd_addr(packet, event_addr); 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), local_cid); printf("Channel successfully opened: %s, avrcp_cid 0x%02x\n", bd_addr_to_str(event_addr), local_cid);
return; return;
@ -377,7 +396,9 @@ static void avrcp_target_packet_handler(uint8_t packet_type, uint16_t channel, u
case AVRCP_SUBEVENT_COMPANY_IDS_QUERY: case AVRCP_SUBEVENT_COMPANY_IDS_QUERY:
avrcp_target_supported_companies(avrcp_cid, companies_num, companies, sizeof(companies)); avrcp_target_supported_companies(avrcp_cid, companies_num, companies, sizeof(companies));
break; break;
case AVRCP_SUBEVENT_PLAY_STATUS_QUERY:
avrcp_target_play_status(avrcp_cid, play_info.song_length_ms, play_info.song_position_ms, play_info.status);
break;
case AVRCP_SUBEVENT_CONNECTION_RELEASED: case AVRCP_SUBEVENT_CONNECTION_RELEASED:
printf("Channel released: avrcp_cid 0x%02x\n", avrcp_subevent_connection_released_get_avrcp_cid(packet)); printf("Channel released: avrcp_cid 0x%02x\n", avrcp_subevent_connection_released_get_avrcp_cid(packet));
avrcp_cid = 0; avrcp_cid = 0;

View File

@ -1684,14 +1684,21 @@ typedef uint8_t sm_key_t[16];
* @param subevent_code * @param subevent_code
* @param avrcp_cid * @param avrcp_cid
*/ */
#define AVRCP_SUBEVENT_COMPANY_IDS_QUERY 0x12 #define AVRCP_SUBEVENT_COMPANY_IDS_QUERY 0x12
/** /**
* @format 12 * @format 12
* @param subevent_code * @param subevent_code
* @param avrcp_cid * @param avrcp_cid
*/ */
#define AVRCP_SUBEVENT_EVENT_IDS_QUERY 0x13 #define AVRCP_SUBEVENT_EVENT_IDS_QUERY 0x13
/**
* @format 12
* @param subevent_code
* @param avrcp_cid
*/
#define AVRCP_SUBEVENT_PLAY_STATUS_QUERY 0x14
/** /**
* @format 121BH1 * @format 121BH1

View File

@ -5478,6 +5478,16 @@ static inline uint16_t avrcp_subevent_event_ids_query_get_avrcp_cid(const uint8_
return little_endian_read_16(event, 3); return little_endian_read_16(event, 3);
} }
/**
* @brief Get field avrcp_cid from event AVRCP_SUBEVENT_PLAY_STATUS_QUERY
* @param event packet
* @return avrcp_cid
* @note: btstack_type 2
*/
static inline uint16_t avrcp_subevent_play_status_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 * @brief Get field goep_cid from event GOEP_SUBEVENT_CONNECTION_OPENED
* @param event packet * @param event packet

View File

@ -119,7 +119,8 @@ typedef enum {
AVRCP_PDU_ID_GET_ELEMENT_ATTRIBUTES = 0x20, AVRCP_PDU_ID_GET_ELEMENT_ATTRIBUTES = 0x20,
AVRCP_PDU_ID_GET_PLAY_STATUS = 0x30, AVRCP_PDU_ID_GET_PLAY_STATUS = 0x30,
AVRCP_PDU_ID_REGISTER_NOTIFICATION = 0x31, AVRCP_PDU_ID_REGISTER_NOTIFICATION = 0x31,
AVRCP_PDU_ID_SET_ABSOLUTE_VOLUME = 0x50 AVRCP_PDU_ID_SET_ABSOLUTE_VOLUME = 0x50,
AVRCP_PDU_ID_UNDEFINED = 0xFF
} avrcp_pdu_id_t; } avrcp_pdu_id_t;
typedef enum { typedef enum {
@ -212,6 +213,15 @@ typedef enum {
AVRCP_OPERATION_ID_UNDEFINED = 0xFF AVRCP_OPERATION_ID_UNDEFINED = 0xFF
} avrcp_operation_id_t; } avrcp_operation_id_t;
typedef enum{
AVRCP_PLAY_STATUS_STOPPED = 0x00,
AVRCP_PLAY_STATUS_PLAYING,
AVRCP_PLAY_STATUS_PAUSED,
AVRCP_PLAY_STATUS_FWD_SEEK,
AVRCP_PLAY_STATUS_REV_SEEK,
AVRCP_PLAY_STATUS_ERROR = 0xFF
} avrcp_play_status_t;
typedef enum { typedef enum {
AVCTP_CONNECTION_IDLE, AVCTP_CONNECTION_IDLE,
AVCTP_SIGNALING_W4_SDP_QUERY_COMPLETE, AVCTP_SIGNALING_W4_SDP_QUERY_COMPLETE,
@ -250,15 +260,6 @@ typedef struct {
uint16_t notifications_to_deregister; uint16_t notifications_to_deregister;
} avrcp_connection_t; } avrcp_connection_t;
typedef enum {
AVRCP_PLAY_STATUS_STOPPED = 0x00,
AVRCP_PLAY_STATUS_PLAYING,
AVRCP_PLAY_STATUS_PAUSED,
AVRCP_PLAY_STATUS_FWD_SEEK,
AVRCP_PLAY_STATUS_REV_SEEK,
AVRCP_PLAY_STATUS_ERROR = 0xFF
} avrcp_play_status_t;
typedef enum { typedef enum {
AVRCP_SHUFFLE_MODE_INVALID, AVRCP_SHUFFLE_MODE_INVALID,
AVRCP_SHUFFLE_MODE_OFF, AVRCP_SHUFFLE_MODE_OFF,

View File

@ -76,16 +76,14 @@ static void avrcp_target_emit_respond_subunit_info_query(btstack_packet_handler_
(*callback)(HCI_EVENT_PACKET, 0, event, sizeof(event)); (*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){ static void avrcp_target_emit_respond_vendor_dependent_query(btstack_packet_handler_t callback, uint16_t avrcp_cid, uint8_t subevent_id){
if (!callback) return; if (!callback) return;
uint8_t event[8]; uint8_t event[5];
int pos = 0; int pos = 0;
event[pos++] = HCI_EVENT_AVRCP_META; event[pos++] = HCI_EVENT_AVRCP_META;
event[pos++] = sizeof(event) - 2; event[pos++] = sizeof(event) - 2;
event[pos++] = subevent_id; event[pos++] = subevent_id;
little_endian_store_16(event, pos, avrcp_cid); little_endian_store_16(event, pos, avrcp_cid);
pos += 2;
memcpy(event+pos, company_id, 3);
(*callback)(HCI_EVENT_PACKET, 0, event, sizeof(event)); (*callback)(HCI_EVENT_PACKET, 0, event, sizeof(event));
} }
@ -124,7 +122,7 @@ static uint8_t avrcp_target_response_reject(avrcp_connection_t * connection, avr
connection->cmd_operands[pos++] = pdu_id; connection->cmd_operands[pos++] = pdu_id;
connection->cmd_operands[pos++] = 0; connection->cmd_operands[pos++] = 0;
// param length // param length
big_endian_store_32(connection->cmd_operands, pos, 1); big_endian_store_16(connection->cmd_operands, pos, 1);
pos += 2; pos += 2;
connection->cmd_operands[pos++] = status; connection->cmd_operands[pos++] = status;
connection->cmd_operands_length = pos; connection->cmd_operands_length = pos;
@ -212,6 +210,36 @@ static uint8_t avrcp_target_capability(uint16_t avrcp_cid, avrcp_capability_id_t
return ERROR_CODE_SUCCESS; return ERROR_CODE_SUCCESS;
} }
uint8_t avrcp_target_play_status(uint16_t avrcp_cid, uint32_t song_length_ms, uint32_t song_position_ms, avrcp_play_status_t status){
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_PLAY_STATUS;
connection->cmd_operands[pos++] = 0;
// param length
big_endian_store_16(connection->cmd_operands, pos, 2+4+4+1);
pos += 2;
big_endian_store_32(connection->cmd_operands, pos, song_length_ms);
pos += 4;
big_endian_store_32(connection->cmd_operands, pos, song_position_ms);
pos += 4;
connection->cmd_operands[pos++] = status;
connection->cmd_operands_length = pos;
connection->state = AVCTP_W2_SEND_RESPONSE;
avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid);
return ERROR_CODE_SUCCESS;
}
uint8_t avrcp_target_supported_companies(uint16_t avrcp_cid, uint8_t capabilities_num, uint8_t * capabilities, uint8_t size ){ 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); return avrcp_target_capability(avrcp_cid, AVRCP_CAPABILITY_ID_COMPANY, capabilities_num, capabilities, size);
@ -262,7 +290,8 @@ static void avrcp_handle_l2cap_data_packet_for_signaling_connection(avrcp_connec
uint8_t * company_id = avrcp_get_company_id(packet, size); uint8_t * company_id = avrcp_get_company_id(packet, size);
uint8_t * pdu = avrcp_get_pdu(packet, size); uint8_t * pdu = avrcp_get_pdu(packet, size);
uint8_t pdu_id = pdu[0]; uint8_t pdu_id;
connection->cmd_operands_length = 0; connection->cmd_operands_length = 0;
switch (opcode){ switch (opcode){
@ -278,17 +307,17 @@ static void avrcp_handle_l2cap_data_packet_for_signaling_connection(avrcp_connec
pdu_id = pdu[0]; pdu_id = pdu[0];
// 1 - reserved // 1 - reserved
// 2-3 param length, // 2-3 param length,
memcpy(connection->cmd_operands, company_id, 3);
connection->cmd_operands_length = 3;
switch (pdu_id){ switch (pdu_id){
case AVRCP_PDU_ID_GET_CAPABILITIES:{ case AVRCP_PDU_ID_GET_CAPABILITIES:{
avrcp_capability_id_t capability_id = (avrcp_capability_id_t) pdu[4]; 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){ switch (capability_id){
case AVRCP_CAPABILITY_ID_EVENT: 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); avrcp_target_emit_respond_vendor_dependent_query(avrcp_target_context.avrcp_callback, connection->avrcp_cid, AVRCP_SUBEVENT_EVENT_IDS_QUERY);
break; break;
case AVRCP_CAPABILITY_ID_COMPANY: 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); avrcp_target_emit_respond_vendor_dependent_query(avrcp_target_context.avrcp_callback, connection->avrcp_cid, AVRCP_SUBEVENT_COMPANY_IDS_QUERY);
break; break;
default: default:
avrcp_target_response_reject(connection, subunit_type, subunit_id, opcode, pdu_id, AVRCP_STATUS_INVALID_PARAMETER); avrcp_target_response_reject(connection, subunit_type, subunit_id, opcode, pdu_id, AVRCP_STATUS_INVALID_PARAMETER);
@ -296,7 +325,12 @@ static void avrcp_handle_l2cap_data_packet_for_signaling_connection(avrcp_connec
} }
break; break;
} }
case AVRCP_PDU_ID_GET_PLAY_STATUS:
avrcp_target_emit_respond_vendor_dependent_query(avrcp_target_context.avrcp_callback, connection->avrcp_cid, AVRCP_SUBEVENT_PLAY_STATUS_QUERY);
break;
default: default:
printf("unhandled pdu id 0x%02x\n", pdu_id);
avrcp_target_response_reject(connection, subunit_type, subunit_id, opcode, pdu_id, AVRCP_STATUS_INVALID_COMMAND);
break; break;
} }
break; break;
@ -317,13 +351,10 @@ static void avrcp_controller_packet_handler(uint8_t packet_type, uint16_t channe
case HCI_EVENT_PACKET: case HCI_EVENT_PACKET:
switch (hci_event_packet_get_type(packet)){ switch (hci_event_packet_get_type(packet)){
case L2CAP_EVENT_CAN_SEND_NOW: case L2CAP_EVENT_CAN_SEND_NOW:
printf("L2CAP_EVENT_CAN_SEND_NOW \n");
connection = get_avrcp_connection_for_l2cap_signaling_cid(channel, &avrcp_target_context); connection = get_avrcp_connection_for_l2cap_signaling_cid(channel, &avrcp_target_context);
if (!connection) break; if (!connection) break;
switch (connection->state){ switch (connection->state){
case AVCTP_W2_SEND_RESPONSE: case AVCTP_W2_SEND_RESPONSE:
printf("AVCTP_W2_SEND_RESPONSE - > OPEN \n");
connection->state = AVCTP_CONNECTION_OPENED; connection->state = AVCTP_CONNECTION_OPENED;
avrcp_send_response(connection->l2cap_signaling_cid, connection); avrcp_send_response(connection->l2cap_signaling_cid, connection);
break; break;

View File

@ -80,6 +80,8 @@ uint8_t avrcp_target_subunit_info(uint16_t avrcp_cid, avrcp_subunit_type_t subun
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_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); uint8_t avrcp_target_supported_events(uint16_t avrcp_cid, uint8_t capabilities_length, uint8_t * capabilities, uint8_t size);
uint8_t avrcp_target_play_status(uint16_t avrcp_cid, uint32_t song_length_ms, uint32_t song_position_ms, avrcp_play_status_t status);
/* API_END */ /* API_END */
#if defined __cplusplus #if defined __cplusplus
} }