avrcp: reorder fields in AVRCP_SUBEVENT_NOW_PLAYING_INFO, extend event generator to handle multiple variable length fields

This commit is contained in:
Matthias Ringwald 2017-02-28 16:26:16 +01:00 committed by Milanka Ringwald
parent f9a34d7a00
commit e222d6a026
5 changed files with 248 additions and 209 deletions

View File

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

View File

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

View File

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

View File

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

View File

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