diff --git a/example/a2dp_sink_demo.c b/example/a2dp_sink_demo.c index 628c41f41..7db2f1ad4 100644 --- a/example/a2dp_sink_demo.c +++ b/example/a2dp_sink_demo.c @@ -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), diff --git a/src/btstack_defines.h b/src/btstack_defines.h index ab9c3126e..c051786f6 100644 --- a/src/btstack_defines.h +++ b/src/btstack_defines.h @@ -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 diff --git a/src/btstack_event.h b/src/btstack_event.h index 891ca6161..2a8975dc2 100644 --- a/src/btstack_event.h +++ b/src/btstack_event.h @@ -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 diff --git a/src/classic/avrcp.h b/src/classic/avrcp.h index 6d9d644ff..9549769be 100644 --- a/src/classic/avrcp.h +++ b/src/classic/avrcp.h @@ -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 { diff --git a/src/classic/avrcp_controller.c b/src/classic/avrcp_controller.c index a517d3bb9..2b6d88b04 100644 --- a/src/classic/avrcp_controller.c +++ b/src/classic/avrcp_controller.c @@ -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;istate = 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){ diff --git a/src/classic/avrcp_target.c b/src/classic/avrcp_target.c index f6b34ef6b..a225145e9 100644 --- a/src/classic/avrcp_target.c +++ b/src/classic/avrcp_target.c @@ -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);