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
};
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,
@ -131,6 +120,19 @@ static int hxcmod_initialized;
static modcontext mod_context;
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 uint8_t companies_num = 1;
@ -155,6 +157,16 @@ static uint8_t events[] = {
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){
int num_bytes_in_frame = btstack_sbc_encoder_sbc_buffer_length();
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;
case A2DP_SUBEVENT_STREAM_STARTED:
play_info.status = AVRCP_PLAY_STATUS_PLAYING;
a2dp_demo_timer_start(&media_tracker);
printf("Stream started.\n");
break;
@ -312,11 +325,13 @@ static void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packe
break;
case A2DP_SUBEVENT_STREAM_SUSPENDED:
play_info.status = AVRCP_PLAY_STATUS_PAUSED;
printf("Stream paused.\n");
a2dp_demo_timer_pause(&media_tracker);
break;
case A2DP_SUBEVENT_STREAM_RELEASED:
play_info.status = AVRCP_PLAY_STATUS_STOPPED;
printf("Stream released.\n");
a2dp_demo_timer_stop(&media_tracker);
break;
@ -359,6 +374,10 @@ static void avrcp_target_packet_handler(uint8_t packet_type, uint16_t channel, u
return;
}
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);
printf("Channel successfully opened: %s, avrcp_cid 0x%02x\n", bd_addr_to_str(event_addr), local_cid);
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:
avrcp_target_supported_companies(avrcp_cid, companies_num, companies, sizeof(companies));
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:
printf("Channel released: avrcp_cid 0x%02x\n", avrcp_subevent_connection_released_get_avrcp_cid(packet));
avrcp_cid = 0;

View File

@ -1684,14 +1684,21 @@ typedef uint8_t sm_key_t[16];
* @param subevent_code
* @param avrcp_cid
*/
#define AVRCP_SUBEVENT_COMPANY_IDS_QUERY 0x12
#define AVRCP_SUBEVENT_COMPANY_IDS_QUERY 0x12
/**
* @format 12
* @param subevent_code
* @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

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);
}
/**
* @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
* @param event packet

View File

@ -119,7 +119,8 @@ typedef enum {
AVRCP_PDU_ID_GET_ELEMENT_ATTRIBUTES = 0x20,
AVRCP_PDU_ID_GET_PLAY_STATUS = 0x30,
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;
typedef enum {
@ -212,6 +213,15 @@ typedef enum {
AVRCP_OPERATION_ID_UNDEFINED = 0xFF
} 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 {
AVCTP_CONNECTION_IDLE,
AVCTP_SIGNALING_W4_SDP_QUERY_COMPLETE,
@ -250,15 +260,6 @@ typedef struct {
uint16_t notifications_to_deregister;
} 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 {
AVRCP_SHUFFLE_MODE_INVALID,
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));
}
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;
uint8_t event[8];
uint8_t event[5];
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));
}
@ -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++] = 0;
// param length
big_endian_store_32(connection->cmd_operands, pos, 1);
big_endian_store_16(connection->cmd_operands, pos, 1);
pos += 2;
connection->cmd_operands[pos++] = status;
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;
}
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 ){
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 * pdu = avrcp_get_pdu(packet, size);
uint8_t pdu_id = pdu[0];
uint8_t pdu_id;
connection->cmd_operands_length = 0;
switch (opcode){
@ -278,17 +307,17 @@ static void avrcp_handle_l2cap_data_packet_for_signaling_connection(avrcp_connec
pdu_id = pdu[0];
// 1 - reserved
// 2-3 param length,
memcpy(connection->cmd_operands, company_id, 3);
connection->cmd_operands_length = 3;
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);
avrcp_target_emit_respond_vendor_dependent_query(avrcp_target_context.avrcp_callback, connection->avrcp_cid, AVRCP_SUBEVENT_EVENT_IDS_QUERY);
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);
avrcp_target_emit_respond_vendor_dependent_query(avrcp_target_context.avrcp_callback, connection->avrcp_cid, AVRCP_SUBEVENT_COMPANY_IDS_QUERY);
break;
default:
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;
}
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:
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;
@ -317,13 +351,10 @@ static void avrcp_controller_packet_handler(uint8_t packet_type, uint16_t channe
case HCI_EVENT_PACKET:
switch (hci_event_packet_get_type(packet)){
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);
if (!connection) break;
switch (connection->state){
case AVCTP_W2_SEND_RESPONSE:
printf("AVCTP_W2_SEND_RESPONSE - > OPEN \n");
connection->state = AVCTP_CONNECTION_OPENED;
avrcp_send_response(connection->l2cap_signaling_cid, connection);
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_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 */
#if defined __cplusplus
}