avrcp: introduced shuffle and repeat

This commit is contained in:
Milanka Ringwald 2017-02-21 16:28:14 +01:00
parent 96b12aa570
commit 2a029560d7
5 changed files with 307 additions and 20 deletions

View File

@ -1327,12 +1327,12 @@ typedef uint8_t sm_key_t[16];
/** AVRCP Subevent */
/**
* @format 1H2B1
* @format 1H12B1
* @param subevent_code
* @param con_handle
* @param status 0 == OK
* @param local_cid
* @param bd_addr
* @param status 0 == OK
*/
#define AVRCP_SUBEVENT_CONNECTION_ESTABLISHED 0x01
@ -1341,13 +1341,13 @@ typedef uint8_t sm_key_t[16];
* @param subevent_code
* @param con_handle
*/
#define AVRCP_SUBEVENT_CONNECTION_CLOSED 0x02
#define AVRCP_SUBEVENT_CONNECTION_CLOSED 0x02
#define AVRCP_NOW_PLAYING_INFO 0x03
/**
* @format 1HJVJVJVJV114
* @format 1H1JVJVJVJV114
* @param subevent_code
* @param con_handle
* @param status
* @param title_len
* @param title
* @param artist_len
@ -1360,6 +1360,16 @@ typedef uint8_t sm_key_t[16];
* @param total_tracks
* @param song_length in ms
*/
#define AVRCP_SUBEVENT_NOW_PLAYING_INFO 0x03
/**
* @format 1H111
* @param subevent_code
* @param con_handle
* @param status
* @param repeat_mode
* @param shuffle_mode
*/
#define AVRCP_SUBEVENT_SHUFFLE_AND_REPEAT_MODE 0x04
#endif

View File

@ -4262,6 +4262,15 @@ static inline const uint8_t * avdtp_subevent_signaling_media_codec_other_configu
static inline hci_con_handle_t avrcp_subevent_connection_established_get_con_handle(const uint8_t * event){
return little_endian_read_16(event, 3);
}
/**
* @brief Get field status from event AVRCP_SUBEVENT_CONNECTION_ESTABLISHED
* @param event packet
* @return status
* @note: btstack_type 1
*/
static inline uint8_t avrcp_subevent_connection_established_get_status(const uint8_t * event){
return event[5];
}
/**
* @brief Get field local_cid from event AVRCP_SUBEVENT_CONNECTION_ESTABLISHED
* @param event packet
@ -4269,7 +4278,7 @@ static inline hci_con_handle_t avrcp_subevent_connection_established_get_con_han
* @note: btstack_type 2
*/
static inline uint16_t avrcp_subevent_connection_established_get_local_cid(const uint8_t * event){
return little_endian_read_16(event, 5);
return little_endian_read_16(event, 6);
}
/**
* @brief Get field bd_addr from event AVRCP_SUBEVENT_CONNECTION_ESTABLISHED
@ -4278,16 +4287,7 @@ static inline uint16_t avrcp_subevent_connection_established_get_local_cid(const
* @note: btstack_type B
*/
static inline void avrcp_subevent_connection_established_get_bd_addr(const uint8_t * event, bd_addr_t bd_addr){
reverse_bd_addr(&event[7], bd_addr);
}
/**
* @brief Get field status from event AVRCP_SUBEVENT_CONNECTION_ESTABLISHED
* @param event packet
* @return status
* @note: btstack_type 1
*/
static inline uint8_t avrcp_subevent_connection_established_get_status(const uint8_t * event){
return event[13];
reverse_bd_addr(&event[8], bd_addr);
}
/**
@ -4300,6 +4300,161 @@ static inline hci_con_handle_t avrcp_subevent_connection_closed_get_con_handle(c
return little_endian_read_16(event, 3);
}
/**
* @brief Get field con_handle from event AVRCP_SUBEVENT_NOW_PLAYING_INFO
* @param event packet
* @return con_handle
* @note: btstack_type H
*/
static inline hci_con_handle_t avrcp_subevent_now_playing_info_get_con_handle(const uint8_t * event){
return little_endian_read_16(event, 3);
}
/**
* @brief Get field status from event AVRCP_SUBEVENT_NOW_PLAYING_INFO
* @param event packet
* @return status
* @note: btstack_type 1
*/
static inline uint8_t avrcp_subevent_now_playing_info_get_status(const uint8_t * event){
return event[5];
}
/**
* @brief Get field title_len from event AVRCP_SUBEVENT_NOW_PLAYING_INFO
* @param event packet
* @return title_len
* @note: btstack_type J
*/
static inline int avrcp_subevent_now_playing_info_get_title_len(const uint8_t * event){
return event[6];
}
/**
* @brief Get field title from event AVRCP_SUBEVENT_NOW_PLAYING_INFO
* @param event packet
* @return title
* @note: btstack_type V
*/
static inline const uint8_t * avrcp_subevent_now_playing_info_get_title(const uint8_t * event){
return &event[7];
}
/**
* @brief Get field artist_len from event AVRCP_SUBEVENT_NOW_PLAYING_INFO
* @param event packet
* @return artist_len
* @note: btstack_type J
*/
static inline int avrcp_subevent_now_playing_info_get_artist_len(const uint8_t * event){
return event[6];
}
/**
* @brief Get field artist from event AVRCP_SUBEVENT_NOW_PLAYING_INFO
* @param event packet
* @return artist
* @note: btstack_type V
*/
static inline const uint8_t * avrcp_subevent_now_playing_info_get_artist(const uint8_t * event){
return &event[7];
}
/**
* @brief Get field album_len from event AVRCP_SUBEVENT_NOW_PLAYING_INFO
* @param event packet
* @return album_len
* @note: btstack_type J
*/
static inline int avrcp_subevent_now_playing_info_get_album_len(const uint8_t * event){
return event[6];
}
/**
* @brief Get field album from event AVRCP_SUBEVENT_NOW_PLAYING_INFO
* @param event packet
* @return album
* @note: btstack_type V
*/
static inline const uint8_t * avrcp_subevent_now_playing_info_get_album(const uint8_t * event){
return &event[7];
}
/**
* @brief Get field genre_len from event AVRCP_SUBEVENT_NOW_PLAYING_INFO
* @param event packet
* @return genre_len
* @note: btstack_type J
*/
static inline int avrcp_subevent_now_playing_info_get_genre_len(const uint8_t * event){
return event[6];
}
/**
* @brief Get field genre from event AVRCP_SUBEVENT_NOW_PLAYING_INFO
* @param event packet
* @return genre
* @note: btstack_type V
*/
static inline const uint8_t * avrcp_subevent_now_playing_info_get_genre(const uint8_t * event){
return &event[7];
}
/**
* @brief Get field track from event AVRCP_SUBEVENT_NOW_PLAYING_INFO
* @param event packet
* @return track
* @note: btstack_type 1
*/
static inline uint8_t avrcp_subevent_now_playing_info_get_track(const uint8_t * event){
return event[6];
}
/**
* @brief Get field total_tracks from event AVRCP_SUBEVENT_NOW_PLAYING_INFO
* @param event packet
* @return total_tracks
* @note: btstack_type 1
*/
static inline uint8_t avrcp_subevent_now_playing_info_get_total_tracks(const uint8_t * event){
return event[7];
}
/**
* @brief Get field song_length from event AVRCP_SUBEVENT_NOW_PLAYING_INFO
* @param event packet
* @return song_length
* @note: btstack_type 4
*/
static inline uint32_t avrcp_subevent_now_playing_info_get_song_length(const uint8_t * event){
return little_endian_read_32(event, 8);
}
/**
* @brief Get field con_handle from event AVRCP_SUBEVENT_SHUFFLE_AND_REPEAT_MODE
* @param event packet
* @return con_handle
* @note: btstack_type H
*/
static inline hci_con_handle_t avrcp_subevent_shuffle_and_repeat_mode_get_con_handle(const uint8_t * event){
return little_endian_read_16(event, 3);
}
/**
* @brief Get field status from event AVRCP_SUBEVENT_SHUFFLE_AND_REPEAT_MODE
* @param event packet
* @return status
* @note: btstack_type 1
*/
static inline uint8_t avrcp_subevent_shuffle_and_repeat_mode_get_status(const uint8_t * event){
return event[5];
}
/**
* @brief Get field repeat_mode from event AVRCP_SUBEVENT_SHUFFLE_AND_REPEAT_MODE
* @param event packet
* @return repeat_mode
* @note: btstack_type 1
*/
static inline uint8_t avrcp_subevent_shuffle_and_repeat_mode_get_repeat_mode(const uint8_t * event){
return event[6];
}
/**
* @brief Get field shuffle_mode from event AVRCP_SUBEVENT_SHUFFLE_AND_REPEAT_MODE
* @param event packet
* @return shuffle_mode
* @note: btstack_type 1
*/
static inline uint8_t avrcp_subevent_shuffle_and_repeat_mode_get_shuffle_mode(const uint8_t * event){
return event[7];
}
/* API_END */

View File

@ -1119,4 +1119,71 @@ void avrcp_set_absolute_volume(uint16_t con_handle, uint8_t volume){
avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid);
}
void avrcp_query_shuffle_and_repeat_modes(uint16_t con_handle){
avrcp_connection_t * connection = get_avrcp_connection_for_con_handle(con_handle);
if (!connection){
log_error("avrcp_get_capabilities: coud not find a connection.");
return;
}
if (connection->state != AVCTP_CONNECTION_OPENED) return;
connection->state = AVCTP_W2_SEND_COMMAND;
connection->transaction_label++;
connection->cmd_to_send = AVRCP_CMD_OPCODE_VENDOR_DEPENDENT;
connection->command_type = AVRCP_CTYPE_STATUS;
connection->subunit_type = AVRCP_SUBUNIT_TYPE_PANEL;
connection->subunit_id = 0;
big_endian_store_24(connection->cmd_operands, 0, BT_SIG_COMPANY_ID);
connection->cmd_operands[3] = AVRCP_PDU_ID_GetCurrentPlayerApplicationSettingValue; // PDU ID
connection->cmd_operands[4] = 0;
big_endian_store_16(connection->cmd_operands, 5, 5); // parameter length
connection->cmd_operands[7] = 4; // NumPlayerApplicationSettingAttributeID
// PlayerApplicationSettingAttributeID1 AVRCP Spec, Appendix F, 133
connection->cmd_operands[8] = 0x01; // equalizer (1-OFF, 2-ON)
connection->cmd_operands[9] = 0x02; // repeat (1-off, 2-single track, 3-all tracks, 4-group repeat)
connection->cmd_operands[10] = 0x03; // shuffle (1-off, 2-all tracks, 3-group shuffle)
connection->cmd_operands[11] = 0x04; // scan (1-off, 2-all tracks, 3-group scan)
connection->cmd_operands_lenght = 12;
avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid);
}
static void avrcp_set_current_player_application_setting_value(uint16_t con_handle, uint8_t attribute_id, uint8_t attribute_value){
avrcp_connection_t * connection = get_avrcp_connection_for_con_handle(con_handle);
if (!connection){
log_error("avrcp_get_capabilities: coud not find a connection.");
return;
}
if (connection->state != AVCTP_CONNECTION_OPENED) return;
connection->state = AVCTP_W2_SEND_COMMAND;
connection->transaction_label++;
connection->cmd_to_send = AVRCP_CMD_OPCODE_VENDOR_DEPENDENT;
connection->command_type = AVRCP_CTYPE_CONTROL;
connection->subunit_type = AVRCP_SUBUNIT_TYPE_PANEL;
connection->subunit_id = 0;
int pos = 0;
big_endian_store_24(connection->cmd_operands, pos, BT_SIG_COMPANY_ID);
pos += 3;
connection->cmd_operands[pos++] = AVRCP_PDU_ID_SetPlayerApplicationSettingValue; // PDU ID
connection->cmd_operands[pos++] = 0;
// Parameter Length
big_endian_store_16(connection->cmd_operands, pos, 3);
pos += 2;
connection->cmd_operands[pos++] = 2;
connection->cmd_operands_lenght = pos;
connection->cmd_operands[pos++] = attribute_id;
connection->cmd_operands[pos++] = attribute_value;
connection->cmd_operands_lenght = pos;
avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid);
}
void avrcp_set_shuffle_mode(uint16_t con_handle, avrcp_shuffle_mode_t mode){
if (mode < AVRCP_SHUFFLE_MODE_OFF || mode > AVRCP_SHUFFLE_MODE_GROUP) return;
avrcp_set_current_player_application_setting_value(con_handle, 0x03, mode);
}
void avrcp_set_repeat_mode(uint16_t con_handle, avrcp_repeat_mode_t mode){
if (mode < AVRCP_REPEAT_MODE_OFF || mode > AVRCP_REPEAT_MODE_GROUP) return;
avrcp_set_current_player_application_setting_value(con_handle, 0x02, mode);
}

View File

@ -70,6 +70,8 @@ typedef enum {
typedef enum {
AVRCP_PDU_ID_GET_CAPABILITIES = 0x10,
AVRCP_PDU_ID_GetCurrentPlayerApplicationSettingValue = 0x13,
AVRCP_PDU_ID_SetPlayerApplicationSettingValue = 0x14,
AVRCP_PDU_ID_GET_ELEMENT_ATTRIBUTES = 0x20,
AVRCP_PDU_ID_GET_PLAY_STATUS = 0x30,
AVRCP_PDU_ID_REGISTER_NOTIFICATION = 0x31,
@ -201,15 +203,28 @@ typedef struct {
uint16_t notifications_to_deregister;
} avrcp_connection_t;
typedef enum {
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;
} avrcp_play_status_t;
typedef enum {
AVRCP_SHUFFLE_MODE_OFF = 0x01,
AVRCP_SHUFFLE_MODE_ALL_TRACKS,
AVRCP_SHUFFLE_MODE_GROUP
} avrcp_shuffle_mode_t;
typedef enum {
AVRCP_REPEAT_MODE_OFF = 0x01,
AVRCP_REPEAT_MODE_SINGLE_TRACK,
AVRCP_REPEAT_MODE_ALL_TRACKS,
AVRCP_REPEAT_MODE_GROUP
} avrcp_repeat_mode_t;
/**
* @brief AVDTP Sink service record.
* @param service
@ -360,6 +375,24 @@ void avrcp_start_mute(uint16_t con_handle);
*/
void avrcp_start_skip(uint16_t con_handle);
/**
* @brief Query repeat and shuffle mode.
* @param con_handle
*/
void avrcp_query_shuffle_and_repeat_modes(uint16_t con_handle);
/**
* @brief Set shuffle mode.
* @param con_handle
*/
void avrcp_set_shuffle_mode(uint16_t con_handle, avrcp_shuffle_mode_t mode);
/**
* @brief Set repeat mode.
* @param con_handle
*/
void avrcp_set_repeat_mode(uint16_t con_handle, avrcp_repeat_mode_t mode);
/* API_END */
#if defined __cplusplus
}

View File

@ -142,6 +142,13 @@ static void show_usage(void){
printf("a - absolute volume of %d percent\n", 5000/127);
printf("m - mute\n");
printf("k - skip\n");
printf("q - query repeat and shuffle mode\n");
printf("x - set repeat mode: SINGLE_TRACK\n");
printf("X - disable repeat mode\n");
printf("y - set shuffle mode ALL_TRACKS\n");
printf("Y - disable shuffle mode\n");
printf("Ctrl-c - exit\n");
printf("---\n");
}
@ -218,7 +225,22 @@ static void stdin_process(btstack_data_source_t *ds, btstack_data_source_callbac
case 'k':
avrcp_start_skip(con_handle);
break;
case 'q':
avrcp_query_shuffle_and_repeat_modes(con_handle);
break;
case 'x':
avrcp_set_repeat_mode(con_handle, AVRCP_REPEAT_MODE_SINGLE_TRACK);
break;
case 'X':
avrcp_set_repeat_mode(con_handle, AVRCP_REPEAT_MODE_OFF);
break;
case 'y':
avrcp_set_shuffle_mode(con_handle, AVRCP_SHUFFLE_MODE_ALL_TRACKS);
break;
case 'Y':
avrcp_set_shuffle_mode(con_handle, AVRCP_SHUFFLE_MODE_OFF);
break;
default:
show_usage();
break;