avrcp: draft fragmentation implementation

This commit is contained in:
Milanka Ringwald 2017-10-12 17:14:28 +02:00
parent e9ff703eac
commit 1bf7a74f31
6 changed files with 720 additions and 313 deletions

View File

@ -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),

View File

@ -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

View File

@ -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

View File

@ -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 {

View File

@ -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){

View File

@ -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);