mirror of
https://github.com/bluekitchen/btstack.git
synced 2025-01-25 09:35:42 +00:00
avrcp target: add get play status query
This commit is contained in:
parent
d13869281f
commit
c045af9951
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user