From e222d6a026b82f927ae9acd2705056aee5e64922 Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Tue, 28 Feb 2017 16:26:16 +0100 Subject: [PATCH] avrcp: reorder fields in AVRCP_SUBEVENT_NOW_PLAYING_INFO, extend event generator to handle multiple variable length fields --- src/btstack_defines.h | 8 +- src/btstack_event.h | 310 ++++++++++++++++---------------- test/avrcp/avrcp.c | 67 ++++--- test/avrcp/avrcp_test.c | 46 ++--- tool/btstack_event_generator.py | 26 ++- 5 files changed, 248 insertions(+), 209 deletions(-) diff --git a/src/btstack_defines.h b/src/btstack_defines.h index b7ac37a42..fbc80b9cd 100644 --- a/src/btstack_defines.h +++ b/src/btstack_defines.h @@ -1344,10 +1344,13 @@ typedef uint8_t sm_key_t[16]; #define AVRCP_SUBEVENT_CONNECTION_CLOSED 0x02 /** - * @format 1H1JVJVJVJV114 + * @format 1H1114JVJVJVJV * @param subevent_code * @param con_handle * @param status + * @param track + * @param total_tracks + * @param song_length in ms * @param title_len * @param title * @param artist_len @@ -1356,9 +1359,6 @@ typedef uint8_t sm_key_t[16]; * @param album * @param genre_len * @param genre - * @param track - * @param total_tracks - * @param song_length in ms */ #define AVRCP_SUBEVENT_NOW_PLAYING_INFO 0x03 diff --git a/src/btstack_event.h b/src/btstack_event.h index 9fe3700ae..9f925bd6a 100644 --- a/src/btstack_event.h +++ b/src/btstack_event.h @@ -2886,161 +2886,6 @@ static inline const uint8_t * gap_event_advertising_report_get_data(const uint8_ return &event[12]; } -/** - * @brief Get field con_handle from event AVRCP_NOW_PLAYING_INFO_EVENT - * @param event packet - * @return con_handle - * @note: btstack_type H - */ -static inline hci_con_handle_t avrcp_now_playing_info_event_get_con_handle(const uint8_t * event){ - return little_endian_read_16(event, 3); -} -/** - * @brief Get field status from event AVRCP_NOW_PLAYING_INFO_EVENT - * @param event packet - * @return status - * @note: btstack_type 1 - */ -static inline uint8_t avrcp_now_playing_info_event_get_status(const uint8_t * event){ - return event[5]; -} -/** - * @brief Get field title_len from event AVRCP_NOW_PLAYING_INFO_EVENT - * @param event packet - * @return title_len - * @note: btstack_type J - */ -static inline int avrcp_now_playing_info_event_get_title_len(const uint8_t * event){ - return event[6]; -} -/** - * @brief Get field title from event AVRCP_NOW_PLAYING_INFO_EVENT - * @param event packet - * @return title - * @note: btstack_type V - */ -static inline const uint8_t * avrcp_now_playing_info_event_get_title(const uint8_t * event){ - return &event[7]; -} -/** - * @brief Get field artist_len from event AVRCP_NOW_PLAYING_INFO_EVENT - * @param event packet - * @return artist_len - * @note: btstack_type J - */ -static inline int avrcp_now_playing_info_event_get_artist_len(const uint8_t * event){ - return event[6]; -} -/** - * @brief Get field artist from event AVRCP_NOW_PLAYING_INFO_EVENT - * @param event packet - * @return artist - * @note: btstack_type V - */ -static inline const uint8_t * avrcp_now_playing_info_event_get_artist(const uint8_t * event){ - return &event[7]; -} -/** - * @brief Get field album_len from event AVRCP_NOW_PLAYING_INFO_EVENT - * @param event packet - * @return album_len - * @note: btstack_type J - */ -static inline int avrcp_now_playing_info_event_get_album_len(const uint8_t * event){ - return event[6]; -} -/** - * @brief Get field album from event AVRCP_NOW_PLAYING_INFO_EVENT - * @param event packet - * @return album - * @note: btstack_type V - */ -static inline const uint8_t * avrcp_now_playing_info_event_get_album(const uint8_t * event){ - return &event[7]; -} -/** - * @brief Get field genre_len from event AVRCP_NOW_PLAYING_INFO_EVENT - * @param event packet - * @return genre_len - * @note: btstack_type J - */ -static inline int avrcp_now_playing_info_event_get_genre_len(const uint8_t * event){ - return event[6]; -} -/** - * @brief Get field genre from event AVRCP_NOW_PLAYING_INFO_EVENT - * @param event packet - * @return genre - * @note: btstack_type V - */ -static inline const uint8_t * avrcp_now_playing_info_event_get_genre(const uint8_t * event){ - return &event[7]; -} -/** - * @brief Get field track from event AVRCP_NOW_PLAYING_INFO_EVENT - * @param event packet - * @return track - * @note: btstack_type 1 - */ -static inline uint8_t avrcp_now_playing_info_event_get_track(const uint8_t * event){ - return event[6]; -} -/** - * @brief Get field total_tracks from event AVRCP_NOW_PLAYING_INFO_EVENT - * @param event packet - * @return total_tracks - * @note: btstack_type 1 - */ -static inline uint8_t avrcp_now_playing_info_event_get_total_tracks(const uint8_t * event){ - return event[7]; -} -/** - * @brief Get field song_length from event AVRCP_NOW_PLAYING_INFO_EVENT - * @param event packet - * @return song_length - * @note: btstack_type 4 - */ -static inline uint32_t avrcp_now_playing_info_event_get_song_length(const uint8_t * event){ - return little_endian_read_32(event, 8); -} - -/** - * @brief Get field con_handle from event AVRCP_SHUFFLE_AND_REPEAT_MODE_EVENT - * @param event packet - * @return con_handle - * @note: btstack_type H - */ -static inline hci_con_handle_t avrcp_shuffle_and_repeat_mode_event_get_con_handle(const uint8_t * event){ - return little_endian_read_16(event, 3); -} -/** - * @brief Get field status from event AVRCP_SHUFFLE_AND_REPEAT_MODE_EVENT - * @param event packet - * @return status - * @note: btstack_type 1 - */ -static inline uint8_t avrcp_shuffle_and_repeat_mode_event_get_status(const uint8_t * event){ - return event[5]; -} -/** - * @brief Get field repeat_mode from event AVRCP_SHUFFLE_AND_REPEAT_MODE_EVENT - * @param event packet - * @return repeat_mode - * @note: btstack_type 1 - */ -static inline uint8_t avrcp_shuffle_and_repeat_mode_event_get_repeat_mode(const uint8_t * event){ - return event[6]; -} -/** - * @brief Get field shuffle_mode from event AVRCP_SHUFFLE_AND_REPEAT_MODE_EVENT - * @param event packet - * @return shuffle_mode - * @note: btstack_type 1 - */ -static inline uint8_t avrcp_shuffle_and_repeat_mode_event_get_shuffle_mode(const uint8_t * event){ - return event[7]; -} - /** * @brief Get field status from event HCI_SUBEVENT_LE_CONNECTION_COMPLETE * @param event packet @@ -4455,6 +4300,161 @@ static inline hci_con_handle_t avrcp_subevent_connection_closed_get_con_handle(c return little_endian_read_16(event, 3); } +/** + * @brief Get field con_handle from event AVRCP_SUBEVENT_NOW_PLAYING_INFO + * @param event packet + * @return con_handle + * @note: btstack_type H + */ +static inline hci_con_handle_t avrcp_subevent_now_playing_info_get_con_handle(const uint8_t * event){ + return little_endian_read_16(event, 3); +} +/** + * @brief Get field status from event AVRCP_SUBEVENT_NOW_PLAYING_INFO + * @param event packet + * @return status + * @note: btstack_type 1 + */ +static inline uint8_t avrcp_subevent_now_playing_info_get_status(const uint8_t * event){ + return event[5]; +} +/** + * @brief Get field 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 con_handle from event AVRCP_SUBEVENT_SHUFFLE_AND_REPEAT_MODE + * @param event packet + * @return con_handle + * @note: btstack_type H + */ +static inline hci_con_handle_t avrcp_subevent_shuffle_and_repeat_mode_get_con_handle(const uint8_t * event){ + return little_endian_read_16(event, 3); +} +/** + * @brief Get field status from event AVRCP_SUBEVENT_SHUFFLE_AND_REPEAT_MODE + * @param event packet + * @return status + * @note: btstack_type 1 + */ +static inline uint8_t avrcp_subevent_shuffle_and_repeat_mode_get_status(const uint8_t * event){ + return event[5]; +} +/** + * @brief Get field repeat_mode from event AVRCP_SUBEVENT_SHUFFLE_AND_REPEAT_MODE + * @param event packet + * @return repeat_mode + * @note: btstack_type 1 + */ +static inline uint8_t avrcp_subevent_shuffle_and_repeat_mode_get_repeat_mode(const uint8_t * event){ + return event[6]; +} +/** + * @brief Get field shuffle_mode from event AVRCP_SUBEVENT_SHUFFLE_AND_REPEAT_MODE + * @param event packet + * @return shuffle_mode + * @note: btstack_type 1 + */ +static inline uint8_t avrcp_subevent_shuffle_and_repeat_mode_get_shuffle_mode(const uint8_t * event){ + return event[7]; +} + /* API_END */ diff --git a/test/avrcp/avrcp.c b/test/avrcp/avrcp.c index 35679ee7b..ca6b22651 100644 --- a/test/avrcp/avrcp.c +++ b/test/avrcp/avrcp.c @@ -755,7 +755,7 @@ static void avrcp_handle_l2cap_data_packet_for_signaling_connection(avrcp_connec 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 total_event_payload_for_string_attributes = HCI_EVENT_PAYLOAD_SIZE-2; uint16_t max_string_attribute_value_len = 0; for (i = 0; i < num_attributes; i++){ avrcp_media_attribute_id_t attr_id = big_endian_read_32(packet, pos); @@ -782,56 +782,79 @@ static void avrcp_handle_l2cap_data_packet_for_signaling_connection(avrcp_connec case AVRCP_MEDIA_ATTR_ALBUM: case AVRCP_MEDIA_ATTR_GENRE: num_string_attributes++; - total_event_payload_for_string_attributes--; // for storing attr_len string_attributes_len += attr_value_length; if (max_string_attribute_value_len < attr_value_length){ max_string_attribute_value_len = attr_value_length; } break; - case AVRCP_MEDIA_ATTR_SONG_LENGTH: - total_event_payload_for_string_attributes -= 4; // for storing 4 byte - break; default: - total_event_payload_for_string_attributes--; // for storing 1 byte break; } } pos += attr_value_length; } - uint8_t event[HCI_EVENT_BUFFER_SIZE]; + // 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->con_handle); pos += 2; event[pos++] = ctype; - - for (i = 0; i < num_attributes; i++){ - if (!items[i].value) continue; - // printf(" found value len %d, max %d, %s\n", items[i].len, max_value_len, items[i].value); - avrcp_media_attribute_id_t attr_id = i + 1; - uint16_t value_len; + for (i = 0; i < sizeof(attribute_order); i++){ + avrcp_media_attribute_id_t attr_id = attribute_order[i]; + if (items[attr_id-1].value){ + printf("Adding attribute id %u, len %d, %s\n", attr_id, items[attr_id-1].len, items[attr_id-1].value); + } else{ + printf("Adding empty attribute id %u, len %d\n", attr_id, items[attr_id-1].len); + } + 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: - value_len = items[i].len <= max_value_len? items[i].len : max_value_len; + 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; - memcpy(event+pos, items[i].value, value_len); - event[pos+value_len] = 0; - 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: - little_endian_store_32(event, pos, btstack_atoi((char *)items[i].value)); + 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; - default: - event[pos++] = items[i].value[0]; // for storing 1 byte + 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; } } diff --git a/test/avrcp/avrcp_test.c b/test/avrcp/avrcp_test.c index 3e9e778c3..e93d25bbd 100644 --- a/test/avrcp/avrcp_test.c +++ b/test/avrcp/avrcp_test.c @@ -62,11 +62,12 @@ static btstack_packet_callback_registration_t hci_event_callback_registration; // mac 2013: static bd_addr_t device_addr = {0x84, 0x38, 0x35, 0x65, 0xd1, 0x15}; // phone: static bd_addr_t device_addr = {0xD8, 0xBB, 0x2C, 0xDF, 0xF1, 0x08}; -// iPhone SE -// static const char * device_addr_string = "BC:EC:5D:E6:15:03"; static bd_addr_t device_addr; -static const char * device_addr_string = "D8:BB:2C:DF:F1:08"; +// iPhone SE +static const char * device_addr_string = "BC:EC:5D:E6:15:03"; + +// static const char * device_addr_string = "D8:BB:2C:DF:F1:08"; static uint16_t con_handle = 0; static uint8_t sdp_avrcp_controller_service_buffer[150]; @@ -101,40 +102,31 @@ static void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packe break; case AVRCP_SUBEVENT_SHUFFLE_AND_REPEAT_MODE:{ - uint8_t shuffle_mode = avrcp_shuffle_and_repeat_mode_event_get_shuffle_mode(packet); - uint8_t repeat_mode = avrcp_shuffle_and_repeat_mode_event_get_repeat_mode(packet); + uint8_t shuffle_mode = avrcp_subevent_shuffle_and_repeat_mode_get_shuffle_mode(packet); + uint8_t repeat_mode = avrcp_subevent_shuffle_and_repeat_mode_get_repeat_mode(packet); printf("AVRCP_SUBEVENT_SHUFFLE_AND_REPEAT_MODE: repeat %d, shuffle %d\n", repeat_mode, shuffle_mode); break; } - case AVRCP_SUBEVENT_NOW_PLAYING_INFO:{ - uint8_t value[100]; + case AVRCP_SUBEVENT_NOW_PLAYING_INFO: printf("Now Playing Info: \n"); printf_hexdump(packet, size); - memset(value, 0 , sizeof(value)); - if (avrcp_now_playing_info_event_get_title_len(packet) > 0){ - memcpy(value, avrcp_now_playing_info_event_get_title(packet), avrcp_now_playing_info_event_get_title_len(packet)); - printf(" Title: %s\n", value); + if (avrcp_subevent_now_playing_info_get_title_len(packet) > 0){ + printf(" Title: %s\n", avrcp_subevent_now_playing_info_get_title(packet)); } - memset(value, 0 , sizeof(value)); - if (avrcp_now_playing_info_event_get_album_len(packet) > 0){ - memcpy(value, avrcp_now_playing_info_event_get_album(packet), avrcp_now_playing_info_event_get_album_len(packet)); - printf(" Album: %s\n", value); + if (avrcp_subevent_now_playing_info_get_album_len(packet) > 0){ + printf(" Album: %s\n", avrcp_subevent_now_playing_info_get_album(packet)); } - memset(value, 0 , sizeof(value)); - if (avrcp_now_playing_info_event_get_artist_len(packet) > 0){ - memcpy(value, avrcp_now_playing_info_event_get_artist(packet), avrcp_now_playing_info_event_get_artist_len(packet)); - printf(" Artist: %s\n", value); + if (avrcp_subevent_now_playing_info_get_artist_len(packet) > 0){ + printf(" Artist: %s\n", avrcp_subevent_now_playing_info_get_artist(packet)); } - memset(value, 0 , sizeof(value)); - if (avrcp_now_playing_info_event_get_genre_len(packet) > 0){ - memcpy(value, avrcp_now_playing_info_event_get_genre(packet), avrcp_now_playing_info_event_get_genre_len(packet)); - printf(" Genre: %s\n", value); + if (avrcp_subevent_now_playing_info_get_genre_len(packet) > 0){ + printf(" Genre: %s\n", avrcp_subevent_now_playing_info_get_genre(packet)); } - printf(" Track: %d\n", avrcp_now_playing_info_event_get_track(packet)); - printf(" Total nr. tracks: %d\n", avrcp_now_playing_info_event_get_total_tracks(packet)); - printf(" Song length: %d ms\n", avrcp_now_playing_info_event_get_song_length(packet)); + 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("AVRCP_SUBEVENT_PLAY_STATUS\n"); // break; diff --git a/tool/btstack_event_generator.py b/tool/btstack_event_generator.py index 9db60c631..ab4972ad6 100755 --- a/tool/btstack_event_generator.py +++ b/tool/btstack_event_generator.py @@ -246,7 +246,10 @@ def create_events(events): base_name = format_function_name(event_name) length_name = '' offset = 2 + offset_is_number = 1 + offset_unknown = 0 supported = all_fields_supported(format) + last_variable_length_field_pos = "" if is_le_event(event_group): fout.write("#ifdef ENABLE_BLE\n") if len(format) != len(args): @@ -258,12 +261,33 @@ def create_events(events): if field_name.lower() == 'subevent_code': offset += 1 continue + if offset_unknown: + print("Param after variable length field without preceding 'J' lenght field") + break field_type = f text = create_getter(base_name, field_name, field_type, offset, supported) fout.write(text) if field_type in 'RT': break - offset += size_for_type(field_type) + if field_type in 'J': + if offset_is_number: + last_variable_length_field_pos = '%u' % offset + else: + last_variable_length_field_pos = offset + if field_type in 'V': + if last_variable_length_field_pos >= 0: + if offset_is_number: + # convert to string + offset = '%u' % offset + offset_is_number = 0 + offset = offset + ' + event[%s]' % last_variable_length_field_pos + else: + offset_unknown = 1 + else: + if offset_is_number: + offset += size_for_type(field_type) + else: + offset = offset + ' + %u' % size_for_type(field_type) if is_le_event(event_group): fout.write("#endif\n") fout.write("\n")