mirror of
https://github.com/bluekitchen/btstack.git
synced 2025-03-29 04:20:20 +00:00
avrcp: draft fragmentation implementation
This commit is contained in:
parent
e9ff703eac
commit
1bf7a74f31
@ -166,6 +166,7 @@ static uint8_t sdp_avdtp_sink_service_buffer[150];
|
||||
static avdtp_media_codec_configuration_sbc_t sbc_configuration;
|
||||
static uint16_t a2dp_cid = 0;
|
||||
static uint8_t local_seid = 0;
|
||||
static uint8_t value[100];
|
||||
|
||||
typedef enum {
|
||||
AVDTP_APPLICATION_IDLE,
|
||||
@ -633,30 +634,59 @@ static void avrcp_controller_packet_handler(uint8_t packet_type, uint16_t channe
|
||||
printf("%s, %s\n", avrcp_shuffle2str(shuffle_mode), avrcp_repeat2str(repeat_mode));
|
||||
break;
|
||||
}
|
||||
case AVRCP_SUBEVENT_NOW_PLAYING_INFO:{
|
||||
uint8_t value[100];
|
||||
printf("now playing: \n");
|
||||
if (avrcp_subevent_now_playing_info_get_title_len(packet) > 0){
|
||||
memcpy(value, avrcp_subevent_now_playing_info_get_title(packet), avrcp_subevent_now_playing_info_get_title_len(packet));
|
||||
case AVRCP_SUBEVENT_NOW_PLAYING_TITLE_INFO:
|
||||
printf("AVRCP_SUBEVENT_NOW_PLAYING_TITLE_INFO len %d \n", avrcp_subevent_now_playing_title_info_get_value_len(packet));
|
||||
if (avrcp_subevent_now_playing_title_info_get_value_len(packet) > 0){
|
||||
memcpy(value, avrcp_subevent_now_playing_title_info_get_value(packet), avrcp_subevent_now_playing_title_info_get_value_len(packet));
|
||||
printf(" Title: %s\n", value);
|
||||
}
|
||||
if (avrcp_subevent_now_playing_info_get_album_len(packet) > 0){
|
||||
memcpy(value, avrcp_subevent_now_playing_info_get_album(packet), avrcp_subevent_now_playing_info_get_album_len(packet));
|
||||
printf(" Album: %s\n", value);
|
||||
}
|
||||
if (avrcp_subevent_now_playing_info_get_artist_len(packet) > 0){
|
||||
memcpy(value, avrcp_subevent_now_playing_info_get_artist(packet), avrcp_subevent_now_playing_info_get_artist_len(packet));
|
||||
printf(" Artist: %s\n", value);
|
||||
}
|
||||
if (avrcp_subevent_now_playing_info_get_genre_len(packet) > 0){
|
||||
memcpy(value, avrcp_subevent_now_playing_info_get_genre(packet), avrcp_subevent_now_playing_info_get_genre_len(packet));
|
||||
printf(" Genre: %s\n", value);
|
||||
}
|
||||
printf(" Track: %d\n", avrcp_subevent_now_playing_info_get_track(packet));
|
||||
printf(" Total nr. tracks: %d\n", avrcp_subevent_now_playing_info_get_total_tracks(packet));
|
||||
printf(" Song length: %d ms\n", avrcp_subevent_now_playing_info_get_song_length(packet));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case AVRCP_SUBEVENT_NOW_PLAYING_ARTIST_INFO:
|
||||
if (avrcp_subevent_now_playing_artist_info_get_value_len(packet) > 0){
|
||||
memcpy(value, avrcp_subevent_now_playing_artist_info_get_value(packet), avrcp_subevent_now_playing_artist_info_get_value_len(packet));
|
||||
printf(" Title: %s\n", value);
|
||||
}
|
||||
break;
|
||||
|
||||
case AVRCP_SUBEVENT_NOW_PLAYING_ALBUM_INFO:
|
||||
if (avrcp_subevent_now_playing_album_info_get_value_len(packet) > 0){
|
||||
memcpy(value, avrcp_subevent_now_playing_album_info_get_value(packet), avrcp_subevent_now_playing_album_info_get_value_len(packet));
|
||||
printf(" Title: %s\n", value);
|
||||
}
|
||||
break;
|
||||
|
||||
case AVRCP_SUBEVENT_NOW_PLAYING_GENRE_INFO:
|
||||
if (avrcp_subevent_now_playing_genre_info_get_value_len(packet) > 0){
|
||||
memcpy(value, avrcp_subevent_now_playing_genre_info_get_value(packet), avrcp_subevent_now_playing_genre_info_get_value_len(packet));
|
||||
printf(" Title: %s\n", value);
|
||||
}
|
||||
break;
|
||||
|
||||
// case AVRCP_SUBEVENT_NOW_PLAYING_INFO:{
|
||||
// uint8_t value[100];
|
||||
// printf("now playing: \n");
|
||||
// if (avrcp_subevent_now_playing_info_get_title_len(packet) > 0){
|
||||
// memcpy(value, avrcp_subevent_now_playing_info_get_title(packet), avrcp_subevent_now_playing_info_get_title_len(packet));
|
||||
// printf(" Title: %s\n", value);
|
||||
// }
|
||||
// if (avrcp_subevent_now_playing_info_get_album_len(packet) > 0){
|
||||
// memcpy(value, avrcp_subevent_now_playing_info_get_album(packet), avrcp_subevent_now_playing_info_get_album_len(packet));
|
||||
// printf(" Album: %s\n", value);
|
||||
// }
|
||||
// if (avrcp_subevent_now_playing_info_get_artist_len(packet) > 0){
|
||||
// memcpy(value, avrcp_subevent_now_playing_info_get_artist(packet), avrcp_subevent_now_playing_info_get_artist_len(packet));
|
||||
// printf(" Artist: %s\n", value);
|
||||
// }
|
||||
// if (avrcp_subevent_now_playing_info_get_genre_len(packet) > 0){
|
||||
// memcpy(value, avrcp_subevent_now_playing_info_get_genre(packet), avrcp_subevent_now_playing_info_get_genre_len(packet));
|
||||
// printf(" Genre: %s\n", value);
|
||||
// }
|
||||
// printf(" Track: %d\n", avrcp_subevent_now_playing_info_get_track(packet));
|
||||
// printf(" Total nr. tracks: %d\n", avrcp_subevent_now_playing_info_get_total_tracks(packet));
|
||||
// printf(" Song length: %d ms\n", avrcp_subevent_now_playing_info_get_song_length(packet));
|
||||
// break;
|
||||
// }
|
||||
case AVRCP_SUBEVENT_PLAY_STATUS:
|
||||
printf("song length: %d ms, song position: %d ms, play status: %s\n",
|
||||
avrcp_subevent_play_status_get_song_length(packet),
|
||||
|
@ -1571,25 +1571,6 @@ typedef uint8_t sm_key_t[16];
|
||||
*/
|
||||
#define AVRCP_SUBEVENT_CONNECTION_RELEASED 0x02
|
||||
|
||||
/**
|
||||
* @format 121114JVJVJVJV
|
||||
* @param subevent_code
|
||||
* @param avrcp_cid
|
||||
* @param command_type
|
||||
* @param track
|
||||
* @param total_tracks
|
||||
* @param song_length in ms
|
||||
* @param title_len
|
||||
* @param title
|
||||
* @param artist_len
|
||||
* @param artist
|
||||
* @param album_len
|
||||
* @param album
|
||||
* @param genre_len
|
||||
* @param genre
|
||||
*/
|
||||
#define AVRCP_SUBEVENT_NOW_PLAYING_INFO 0x03
|
||||
|
||||
/**
|
||||
* @format 12111
|
||||
* @param subevent_code
|
||||
@ -1598,7 +1579,7 @@ typedef uint8_t sm_key_t[16];
|
||||
* @param repeat_mode
|
||||
* @param shuffle_mode
|
||||
*/
|
||||
#define AVRCP_SUBEVENT_SHUFFLE_AND_REPEAT_MODE 0x04
|
||||
#define AVRCP_SUBEVENT_SHUFFLE_AND_REPEAT_MODE 0x03
|
||||
|
||||
/**
|
||||
* @format 121441
|
||||
@ -1609,7 +1590,7 @@ typedef uint8_t sm_key_t[16];
|
||||
* @param song_position
|
||||
* @param play_status
|
||||
*/
|
||||
#define AVRCP_SUBEVENT_PLAY_STATUS 0x05
|
||||
#define AVRCP_SUBEVENT_PLAY_STATUS 0x04
|
||||
|
||||
/**
|
||||
* @format 1211
|
||||
@ -1618,7 +1599,7 @@ typedef uint8_t sm_key_t[16];
|
||||
* @param command_type
|
||||
* @param play_status
|
||||
*/
|
||||
#define AVRCP_SUBEVENT_NOTIFICATION_PLAYBACK_STATUS_CHANGED 0x06
|
||||
#define AVRCP_SUBEVENT_NOTIFICATION_PLAYBACK_STATUS_CHANGED 0x05
|
||||
|
||||
/**
|
||||
* @format 121
|
||||
@ -1626,7 +1607,7 @@ typedef uint8_t sm_key_t[16];
|
||||
* @param avrcp_cid
|
||||
* @param command_type
|
||||
*/
|
||||
#define AVRCP_SUBEVENT_NOTIFICATION_TRACK_CHANGED 0x07
|
||||
#define AVRCP_SUBEVENT_NOTIFICATION_TRACK_CHANGED 0x06
|
||||
|
||||
/**
|
||||
* @format 121
|
||||
@ -1634,7 +1615,7 @@ typedef uint8_t sm_key_t[16];
|
||||
* @param avrcp_cid
|
||||
* @param command_type
|
||||
*/
|
||||
#define AVRCP_SUBEVENT_NOTIFICATION_NOW_PLAYING_CONTENT_CHANGED 0x08
|
||||
#define AVRCP_SUBEVENT_NOTIFICATION_NOW_PLAYING_CONTENT_CHANGED 0x07
|
||||
|
||||
/**
|
||||
* @format 121
|
||||
@ -1642,7 +1623,7 @@ typedef uint8_t sm_key_t[16];
|
||||
* @param avrcp_cid
|
||||
* @param command_type
|
||||
*/
|
||||
#define AVRCP_SUBEVENT_NOTIFICATION_AVAILABLE_PLAYERS_CHANGED 0x09
|
||||
#define AVRCP_SUBEVENT_NOTIFICATION_AVAILABLE_PLAYERS_CHANGED 0x08
|
||||
|
||||
/**
|
||||
* @format 1211
|
||||
@ -1651,7 +1632,7 @@ typedef uint8_t sm_key_t[16];
|
||||
* @param command_type
|
||||
* @param absolute_volume
|
||||
*/
|
||||
#define AVRCP_SUBEVENT_NOTIFICATION_VOLUME_CHANGED 0x0A
|
||||
#define AVRCP_SUBEVENT_NOTIFICATION_VOLUME_CHANGED 0x09
|
||||
|
||||
/**
|
||||
* @format 1211
|
||||
@ -1660,7 +1641,7 @@ typedef uint8_t sm_key_t[16];
|
||||
* @param command_type
|
||||
* @param absolute_volume
|
||||
*/
|
||||
#define AVRCP_SUBEVENT_SET_ABSOLUTE_VOLUME_RESPONSE 0x0B
|
||||
#define AVRCP_SUBEVENT_SET_ABSOLUTE_VOLUME_RESPONSE 0x0A
|
||||
|
||||
/**
|
||||
* @format 1211
|
||||
@ -1669,7 +1650,7 @@ typedef uint8_t sm_key_t[16];
|
||||
* @param command_type
|
||||
* @param notification_id
|
||||
*/
|
||||
#define AVRCP_SUBEVENT_ENABLE_NOTIFICATION_COMPLETE 0x0C
|
||||
#define AVRCP_SUBEVENT_ENABLE_NOTIFICATION_COMPLETE 0x0B
|
||||
|
||||
/**
|
||||
* @format 1211
|
||||
@ -1678,7 +1659,7 @@ typedef uint8_t sm_key_t[16];
|
||||
* @param command_type
|
||||
* @param operation_id
|
||||
*/
|
||||
#define AVRCP_SUBEVENT_OPERATION_START 0x0D
|
||||
#define AVRCP_SUBEVENT_OPERATION_START 0x0C
|
||||
|
||||
/**
|
||||
* @format 1211
|
||||
@ -1687,7 +1668,7 @@ typedef uint8_t sm_key_t[16];
|
||||
* @param command_type
|
||||
* @param operation_id
|
||||
*/
|
||||
#define AVRCP_SUBEVENT_OPERATION_COMPLETE 0x0E
|
||||
#define AVRCP_SUBEVENT_OPERATION_COMPLETE 0x0D
|
||||
|
||||
/**
|
||||
* @format 121
|
||||
@ -1695,28 +1676,28 @@ typedef uint8_t sm_key_t[16];
|
||||
* @param avrcp_cid
|
||||
* @param command_type
|
||||
*/
|
||||
#define AVRCP_SUBEVENT_PLAYER_APPLICATION_VALUE_RESPONSE 0x0F
|
||||
#define AVRCP_SUBEVENT_PLAYER_APPLICATION_VALUE_RESPONSE 0x0E
|
||||
|
||||
/**
|
||||
* @format 12
|
||||
* @param subevent_code
|
||||
* @param avrcp_cid
|
||||
*/
|
||||
#define AVRCP_SUBEVENT_COMPANY_IDS_QUERY 0x12
|
||||
#define AVRCP_SUBEVENT_COMPANY_IDS_QUERY 0x0F
|
||||
|
||||
/**
|
||||
* @format 12
|
||||
* @param subevent_code
|
||||
* @param avrcp_cid
|
||||
*/
|
||||
#define AVRCP_SUBEVENT_EVENT_IDS_QUERY 0x13
|
||||
#define AVRCP_SUBEVENT_EVENT_IDS_QUERY 0x10
|
||||
|
||||
/**
|
||||
* @format 12
|
||||
* @param subevent_code
|
||||
* @param avrcp_cid
|
||||
*/
|
||||
#define AVRCP_SUBEVENT_PLAY_STATUS_QUERY 0x14
|
||||
#define AVRCP_SUBEVENT_PLAY_STATUS_QUERY 0x11
|
||||
|
||||
/**
|
||||
* @format 12111
|
||||
@ -1726,7 +1707,84 @@ typedef uint8_t sm_key_t[16];
|
||||
* @param operands_length
|
||||
* @param operand
|
||||
*/
|
||||
#define AVRCP_SUBEVENT_OPERATION 0x15
|
||||
#define AVRCP_SUBEVENT_OPERATION 0x12
|
||||
|
||||
/**
|
||||
* @format 1211
|
||||
* @param subevent_code
|
||||
* @param avrcp_cid
|
||||
* @param command_type
|
||||
* @param track
|
||||
*/
|
||||
#define AVRCP_SUBEVENT_NOW_PLAYING_TRACK_INFO 0x13
|
||||
|
||||
/**
|
||||
* @format 1211
|
||||
* @param subevent_code
|
||||
* @param avrcp_cid
|
||||
* @param command_type
|
||||
* @param total_tracks
|
||||
*/
|
||||
#define AVRCP_SUBEVENT_NOW_PLAYING_TOTAL_TRACKS_INFO 0x14
|
||||
|
||||
/**
|
||||
* @format 1214
|
||||
* @param subevent_code
|
||||
* @param avrcp_cid
|
||||
* @param command_type
|
||||
* @param song_length in ms
|
||||
*/
|
||||
#define AVRCP_SUBEVENT_NOW_PLAYING_SONG_LENGTH_MS_INFO 0x15
|
||||
|
||||
/**
|
||||
* @format 121JV
|
||||
* @param subevent_code
|
||||
* @param avrcp_cid
|
||||
* @param command_type
|
||||
* @param value_len
|
||||
* @param value
|
||||
*/
|
||||
#define AVRCP_SUBEVENT_NOW_PLAYING_TITLE_INFO 0x16
|
||||
|
||||
/*
|
||||
* @format 121JV
|
||||
* @param subevent_code
|
||||
* @param avrcp_cid
|
||||
* @param command_type
|
||||
* @param value_len
|
||||
* @param value
|
||||
*/
|
||||
#define AVRCP_SUBEVENT_NOW_PLAYING_ARTIST_INFO 0x17
|
||||
|
||||
/*
|
||||
* @format 121JV
|
||||
* @param subevent_code
|
||||
* @param avrcp_cid
|
||||
* @param command_type
|
||||
* @param value_len
|
||||
* @param value
|
||||
*/
|
||||
#define AVRCP_SUBEVENT_NOW_PLAYING_ALBUM_INFO 0x18
|
||||
|
||||
/*
|
||||
* @format 121JV
|
||||
* @param subevent_code
|
||||
* @param avrcp_cid
|
||||
* @param command_type
|
||||
* @param value_len
|
||||
* @param value
|
||||
*/
|
||||
#define AVRCP_SUBEVENT_NOW_PLAYING_GENRE_INFO 0x19
|
||||
|
||||
/*
|
||||
* @format 1211
|
||||
* @param subevent_code
|
||||
* @param avrcp_cid
|
||||
* @param command_type
|
||||
* @param status
|
||||
*/
|
||||
#define AVRCP_SUBEVENT_NOW_PLAYING_INFO_DONE 0x1A
|
||||
|
||||
|
||||
/**
|
||||
* @format 121BH1
|
||||
|
@ -5049,124 +5049,6 @@ static inline uint16_t avrcp_subevent_connection_released_get_avrcp_cid(const ui
|
||||
return little_endian_read_16(event, 3);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get field avrcp_cid from event AVRCP_SUBEVENT_NOW_PLAYING_INFO
|
||||
* @param event packet
|
||||
* @return avrcp_cid
|
||||
* @note: btstack_type 2
|
||||
*/
|
||||
static inline uint16_t avrcp_subevent_now_playing_info_get_avrcp_cid(const uint8_t * event){
|
||||
return little_endian_read_16(event, 3);
|
||||
}
|
||||
/**
|
||||
* @brief Get field command_type from event AVRCP_SUBEVENT_NOW_PLAYING_INFO
|
||||
* @param event packet
|
||||
* @return command_type
|
||||
* @note: btstack_type 1
|
||||
*/
|
||||
static inline uint8_t avrcp_subevent_now_playing_info_get_command_type(const uint8_t * event){
|
||||
return event[5];
|
||||
}
|
||||
/**
|
||||
* @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 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[12];
|
||||
}
|
||||
/**
|
||||
* @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[13];
|
||||
}
|
||||
/**
|
||||
* @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[13 + event[12]];
|
||||
}
|
||||
/**
|
||||
* @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[13 + event[12] + 1];
|
||||
}
|
||||
/**
|
||||
* @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[13 + event[12] + 1 + event[13 + event[12]]];
|
||||
}
|
||||
/**
|
||||
* @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[13 + event[12] + 1 + event[13 + event[12]] + 1];
|
||||
}
|
||||
/**
|
||||
* @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[13 + event[12] + 1 + event[13 + event[12]] + 1 + event[13 + event[12] + 1 + event[13 + event[12]]]];
|
||||
}
|
||||
/**
|
||||
* @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[13 + event[12] + 1 + event[13 + event[12]] + 1 + event[13 + event[12] + 1 + event[13 + event[12]]] + 1];
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get field avrcp_cid from event AVRCP_SUBEVENT_SHUFFLE_AND_REPEAT_MODE
|
||||
* @param event packet
|
||||
@ -5561,6 +5443,266 @@ static inline uint8_t avrcp_subevent_operation_get_operand(const uint8_t * event
|
||||
return event[7];
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get field avrcp_cid from event AVRCP_SUBEVENT_NOW_PLAYING_TRACK_INFO
|
||||
* @param event packet
|
||||
* @return avrcp_cid
|
||||
* @note: btstack_type 2
|
||||
*/
|
||||
static inline uint16_t avrcp_subevent_now_playing_track_info_get_avrcp_cid(const uint8_t * event){
|
||||
return little_endian_read_16(event, 3);
|
||||
}
|
||||
/**
|
||||
* @brief Get field command_type from event AVRCP_SUBEVENT_NOW_PLAYING_TRACK_INFO
|
||||
* @param event packet
|
||||
* @return command_type
|
||||
* @note: btstack_type 1
|
||||
*/
|
||||
static inline uint8_t avrcp_subevent_now_playing_track_info_get_command_type(const uint8_t * event){
|
||||
return event[5];
|
||||
}
|
||||
/**
|
||||
* @brief Get field track from event AVRCP_SUBEVENT_NOW_PLAYING_TRACK_INFO
|
||||
* @param event packet
|
||||
* @return track
|
||||
* @note: btstack_type 1
|
||||
*/
|
||||
static inline uint8_t avrcp_subevent_now_playing_track_info_get_track(const uint8_t * event){
|
||||
return event[6];
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get field avrcp_cid from event AVRCP_SUBEVENT_NOW_PLAYING_TOTAL_TRACKS_INFO
|
||||
* @param event packet
|
||||
* @return avrcp_cid
|
||||
* @note: btstack_type 2
|
||||
*/
|
||||
static inline uint16_t avrcp_subevent_now_playing_total_tracks_info_get_avrcp_cid(const uint8_t * event){
|
||||
return little_endian_read_16(event, 3);
|
||||
}
|
||||
/**
|
||||
* @brief Get field command_type from event AVRCP_SUBEVENT_NOW_PLAYING_TOTAL_TRACKS_INFO
|
||||
* @param event packet
|
||||
* @return command_type
|
||||
* @note: btstack_type 1
|
||||
*/
|
||||
static inline uint8_t avrcp_subevent_now_playing_total_tracks_info_get_command_type(const uint8_t * event){
|
||||
return event[5];
|
||||
}
|
||||
/**
|
||||
* @brief Get field total_tracks from event AVRCP_SUBEVENT_NOW_PLAYING_TOTAL_TRACKS_INFO
|
||||
* @param event packet
|
||||
* @return total_tracks
|
||||
* @note: btstack_type 1
|
||||
*/
|
||||
static inline uint8_t avrcp_subevent_now_playing_total_tracks_info_get_total_tracks(const uint8_t * event){
|
||||
return event[6];
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get field avrcp_cid from event AVRCP_SUBEVENT_NOW_PLAYING_SONG_LENGTH_MS_INFO
|
||||
* @param event packet
|
||||
* @return avrcp_cid
|
||||
* @note: btstack_type 2
|
||||
*/
|
||||
static inline uint16_t avrcp_subevent_now_playing_song_length_ms_info_get_avrcp_cid(const uint8_t * event){
|
||||
return little_endian_read_16(event, 3);
|
||||
}
|
||||
/**
|
||||
* @brief Get field command_type from event AVRCP_SUBEVENT_NOW_PLAYING_SONG_LENGTH_MS_INFO
|
||||
* @param event packet
|
||||
* @return command_type
|
||||
* @note: btstack_type 1
|
||||
*/
|
||||
static inline uint8_t avrcp_subevent_now_playing_song_length_ms_info_get_command_type(const uint8_t * event){
|
||||
return event[5];
|
||||
}
|
||||
/**
|
||||
* @brief Get field song_length from event AVRCP_SUBEVENT_NOW_PLAYING_SONG_LENGTH_MS_INFO
|
||||
* @param event packet
|
||||
* @return song_length
|
||||
* @note: btstack_type 4
|
||||
*/
|
||||
static inline uint32_t avrcp_subevent_now_playing_song_length_ms_info_get_song_length(const uint8_t * event){
|
||||
return little_endian_read_32(event, 6);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get field avrcp_cid from event AVRCP_SUBEVENT_NOW_PLAYING_TITLE_INFO
|
||||
* @param event packet
|
||||
* @return avrcp_cid
|
||||
* @note: btstack_type 2
|
||||
*/
|
||||
static inline uint16_t avrcp_subevent_now_playing_title_info_get_avrcp_cid(const uint8_t * event){
|
||||
return little_endian_read_16(event, 3);
|
||||
}
|
||||
/**
|
||||
* @brief Get field command_type from event AVRCP_SUBEVENT_NOW_PLAYING_TITLE_INFO
|
||||
* @param event packet
|
||||
* @return command_type
|
||||
* @note: btstack_type 1
|
||||
*/
|
||||
static inline uint8_t avrcp_subevent_now_playing_title_info_get_command_type(const uint8_t * event){
|
||||
return event[5];
|
||||
}
|
||||
/**
|
||||
* @brief Get field value_len from event AVRCP_SUBEVENT_NOW_PLAYING_TITLE_INFO
|
||||
* @param event packet
|
||||
* @return value_len
|
||||
* @note: btstack_type J
|
||||
*/
|
||||
static inline int avrcp_subevent_now_playing_title_info_get_value_len(const uint8_t * event){
|
||||
return event[6];
|
||||
}
|
||||
/**
|
||||
* @brief Get field value from event AVRCP_SUBEVENT_NOW_PLAYING_TITLE_INFO
|
||||
* @param event packet
|
||||
* @return value
|
||||
* @note: btstack_type V
|
||||
*/
|
||||
static inline const uint8_t * avrcp_subevent_now_playing_title_info_get_value(const uint8_t * event){
|
||||
return &event[7];
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get field avrcp_cid from event AVRCP_SUBEVENT_NOW_PLAYING_ARTIST_INFO
|
||||
* @param event packet
|
||||
* @return avrcp_cid
|
||||
* @note: btstack_type 2
|
||||
*/
|
||||
static inline uint16_t avrcp_subevent_now_playing_artist_info_get_avrcp_cid(const uint8_t * event){
|
||||
return little_endian_read_16(event, 3);
|
||||
}
|
||||
/**
|
||||
* @brief Get field command_type from event AVRCP_SUBEVENT_NOW_PLAYING_ARTIST_INFO
|
||||
* @param event packet
|
||||
* @return command_type
|
||||
* @note: btstack_type 1
|
||||
*/
|
||||
static inline uint8_t avrcp_subevent_now_playing_artist_info_get_command_type(const uint8_t * event){
|
||||
return event[5];
|
||||
}
|
||||
/**
|
||||
* @brief Get field value_len from event AVRCP_SUBEVENT_NOW_PLAYING_ARTIST_INFO
|
||||
* @param event packet
|
||||
* @return value_len
|
||||
* @note: btstack_type J
|
||||
*/
|
||||
static inline int avrcp_subevent_now_playing_artist_info_get_value_len(const uint8_t * event){
|
||||
return event[6];
|
||||
}
|
||||
/**
|
||||
* @brief Get field value from event AVRCP_SUBEVENT_NOW_PLAYING_ARTIST_INFO
|
||||
* @param event packet
|
||||
* @return value
|
||||
* @note: btstack_type V
|
||||
*/
|
||||
static inline const uint8_t * avrcp_subevent_now_playing_artist_info_get_value(const uint8_t * event){
|
||||
return &event[7];
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get field avrcp_cid from event AVRCP_SUBEVENT_NOW_PLAYING_ALBUM_INFO
|
||||
* @param event packet
|
||||
* @return avrcp_cid
|
||||
* @note: btstack_type 2
|
||||
*/
|
||||
static inline uint16_t avrcp_subevent_now_playing_album_info_get_avrcp_cid(const uint8_t * event){
|
||||
return little_endian_read_16(event, 3);
|
||||
}
|
||||
/**
|
||||
* @brief Get field command_type from event AVRCP_SUBEVENT_NOW_PLAYING_ALBUM_INFO
|
||||
* @param event packet
|
||||
* @return command_type
|
||||
* @note: btstack_type 1
|
||||
*/
|
||||
static inline uint8_t avrcp_subevent_now_playing_album_info_get_command_type(const uint8_t * event){
|
||||
return event[5];
|
||||
}
|
||||
/**
|
||||
* @brief Get field value_len from event AVRCP_SUBEVENT_NOW_PLAYING_ALBUM_INFO
|
||||
* @param event packet
|
||||
* @return value_len
|
||||
* @note: btstack_type J
|
||||
*/
|
||||
static inline int avrcp_subevent_now_playing_album_info_get_value_len(const uint8_t * event){
|
||||
return event[6];
|
||||
}
|
||||
/**
|
||||
* @brief Get field value from event AVRCP_SUBEVENT_NOW_PLAYING_ALBUM_INFO
|
||||
* @param event packet
|
||||
* @return value
|
||||
* @note: btstack_type V
|
||||
*/
|
||||
static inline const uint8_t * avrcp_subevent_now_playing_album_info_get_value(const uint8_t * event){
|
||||
return &event[7];
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get field avrcp_cid from event AVRCP_SUBEVENT_NOW_PLAYING_GENRE_INFO
|
||||
* @param event packet
|
||||
* @return avrcp_cid
|
||||
* @note: btstack_type 2
|
||||
*/
|
||||
static inline uint16_t avrcp_subevent_now_playing_genre_info_get_avrcp_cid(const uint8_t * event){
|
||||
return little_endian_read_16(event, 3);
|
||||
}
|
||||
/**
|
||||
* @brief Get field command_type from event AVRCP_SUBEVENT_NOW_PLAYING_GENRE_INFO
|
||||
* @param event packet
|
||||
* @return command_type
|
||||
* @note: btstack_type 1
|
||||
*/
|
||||
static inline uint8_t avrcp_subevent_now_playing_genre_info_get_command_type(const uint8_t * event){
|
||||
return event[5];
|
||||
}
|
||||
/**
|
||||
* @brief Get field value_len from event AVRCP_SUBEVENT_NOW_PLAYING_GENRE_INFO
|
||||
* @param event packet
|
||||
* @return value_len
|
||||
* @note: btstack_type J
|
||||
*/
|
||||
static inline int avrcp_subevent_now_playing_genre_info_get_value_len(const uint8_t * event){
|
||||
return event[6];
|
||||
}
|
||||
/**
|
||||
* @brief Get field value from event AVRCP_SUBEVENT_NOW_PLAYING_GENRE_INFO
|
||||
* @param event packet
|
||||
* @return value
|
||||
* @note: btstack_type V
|
||||
*/
|
||||
static inline const uint8_t * avrcp_subevent_now_playing_genre_info_get_value(const uint8_t * event){
|
||||
return &event[7];
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get field avrcp_cid from event AVRCP_SUBEVENT_NOW_PLAYING_INFO_DONE
|
||||
* @param event packet
|
||||
* @return avrcp_cid
|
||||
* @note: btstack_type 2
|
||||
*/
|
||||
static inline uint16_t avrcp_subevent_now_playing_info_done_get_avrcp_cid(const uint8_t * event){
|
||||
return little_endian_read_16(event, 3);
|
||||
}
|
||||
/**
|
||||
* @brief Get field command_type from event AVRCP_SUBEVENT_NOW_PLAYING_INFO_DONE
|
||||
* @param event packet
|
||||
* @return command_type
|
||||
* @note: btstack_type 1
|
||||
*/
|
||||
static inline uint8_t avrcp_subevent_now_playing_info_done_get_command_type(const uint8_t * event){
|
||||
return event[5];
|
||||
}
|
||||
/**
|
||||
* @brief Get field status from event AVRCP_SUBEVENT_NOW_PLAYING_INFO_DONE
|
||||
* @param event packet
|
||||
* @return status
|
||||
* @note: btstack_type 1
|
||||
*/
|
||||
static inline uint8_t avrcp_subevent_now_playing_info_done_get_status(const uint8_t * event){
|
||||
return event[6];
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get field goep_cid from event GOEP_SUBEVENT_CONNECTION_OPENED
|
||||
* @param event packet
|
||||
|
@ -55,6 +55,8 @@ extern "C" {
|
||||
|
||||
#define BT_SIG_COMPANY_ID 0x001958
|
||||
#define AVRCP_MEDIA_ATTR_COUNT 7
|
||||
#define AVRCP_MAX_ATTRIBUTTE_SIZE 100
|
||||
#define AVRCP_ATTRIBUTE_HEADER_LEN 8
|
||||
|
||||
typedef enum {
|
||||
AVRCP_STATUS_INVALID_COMMAND = 0, // sent if TG received a PDU that it did not understand.
|
||||
@ -120,6 +122,7 @@ typedef enum {
|
||||
AVRCP_PDU_ID_GET_PLAY_STATUS = 0x30,
|
||||
AVRCP_PDU_ID_REGISTER_NOTIFICATION = 0x31,
|
||||
AVRCP_PDU_ID_REQUEST_CONTINUING_RESPONSE = 0x40,
|
||||
AVRCP_PDU_ID_REQUEST_ABORT_CONTINUING_RESPONSE = 0x41,
|
||||
AVRCP_PDU_ID_SET_ABSOLUTE_VOLUME = 0x50,
|
||||
AVRCP_PDU_ID_UNDEFINED = 0xFF
|
||||
} avrcp_pdu_id_t;
|
||||
@ -263,6 +266,13 @@ typedef struct {
|
||||
uint32_t song_position_ms;
|
||||
} avrcp_track_t;
|
||||
|
||||
typedef enum {
|
||||
AVRCP_PARSER_IDLE = 0,
|
||||
AVRCP_PARSER_GET_ATTRIBUTE_HEADER, // 8 bytes
|
||||
AVRCP_PARSER_GET_ATTRIBUTE_VALUE,
|
||||
AVRCP_PARSER_IGNORE_ATTRIBUTE_VALUE
|
||||
} avrcp_parser_state_t;
|
||||
|
||||
typedef struct {
|
||||
btstack_linked_item_t item;
|
||||
bd_addr_t remote_addr;
|
||||
@ -314,10 +324,25 @@ typedef struct {
|
||||
uint8_t volume_percentage_changed;
|
||||
uint8_t now_playing_info_response;
|
||||
uint8_t now_playing_info_attr_bitmap;
|
||||
|
||||
uint8_t abort_continue_response;
|
||||
|
||||
// used for fragmentation
|
||||
avrcp_media_attribute_id_t next_attr_id;
|
||||
int fragmented_value_offset;
|
||||
|
||||
avrcp_parser_state_t parser_state;
|
||||
uint8_t parser_attribute_header[AVRCP_ATTRIBUTE_HEADER_LEN];
|
||||
uint8_t parser_attribute_header_pos;
|
||||
|
||||
uint16_t list_size;
|
||||
uint16_t list_offset;
|
||||
uint8_t attribute_value[AVRCP_MAX_ATTRIBUTTE_SIZE];
|
||||
uint16_t attribute_value_len;
|
||||
uint16_t attribute_value_offset;
|
||||
|
||||
uint32_t attribute_id;
|
||||
|
||||
uint8_t num_attributes;
|
||||
uint8_t num_parsed_attributes;
|
||||
} avrcp_connection_t;
|
||||
|
||||
typedef enum {
|
||||
|
@ -208,6 +208,207 @@ static void avrcp_prepare_notification(avrcp_connection_t * connection, avrcp_no
|
||||
// answer page 61
|
||||
}
|
||||
|
||||
|
||||
static void avrcp_parser_reset(avrcp_connection_t * connection){
|
||||
connection->list_offset = 0;
|
||||
connection->num_attributes = 0;
|
||||
connection->num_parsed_attributes = 0;
|
||||
connection->parser_attribute_header_pos = 0;
|
||||
connection->parser_state = AVRCP_PARSER_IDLE;
|
||||
}
|
||||
|
||||
static void avrcp_controller_emit_now_playing_info_event_done(btstack_packet_handler_t callback, uint16_t avrcp_cid, uint8_t ctype, uint8_t status){
|
||||
uint8_t event[7];
|
||||
int pos = 0;
|
||||
event[pos++] = HCI_EVENT_AVRCP_META;
|
||||
event[pos++] = sizeof(event) - 2;
|
||||
event[pos++] = AVRCP_SUBEVENT_NOW_PLAYING_INFO_DONE;
|
||||
little_endian_store_16(event, pos, avrcp_cid);
|
||||
pos += 2;
|
||||
event[pos++] = ctype;
|
||||
event[pos++] = status;
|
||||
// printf_hexdump(event, pos);
|
||||
(*callback)(HCI_EVENT_PACKET, 0, event, pos);
|
||||
}
|
||||
|
||||
static void avrcp_controller_emit_now_playing_info_event(btstack_packet_handler_t callback, uint16_t avrcp_cid, uint8_t ctype, avrcp_media_attribute_id_t attr_id, uint8_t * value, uint16_t value_len){
|
||||
uint8_t event[HCI_EVENT_BUFFER_SIZE];
|
||||
int pos = 0;
|
||||
event[pos++] = HCI_EVENT_AVRCP_META;
|
||||
// reserve one byte for subevent type and data len
|
||||
int data_len_pos = pos;
|
||||
pos++;
|
||||
int subevent_type_pos = pos;
|
||||
pos++;
|
||||
little_endian_store_16(event, pos, avrcp_cid);
|
||||
pos += 2;
|
||||
event[pos++] = ctype;
|
||||
|
||||
switch (attr_id){
|
||||
case AVRCP_MEDIA_ATTR_TITLE:
|
||||
event[subevent_type_pos] = AVRCP_SUBEVENT_NOW_PLAYING_TITLE_INFO;
|
||||
event[pos++] = value_len;
|
||||
memcpy(event+pos, value, value_len);
|
||||
break;
|
||||
case AVRCP_MEDIA_ATTR_ARTIST:
|
||||
event[subevent_type_pos] = AVRCP_SUBEVENT_NOW_PLAYING_ARTIST_INFO;
|
||||
event[pos++] = value_len;
|
||||
memcpy(event+pos, value, value_len);
|
||||
break;
|
||||
case AVRCP_MEDIA_ATTR_ALBUM:
|
||||
event[subevent_type_pos] = AVRCP_SUBEVENT_NOW_PLAYING_ALBUM_INFO;
|
||||
event[pos++] = value_len;
|
||||
memcpy(event+pos, value, value_len);
|
||||
break;
|
||||
case AVRCP_MEDIA_ATTR_GENRE:
|
||||
event[subevent_type_pos] = AVRCP_SUBEVENT_NOW_PLAYING_GENRE_INFO;
|
||||
event[pos++] = value_len;
|
||||
memcpy(event+pos, value, value_len);
|
||||
break;
|
||||
case AVRCP_MEDIA_ATTR_SONG_LENGTH:
|
||||
event[subevent_type_pos] = AVRCP_SUBEVENT_NOW_PLAYING_SONG_LENGTH_MS_INFO;
|
||||
if (value){
|
||||
little_endian_store_32(event, pos, btstack_atoi((char *)value));
|
||||
} else {
|
||||
little_endian_store_32(event, pos, 0);
|
||||
}
|
||||
pos += 4;
|
||||
break;
|
||||
case AVRCP_MEDIA_ATTR_TRACK:
|
||||
event[subevent_type_pos] = AVRCP_SUBEVENT_NOW_PLAYING_TRACK_INFO;
|
||||
if (value){
|
||||
event[pos++] = btstack_atoi((char *)value);
|
||||
} else {
|
||||
event[pos++] = 0;
|
||||
}
|
||||
break;
|
||||
case AVRCP_MEDIA_ATTR_TOTAL_TRACKS:
|
||||
event[subevent_type_pos] = AVRCP_SUBEVENT_NOW_PLAYING_TOTAL_TRACKS_INFO;
|
||||
if (value){
|
||||
event[pos++] = btstack_atoi((char *)value);
|
||||
} else {
|
||||
event[pos++] = 0;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
event[data_len_pos] = pos - 2;
|
||||
// printf("send attr len %d, value %s\n", value_len, value);
|
||||
(*callback)(HCI_EVENT_PACKET, 0, event, pos);
|
||||
}
|
||||
|
||||
static void avrcp_parser_process_byte(uint8_t byte, avrcp_connection_t * connection, avrcp_command_type_t ctype){
|
||||
switch(connection->parser_state){
|
||||
case AVRCP_PARSER_GET_ATTRIBUTE_HEADER:
|
||||
if (connection->parser_attribute_header_pos < AVRCP_ATTRIBUTE_HEADER_LEN) {
|
||||
connection->parser_attribute_header[connection->parser_attribute_header_pos++] = byte;
|
||||
connection->list_offset++;
|
||||
break;
|
||||
}
|
||||
connection->attribute_value_len = btstack_min(big_endian_read_16(connection->parser_attribute_header, 6), AVRCP_MAX_ATTRIBUTTE_SIZE );
|
||||
// printf(" attr id %d, len to read %d, total len %d \n", big_endian_read_32(connection->parser_attribute_header, 0), connection->attribute_value_len, big_endian_read_16(connection->parser_attribute_header, 6));
|
||||
connection->parser_state = AVRCP_PARSER_GET_ATTRIBUTE_VALUE;
|
||||
break;
|
||||
case AVRCP_PARSER_GET_ATTRIBUTE_VALUE:{
|
||||
if (connection->attribute_value_offset < connection->attribute_value_len){
|
||||
connection->attribute_value[connection->attribute_value_offset++] = byte;
|
||||
connection->list_offset++;
|
||||
break;
|
||||
}
|
||||
// TODO emit event
|
||||
uint32_t attribute_id = big_endian_read_32(connection->parser_attribute_header, 0);
|
||||
if (attribute_id > AVRCP_MEDIA_ATTR_NONE && attribute_id <= AVRCP_MEDIA_ATTR_SONG_LENGTH){
|
||||
avrcp_controller_emit_now_playing_info_event(avrcp_controller_context.avrcp_callback, connection->avrcp_cid, ctype, attribute_id, connection->attribute_value, connection->attribute_value_len);
|
||||
}
|
||||
|
||||
if (connection->attribute_value_offset < big_endian_read_16(connection->parser_attribute_header, 6)){
|
||||
// printf("parse until end of valuE, and ignore it\n");
|
||||
connection->parser_state = AVRCP_PARSER_IGNORE_ATTRIBUTE_VALUE;
|
||||
break;
|
||||
}
|
||||
|
||||
if (connection->list_offset == connection->list_size){
|
||||
avrcp_parser_reset(connection);
|
||||
avrcp_controller_emit_now_playing_info_event_done(avrcp_controller_context.avrcp_callback, connection->avrcp_cid, ctype, 0);
|
||||
break;
|
||||
}
|
||||
|
||||
connection->parser_state = AVRCP_PARSER_GET_ATTRIBUTE_HEADER;
|
||||
connection->parser_attribute_header_pos = 0;
|
||||
break;
|
||||
}
|
||||
case AVRCP_PARSER_IGNORE_ATTRIBUTE_VALUE:
|
||||
if (connection->attribute_value_offset < big_endian_read_16(connection->parser_attribute_header, 6)){
|
||||
connection->list_offset++;
|
||||
connection->attribute_value_offset++;
|
||||
break;
|
||||
}
|
||||
// printf("read %d, total %d\n", connection->attribute_value_offset, big_endian_read_16(connection->parser_attribute_header, 6));
|
||||
|
||||
if (connection->list_offset == connection->list_size){
|
||||
avrcp_parser_reset(connection);
|
||||
avrcp_controller_emit_now_playing_info_event_done(avrcp_controller_context.avrcp_callback, connection->avrcp_cid, ctype, 0);
|
||||
break;
|
||||
}
|
||||
connection->parser_state = AVRCP_PARSER_GET_ATTRIBUTE_HEADER;
|
||||
connection->parser_attribute_header_pos = 0;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void avrcp_source_parse_and_emit_element_attrs(uint8_t * packet, uint16_t num_bytes_to_read, avrcp_connection_t * connection, avrcp_command_type_t ctype){
|
||||
int i;
|
||||
for (i=0;i<num_bytes_to_read;i++){
|
||||
avrcp_parser_process_byte(packet[i], connection, ctype);
|
||||
}
|
||||
}
|
||||
|
||||
static uint8_t avrcp_controller_request_abort_continuation(avrcp_connection_t * connection){
|
||||
connection->state = AVCTP_W2_SEND_COMMAND;
|
||||
connection->transaction_label++;
|
||||
connection->command_opcode = AVRCP_CMD_OPCODE_VENDOR_DEPENDENT;
|
||||
connection->command_type = AVRCP_CTYPE_CONTROL;
|
||||
connection->subunit_type = AVRCP_SUBUNIT_TYPE_PANEL;
|
||||
connection->subunit_id = AVRCP_SUBUNIT_ID;
|
||||
int pos = 0;
|
||||
big_endian_store_24(connection->cmd_operands, pos, BT_SIG_COMPANY_ID);
|
||||
pos += 3;
|
||||
connection->cmd_operands[pos++] = AVRCP_PDU_ID_REQUEST_ABORT_CONTINUING_RESPONSE; // PDU ID
|
||||
connection->cmd_operands[pos++] = 0;
|
||||
// Parameter Length
|
||||
connection->cmd_operands_length = 8;
|
||||
big_endian_store_16(connection->cmd_operands, pos, 1);
|
||||
pos += 2;
|
||||
connection->cmd_operands[pos++] = AVRCP_PDU_ID_GET_ELEMENT_ATTRIBUTES;
|
||||
avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid);
|
||||
return ERROR_CODE_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
static uint8_t avrcp_controller_request_continue_response(avrcp_connection_t * connection){
|
||||
connection->state = AVCTP_W2_SEND_COMMAND;
|
||||
connection->transaction_label++;
|
||||
connection->command_opcode = AVRCP_CMD_OPCODE_VENDOR_DEPENDENT;
|
||||
connection->command_type = AVRCP_CTYPE_CONTROL;
|
||||
connection->subunit_type = AVRCP_SUBUNIT_TYPE_PANEL;
|
||||
connection->subunit_id = AVRCP_SUBUNIT_ID;
|
||||
int pos = 0;
|
||||
big_endian_store_24(connection->cmd_operands, pos, BT_SIG_COMPANY_ID);
|
||||
pos += 3;
|
||||
connection->cmd_operands[pos++] = AVRCP_PDU_ID_REQUEST_CONTINUING_RESPONSE; // PDU ID
|
||||
connection->cmd_operands[pos++] = 0;
|
||||
// Parameter Length
|
||||
connection->cmd_operands_length = 8;
|
||||
big_endian_store_16(connection->cmd_operands, pos, 1);
|
||||
pos += 2;
|
||||
connection->cmd_operands[pos++] = AVRCP_PDU_ID_GET_ELEMENT_ATTRIBUTES;
|
||||
avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid);
|
||||
return ERROR_CODE_SUCCESS;
|
||||
}
|
||||
|
||||
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;
|
||||
@ -490,125 +691,33 @@ static void avrcp_handle_l2cap_data_packet_for_signaling_connection(avrcp_connec
|
||||
}
|
||||
|
||||
case AVRCP_PDU_ID_GET_ELEMENT_ATTRIBUTES:{
|
||||
uint8_t num_attributes = packet[pos++];
|
||||
unsigned int i;
|
||||
struct item {
|
||||
uint16_t len;
|
||||
uint8_t * value;
|
||||
} items[AVRCP_MEDIA_ATTR_COUNT];
|
||||
memset(items, 0, sizeof(items));
|
||||
|
||||
uint16_t string_attributes_len = 0;
|
||||
uint8_t num_string_attributes = 0;
|
||||
uint16_t total_event_payload_for_string_attributes = HCI_EVENT_PAYLOAD_SIZE-2;
|
||||
uint16_t max_string_attribute_value_len = 0;
|
||||
if (ctype == AVRCP_CTYPE_RESPONSE_IMPLEMENTED_STABLE || ctype == AVRCP_CTYPE_RESPONSE_CHANGED_STABLE){
|
||||
for (i = 0; i < num_attributes; i++){
|
||||
// use local variables to avoid compiler warning
|
||||
uint32_t avrcp_media_attribute_id = big_endian_read_32(packet, pos);
|
||||
avrcp_media_attribute_id_t attr_id = (avrcp_media_attribute_id_t) avrcp_media_attribute_id;
|
||||
pos += 4;
|
||||
// uint16_t character_set = big_endian_read_16(packet, pos);
|
||||
pos += 2;
|
||||
uint16_t attr_value_length = big_endian_read_16(packet, pos);
|
||||
pos += 2;
|
||||
|
||||
// debug - to remove later
|
||||
uint8_t value[100];
|
||||
uint16_t value_len = sizeof(value) <= attr_value_length? sizeof(value) - 1 : attr_value_length;
|
||||
memcpy(value, packet+pos, value_len);
|
||||
value[value_len] = 0;
|
||||
// printf("Now Playing Info %s: %s \n", attribute2str(attr_id), value);
|
||||
// end debug
|
||||
|
||||
if ((attr_id >= 1) || (attr_id <= AVRCP_MEDIA_ATTR_COUNT)) {
|
||||
items[attr_id-1].len = attr_value_length;
|
||||
items[attr_id-1].value = &packet[pos];
|
||||
switch (attr_id){
|
||||
case AVRCP_MEDIA_ATTR_TITLE:
|
||||
case AVRCP_MEDIA_ATTR_ARTIST:
|
||||
case AVRCP_MEDIA_ATTR_ALBUM:
|
||||
case AVRCP_MEDIA_ATTR_GENRE:
|
||||
num_string_attributes++;
|
||||
string_attributes_len += attr_value_length;
|
||||
if (max_string_attribute_value_len < attr_value_length){
|
||||
max_string_attribute_value_len = attr_value_length;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
avrcp_packet_type_t packet_type = operands[4] & 0x03;
|
||||
switch (packet_type){
|
||||
case AVRCP_START_PACKET:
|
||||
case AVRCP_SINGLE_PACKET:
|
||||
avrcp_parser_reset(connection);
|
||||
connection->list_size = param_length;
|
||||
connection->num_attributes = packet[pos++];
|
||||
// printf("AVRCP_PDU_ID_GET_ELEMENT_ATTRIBUTES num_attributes %d, total size %d, packet type 0x%02x \n", connection->num_attributes, connection->list_size, operands[4] & 0x03);
|
||||
connection->parser_state = AVRCP_PARSER_GET_ATTRIBUTE_HEADER;
|
||||
avrcp_source_parse_and_emit_element_attrs(packet+pos, size-pos, connection, ctype);
|
||||
|
||||
if (packet_type == AVRCP_START_PACKET){
|
||||
if (connection->num_attributes == 1 && connection->parser_state == AVRCP_PARSER_IGNORE_ATTRIBUTE_VALUE){
|
||||
avrcp_controller_request_abort_continuation(connection);
|
||||
} else {
|
||||
avrcp_controller_request_continue_response(connection);
|
||||
}
|
||||
}
|
||||
pos += attr_value_length;
|
||||
}
|
||||
break;
|
||||
case AVRCP_CONTINUE_PACKET:
|
||||
avrcp_source_parse_and_emit_element_attrs(packet+pos, size-pos, connection, ctype);
|
||||
avrcp_controller_request_continue_response(connection);
|
||||
break;
|
||||
case AVRCP_END_PACKET:
|
||||
avrcp_source_parse_and_emit_element_attrs(packet+pos, size-pos, connection, ctype);
|
||||
break;
|
||||
}
|
||||
|
||||
// subtract space for fixed fields
|
||||
total_event_payload_for_string_attributes -= 14 + 4; // 4 for '\0'
|
||||
|
||||
// @TODO optimize space by repeatedly decreasing max_string_attribute_value_len until it fits into buffer instead of crude divion
|
||||
uint16_t max_value_len = total_event_payload_for_string_attributes > string_attributes_len? max_string_attribute_value_len : total_event_payload_for_string_attributes/(string_attributes_len+1) - 1;
|
||||
// printf("num_string_attributes %d, string_attributes_len %d, total_event_payload_for_string_attributes %d, max_value_len %d \n", num_string_attributes, string_attributes_len, total_event_payload_for_string_attributes, max_value_len);
|
||||
|
||||
const uint8_t attribute_order[] = {
|
||||
AVRCP_MEDIA_ATTR_TRACK,
|
||||
AVRCP_MEDIA_ATTR_TOTAL_TRACKS,
|
||||
AVRCP_MEDIA_ATTR_SONG_LENGTH,
|
||||
AVRCP_MEDIA_ATTR_TITLE,
|
||||
AVRCP_MEDIA_ATTR_ARTIST,
|
||||
AVRCP_MEDIA_ATTR_ALBUM,
|
||||
AVRCP_MEDIA_ATTR_GENRE
|
||||
};
|
||||
|
||||
uint8_t event[HCI_EVENT_BUFFER_SIZE];
|
||||
event[0] = HCI_EVENT_AVRCP_META;
|
||||
pos = 2;
|
||||
event[pos++] = AVRCP_SUBEVENT_NOW_PLAYING_INFO;
|
||||
little_endian_store_16(event, pos, connection->avrcp_cid);
|
||||
pos += 2;
|
||||
event[pos++] = ctype;
|
||||
for (i = 0; i < sizeof(attribute_order); i++){
|
||||
avrcp_media_attribute_id_t attr_id = (avrcp_media_attribute_id_t) attribute_order[i];
|
||||
uint16_t value_len = 0;
|
||||
switch (attr_id){
|
||||
case AVRCP_MEDIA_ATTR_TITLE:
|
||||
case AVRCP_MEDIA_ATTR_ARTIST:
|
||||
case AVRCP_MEDIA_ATTR_ALBUM:
|
||||
case AVRCP_MEDIA_ATTR_GENRE:
|
||||
if (items[attr_id-1].value){
|
||||
value_len = items[attr_id-1].len <= max_value_len ? items[attr_id-1].len : max_value_len;
|
||||
}
|
||||
event[pos++] = value_len + 1;
|
||||
if (value_len){
|
||||
memcpy(event+pos, items[attr_id-1].value, value_len);
|
||||
pos += value_len;
|
||||
}
|
||||
event[pos++] = 0;
|
||||
break;
|
||||
case AVRCP_MEDIA_ATTR_SONG_LENGTH:
|
||||
if (items[attr_id-1].value){
|
||||
little_endian_store_32(event, pos, btstack_atoi((char *)items[attr_id-1].value));
|
||||
} else {
|
||||
little_endian_store_32(event, pos, 0);
|
||||
}
|
||||
pos += 4;
|
||||
break;
|
||||
case AVRCP_MEDIA_ATTR_TRACK:
|
||||
case AVRCP_MEDIA_ATTR_TOTAL_TRACKS:
|
||||
if (items[attr_id-1].value){
|
||||
event[pos++] = btstack_atoi((char *)items[attr_id-1].value);
|
||||
} else {
|
||||
event[pos++] = 0;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
event[1] = pos - 2;
|
||||
// printf_hexdump(event, pos);
|
||||
(*avrcp_controller_context.avrcp_callback)(HCI_EVENT_PACKET, 0, event, pos);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
@ -894,6 +1003,7 @@ uint8_t avrcp_controller_disable_notification(uint16_t avrcp_cid, avrcp_notifica
|
||||
return ERROR_CODE_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
uint8_t avrcp_controller_get_now_playing_info(uint16_t avrcp_cid){
|
||||
avrcp_connection_t * connection = get_avrcp_connection_for_avrcp_cid(avrcp_cid, &avrcp_controller_context);
|
||||
if (!connection){
|
||||
|
@ -111,6 +111,40 @@ static uint16_t avrcp_target_pack_single_element_attribute_string(uint8_t * pack
|
||||
return header * 8 + attr_value_size;
|
||||
}
|
||||
|
||||
static int avrcp_target_abort_continue_response(uint16_t cid, avrcp_connection_t * connection){
|
||||
uint16_t pos = 0;
|
||||
l2cap_reserve_packet_buffer();
|
||||
uint8_t * packet = l2cap_get_outgoing_buffer();
|
||||
|
||||
connection->command_opcode = AVRCP_CMD_OPCODE_VENDOR_DEPENDENT;
|
||||
connection->command_type = AVRCP_CTYPE_RESPONSE_ACCEPTED;
|
||||
connection->subunit_type = AVRCP_SUBUNIT_TYPE_PANEL;
|
||||
connection->subunit_id = AVRCP_SUBUNIT_ID;
|
||||
|
||||
packet[pos++] = (connection->transaction_label << 4) | (AVRCP_SINGLE_PACKET << 2) | (AVRCP_RESPONSE_FRAME << 1) | 0;
|
||||
// Profile IDentifier (PID)
|
||||
packet[pos++] = BLUETOOTH_SERVICE_CLASS_AV_REMOTE_CONTROL >> 8;
|
||||
packet[pos++] = BLUETOOTH_SERVICE_CLASS_AV_REMOTE_CONTROL & 0x00FF;
|
||||
|
||||
// command_type
|
||||
packet[pos++] = connection->command_type;
|
||||
// subunit_type | subunit ID
|
||||
packet[pos++] = (connection->subunit_type << 3) | connection->subunit_id;
|
||||
// opcode
|
||||
packet[pos++] = (uint8_t)connection->command_opcode;
|
||||
|
||||
// company id is 3 bytes long
|
||||
big_endian_store_24(packet, pos, BT_SIG_COMPANY_ID);
|
||||
pos += 3;
|
||||
|
||||
packet[pos++] = AVRCP_PDU_ID_REQUEST_ABORT_CONTINUING_RESPONSE;
|
||||
|
||||
// reserve byte for packet type
|
||||
packet[pos++] = AVRCP_SINGLE_PACKET;
|
||||
big_endian_store_16(packet, pos, 0);
|
||||
pos += 2;
|
||||
return l2cap_send_prepared(cid, pos);
|
||||
}
|
||||
|
||||
static int avrcp_target_send_now_playing_info(uint16_t cid, avrcp_connection_t * connection){
|
||||
uint16_t pos = 0;
|
||||
@ -142,14 +176,15 @@ static int avrcp_target_send_now_playing_info(uint16_t cid, avrcp_connection_t *
|
||||
|
||||
uint16_t playing_info_buffer_len_position = pos;
|
||||
pos += 2;
|
||||
|
||||
// printf("connection->next_attr_id %d \n", connection->next_attr_id);
|
||||
if (connection->next_attr_id == AVRCP_MEDIA_ATTR_NONE){
|
||||
packet[pos_packet_type] = AVRCP_SINGLE_PACKET;
|
||||
connection->packet_type = AVRCP_SINGLE_PACKET;
|
||||
packet[pos++] = count_set_bits_uint32(connection->now_playing_info_attr_bitmap);
|
||||
connection->next_attr_id++;
|
||||
}
|
||||
|
||||
// printf("updated connection->next_attr_id %d, connection->attribute_value_offset %d \n", connection->next_attr_id, connection->attribute_value_offset);
|
||||
|
||||
uint8_t fragmented = 0;
|
||||
int num_free_bytes = size - pos - 2;
|
||||
uint8_t MAX_NUMBER_ATTR_LEN = 10;
|
||||
@ -171,7 +206,7 @@ static int avrcp_target_send_now_playing_info(uint16_t cid, avrcp_connection_t *
|
||||
break;
|
||||
}
|
||||
fragmented = 1;
|
||||
connection->fragmented_value_offset = 0;
|
||||
connection->attribute_value_offset = 0;
|
||||
break;
|
||||
case AVRCP_MEDIA_ATTR_TOTAL_TRACKS:
|
||||
num_bytes_to_write = AVRCP_ATTR_HEADER_LEN + MAX_NUMBER_ATTR_LEN;
|
||||
@ -180,7 +215,7 @@ static int avrcp_target_send_now_playing_info(uint16_t cid, avrcp_connection_t *
|
||||
break;
|
||||
}
|
||||
fragmented = 1;
|
||||
connection->fragmented_value_offset = 0;
|
||||
connection->attribute_value_offset = 0;
|
||||
break;
|
||||
case AVRCP_MEDIA_ATTR_SONG_LENGTH:
|
||||
num_bytes_to_write = AVRCP_ATTR_HEADER_LEN + MAX_NUMBER_ATTR_LEN;
|
||||
@ -189,16 +224,16 @@ static int avrcp_target_send_now_playing_info(uint16_t cid, avrcp_connection_t *
|
||||
break;
|
||||
}
|
||||
fragmented = 1;
|
||||
connection->fragmented_value_offset = 0;
|
||||
connection->attribute_value_offset = 0;
|
||||
break;
|
||||
default:{
|
||||
uint8_t header = (connection->fragmented_value_offset == 0);
|
||||
uint8_t * attr_value = (uint8_t *) (connection->now_playing_info[attr_index].value + connection->fragmented_value_offset);
|
||||
uint16_t attr_value_len = connection->now_playing_info[attr_index].len - connection->fragmented_value_offset;
|
||||
uint8_t header = (connection->attribute_value_offset == 0);
|
||||
uint8_t * attr_value = (uint8_t *) (connection->now_playing_info[attr_index].value + connection->attribute_value_offset);
|
||||
uint16_t attr_value_len = connection->now_playing_info[attr_index].len - connection->attribute_value_offset;
|
||||
|
||||
num_bytes_to_write = attr_value_len + header * AVRCP_ATTR_HEADER_LEN;
|
||||
if (num_bytes_to_write <= num_free_bytes){
|
||||
connection->fragmented_value_offset = 0;
|
||||
connection->attribute_value_offset = 0;
|
||||
num_written_bytes = num_bytes_to_write;
|
||||
avrcp_target_pack_single_element_attribute_string(packet, pos, UTF8, attr_id, attr_value, attr_value_len, connection->now_playing_info[attr_index].len, header);
|
||||
break;
|
||||
@ -207,7 +242,7 @@ static int avrcp_target_send_now_playing_info(uint16_t cid, avrcp_connection_t *
|
||||
num_written_bytes = num_free_bytes;
|
||||
attr_value_len = num_free_bytes - header * AVRCP_ATTR_HEADER_LEN;
|
||||
avrcp_target_pack_single_element_attribute_string(packet, pos, UTF8, attr_id, attr_value, attr_value_len, connection->now_playing_info[attr_index].len, header);
|
||||
connection->fragmented_value_offset += attr_value_len;
|
||||
connection->attribute_value_offset += attr_value_len;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -367,7 +402,7 @@ static uint8_t avrcp_target_pass_through_response(uint16_t avrcp_cid, avrcp_comm
|
||||
return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
|
||||
}
|
||||
if (connection->state != AVCTP_CONNECTION_OPENED) return ERROR_CODE_COMMAND_DISALLOWED;
|
||||
printf("avrcp_target_pass_through_response: operation 0x%02x, operands length %d\n", opid, operands_length);
|
||||
// printf("avrcp_target_pass_through_response: operation 0x%02x, operands length %d\n", opid, operands_length);
|
||||
|
||||
connection->command_type = cmd_type;
|
||||
connection->subunit_type = AVRCP_SUBUNIT_TYPE_PANEL;
|
||||
@ -448,7 +483,7 @@ static uint8_t avrcp_target_subunit_info(avrcp_connection_t * connection, uint8_
|
||||
connection->command_type = AVRCP_CTYPE_RESPONSE_IMPLEMENTED_STABLE;
|
||||
connection->subunit_type = AVRCP_SUBUNIT_TYPE_UNIT; //vendor unique
|
||||
connection->subunit_id = AVRCP_SUBUNIT_ID_IGNORE;
|
||||
printf("avrcp_target_subunit_info subunit_type %d\n", connection->subunit_type);
|
||||
// printf("avrcp_target_subunit_info subunit_type %d\n", connection->subunit_type);
|
||||
|
||||
uint8_t page = offset / 4;
|
||||
uint8_t extension_code = 7;
|
||||
@ -541,7 +576,7 @@ static uint8_t avrcp_target_store_media_attr(avrcp_connection_t * connection, av
|
||||
if (!value) return AVRCP_STATUS_INVALID_PARAMETER;
|
||||
connection->now_playing_info[index].value = (uint8_t*)value;
|
||||
connection->now_playing_info[index].len = strlen(value);
|
||||
printf("store %lu bytes, %s\n", strlen(value), value);
|
||||
// printf("store %lu bytes, %s\n", strlen(value), value);
|
||||
return ERROR_CODE_SUCCESS;
|
||||
}
|
||||
|
||||
@ -561,20 +596,15 @@ uint8_t avrcp_target_set_playback_status(uint16_t avrcp_cid, avrcp_playback_stat
|
||||
|
||||
void avrcp_target_set_now_playing_info(uint16_t avrcp_cid, const avrcp_track_t * current_track, uint16_t total_tracks){
|
||||
avrcp_connection_t * connection = get_avrcp_connection_for_avrcp_cid(avrcp_cid, &avrcp_target_context);
|
||||
printf("avrcp_target_now_playing_info 0\n");
|
||||
if (!connection){
|
||||
printf("avrcp_unit_info: could not find a connection. cid 0x%02x\n", avrcp_cid);
|
||||
return;
|
||||
}
|
||||
printf("avrcp_target_set_now_playing_info 1\n");
|
||||
if (!current_track){
|
||||
connection->track_selected = 0;
|
||||
connection->playback_status = AVRCP_PLAYBACK_STATUS_ERROR;
|
||||
return;
|
||||
}
|
||||
printf("avrcp_target_set_now_playing_info 2\n");
|
||||
|
||||
printf("store track_id %s\n", current_track->track_id );
|
||||
memcpy(connection->track_id, current_track->track_id, 8);
|
||||
connection->song_length_ms = current_track->song_length_ms;
|
||||
connection->track_nr = current_track->track_nr;
|
||||
@ -746,6 +776,11 @@ static void avrcp_handle_l2cap_data_packet_for_signaling_connection(avrcp_connec
|
||||
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;
|
||||
case AVRCP_PDU_ID_REQUEST_ABORT_CONTINUING_RESPONSE:
|
||||
connection->cmd_operands[0] = pdu[4];
|
||||
connection->abort_continue_response = 1;
|
||||
avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid);
|
||||
break;
|
||||
case AVRCP_PDU_ID_REQUEST_CONTINUING_RESPONSE:
|
||||
if (pdu[4] != AVRCP_PDU_ID_GET_ELEMENT_ATTRIBUTES){
|
||||
avrcp_target_response_reject(connection, subunit_type, subunit_id, opcode, pdu_id, AVRCP_STATUS_INVALID_COMMAND);
|
||||
@ -763,6 +798,7 @@ static void avrcp_handle_l2cap_data_packet_for_signaling_connection(avrcp_connec
|
||||
}
|
||||
pos += 8;
|
||||
uint8_t attribute_count = pdu[pos++];
|
||||
// printf("AVRCP_PDU_ID_GET_ELEMENT_ATTRIBUTES attribute count %d\n", attribute_count);
|
||||
connection->next_attr_id = AVRCP_MEDIA_ATTR_NONE;
|
||||
if (!attribute_count){
|
||||
connection->now_playing_info_attr_bitmap = 0xFE;
|
||||
@ -824,13 +860,13 @@ static void avrcp_handle_l2cap_data_packet_for_signaling_connection(avrcp_connec
|
||||
break;
|
||||
}
|
||||
default:
|
||||
printf("AVRCP target: unhandled pdu id 0x%02x\n", pdu_id);
|
||||
log_info("AVRCP target: unhandled pdu id 0x%02x", pdu_id);
|
||||
avrcp_target_response_reject(connection, subunit_type, subunit_id, opcode, pdu_id, AVRCP_STATUS_INVALID_COMMAND);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
printf("AVRCP target: opcode 0x%02x not implemented\n", avrcp_cmd_opcode(packet,size));
|
||||
log_info("AVRCP target: opcode 0x%02x not implemented", avrcp_cmd_opcode(packet,size));
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -896,6 +932,12 @@ static void avrcp_controller_packet_handler(uint8_t packet_type, uint16_t channe
|
||||
connection = get_avrcp_connection_for_l2cap_signaling_cid(channel, &avrcp_target_context);
|
||||
if (!connection) break;
|
||||
|
||||
if (connection->abort_continue_response){
|
||||
connection->abort_continue_response = 0;
|
||||
connection->now_playing_info_response = 0;
|
||||
avrcp_target_abort_continue_response(connection->l2cap_signaling_cid, connection);
|
||||
}
|
||||
|
||||
if (connection->now_playing_info_response){
|
||||
connection->now_playing_info_response = 0;
|
||||
avrcp_target_send_now_playing_info(connection->l2cap_signaling_cid, connection);
|
||||
|
Loading…
x
Reference in New Issue
Block a user