/* packet-btavrcp.c * Routines for Bluetooth AVRCP dissection * * Copyright 2012, Michal Labedzki for Tieto Corporation * * Wireshark - Network traffic analyzer * By Gerald Combs * Copyright 1998 Gerald Combs * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "config.h" #include #include #include #include #include #include "packet-btl2cap.h" #include "packet-btsdp.h" #include "packet-btavctp.h" #include "packet-btavrcp.h" static int proto_btavrcp = -1; static int hf_btavrcp_rfa = -1; static int hf_btavrcp_ctype = -1; static int hf_btavrcp_subunit_type = -1; static int hf_btavrcp_subunit_id = -1; static int hf_btavrcp_opcode = -1; static int hf_btavrcp_company_id = -1; static int hf_btavrcp_length = -1; static int hf_btavrcp_pdu_id = -1; static int hf_btavrcp_bt_pdu_id = -1; static int hf_btavrcp_bt_continuing_pdu_id = -1; static int hf_btavrcp_browsing_pdu_id = -1; static int hf_btavrcp_reserved = -1; static int hf_btavrcp_packet_type = -1; static int hf_btavrcp_passthrough_state = -1; static int hf_btavrcp_passthrough_operation = -1; static int hf_btavrcp_passthrough_data_length = -1; static int hf_btavrcp_passthrough_company_id = -1; static int hf_btavrcp_passthrough_vendor_unique_id = -1; static int hf_btavrcp_unit_unknown = -1; static int hf_btavrcp_unit_type = -1; static int hf_btavrcp_unit_id = -1; static int hf_btavrcp_subunit_page = -1; static int hf_btavrcp_subunit_extension_code = -1; static int hf_btavrcp_item = -1; static int hf_btavrcp_folder = -1; static int hf_btavrcp_player_id = -1; static int hf_btavrcp_status = -1; static int hf_btavrcp_uid_counter = -1; static int hf_btavrcp_number_of_items = -1; static int hf_btavrcp_number_of_items16 = -1; static int hf_btavrcp_character_set = -1; static int hf_btavrcp_folder_depth = -1; static int hf_btavrcp_folder_name_length = -1; static int hf_btavrcp_folder_name = -1; static int hf_btavrcp_search = -1; static int hf_btavrcp_search_length = -1; static int hf_btavrcp_number_of_attributes = -1; static int hf_btavrcp_uid = -1; static int hf_btavrcp_scope = -1; static int hf_btavrcp_start_item = -1; static int hf_btavrcp_end_item = -1; static int hf_btavrcp_direction = -1; static int hf_btavrcp_identifier = -1; static int hf_btavrcp_song_length = -1; static int hf_btavrcp_song_position = -1; static int hf_btavrcp_play_status = -1; static int hf_btavrcp_notification_interval = -1; static int hf_btavrcp_event_id = -1; static int hf_btavrcp_battery_status = -1; static int hf_btavrcp_number_of_character_set = -1; static int hf_btavrcp_absolute_volume_rfa = -1; static int hf_btavrcp_absolute_volume = -1; static int hf_btavrcp_capability = -1; static int hf_btavrcp_capability_count = -1; static int hf_btavrcp_item_type = -1; static int hf_btavrcp_item_length = -1; static int hf_btavrcp_system_status = -1; static int hf_btavrcp_number_of_settings = -1; static int hf_btavrcp_settings_attribute = -1; static int hf_btavrcp_settings_value = -1; static int hf_btavrcp_displayable_name = -1; static int hf_btavrcp_displayable_name_length = -1; static int hf_btavrcp_media_type = -1; static int hf_btavrcp_folder_type = -1; static int hf_btavrcp_folder_playable = -1; static int hf_btavrcp_major_player_type = -1; static int hf_btavrcp_player_subtype = -1; static int hf_btavrcp_player_item = -1; static int hf_btavrcp_attribute = -1; static int hf_btavrcp_attribute_count = -1; static int hf_btavrcp_attribute_value = -1; static int hf_btavrcp_attribute_value_length = -1; static int hf_btavrcp_attribute_item = -1; static int hf_btavrcp_attribute_list = -1; static int hf_btavrcp_attribute_entries = -1; static int hf_btavrcp_attribute_name_length = -1; static int hf_btavrcp_attribute_name = -1; static int hf_btavrcp_setting_value_length = -1; static int hf_btavrcp_setting_value = -1; static int hf_btavrcp_features = -1; static int hf_btavrcp_not_used_features = -1; static int hf_btavrcp_feature_reserved_0 = -1; static int hf_btavrcp_feature_reserved_1 = -1; static int hf_btavrcp_feature_reserved_2 = -1; static int hf_btavrcp_feature_reserved_3 = -1; static int hf_btavrcp_feature_reserved_4 = -1; static int hf_btavrcp_feature_reserved_5 = -1; static int hf_btavrcp_feature_reserved_6 = -1; static int hf_btavrcp_feature_reserved_7 = -1; static int hf_btavrcp_feature_passthrough_select = -1; static int hf_btavrcp_feature_passthrough_up = -1; static int hf_btavrcp_feature_passthrough_down = -1; static int hf_btavrcp_feature_passthrough_left = -1; static int hf_btavrcp_feature_passthrough_right = -1; static int hf_btavrcp_feature_passthrough_right_up = -1; static int hf_btavrcp_feature_passthrough_right_down = -1; static int hf_btavrcp_feature_passthrough_left_up = -1; static int hf_btavrcp_feature_passthrough_left_down = -1; static int hf_btavrcp_feature_passthrough_root_menu = -1; static int hf_btavrcp_feature_passthrough_setup_menu = -1; static int hf_btavrcp_feature_passthrough_contents_menu = -1; static int hf_btavrcp_feature_passthrough_favorite_menu = -1; static int hf_btavrcp_feature_passthrough_exit = -1; static int hf_btavrcp_feature_passthrough_0 = -1; static int hf_btavrcp_feature_passthrough_1 = -1; static int hf_btavrcp_feature_passthrough_2 = -1; static int hf_btavrcp_feature_passthrough_3 = -1; static int hf_btavrcp_feature_passthrough_4 = -1; static int hf_btavrcp_feature_passthrough_5 = -1; static int hf_btavrcp_feature_passthrough_6 = -1; static int hf_btavrcp_feature_passthrough_7 = -1; static int hf_btavrcp_feature_passthrough_8 = -1; static int hf_btavrcp_feature_passthrough_9 = -1; static int hf_btavrcp_feature_passthrough_dot = -1; static int hf_btavrcp_feature_passthrough_enter = -1; static int hf_btavrcp_feature_passthrough_clear = -1; static int hf_btavrcp_feature_passthrough_channel_up = -1; static int hf_btavrcp_feature_passthrough_channel_down = -1; static int hf_btavrcp_feature_passthrough_previous_channel = -1; static int hf_btavrcp_feature_passthrough_sound_select = -1; static int hf_btavrcp_feature_passthrough_input_select = -1; static int hf_btavrcp_feature_passthrough_display_information = -1; static int hf_btavrcp_feature_passthrough_help = -1; static int hf_btavrcp_feature_passthrough_page_up = -1; static int hf_btavrcp_feature_passthrough_page_down = -1; static int hf_btavrcp_feature_passthrough_power = -1; static int hf_btavrcp_feature_passthrough_volume_up = -1; static int hf_btavrcp_feature_passthrough_volume_down = -1; static int hf_btavrcp_feature_passthrough_mute = -1; static int hf_btavrcp_feature_passthrough_play = -1; static int hf_btavrcp_feature_passthrough_stop = -1; static int hf_btavrcp_feature_passthrough_pause = -1; static int hf_btavrcp_feature_passthrough_record = -1; static int hf_btavrcp_feature_passthrough_rewind = -1; static int hf_btavrcp_feature_passthrough_fast_forward = -1; static int hf_btavrcp_feature_passthrough_eject = -1; static int hf_btavrcp_feature_passthrough_forward = -1; static int hf_btavrcp_feature_passthrough_backward = -1; static int hf_btavrcp_feature_passthrough_angle = -1; static int hf_btavrcp_feature_passthrough_subpicture = -1; static int hf_btavrcp_feature_passthrough_f1 = -1; static int hf_btavrcp_feature_passthrough_f2 = -1; static int hf_btavrcp_feature_passthrough_f3 = -1; static int hf_btavrcp_feature_passthrough_f4 = -1; static int hf_btavrcp_feature_passthrough_f5 = -1; static int hf_btavrcp_feature_vendor_unique = -1; static int hf_btavrcp_feature_basic_group_navigation = -1; static int hf_btavrcp_feature_advanced_control_player = -1; static int hf_btavrcp_feature_browsing = -1; static int hf_btavrcp_feature_searching = -1; static int hf_btavrcp_feature_addtonowplayer = -1; static int hf_btavrcp_feature_uid_unique = -1; static int hf_btavrcp_feature_only_browsable_when_addressed = -1; static int hf_btavrcp_feature_only_searchable_when_addressed = -1; static int hf_btavrcp_feature_nowplaying = -1; static int hf_btavrcp_feature_uid_persistency = -1; static int hf_btavrcp_reassembled = -1; static int hf_btavrcp_currect_path = -1; static int hf_btavrcp_response_time = -1; static int hf_btavrcp_command_in_frame = -1; static int hf_btavrcp_response_in_frame = -1; static int hf_btavrcp_data = -1; static gint ett_btavrcp = -1; static gint ett_btavrcp_attribute_list = -1; static gint ett_btavrcp_attribute_entry = -1; static gint ett_btavrcp_attribute_entries = -1; static gint ett_btavrcp_element = -1; static gint ett_btavrcp_folder = -1; static gint ett_btavrcp_player = -1; static gint ett_btavrcp_features = -1; static gint ett_btavrcp_features_not_used = -1; static gint ett_btavrcp_path = -1; static expert_field ei_btavrcp_no_response = EI_INIT; static expert_field ei_btavrcp_item_length_bad = EI_INIT; static expert_field ei_btavrcp_unexpected_data = EI_INIT; static dissector_handle_t btavrcp_handle; #define OPCODE_VENDOR_DEPENDENT 0x00 #define OPCODE_UNIT 0x30 #define OPCODE_SUBUNIT 0x31 #define OPCODE_PASSTHROUGH 0x7C #define ITEM_MEDIAPLAYER 0x01 #define ITEM_FOLDER 0x02 #define ITEM_MEDIA_ELEMENT 0x03 #define PACKET_TYPE_SINGLE 0x00 #define PACKET_TYPE_START 0x01 #define PACKET_TYPE_CONTINUE 0x02 #define PACKET_TYPE_END 0x03 #define EVENT_PLAYBACK_STATUS_CHANGED 0x01 #define EVENT_TRACK_CHANGED 0x02 #define EVENT_TRACK_REACHED_END 0x03 #define EVENT_TRACK_REACHED_START 0x04 #define EVENT_PLAYBACK_POSITION_CHANGED 0x05 #define EVENT_BATTERY_STATUS_CHANGED 0x06 #define EVENT_SYSTEM_STATUS_CHANGED 0x07 #define EVENT_PLAYER_APPLICATION_SETTING_CHANGED 0x08 #define EVENT_NOWPLAYING_CONTENT_CHANGED 0x09 #define EVENT_AVAILABLE_PLAYERS_CHANGED 0x0A #define EVENT_ADDRESSEDPLAYER_CHANGED 0x0B #define EVENT_UIDS_CHANGED 0x0C #define EVENT_VOLUME_CHANGED 0x0D #define PDU_GET_CAPABILITIES 0x10 #define PDU_LIST_PLAYER_APPLICATION_SETTING_ATTRIBUTES 0x11 #define PDU_LIST_PLAYER_APPLICATION_SETTING_VALUE 0x12 #define PDU_GET_CURRENT_PLAYER_APPLICATION_SETTING_VALUE 0x13 #define PDU_SET_PLAYER_APPLICATION_SETTING_VALUE 0x14 #define PDU_GET_PLAYER_APPLICATION_SETTING_ATTRIBUTE_TEXT 0x15 #define PDU_GET_PLAYER_APPLICATION_SETTING_VALUE_TEXT 0x16 #define PDU_INFORM_DISPLAYABLE_CHARACTER_SET 0x17 #define PDU_INFORM_BATTERY_STATUS_OF_CT 0x18 #define PDU_GET_ELEMENT_ATTRIBUTES 0x20 #define PDU_GET_PLAY_STATUS 0x30 #define PDU_REGISTER_NOTIFICATION 0x31 #define PDU_REQUEST_CONTINUING_RESPONSE 0x40 #define PDU_ABORT_CONTINUING_RESPONSE 0x41 #define PDU_SET_ABSOLUTE_VOLUME 0x50 #define PDU_SET_ADDRESSED_PLAYER 0x60 #define PDU_SET_BROWSED_PLAYER 0x70 #define PDU_GET_FOLDER_ITEMS 0x71 #define PDU_CHANGE_PATH 0x72 #define PDU_GET_ITEM_ATTRIBUTES 0x73 #define PDU_PLAY_ITEM 0x74 #define PDU_SEARCH 0x80 #define PDU_ADD_TO_NOW_PLAYING 0x90 #define PDU_GENERAL_REJECT 0xA0 #define STATUS_OK 0x04 static wmem_tree_t *reassembling = NULL; static wmem_tree_t *timing = NULL; wmem_tree_t *btavrcp_song_positions = NULL; typedef struct _avrcp_proto_data_t { guint32 interface_id; guint32 adapter_id; guint32 chandle; guint32 channel; } avrcp_proto_data_t; typedef struct _fragment { guint start_frame_number; guint end_frame_number; guint32 interface_id; guint32 adapter_id; guint32 chandle; guint32 psm; guint32 op; guint state; guint32 count; wmem_tree_t *fragments; } fragment_t; typedef struct _data_fragment_t { guint32 length; guint8 *data; } data_fragment_t; typedef struct _timing_info { guint command_frame_number; nstime_t command_timestamp; guint response_frame_number; nstime_t response_timestamp; guint max_response_time; guint used; guint32 interface_id; guint32 adapter_id; guint32 chandle; guint32 psm; guint32 opcode; guint32 op; guint32 op_arg; } timing_info_t; static const value_string packet_type_vals[] = { { PACKET_TYPE_SINGLE, "Single" }, { PACKET_TYPE_START, "Start" }, { PACKET_TYPE_CONTINUE, "Continue" }, { PACKET_TYPE_END, "End" }, { 0, NULL } }; static const value_string ctype_vals[] = { { 0x00, "Control" }, { 0x01, "Status" }, { 0x02, "Specific Inquiry" }, { 0x03, "Notify" }, { 0x04, "General Inquiry" }, { 0x05, "reserved" }, { 0x06, "reserved" }, { 0x07, "reserved" }, { 0x08, "Not Implemented" }, { 0x09, "Accepted" }, { 0x0A, "Rejected" }, { 0x0B, "In Transition" }, { 0x0C, "Stable" }, { 0x0D, "Changed" }, { 0x0E, "reserved" }, { 0x0F, "Interim" }, { 0, NULL } }; static const value_string opcode_vals[] = { { OPCODE_VENDOR_DEPENDENT, "Vendor dependent" }, { OPCODE_UNIT, "Unit Info" }, { OPCODE_SUBUNIT, "Subunit Info" }, { OPCODE_PASSTHROUGH, "Pass Through" }, { 0, NULL } }; static const value_string subunit_type_vals[] = { { 0x00, "Monitor" }, { 0x01, "Audio" }, { 0x02, "Printer" }, { 0x03, "Disc" }, { 0x04, "Tape Recorder/Player" }, { 0x05, "Tuner" }, { 0x06, "CA" }, { 0x07, "Camera" }, { 0x08, "reserved" }, { 0x09, "Panel" }, { 0x0A, "Bulletin Board" }, { 0x0B, "Camera Storage" }, { 0x1C, "Vendor Unique" }, { 0x1D, "All subunit types" }, { 0x1E, "Subunit_type extended to next byte" }, { 0x1F, "Unit" }, { 0, NULL } }; static const value_string passthrough_state_vals[] = { { 0x00, "Pushed" }, { 0x01, "Released" }, { 0, NULL } }; static const value_string passthrough_operation_vals[] = { { 0x41, "VOLUME UP" }, { 0x42, "VOLUME DOWN" }, { 0x43, "MUTE" }, { 0x44, "PLAY" }, { 0x45, "STOP" }, { 0x46, "PAUSE" }, { 0x47, "RECORD" }, { 0x48, "REWIND" }, { 0x49, "FAST FORWARD" }, { 0x4A, "EJECT" }, { 0x4B, "FORWARD" }, { 0x4C, "BACKWARD" }, { 0, NULL } }; static const value_string pdu_id_vals[] = { { PDU_GET_CAPABILITIES, "GetCapabilities" }, { PDU_LIST_PLAYER_APPLICATION_SETTING_ATTRIBUTES, "ListPlayerApplicationSettingAttributes" }, { PDU_LIST_PLAYER_APPLICATION_SETTING_VALUE, "ListPlayerApplicationSettingValue" }, { PDU_GET_CURRENT_PLAYER_APPLICATION_SETTING_VALUE, "GetCurrentPlayerApplicationSettingValue" }, { PDU_SET_PLAYER_APPLICATION_SETTING_VALUE, "SetPlayerApplicationSettingValue" }, { PDU_GET_PLAYER_APPLICATION_SETTING_ATTRIBUTE_TEXT, "GetPlayerApplicationSettingAttributeText" }, { PDU_GET_PLAYER_APPLICATION_SETTING_VALUE_TEXT, "GetPlayerApplicationSettingValueText" }, { PDU_INFORM_DISPLAYABLE_CHARACTER_SET, "InformDisplayableCharacterSet" }, { PDU_INFORM_BATTERY_STATUS_OF_CT, "InformBatteryStatusOfCT" }, { PDU_GET_ELEMENT_ATTRIBUTES, "GetElementAttributes" }, { PDU_GET_PLAY_STATUS, "GetPlayStatus" }, { PDU_REGISTER_NOTIFICATION, "RegisterNotification" }, { PDU_REQUEST_CONTINUING_RESPONSE, "RequestContinuingResponse" }, { PDU_ABORT_CONTINUING_RESPONSE, "AbortContinuingResponse" }, { PDU_SET_ABSOLUTE_VOLUME, "SetAbsoluteVolume" }, { PDU_SET_ADDRESSED_PLAYER, "SetAddressedPlayer" }, { PDU_PLAY_ITEM, "PlayItem" }, { PDU_ADD_TO_NOW_PLAYING, "AddToNowPlaying" }, { 0, NULL } }; static const value_string browsing_pdu_id_vals[] = { { PDU_SET_BROWSED_PLAYER, "SetBrowsedPlayer" }, { PDU_GET_FOLDER_ITEMS, "GetFolderItems" }, { PDU_CHANGE_PATH, "ChangePath" }, { PDU_GET_ITEM_ATTRIBUTES, "GetItemAttributes" }, { PDU_SEARCH, "Search" }, { PDU_GENERAL_REJECT, "GeneralReject" }, { 0, NULL } }; static const value_string status_vals[] = { { 0x00, "Invalid Command" }, { 0x01, "Invalid Parameter" }, { 0x02, "Parameter Not Found" }, { 0x03, "Internal Error" }, { 0x04, "OK" }, { 0x05, "UID Changed" }, { 0x06, "Reserved" }, { 0x07, "Invalid Direction" }, { 0x08, "Not a Directory" }, { 0x09, "Does Not Exist" }, { 0x0A, "Invalid Scope" }, { 0x0B, "Range Out of Bounds" }, { 0x0C, "UID is a Directory" }, { 0x0D, "Media In Use" }, { 0x0E, "Now Player List Full" }, { 0x0F, "Search Not Supported" }, { 0x10, "Search in Progress" }, { 0x11, "Invalid Player Id" }, { 0x12, "Player Not Browsable" }, { 0x13, "Player Not Addressed" }, { 0x14, "No Valid Search Results" }, { 0x15, "No Available Players" }, { 0x16, "Addressed Player Changed" }, { 0, NULL } }; static const value_string vendor_unique_id_vals[] = { { 0x0000, "Next Group" }, { 0x0001, "Previous Group" }, { 0, NULL } }; static const value_string direction_vals[] = { { 0x00, "Folder Up" }, { 0x01, "Folder Down" }, { 0, NULL } }; static const value_string attribute_count_vals[] = { { 0x00, "All attributes are requested" }, { 0xFF, "No attributes are requested" }, { 0, NULL } }; static const value_string scope_vals[] = { { 0x00, "MediaPlayerList" }, { 0x01, "VFS" }, { 0x02, "Search" }, { 0x03, "NowPlaying" }, { 0, NULL } }; static const value_string attribute_id_vals[] = { { 0x00, "Invalid" }, { 0x01, "Title" }, { 0x02, "Artist" }, { 0x03, "Album" }, { 0x04, "Media Number" }, { 0x05, "Total Number of Media" }, { 0x06, "Genre" }, { 0x07, "Playing Time" }, { 0, NULL } }; static const value_string settings_vals[] = { { 0x00, "Illegal" }, { 0x01, "Equalizer" }, { 0x02, "Repeat mode" }, { 0x03, "Shuffle" }, { 0x04, "Scan" }, { 0, NULL } }; static const value_string notification_vals[] = { { EVENT_PLAYBACK_STATUS_CHANGED, "PlaybackStatusChanged" }, { EVENT_TRACK_CHANGED, "TrackChanged" }, { EVENT_TRACK_REACHED_END, "TrackReachedEnd" }, { EVENT_TRACK_REACHED_START, "TrackReachedStart" }, { EVENT_PLAYBACK_POSITION_CHANGED, "PlaybackPositionChanged" }, { EVENT_BATTERY_STATUS_CHANGED, "BatteryStatusChanged" }, { EVENT_SYSTEM_STATUS_CHANGED, "SystemStatusChanged" }, { EVENT_PLAYER_APPLICATION_SETTING_CHANGED, "PlayerApplicationSettingChanged" }, { EVENT_NOWPLAYING_CONTENT_CHANGED, "NowPlayingContentChanged" }, { EVENT_AVAILABLE_PLAYERS_CHANGED, "AvailablePlayersChanged" }, { EVENT_ADDRESSEDPLAYER_CHANGED, "AddressedPlayerChanged" }, { EVENT_UIDS_CHANGED, "UIDsChanged" }, { EVENT_VOLUME_CHANGED, "VolumeChanged" }, { 0, NULL } }; static const value_string play_status_vals[] = { { 0x00, "Stopped" }, { 0x01, "Playing" }, { 0x02, "Paused" }, { 0x03, "Forward Seek" }, { 0x04, "Revrse Seek" }, { 0xFF, "Error" }, { 0, NULL } }; static const value_string battery_status_vals[] = { { 0x00, "Normal" }, { 0x01, "Warning" }, { 0x02, "Critical" }, { 0x03, "External" }, { 0x04, "Full Charge" }, { 0, NULL } }; static const value_string capability_vals[] = { { 0x02, "Company ID" }, { 0x03, "Events Supported" }, { 0, NULL } }; static const value_string item_type_vals[] = { { 0x01, "Media Player Item" }, { 0x02, "Folder Item" }, { 0x03, "Media Element Item" }, { 0, NULL } }; static const value_string system_status_vals[] = { { 0x00, "Power On" }, { 0x01, "Power Off" }, { 0x02, "Unplugged" }, { 0, NULL } }; static const value_string media_type_vals[] = { { 0x00, "Audio" }, { 0x01, "Video" }, { 0, NULL } }; static const value_string folder_type_vals[] = { { 0x00, "Mixed" }, { 0x01, "Titles" }, { 0x02, "Albums" }, { 0x03, "Artists" }, { 0x04, "Genres" }, { 0x05, "Playlists" }, { 0x06, "Years" }, { 0, NULL } }; static const value_string folder_playable_vals[] = { { 0x00, "Not Playable" }, { 0x01, "Playable" }, { 0, NULL } }; static const value_string major_player_type_vals[] = { { 0x00, "Audio" }, { 0x02, "Video" }, { 0x04, "Broadcasting Audio" }, { 0x08, "Broadcasting Video" }, { 0, NULL } }; static const value_string player_subtype_vals[] = { { 0x00, "Audio Book" }, { 0x02, "Podcast" }, { 0, NULL } }; static const value_string unique_all_supported_attributes[] = { { 0x00, "All Supported Attributes" }, { 0, NULL } }; void proto_register_btavrcp(void); void proto_reg_handoff_btavrcp(void); static gint dissect_attribute_id_list(tvbuff_t *tvb, proto_tree *tree, gint offset, guint count) { guint i_attribute; proto_item *pitem; proto_tree *ptree; pitem = proto_tree_add_item(tree, hf_btavrcp_attribute_list, tvb, offset, count * 4, ENC_NA); ptree = proto_item_add_subtree(pitem, ett_btavrcp_attribute_list); for (i_attribute = 0; i_attribute < count; ++i_attribute) { proto_tree_add_item(ptree, hf_btavrcp_attribute, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4; } return offset; } static gint dissect_attribute_entries(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, gint offset, guint count) { guint i_entry; guint attribute_id; guint value_length; guint length; guint8 *value; proto_item *pitem = NULL; proto_tree *ptree = NULL; proto_item *entry_item = NULL; proto_tree *entry_tree = NULL; length = 0; for (i_entry = 0; i_entry < count; ++i_entry) { length += 4 + 2 + 2 + tvb_get_ntohs(tvb, offset + length + 4 + 2); } pitem = proto_tree_add_item(tree, hf_btavrcp_attribute_entries, tvb, offset, length, ENC_NA); ptree = proto_item_add_subtree(pitem, ett_btavrcp_attribute_entries); for (i_entry = 0; i_entry < count; ++i_entry) { attribute_id = tvb_get_ntohl(tvb, offset); value_length = tvb_get_ntohs(tvb, offset + 4 + 2); value = tvb_get_string_enc(wmem_packet_scope(), tvb, offset + 4 + 2 + 2, value_length, ENC_ASCII); if (attribute_id == 0x01) col_append_fstr(pinfo->cinfo, COL_INFO, " - Title: \"%s\"", value); entry_item = proto_tree_add_none_format(ptree, hf_btavrcp_attribute_item, tvb, offset, 4 + 2 + 2 + value_length, "Attribute [%21s]: %s", val_to_str_const(attribute_id, attribute_id_vals, "Unknown"), value); entry_tree = proto_item_add_subtree(entry_item, ett_btavrcp_attribute_entry); proto_tree_add_item(entry_tree, hf_btavrcp_attribute, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4; proto_tree_add_item(entry_tree, hf_btavrcp_character_set, tvb, offset, 2, ENC_BIG_ENDIAN); offset += 2; proto_tree_add_item(entry_tree, hf_btavrcp_setting_value_length, tvb, offset, 2, ENC_BIG_ENDIAN); offset += 2; proto_tree_add_item(entry_tree, hf_btavrcp_setting_value, tvb, offset, value_length, ENC_UTF_8 | ENC_NA); offset += value_length; } return offset; } static gint dissect_item_mediaplayer(tvbuff_t *tvb, proto_tree *tree, gint offset) { guint displayable_name_length; guint item_length; guint feature_octet; guint i_feature; guint8 *displayable_name; proto_item *pitem; proto_tree *ptree; proto_item *features_item; proto_tree *features_tree; proto_item *features_not_set_item; proto_tree *features_not_set_tree; item_length = tvb_get_ntohs(tvb, offset + 1); displayable_name_length = tvb_get_ntohs(tvb, offset + 1 + 2 + 1 + 1 + 4 + 16 + 1 + 2); displayable_name = tvb_get_string_enc(wmem_packet_scope(), tvb, offset + 1 + 2 + 1 + 1 + 4 + 16 + 1 + 2 + 2, displayable_name_length, ENC_ASCII); pitem = proto_tree_add_none_format(tree, hf_btavrcp_player_item, tvb, offset, 1 + 2 + item_length, "Player: %s", displayable_name); ptree = proto_item_add_subtree(pitem, ett_btavrcp_player); proto_tree_add_item(ptree, hf_btavrcp_item_type, tvb, offset, 1, ENC_BIG_ENDIAN); offset += 1; proto_tree_add_item(ptree, hf_btavrcp_item_length, tvb, offset, 2, ENC_BIG_ENDIAN); offset += 2; proto_tree_add_item(ptree, hf_btavrcp_player_id, tvb, offset, 1, ENC_BIG_ENDIAN); offset += 1; proto_tree_add_item(ptree, hf_btavrcp_major_player_type, tvb, offset, 1, ENC_BIG_ENDIAN); offset += 1; proto_tree_add_item(ptree, hf_btavrcp_player_subtype, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4; /* feature bit mask */ features_item = proto_tree_add_item(ptree, hf_btavrcp_features, tvb, offset, 16, ENC_NA); features_tree = proto_item_add_subtree(features_item, ett_btavrcp_features); features_not_set_item = proto_tree_add_item(features_tree, hf_btavrcp_not_used_features, tvb, offset, 16, ENC_NA); features_not_set_tree = proto_item_add_subtree(features_not_set_item, ett_btavrcp_features_not_used); feature_octet = tvb_get_guint8(tvb, offset + 0); proto_tree_add_item((feature_octet & (1 << 0)) ? features_tree : features_not_set_tree, hf_btavrcp_feature_passthrough_select, tvb, offset + 0, 1, ENC_BIG_ENDIAN); proto_tree_add_item((feature_octet & (1 << 1)) ? features_tree : features_not_set_tree, hf_btavrcp_feature_passthrough_up, tvb, offset + 0, 1, ENC_BIG_ENDIAN); proto_tree_add_item((feature_octet & (1 << 2)) ? features_tree : features_not_set_tree, hf_btavrcp_feature_passthrough_down, tvb, offset + 0, 1, ENC_BIG_ENDIAN); proto_tree_add_item((feature_octet & (1 << 3)) ? features_tree : features_not_set_tree, hf_btavrcp_feature_passthrough_left, tvb, offset + 0, 1, ENC_BIG_ENDIAN); proto_tree_add_item((feature_octet & (1 << 4)) ? features_tree : features_not_set_tree, hf_btavrcp_feature_passthrough_right, tvb, offset + 0, 1, ENC_BIG_ENDIAN); proto_tree_add_item((feature_octet & (1 << 5)) ? features_tree : features_not_set_tree, hf_btavrcp_feature_passthrough_right_up, tvb, offset + 0, 1, ENC_BIG_ENDIAN); proto_tree_add_item((feature_octet & (1 << 6)) ? features_tree : features_not_set_tree, hf_btavrcp_feature_passthrough_right_down, tvb, offset + 0, 1, ENC_BIG_ENDIAN); proto_tree_add_item((feature_octet & (1 << 7)) ? features_tree : features_not_set_tree, hf_btavrcp_feature_passthrough_left_up, tvb, offset + 0, 1, ENC_BIG_ENDIAN); feature_octet = tvb_get_guint8(tvb, offset + 1); proto_tree_add_item((feature_octet & (1 << 0)) ? features_tree : features_not_set_tree, hf_btavrcp_feature_passthrough_left_down , tvb, offset + 1, 1, ENC_BIG_ENDIAN); proto_tree_add_item((feature_octet & (1 << 1)) ? features_tree : features_not_set_tree, hf_btavrcp_feature_passthrough_root_menu , tvb, offset + 1, 1, ENC_BIG_ENDIAN); proto_tree_add_item((feature_octet & (1 << 2)) ? features_tree : features_not_set_tree, hf_btavrcp_feature_passthrough_setup_menu, tvb, offset + 1, 1, ENC_BIG_ENDIAN); proto_tree_add_item((feature_octet & (1 << 3)) ? features_tree : features_not_set_tree, hf_btavrcp_feature_passthrough_contents_menu, tvb, offset + 1, 1, ENC_BIG_ENDIAN); proto_tree_add_item((feature_octet & (1 << 4)) ? features_tree : features_not_set_tree, hf_btavrcp_feature_passthrough_favorite_menu, tvb, offset + 1, 1, ENC_BIG_ENDIAN); proto_tree_add_item((feature_octet & (1 << 5)) ? features_tree : features_not_set_tree, hf_btavrcp_feature_passthrough_exit, tvb, offset + 1, 1, ENC_BIG_ENDIAN); proto_tree_add_item((feature_octet & (1 << 6)) ? features_tree : features_not_set_tree, hf_btavrcp_feature_passthrough_0, tvb, offset + 1, 1, ENC_BIG_ENDIAN); proto_tree_add_item((feature_octet & (1 << 7)) ? features_tree : features_not_set_tree, hf_btavrcp_feature_passthrough_1, tvb, offset + 1, 1, ENC_BIG_ENDIAN); feature_octet = tvb_get_guint8(tvb, offset + 2); proto_tree_add_item((feature_octet & (1 << 0)) ? features_tree : features_not_set_tree, hf_btavrcp_feature_passthrough_2, tvb, offset + 2, 1, ENC_BIG_ENDIAN); proto_tree_add_item((feature_octet & (1 << 1)) ? features_tree : features_not_set_tree, hf_btavrcp_feature_passthrough_3, tvb, offset + 2, 1, ENC_BIG_ENDIAN); proto_tree_add_item((feature_octet & (1 << 2)) ? features_tree : features_not_set_tree, hf_btavrcp_feature_passthrough_4, tvb, offset + 2, 1, ENC_BIG_ENDIAN); proto_tree_add_item((feature_octet & (1 << 3)) ? features_tree : features_not_set_tree, hf_btavrcp_feature_passthrough_5, tvb, offset + 2, 1, ENC_BIG_ENDIAN); proto_tree_add_item((feature_octet & (1 << 4)) ? features_tree : features_not_set_tree, hf_btavrcp_feature_passthrough_6, tvb, offset + 2, 1, ENC_BIG_ENDIAN); proto_tree_add_item((feature_octet & (1 << 5)) ? features_tree : features_not_set_tree, hf_btavrcp_feature_passthrough_7, tvb, offset + 2, 1, ENC_BIG_ENDIAN); proto_tree_add_item((feature_octet & (1 << 6)) ? features_tree : features_not_set_tree, hf_btavrcp_feature_passthrough_8, tvb, offset + 2, 1, ENC_BIG_ENDIAN); proto_tree_add_item((feature_octet & (1 << 7)) ? features_tree : features_not_set_tree, hf_btavrcp_feature_passthrough_9, tvb, offset + 2, 1, ENC_BIG_ENDIAN); feature_octet = tvb_get_guint8(tvb, offset + 3); proto_tree_add_item((feature_octet & (1 << 0)) ? features_tree : features_not_set_tree, hf_btavrcp_feature_passthrough_dot, tvb, offset + 3, 1, ENC_BIG_ENDIAN); proto_tree_add_item((feature_octet & (1 << 1)) ? features_tree : features_not_set_tree, hf_btavrcp_feature_passthrough_enter, tvb, offset + 3, 1, ENC_BIG_ENDIAN); proto_tree_add_item((feature_octet & (1 << 2)) ? features_tree : features_not_set_tree, hf_btavrcp_feature_passthrough_clear, tvb, offset + 3, 1, ENC_BIG_ENDIAN); proto_tree_add_item((feature_octet & (1 << 3)) ? features_tree : features_not_set_tree, hf_btavrcp_feature_passthrough_channel_up, tvb, offset + 3, 1, ENC_BIG_ENDIAN); proto_tree_add_item((feature_octet & (1 << 4)) ? features_tree : features_not_set_tree, hf_btavrcp_feature_passthrough_channel_down, tvb, offset + 3, 1, ENC_BIG_ENDIAN); proto_tree_add_item((feature_octet & (1 << 5)) ? features_tree : features_not_set_tree, hf_btavrcp_feature_passthrough_previous_channel, tvb, offset + 3, 1, ENC_BIG_ENDIAN); proto_tree_add_item((feature_octet & (1 << 6)) ? features_tree : features_not_set_tree, hf_btavrcp_feature_passthrough_sound_select, tvb, offset + 3, 1, ENC_BIG_ENDIAN); proto_tree_add_item((feature_octet & (1 << 7)) ? features_tree : features_not_set_tree, hf_btavrcp_feature_passthrough_input_select, tvb, offset + 3, 1, ENC_BIG_ENDIAN); feature_octet = tvb_get_guint8(tvb, offset + 4); proto_tree_add_item((feature_octet & (1 << 0)) ? features_tree : features_not_set_tree, hf_btavrcp_feature_passthrough_display_information, tvb, offset + 4, 1, ENC_BIG_ENDIAN); proto_tree_add_item((feature_octet & (1 << 1)) ? features_tree : features_not_set_tree, hf_btavrcp_feature_passthrough_help, tvb, offset + 4, 1, ENC_BIG_ENDIAN); proto_tree_add_item((feature_octet & (1 << 2)) ? features_tree : features_not_set_tree, hf_btavrcp_feature_passthrough_page_up, tvb, offset + 4, 1, ENC_BIG_ENDIAN); proto_tree_add_item((feature_octet & (1 << 3)) ? features_tree : features_not_set_tree, hf_btavrcp_feature_passthrough_page_down, tvb, offset + 4, 1, ENC_BIG_ENDIAN); proto_tree_add_item((feature_octet & (1 << 4)) ? features_tree : features_not_set_tree, hf_btavrcp_feature_passthrough_power, tvb, offset + 4, 1, ENC_BIG_ENDIAN); proto_tree_add_item((feature_octet & (1 << 5)) ? features_tree : features_not_set_tree, hf_btavrcp_feature_passthrough_volume_up, tvb, offset + 4, 1, ENC_BIG_ENDIAN); proto_tree_add_item((feature_octet & (1 << 6)) ? features_tree : features_not_set_tree, hf_btavrcp_feature_passthrough_volume_down, tvb, offset + 4, 1, ENC_BIG_ENDIAN); proto_tree_add_item((feature_octet & (1 << 7)) ? features_tree : features_not_set_tree, hf_btavrcp_feature_passthrough_mute, tvb, offset + 4, 1, ENC_BIG_ENDIAN); feature_octet = tvb_get_guint8(tvb, offset + 5); proto_tree_add_item((feature_octet & (1 << 0)) ? features_tree : features_not_set_tree, hf_btavrcp_feature_passthrough_play, tvb, offset + 5, 1, ENC_BIG_ENDIAN); proto_tree_add_item((feature_octet & (1 << 1)) ? features_tree : features_not_set_tree, hf_btavrcp_feature_passthrough_stop, tvb, offset + 5, 1, ENC_BIG_ENDIAN); proto_tree_add_item((feature_octet & (1 << 2)) ? features_tree : features_not_set_tree, hf_btavrcp_feature_passthrough_pause, tvb, offset + 5, 1, ENC_BIG_ENDIAN); proto_tree_add_item((feature_octet & (1 << 3)) ? features_tree : features_not_set_tree, hf_btavrcp_feature_passthrough_record, tvb, offset + 5, 1, ENC_BIG_ENDIAN); proto_tree_add_item((feature_octet & (1 << 4)) ? features_tree : features_not_set_tree, hf_btavrcp_feature_passthrough_rewind, tvb, offset + 5, 1, ENC_BIG_ENDIAN); proto_tree_add_item((feature_octet & (1 << 5)) ? features_tree : features_not_set_tree, hf_btavrcp_feature_passthrough_fast_forward, tvb, offset + 5, 1, ENC_BIG_ENDIAN); proto_tree_add_item((feature_octet & (1 << 6)) ? features_tree : features_not_set_tree, hf_btavrcp_feature_passthrough_eject, tvb, offset + 5, 1, ENC_BIG_ENDIAN); proto_tree_add_item((feature_octet & (1 << 7)) ? features_tree : features_not_set_tree, hf_btavrcp_feature_passthrough_forward, tvb, offset + 5, 1, ENC_BIG_ENDIAN); feature_octet = tvb_get_guint8(tvb, offset + 6); proto_tree_add_item((feature_octet & (1 << 0)) ? features_tree : features_not_set_tree, hf_btavrcp_feature_passthrough_backward, tvb, offset + 6, 1, ENC_BIG_ENDIAN); proto_tree_add_item((feature_octet & (1 << 1)) ? features_tree : features_not_set_tree, hf_btavrcp_feature_passthrough_angle, tvb, offset + 6, 1, ENC_BIG_ENDIAN); proto_tree_add_item((feature_octet & (1 << 2)) ? features_tree : features_not_set_tree, hf_btavrcp_feature_passthrough_subpicture, tvb, offset + 6, 1, ENC_BIG_ENDIAN); proto_tree_add_item((feature_octet & (1 << 3)) ? features_tree : features_not_set_tree, hf_btavrcp_feature_passthrough_f1, tvb, offset + 6, 1, ENC_BIG_ENDIAN); proto_tree_add_item((feature_octet & (1 << 4)) ? features_tree : features_not_set_tree, hf_btavrcp_feature_passthrough_f2, tvb, offset + 6, 1, ENC_BIG_ENDIAN); proto_tree_add_item((feature_octet & (1 << 5)) ? features_tree : features_not_set_tree, hf_btavrcp_feature_passthrough_f3, tvb, offset + 6, 1, ENC_BIG_ENDIAN); proto_tree_add_item((feature_octet & (1 << 6)) ? features_tree : features_not_set_tree, hf_btavrcp_feature_passthrough_f4, tvb, offset + 6, 1, ENC_BIG_ENDIAN); proto_tree_add_item((feature_octet & (1 << 7)) ? features_tree : features_not_set_tree, hf_btavrcp_feature_passthrough_f5, tvb, offset + 6, 1, ENC_BIG_ENDIAN); feature_octet = tvb_get_guint8(tvb, offset + 7); proto_tree_add_item((feature_octet & (1 << 0)) ? features_tree : features_not_set_tree, hf_btavrcp_feature_vendor_unique, tvb, offset + 7, 1, ENC_BIG_ENDIAN); proto_tree_add_item((feature_octet & (1 << 1)) ? features_tree : features_not_set_tree, hf_btavrcp_feature_basic_group_navigation, tvb, offset + 7, 1, ENC_BIG_ENDIAN); proto_tree_add_item((feature_octet & (1 << 2)) ? features_tree : features_not_set_tree, hf_btavrcp_feature_advanced_control_player, tvb, offset + 7, 1, ENC_BIG_ENDIAN); proto_tree_add_item((feature_octet & (1 << 3)) ? features_tree : features_not_set_tree, hf_btavrcp_feature_browsing, tvb, offset + 7, 1, ENC_BIG_ENDIAN); proto_tree_add_item((feature_octet & (1 << 4)) ? features_tree : features_not_set_tree, hf_btavrcp_feature_searching, tvb, offset + 7, 1, ENC_BIG_ENDIAN); proto_tree_add_item((feature_octet & (1 << 5)) ? features_tree : features_not_set_tree, hf_btavrcp_feature_addtonowplayer, tvb, offset + 7, 1, ENC_BIG_ENDIAN); proto_tree_add_item((feature_octet & (1 << 6)) ? features_tree : features_not_set_tree, hf_btavrcp_feature_uid_unique, tvb, offset + 7, 1, ENC_BIG_ENDIAN); proto_tree_add_item((feature_octet & (1 << 7)) ? features_tree : features_not_set_tree, hf_btavrcp_feature_only_browsable_when_addressed, tvb, offset + 7, 1, ENC_BIG_ENDIAN); feature_octet = tvb_get_guint8(tvb, offset + 8); proto_tree_add_item((feature_octet & (1 << 0)) ? features_tree : features_not_set_tree, hf_btavrcp_feature_only_searchable_when_addressed, tvb, offset + 8, 1, ENC_BIG_ENDIAN); proto_tree_add_item((feature_octet & (1 << 1)) ? features_tree : features_not_set_tree, hf_btavrcp_feature_nowplaying, tvb, offset + 8, 1, ENC_BIG_ENDIAN); proto_tree_add_item((feature_octet & (1 << 2)) ? features_tree : features_not_set_tree, hf_btavrcp_feature_uid_persistency, tvb, offset + 8, 1, ENC_BIG_ENDIAN); /* reserved */ proto_tree_add_item((feature_octet & (1 << 3)) ? features_tree : features_not_set_tree, hf_btavrcp_feature_reserved_3, tvb, offset + 8, 1, ENC_BIG_ENDIAN); proto_tree_add_item((feature_octet & (1 << 4)) ? features_tree : features_not_set_tree, hf_btavrcp_feature_reserved_4, tvb, offset + 8, 1, ENC_BIG_ENDIAN); proto_tree_add_item((feature_octet & (1 << 5)) ? features_tree : features_not_set_tree, hf_btavrcp_feature_reserved_5, tvb, offset + 8, 1, ENC_BIG_ENDIAN); proto_tree_add_item((feature_octet & (1 << 6)) ? features_tree : features_not_set_tree, hf_btavrcp_feature_reserved_6, tvb, offset + 8, 1, ENC_BIG_ENDIAN); proto_tree_add_item((feature_octet & (1 << 7)) ? features_tree : features_not_set_tree, hf_btavrcp_feature_reserved_7, tvb, offset + 8, 1, ENC_BIG_ENDIAN); for (i_feature = 9; i_feature <= 16; ++i_feature) { feature_octet = tvb_get_guint8(tvb, offset + i_feature); proto_tree_add_item((feature_octet & (1 << 0)) ? features_tree : features_not_set_tree, hf_btavrcp_feature_reserved_0, tvb, offset + i_feature, 1, ENC_BIG_ENDIAN); proto_tree_add_item((feature_octet & (1 << 1)) ? features_tree : features_not_set_tree, hf_btavrcp_feature_reserved_1, tvb, offset + i_feature, 1, ENC_BIG_ENDIAN); proto_tree_add_item((feature_octet & (1 << 2)) ? features_tree : features_not_set_tree, hf_btavrcp_feature_reserved_2, tvb, offset + i_feature, 1, ENC_BIG_ENDIAN); proto_tree_add_item((feature_octet & (1 << 3)) ? features_tree : features_not_set_tree, hf_btavrcp_feature_reserved_3, tvb, offset + i_feature, 1, ENC_BIG_ENDIAN); proto_tree_add_item((feature_octet & (1 << 4)) ? features_tree : features_not_set_tree, hf_btavrcp_feature_reserved_4, tvb, offset + i_feature, 1, ENC_BIG_ENDIAN); proto_tree_add_item((feature_octet & (1 << 5)) ? features_tree : features_not_set_tree, hf_btavrcp_feature_reserved_5, tvb, offset + i_feature, 1, ENC_BIG_ENDIAN); proto_tree_add_item((feature_octet & (1 << 6)) ? features_tree : features_not_set_tree, hf_btavrcp_feature_reserved_6, tvb, offset + i_feature, 1, ENC_BIG_ENDIAN); proto_tree_add_item((feature_octet & (1 << 7)) ? features_tree : features_not_set_tree, hf_btavrcp_feature_reserved_7, tvb, offset + i_feature, 1, ENC_BIG_ENDIAN); } offset += 16; proto_tree_add_item(ptree, hf_btavrcp_play_status, tvb, offset, 1, ENC_BIG_ENDIAN); offset += 1; proto_tree_add_item(ptree, hf_btavrcp_character_set, tvb, offset, 2, ENC_BIG_ENDIAN); offset += 2; proto_tree_add_item(ptree, hf_btavrcp_displayable_name_length, tvb, offset, 2, ENC_BIG_ENDIAN); displayable_name_length = tvb_get_ntohs(tvb, offset); offset += 2; proto_tree_add_item(ptree, hf_btavrcp_displayable_name, tvb, offset, displayable_name_length, ENC_UTF_8 | ENC_NA); offset += displayable_name_length; return offset; } static gint dissect_item_media_element(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, gint offset) { guint number_of_attributes; guint displayable_name_length; guint item_length; guint8 *displayable_name; proto_item *pitem; proto_tree *ptree; gint offset_in; item_length = tvb_get_ntohs(tvb, offset + 1); displayable_name_length = tvb_get_ntohs(tvb, offset + 1 + 2 + 8 + 1 + 2); displayable_name = tvb_get_string_enc(wmem_packet_scope(), tvb, offset + 1 + 2 + 8 + 1 + 2 + 2, displayable_name_length, ENC_ASCII); pitem = proto_tree_add_none_format(tree, hf_btavrcp_item , tvb, offset, 1 + 2 + item_length, "Element: %s", displayable_name); ptree = proto_item_add_subtree(pitem, ett_btavrcp_element); proto_tree_add_item(ptree, hf_btavrcp_item_type, tvb, offset, 1, ENC_BIG_ENDIAN); offset += 1; proto_tree_add_item(ptree, hf_btavrcp_item_length, tvb, offset, 2, ENC_BIG_ENDIAN); offset += 2; offset_in = offset; proto_tree_add_item(ptree, hf_btavrcp_uid, tvb, offset, 8, ENC_BIG_ENDIAN); offset += 8; proto_tree_add_item(ptree, hf_btavrcp_media_type, tvb, offset, 1, ENC_BIG_ENDIAN); offset += 1; proto_tree_add_item(ptree, hf_btavrcp_character_set, tvb, offset, 2, ENC_BIG_ENDIAN); offset += 2; proto_tree_add_item(ptree, hf_btavrcp_displayable_name_length, tvb, offset, 2, ENC_BIG_ENDIAN); displayable_name_length = tvb_get_ntohs(tvb, offset); offset += 2; proto_tree_add_item(ptree, hf_btavrcp_displayable_name, tvb, offset, displayable_name_length, ENC_UTF_8 | ENC_NA); offset += displayable_name_length; proto_tree_add_item(ptree, hf_btavrcp_number_of_attributes, tvb, offset, 1, ENC_BIG_ENDIAN); number_of_attributes = tvb_get_guint8(tvb, offset); offset += 1; offset = dissect_attribute_entries(tvb, pinfo, ptree, offset, number_of_attributes); if ( item_length != (guint) offset - offset_in) { expert_add_info(pinfo, pitem, &ei_btavrcp_item_length_bad); } return offset; } static gint dissect_item_folder(tvbuff_t *tvb, proto_tree *tree, gint offset) { guint displayable_name_length; guint item_length; guint8 *displayable_name; proto_item *pitem; proto_tree *ptree; item_length = tvb_get_ntohs(tvb, offset + 1); displayable_name_length = tvb_get_ntohs(tvb, offset + 1 + 2 + 8 + 1 + 1 + 2); displayable_name = tvb_get_string_enc(wmem_packet_scope(), tvb, offset + 1 + 2 + 8 + 1 + 1 + 2 + 2, displayable_name_length, ENC_ASCII); pitem = proto_tree_add_none_format(tree, hf_btavrcp_folder, tvb, offset, 1 + 2 + item_length, "Folder : %s", displayable_name); ptree = proto_item_add_subtree(pitem, ett_btavrcp_folder); proto_tree_add_item(ptree, hf_btavrcp_item_type, tvb, offset, 1, ENC_BIG_ENDIAN); offset += 1; proto_tree_add_item(ptree, hf_btavrcp_item_length, tvb, offset, 2, ENC_BIG_ENDIAN); offset += 2; proto_tree_add_item(ptree, hf_btavrcp_uid, tvb, offset, 8, ENC_BIG_ENDIAN); offset += 8; proto_tree_add_item(ptree, hf_btavrcp_folder_type, tvb, offset, 1, ENC_BIG_ENDIAN); offset += 1; proto_tree_add_item(ptree, hf_btavrcp_folder_playable, tvb, offset, 1, ENC_BIG_ENDIAN); offset += 1; proto_tree_add_item(ptree, hf_btavrcp_character_set, tvb, offset, 2, ENC_BIG_ENDIAN); offset += 2; proto_tree_add_item(ptree, hf_btavrcp_displayable_name_length, tvb, offset, 2, ENC_BIG_ENDIAN); displayable_name_length = tvb_get_ntohs(tvb, offset); offset += 2; proto_tree_add_item(ptree, hf_btavrcp_displayable_name, tvb, offset, displayable_name_length, ENC_UTF_8 | ENC_NA); offset += displayable_name_length; return offset; } static gint dissect_passthrough(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, gint offset, gboolean is_command, guint32 *op) { guint operation; guint state; proto_tree_add_item(tree, hf_btavrcp_passthrough_state, tvb, offset, 1, ENC_BIG_ENDIAN); proto_tree_add_item(tree, hf_btavrcp_passthrough_operation, tvb, offset, 1, ENC_BIG_ENDIAN); *op = tvb_get_guint8(tvb, offset); operation = tvb_get_guint8(tvb, offset) & 0x7F; state = (tvb_get_guint8(tvb, offset) & 0x80) >> 7; offset += 1; proto_tree_add_item(tree, hf_btavrcp_passthrough_data_length, tvb, offset, 1, ENC_BIG_ENDIAN); offset += 1; if (!is_command) { if (operation == 0x7E) { proto_tree_add_item(tree, hf_btavrcp_passthrough_company_id, tvb, offset, 3, ENC_BIG_ENDIAN); offset += 3; proto_tree_add_item(tree, hf_btavrcp_passthrough_vendor_unique_id, tvb, offset, 2, ENC_BIG_ENDIAN); offset += 2; } } col_append_fstr(pinfo->cinfo, COL_INFO, " - %s (%s)", val_to_str_const(operation, passthrough_operation_vals, "Unknown opcode"), val_to_str_const(state, passthrough_state_vals, "unknown")); return offset; } static gint dissect_unit(tvbuff_t *tvb, proto_tree *tree, gint offset, gboolean is_command) { if (is_command) { proto_tree_add_item(tree, hf_btavrcp_data, tvb, offset, 5, ENC_NA); offset += 5; } else { proto_tree_add_item(tree, hf_btavrcp_unit_unknown, tvb, offset, 1, ENC_BIG_ENDIAN); offset += 1; proto_tree_add_item(tree, hf_btavrcp_unit_type, tvb, offset, 1, ENC_BIG_ENDIAN); proto_tree_add_item(tree, hf_btavrcp_unit_id, tvb, offset, 1, ENC_BIG_ENDIAN); offset += 1; proto_tree_add_item(tree, hf_btavrcp_passthrough_company_id, tvb, offset, 3, ENC_BIG_ENDIAN); offset += 3; } return offset; } static gint dissect_subunit(tvbuff_t *tvb, proto_tree *tree, gint offset, gboolean is_command) { proto_tree_add_item(tree, hf_btavrcp_subunit_page, tvb, offset, 1, ENC_BIG_ENDIAN); proto_tree_add_item(tree, hf_btavrcp_subunit_extension_code, tvb, offset, 1, ENC_BIG_ENDIAN); offset += 1; if (!is_command) { proto_tree_add_item(tree, hf_btavrcp_subunit_type, tvb, offset, 1, ENC_BIG_ENDIAN); proto_tree_add_item(tree, hf_btavrcp_subunit_id, tvb, offset, 1, ENC_BIG_ENDIAN); offset += 1; proto_tree_add_item(tree, hf_btavrcp_data, tvb, offset, 3, ENC_NA); offset += 3; } else { proto_tree_add_item(tree, hf_btavrcp_data, tvb, offset, 4, ENC_NA); offset += 4; } return offset; } static gint dissect_vendor_dependent(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, gint offset, guint ctype, guint32 *op, guint32 *op_arg, gboolean is_command, avrcp_proto_data_t *avrcp_proto_data) { proto_item *pitem; guint pdu_id; guint company_id; guint event_id; guint packet_type; guint parameter_length; gint length; wmem_tree_key_t key[7]; guint32 k_op; guint32 frame_number; guint volume; guint volume_percent; fragment_t *fragment; data_fragment_t *data_fragment; *op_arg = 0; proto_tree_add_item(tree, hf_btavrcp_company_id, tvb, offset, 3, ENC_BIG_ENDIAN); company_id = tvb_get_ntoh24(tvb, offset); offset += 3; if (company_id == OUI_BLUETOOTH) { proto_tree_add_item(tree, hf_btavrcp_bt_pdu_id, tvb, offset, 1, ENC_BIG_ENDIAN); } else { if (tvb_reported_length_remaining(tvb, offset) == 0) { col_append_str(pinfo->cinfo, COL_INFO, " - No PDU ID"); return offset; } proto_tree_add_item(tree, hf_btavrcp_pdu_id, tvb, offset, 1, ENC_BIG_ENDIAN); } pdu_id = tvb_get_guint8(tvb, offset); *op = pdu_id | (company_id << 8); offset += 1; if (company_id != OUI_BLUETOOTH) { col_append_fstr(pinfo->cinfo, COL_INFO, " - %s", val_to_str_const(pdu_id, NULL, "Unknown PDU ID")); } proto_tree_add_item(tree, hf_btavrcp_rfa, tvb, offset, 1, ENC_BIG_ENDIAN); proto_tree_add_item(tree, hf_btavrcp_packet_type, tvb, offset, 1, ENC_BIG_ENDIAN); packet_type = tvb_get_guint8(tvb, offset) & 0x03; offset += 1; proto_tree_add_item(tree, hf_btavrcp_length, tvb, offset, 2, ENC_BIG_ENDIAN); parameter_length = tvb_get_ntohs(tvb, offset); offset += 2; if (company_id != OUI_BLUETOOTH) return offset; col_append_fstr(pinfo->cinfo, COL_INFO, " - %s", val_to_str_const(pdu_id, pdu_id_vals, "Unknown PDU ID")); if (parameter_length == 0) return offset; length = tvb_reported_length_remaining(tvb, offset); if (packet_type == PACKET_TYPE_START) { if (pinfo->fd->flags.visited == 0 && tvb_captured_length_remaining(tvb, offset) == length) { k_op = pdu_id | (company_id << 8); frame_number = pinfo->num; fragment = wmem_new(wmem_file_scope(), fragment_t); fragment->start_frame_number = pinfo->num; fragment->end_frame_number = 0; fragment->state = 0; fragment->count = 1; fragment->fragments = wmem_tree_new(wmem_file_scope()); data_fragment = wmem_new(wmem_file_scope(), data_fragment_t); data_fragment->length = length; data_fragment->data = (guint8 *) wmem_alloc(wmem_file_scope(), data_fragment->length); tvb_memcpy(tvb, data_fragment->data, offset, data_fragment->length); wmem_tree_insert32(fragment->fragments, fragment->count, data_fragment); key[0].length = 1; key[0].key = &avrcp_proto_data->interface_id; key[1].length = 1; key[1].key = &avrcp_proto_data->adapter_id; key[2].length = 1; key[2].key = &avrcp_proto_data->chandle; key[3].length = 1; key[3].key = &avrcp_proto_data->channel; key[4].length = 1; key[4].key = &k_op; key[5].length = 1; key[5].key = &frame_number; key[6].length = 0; key[6].key = NULL; fragment->interface_id = avrcp_proto_data->interface_id; fragment->adapter_id = avrcp_proto_data->adapter_id; fragment->chandle = avrcp_proto_data->chandle; fragment->psm = avrcp_proto_data->channel; fragment->op = pdu_id | (company_id << 8); wmem_tree_insert32_array(reassembling, key, fragment); } col_append_str(pinfo->cinfo, COL_INFO, " [start]"); return offset; } else if (packet_type == PACKET_TYPE_CONTINUE) { if (pinfo->fd->flags.visited == 0 && tvb_captured_length_remaining(tvb, offset) == length) { k_op = pdu_id | (company_id << 8); frame_number = pinfo->num; key[0].length = 1; key[0].key = &avrcp_proto_data->interface_id; key[1].length = 1; key[1].key = &avrcp_proto_data->adapter_id; key[2].length = 1; key[2].key = &avrcp_proto_data->chandle; key[3].length = 1; key[3].key = &avrcp_proto_data->channel; key[4].length = 1; key[4].key = &k_op; key[5].length = 1; key[5].key = &frame_number; key[6].length = 0; key[6].key = NULL; fragment = (fragment_t *)wmem_tree_lookup32_array_le(reassembling, key); if (fragment && fragment->interface_id == avrcp_proto_data->interface_id && fragment->adapter_id == avrcp_proto_data->adapter_id && fragment->chandle == avrcp_proto_data->chandle && fragment->psm == avrcp_proto_data->channel && fragment->op == (pdu_id | (company_id << 8)) && fragment->state == 1) { fragment->count += 1; fragment->state = 0; data_fragment = wmem_new(wmem_file_scope(), data_fragment_t); data_fragment->length = length; data_fragment->data = (guint8 *) wmem_alloc(wmem_file_scope(), data_fragment->length); tvb_memcpy(tvb, data_fragment->data, offset, data_fragment->length); wmem_tree_insert32(fragment->fragments, fragment->count, data_fragment); } } col_append_str(pinfo->cinfo, COL_INFO, " [continue]"); return offset; } else if (packet_type == PACKET_TYPE_END) { guint i_frame; tvbuff_t *next_tvb; col_append_str(pinfo->cinfo, COL_INFO, " [end]"); k_op = pdu_id | (company_id << 8); frame_number = pinfo->num; key[0].length = 1; key[0].key = &avrcp_proto_data->interface_id; key[1].length = 1; key[1].key = &avrcp_proto_data->adapter_id; key[2].length = 1; key[2].key = &avrcp_proto_data->chandle; key[3].length = 1; key[3].key = &avrcp_proto_data->channel; key[4].length = 1; key[4].key = &k_op; key[5].length = 1; key[5].key = &frame_number; key[6].length = 0; key[6].key = NULL; fragment = (fragment_t *)wmem_tree_lookup32_array_le(reassembling, key); if (fragment && fragment->interface_id == avrcp_proto_data->interface_id && fragment->adapter_id == avrcp_proto_data->adapter_id && fragment->chandle == avrcp_proto_data->chandle && fragment->psm == avrcp_proto_data->channel && fragment->op == (pdu_id | (company_id << 8))) { if (fragment->state == 1 && pinfo->fd->flags.visited == 0) { fragment->end_frame_number = pinfo->num; fragment->count += 1; fragment->state = 2; data_fragment = wmem_new(wmem_file_scope(), data_fragment_t); data_fragment->length = length; data_fragment->data = (guint8 *) wmem_alloc(wmem_file_scope(), data_fragment->length); tvb_memcpy(tvb, data_fragment->data, offset, data_fragment->length); wmem_tree_insert32(fragment->fragments, fragment->count, data_fragment); } /* reassembling*/ length = 0; if (fragment->state == 2) { guint i_length = 0; guint8 *reassembled; for (i_frame = 1; i_frame <= fragment->count; ++i_frame) { data_fragment = (data_fragment_t *)wmem_tree_lookup32_le(fragment->fragments, i_frame); length += data_fragment->length; } reassembled = (guint8 *) wmem_alloc(pinfo->pool, length); for (i_frame = 1; i_frame <= fragment->count; ++i_frame) { data_fragment = (data_fragment_t *)wmem_tree_lookup32_le(fragment->fragments, i_frame); memcpy(reassembled + i_length, data_fragment->data, data_fragment->length); i_length += data_fragment->length; } next_tvb = tvb_new_child_real_data(tvb, reassembled, length, length); add_new_data_source(pinfo, next_tvb, "Reassembled AVRCP"); tvb = next_tvb; offset = 0; pitem = proto_tree_add_item(tree, hf_btavrcp_reassembled, tvb, offset, 0, ENC_NA); PROTO_ITEM_SET_GENERATED(pitem); } } } if (ctype == 0x0a) { /* REJECT */ guint status; proto_tree_add_item(tree, hf_btavrcp_status, tvb, offset, 1, ENC_BIG_ENDIAN); status = tvb_get_guint8(tvb, offset); offset += 1; col_append_fstr(pinfo->cinfo, COL_INFO, " - Status: %s", val_to_str_const(status, status_vals, "Unknown status")); } else switch(pdu_id) { case PDU_GET_CAPABILITIES: if (is_command) { guint capability; proto_tree_add_item(tree, hf_btavrcp_capability, tvb, offset, 1, ENC_BIG_ENDIAN); capability = tvb_get_guint8(tvb, offset); *op_arg = capability; col_append_fstr(pinfo->cinfo, COL_INFO, "(%s)", val_to_str_const(capability, capability_vals, "unknown")); offset += 1; } else { guint capability; guint capability_count; guint i_capability; proto_tree_add_item(tree, hf_btavrcp_capability, tvb, offset, 1, ENC_BIG_ENDIAN); capability = tvb_get_guint8(tvb, offset); *op_arg = capability; offset += 1; proto_tree_add_item(tree, hf_btavrcp_capability_count, tvb, offset, 1, ENC_BIG_ENDIAN); capability_count = tvb_get_guint8(tvb, offset); offset += 1; for (i_capability = 0; i_capability < capability_count; ++i_capability) { if (capability == 0x02) { proto_tree_add_item(tree, hf_btavrcp_company_id, tvb, offset, 3, ENC_BIG_ENDIAN); offset += 3; } else if (capability == 0x03) { proto_tree_add_item(tree, hf_btavrcp_event_id, tvb, offset, 1, ENC_BIG_ENDIAN); offset += 1; } } col_append_fstr(pinfo->cinfo, COL_INFO, "(%s) - Count: %u", val_to_str_const(capability, capability_vals, "unknown"), capability_count); } break; case PDU_LIST_PLAYER_APPLICATION_SETTING_ATTRIBUTES: if (is_command) { /* non */ } else { guint number_of_attributes; guint i_attribute; proto_tree_add_item(tree, hf_btavrcp_number_of_settings, tvb, offset, 1, ENC_BIG_ENDIAN); number_of_attributes = tvb_get_guint8(tvb, offset); offset += 1; for (i_attribute = 0; i_attribute < number_of_attributes; ++i_attribute) { proto_tree_add_item(tree, hf_btavrcp_settings_attribute, tvb, offset, 1, ENC_BIG_ENDIAN); offset += 1; } } break; case PDU_LIST_PLAYER_APPLICATION_SETTING_VALUE: if (is_command) { proto_tree_add_item(tree, hf_btavrcp_settings_attribute, tvb, offset, 1, ENC_BIG_ENDIAN); offset += 1; } else { guint number_of_values; guint i_value; proto_tree_add_item(tree, hf_btavrcp_number_of_settings, tvb, offset, 1, ENC_BIG_ENDIAN); number_of_values = tvb_get_guint8(tvb, offset); offset += 1; for (i_value = 0; i_value < number_of_values; ++i_value) { proto_tree_add_item(tree, hf_btavrcp_settings_value, tvb, offset, 1, ENC_BIG_ENDIAN); offset += 1; } } break; case PDU_GET_CURRENT_PLAYER_APPLICATION_SETTING_VALUE: if (is_command) { guint number_of_attributes; guint i_attribute; proto_tree_add_item(tree, hf_btavrcp_number_of_settings, tvb, offset, 1, ENC_BIG_ENDIAN); number_of_attributes = tvb_get_guint8(tvb, offset); offset += 1; for (i_attribute = 0; i_attribute < number_of_attributes; ++i_attribute) { proto_tree_add_item(tree, hf_btavrcp_settings_attribute, tvb, offset, 1, ENC_BIG_ENDIAN); offset += 1; } } else { guint number_of_settings; guint i_setting; proto_tree_add_item(tree, hf_btavrcp_number_of_settings, tvb, offset, 1, ENC_BIG_ENDIAN); number_of_settings = tvb_get_guint8(tvb, offset); offset += 1; for (i_setting = 0; i_setting < number_of_settings; ++i_setting) { proto_tree_add_item(tree, hf_btavrcp_settings_attribute, tvb, offset, 1, ENC_BIG_ENDIAN); offset += 1; proto_tree_add_item(tree, hf_btavrcp_settings_value, tvb, offset, 1, ENC_BIG_ENDIAN); offset += 1; } } break; case PDU_SET_PLAYER_APPLICATION_SETTING_VALUE: if (is_command) { guint number_of_settings; guint i_setting; proto_tree_add_item(tree, hf_btavrcp_number_of_settings, tvb, offset, 1, ENC_BIG_ENDIAN); number_of_settings = tvb_get_guint8(tvb, offset); offset += 1; for (i_setting = 0; i_setting < number_of_settings; ++i_setting) { proto_tree_add_item(tree, hf_btavrcp_settings_attribute, tvb, offset, 1, ENC_BIG_ENDIAN); offset += 1; proto_tree_add_item(tree, hf_btavrcp_settings_value, tvb, offset, 1, ENC_BIG_ENDIAN); offset += 1; } } else { /* non */ } break; case PDU_GET_PLAYER_APPLICATION_SETTING_ATTRIBUTE_TEXT: if (is_command) { guint number_of_attributes; guint i_attribute; proto_tree_add_item(tree, hf_btavrcp_number_of_settings, tvb, offset, 1, ENC_BIG_ENDIAN); number_of_attributes = tvb_get_guint8(tvb, offset); offset += 1; for (i_attribute = 0; i_attribute < number_of_attributes; ++i_attribute) { proto_tree_add_item(tree, hf_btavrcp_settings_attribute, tvb, offset, 1, ENC_BIG_ENDIAN); offset += 1; } } else { guint number_of_attributes; guint i_attribute; guint attribute_name_length; proto_tree_add_item(tree, hf_btavrcp_number_of_settings, tvb, offset, 1, ENC_BIG_ENDIAN); number_of_attributes = tvb_get_guint8(tvb, offset); offset += 1; for (i_attribute = 0; i_attribute < number_of_attributes; ++i_attribute) { proto_tree_add_item(tree, hf_btavrcp_settings_attribute, tvb, offset, 1, ENC_BIG_ENDIAN); offset += 1; proto_tree_add_item(tree, hf_btavrcp_character_set, tvb, offset, 1, ENC_BIG_ENDIAN); offset += 2; proto_tree_add_item(tree, hf_btavrcp_attribute_name_length, tvb, offset, 1, ENC_BIG_ENDIAN); attribute_name_length = tvb_get_ntohs(tvb, offset); offset += 1; proto_tree_add_item(tree, hf_btavrcp_attribute_name, tvb, offset, attribute_name_length, ENC_UTF_8 | ENC_NA); offset += attribute_name_length; } } break; case PDU_GET_PLAYER_APPLICATION_SETTING_VALUE_TEXT: if (is_command) { guint number_of_values; guint i_value; proto_tree_add_item(tree, hf_btavrcp_settings_attribute, tvb, offset, 1, ENC_BIG_ENDIAN); offset += 1; proto_tree_add_item(tree, hf_btavrcp_number_of_settings, tvb, offset, 1, ENC_BIG_ENDIAN); number_of_values = tvb_get_guint8(tvb, offset); offset += 1; for (i_value = 0; i_value < number_of_values; ++i_value) { proto_tree_add_item(tree, hf_btavrcp_settings_value, tvb, offset, 1, ENC_BIG_ENDIAN); offset += 1; } } else { guint number_of_values; guint i_attribute; guint attribute_value_length; proto_tree_add_item(tree, hf_btavrcp_number_of_settings, tvb, offset, 1, ENC_BIG_ENDIAN); number_of_values = tvb_get_guint8(tvb, offset); offset += 1; for (i_attribute = 0; i_attribute < number_of_values; ++i_attribute) { proto_tree_add_item(tree, hf_btavrcp_settings_attribute, tvb, offset, 1, ENC_BIG_ENDIAN); offset += 1; proto_tree_add_item(tree, hf_btavrcp_character_set, tvb, offset, 1, ENC_BIG_ENDIAN); offset += 2; proto_tree_add_item(tree, hf_btavrcp_attribute_value_length, tvb, offset, 1, ENC_BIG_ENDIAN); attribute_value_length = tvb_get_ntohs(tvb, offset); offset += 1; proto_tree_add_item(tree, hf_btavrcp_attribute_value, tvb, offset, attribute_value_length, ENC_UTF_8 | ENC_NA); offset += attribute_value_length; } } break; case PDU_INFORM_DISPLAYABLE_CHARACTER_SET: if (is_command) { guint number_of_character_set; guint i_character_set; proto_tree_add_item(tree, hf_btavrcp_number_of_character_set, tvb, offset, 1, ENC_BIG_ENDIAN); number_of_character_set = tvb_get_guint8(tvb, offset); offset += 1; for (i_character_set = 0; i_character_set < number_of_character_set; ++i_character_set) { proto_tree_add_item(tree, hf_btavrcp_character_set, tvb, offset, 2, ENC_BIG_ENDIAN); offset += 2; } } else { /* non */ } break; case PDU_INFORM_BATTERY_STATUS_OF_CT: if (is_command) { guint battery_status; proto_tree_add_item(tree, hf_btavrcp_battery_status, tvb, offset, 1, ENC_BIG_ENDIAN); battery_status = tvb_get_guint8(tvb, offset); offset += 1; col_append_fstr(pinfo->cinfo, COL_INFO, " - Battery: %s", val_to_str_const(battery_status, battery_status_vals, "unknown")); } else { /* non */ } break; case PDU_GET_ELEMENT_ATTRIBUTES: if (is_command) { guint number_of_attributes; guint64 identifier; proto_tree_add_item(tree, hf_btavrcp_identifier, tvb, offset, 8, ENC_BIG_ENDIAN); identifier = tvb_get_ntoh64(tvb, offset); offset += 8; col_append_fstr(pinfo->cinfo, COL_INFO, " - 0x%08X%08X", (guint) (identifier >> 32), (guint) (identifier & 0xFFFFFFFF)); if (identifier == 0x00) col_append_str(pinfo->cinfo, COL_INFO, " (PLAYING)"); proto_tree_add_item(tree, hf_btavrcp_number_of_attributes, tvb, offset, 1, ENC_BIG_ENDIAN); number_of_attributes = tvb_get_guint8(tvb, offset); offset += 1; offset = dissect_attribute_id_list(tvb, tree, offset, number_of_attributes); } else { guint number_of_attributes; proto_tree_add_item(tree, hf_btavrcp_number_of_attributes, tvb, offset, 1, ENC_BIG_ENDIAN); number_of_attributes = tvb_get_guint8(tvb, offset); offset += 1; offset = dissect_attribute_entries(tvb, pinfo, tree, offset, number_of_attributes); } break; case PDU_GET_PLAY_STATUS: if (is_command) { /* non */ } else { guint song_length; guint song_position; guint play_status; proto_tree_add_item(tree, hf_btavrcp_song_length, tvb, offset, 4, ENC_BIG_ENDIAN); song_length = tvb_get_ntohl(tvb, offset); offset += 4; proto_tree_add_item(tree, hf_btavrcp_song_position, tvb, offset, 4, ENC_BIG_ENDIAN); song_position = tvb_get_ntohl(tvb, offset); offset += 4; proto_tree_add_item(tree, hf_btavrcp_play_status, tvb, offset, 1, ENC_BIG_ENDIAN); play_status = tvb_get_guint8(tvb, offset); offset += 1; col_append_fstr(pinfo->cinfo, COL_INFO, " PlayStatus: %s, SongPosition: %ums, SongLength: %ums", val_to_str_const(play_status, play_status_vals, "unknown"), song_position, song_length); } break; case PDU_REGISTER_NOTIFICATION: event_id = tvb_get_guint8(tvb, offset); *op_arg = event_id; col_append_fstr(pinfo->cinfo, COL_INFO, " - %s", val_to_str_const(event_id, notification_vals, "Unknown Event ID")); if (is_command) { proto_tree_add_item(tree, hf_btavrcp_event_id, tvb, offset, 1, ENC_BIG_ENDIAN); offset += 1; proto_tree_add_item(tree, hf_btavrcp_notification_interval, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4; } else { guint number_of_settings; guint i_setting; guint64 identifier; guint play_status; guint song_position; guint battery_status; guint uid_counter; guint player_id; guint system_status; proto_tree_add_item(tree, hf_btavrcp_event_id, tvb, offset, 1, ENC_BIG_ENDIAN); offset += 1; if (ctype == 0x0D || ctype == 0x0F) switch(event_id) { case EVENT_PLAYBACK_STATUS_CHANGED: proto_tree_add_item(tree, hf_btavrcp_play_status, tvb, offset, 1, ENC_BIG_ENDIAN); play_status = tvb_get_guint8(tvb, offset); offset += 1; col_append_fstr(pinfo->cinfo, COL_INFO, " - PlayStatus: %s", val_to_str_const(play_status, play_status_vals, "unknown")); break; case EVENT_TRACK_CHANGED: pitem = proto_tree_add_item(tree, hf_btavrcp_identifier, tvb, offset, 8, ENC_BIG_ENDIAN); identifier = tvb_get_ntoh64(tvb, offset); offset += 8; col_append_fstr(pinfo->cinfo, COL_INFO, " - 0x%08X%08X", (guint) (identifier >> 32), (guint) (identifier & 0xFFFFFFFF)); if (identifier == G_GUINT64_CONSTANT(0x0000000000000000)) { col_append_str(pinfo->cinfo, COL_INFO, " (SELECTED)"); proto_item_append_text(pitem, " (SELECTED)"); } else if (identifier == G_GUINT64_CONSTANT(0xFFFFFFFFFFFFFFFF)) { col_append_str(pinfo->cinfo, COL_INFO, " (NOT SELECTED)"); proto_item_append_text(pitem, " (NOT SELECTED)"); } break; case EVENT_TRACK_REACHED_END: /* non */ break; case EVENT_TRACK_REACHED_START: /* non */ break; case EVENT_PLAYBACK_POSITION_CHANGED: pitem = proto_tree_add_item(tree, hf_btavrcp_song_position, tvb, offset, 4, ENC_BIG_ENDIAN); song_position = tvb_get_ntohl(tvb, offset); offset += 4; col_append_fstr(pinfo->cinfo, COL_INFO, " - SongPosition: %ums", song_position); if (song_position == 0xFFFFFFFF) { proto_item_append_text(pitem, " (NOT SELECTED)"); col_append_str(pinfo->cinfo, COL_INFO, " (NOT SELECTED)"); } else if (!pinfo->fd->flags.visited) { btavrcp_song_position_data_t *song_position_data; frame_number = pinfo->num; key[0].length = 1; key[0].key = &avrcp_proto_data->interface_id; key[1].length = 1; key[1].key = &avrcp_proto_data->adapter_id; key[2].length = 1; key[2].key = &frame_number; key[3].length = 0; key[3].key = NULL; song_position_data = wmem_new(wmem_file_scope(), btavrcp_song_position_data_t); song_position_data->song_position = song_position; song_position_data->used_in_frame = 0; wmem_tree_insert32_array(btavrcp_song_positions, key, song_position_data); } break; case EVENT_BATTERY_STATUS_CHANGED: proto_tree_add_item(tree, hf_btavrcp_battery_status, tvb, offset, 1, ENC_BIG_ENDIAN); battery_status = tvb_get_guint8(tvb, offset); offset += 1; col_append_fstr(pinfo->cinfo, COL_INFO, " - Battery: %s", val_to_str_const(battery_status, battery_status_vals, "unknown")); break; case EVENT_SYSTEM_STATUS_CHANGED: proto_tree_add_item(tree, hf_btavrcp_system_status, tvb, offset, 1, ENC_BIG_ENDIAN); system_status = tvb_get_guint8(tvb, offset); offset += 1; col_append_fstr(pinfo->cinfo, COL_INFO, " - SystemStatus: %s", val_to_str_const(system_status, system_status_vals, "unknown")); break; case EVENT_PLAYER_APPLICATION_SETTING_CHANGED: proto_tree_add_item(tree, hf_btavrcp_number_of_settings, tvb, offset, 1, ENC_BIG_ENDIAN); number_of_settings = tvb_get_guint8(tvb, offset); offset += 1; for (i_setting = 0; i_setting < number_of_settings; ++i_setting) { proto_tree_add_item(tree, hf_btavrcp_settings_attribute, tvb, offset, 1, ENC_BIG_ENDIAN); offset += 1; proto_tree_add_item(tree, hf_btavrcp_settings_value, tvb, offset, 1, ENC_BIG_ENDIAN); offset += 1; } break; case EVENT_NOWPLAYING_CONTENT_CHANGED: /* non */ break; case EVENT_AVAILABLE_PLAYERS_CHANGED: /* non */ break; case EVENT_ADDRESSEDPLAYER_CHANGED: proto_tree_add_item(tree, hf_btavrcp_player_id, tvb, offset, 2, ENC_BIG_ENDIAN); player_id = tvb_get_ntohs(tvb, offset); offset += 2; proto_tree_add_item(tree, hf_btavrcp_uid_counter, tvb, offset, 2, ENC_BIG_ENDIAN); uid_counter = tvb_get_ntohs(tvb, offset); offset += 2; col_append_fstr(pinfo->cinfo, COL_INFO, " - PlayerID: %u, UidCounter: 0x%04x", player_id, uid_counter); break; case EVENT_UIDS_CHANGED: proto_tree_add_item(tree, hf_btavrcp_uid_counter, tvb, offset, 2, ENC_BIG_ENDIAN); uid_counter = tvb_get_ntohs(tvb, offset); offset += 2; col_append_fstr(pinfo->cinfo, COL_INFO, " - UidCounter: 0x%04x", uid_counter); break; case EVENT_VOLUME_CHANGED: proto_tree_add_item(tree, hf_btavrcp_absolute_volume_rfa, tvb, offset, 1, ENC_BIG_ENDIAN); pitem = proto_tree_add_item(tree, hf_btavrcp_absolute_volume, tvb, offset, 1, ENC_BIG_ENDIAN); volume = tvb_get_guint8(tvb, offset) & 0x7F; volume_percent = (guint) ((double) volume * 100 / (double) 0x7F); offset += 1; proto_item_append_text(pitem, " (%u%%)", volume_percent); col_append_fstr(pinfo->cinfo, COL_INFO, " - Volume: %u%%", volume_percent); break; default: proto_tree_add_item(tree, hf_btavrcp_data, tvb, offset, tvb_reported_length_remaining(tvb, offset), ENC_NA); offset = tvb_reported_length(tvb); break; } } break; case PDU_REQUEST_CONTINUING_RESPONSE: if (is_command) { guint continuing_op; proto_tree_add_item(tree, hf_btavrcp_bt_continuing_pdu_id, tvb, offset, 1, ENC_BIG_ENDIAN); continuing_op = tvb_get_guint8(tvb, offset) | (company_id << 8); col_append_fstr(pinfo->cinfo, COL_INFO, " - %s", val_to_str_const(tvb_get_guint8(tvb, offset), pdu_id_vals, "Unknown opcode")); offset += 1; if (pinfo->fd->flags.visited == 0) { k_op = continuing_op; frame_number = pinfo->num; key[0].length = 1; key[0].key = &avrcp_proto_data->interface_id; key[1].length = 1; key[1].key = &avrcp_proto_data->adapter_id; key[2].length = 1; key[2].key = &avrcp_proto_data->chandle; key[3].length = 1; key[3].key = &avrcp_proto_data->channel; key[4].length = 1; key[4].key = &k_op; key[5].length = 1; key[5].key = &frame_number; key[6].length = 0; key[6].key = NULL; fragment = (fragment_t *)wmem_tree_lookup32_array_le(reassembling, key); if (fragment && fragment->interface_id == avrcp_proto_data->interface_id && fragment->adapter_id == avrcp_proto_data->adapter_id && fragment->chandle == avrcp_proto_data->chandle && fragment->psm == avrcp_proto_data->channel && fragment->op == continuing_op && fragment->state == 0) { fragment->state = 1; } } *op = continuing_op; } else { /* non */ } break; case PDU_ABORT_CONTINUING_RESPONSE: if (is_command) { guint continuing_op; proto_tree_add_item(tree, hf_btavrcp_bt_continuing_pdu_id, tvb, offset, 1, ENC_BIG_ENDIAN); continuing_op = tvb_get_guint8(tvb, offset) | (company_id << 8); col_append_fstr(pinfo->cinfo, COL_INFO, " - %s", val_to_str_const(tvb_get_guint8(tvb, offset), pdu_id_vals, "Unknown opcode")); offset += 1; if (pinfo->fd->flags.visited == 0) { k_op = continuing_op; frame_number = pinfo->num; key[0].length = 1; key[0].key = &avrcp_proto_data->interface_id; key[1].length = 1; key[1].key = &avrcp_proto_data->adapter_id; key[2].length = 1; key[2].key = &avrcp_proto_data->chandle; key[3].length = 1; key[3].key = &avrcp_proto_data->channel; key[4].length = 1; key[4].key = &k_op; key[5].length = 1; key[5].key = &frame_number; key[6].length = 0; key[6].key = NULL; fragment = (fragment_t *)wmem_tree_lookup32_array_le(reassembling, key); if (fragment && fragment->interface_id == avrcp_proto_data->interface_id && fragment->adapter_id == avrcp_proto_data->adapter_id && fragment->chandle == avrcp_proto_data->chandle && fragment->psm == avrcp_proto_data->channel && fragment->op == continuing_op && fragment->state == 0) { fragment->state = 3; } } } else { /* non */ } break; case PDU_SET_ABSOLUTE_VOLUME: proto_tree_add_item(tree, hf_btavrcp_absolute_volume_rfa, tvb, offset, 1, ENC_BIG_ENDIAN); pitem = proto_tree_add_item(tree, hf_btavrcp_absolute_volume, tvb, offset, 1, ENC_BIG_ENDIAN); volume = tvb_get_guint8(tvb, offset) & 0x7F; volume_percent = (guint) ((double) volume * 100 / (double) 0x7F); offset += 1; proto_item_append_text(pitem, " (%u%%)", volume_percent); col_append_fstr(pinfo->cinfo, COL_INFO, " - Volume: %u%%", volume_percent); break; case PDU_SET_ADDRESSED_PLAYER: if (is_command) { guint player_id; proto_tree_add_item(tree, hf_btavrcp_player_id, tvb, offset, 2, ENC_BIG_ENDIAN); player_id = tvb_get_ntohs(tvb, offset); offset += 2; col_append_fstr(pinfo->cinfo, COL_INFO, " - Player ID: %u", player_id); } else { guint status; proto_tree_add_item(tree, hf_btavrcp_status, tvb, offset, 1, ENC_BIG_ENDIAN); status = tvb_get_guint8(tvb, offset); offset += 1; col_append_fstr(pinfo->cinfo, COL_INFO, " - Status: %s", val_to_str_const(status, status_vals, "Unknown status")); } break; case PDU_PLAY_ITEM: if (is_command) { guint scope; guint64 uid; guint uid_counter; proto_tree_add_item(tree, hf_btavrcp_scope, tvb, offset, 1, ENC_BIG_ENDIAN); scope = tvb_get_guint8(tvb, offset); offset += 1; proto_tree_add_item(tree, hf_btavrcp_uid, tvb, offset, 8, ENC_BIG_ENDIAN); uid = tvb_get_ntoh64(tvb, offset); offset += 8; proto_tree_add_item(tree, hf_btavrcp_uid_counter, tvb, offset, 2, ENC_BIG_ENDIAN); uid_counter = tvb_get_ntohs(tvb, offset); offset += 2; col_append_fstr(pinfo->cinfo, COL_INFO, " - Scope: %s, Uid: 0x%016" G_GINT64_MODIFIER "x, UidCounter: 0x%04x", val_to_str_const(scope, scope_vals, "unknown"), uid, uid_counter); } else { guint status; proto_tree_add_item(tree, hf_btavrcp_status, tvb, offset, 1, ENC_BIG_ENDIAN); status = tvb_get_guint8(tvb, offset); offset += 1; col_append_fstr(pinfo->cinfo, COL_INFO, " - Status: %s", val_to_str_const(status, status_vals, "Unknown status")); } break; case PDU_ADD_TO_NOW_PLAYING: if (is_command) { guint scope; guint64 uid; guint uid_counter; proto_tree_add_item(tree, hf_btavrcp_scope, tvb, offset, 1, ENC_BIG_ENDIAN); scope = tvb_get_guint8(tvb, offset); offset += 1; proto_tree_add_item(tree, hf_btavrcp_uid, tvb, offset, 8, ENC_BIG_ENDIAN); uid = tvb_get_ntoh64(tvb, offset); offset += 8; proto_tree_add_item(tree, hf_btavrcp_uid_counter, tvb, offset, 2, ENC_BIG_ENDIAN); uid_counter = tvb_get_ntohs(tvb, offset); offset += 2; col_append_fstr(pinfo->cinfo, COL_INFO, " - Scope: %s, Uid: 0x%016" G_GINT64_MODIFIER "x, UidCounter: 0x%04x", val_to_str_const(scope, scope_vals, "unknown"), uid, uid_counter); } else { guint status; proto_tree_add_item(tree, hf_btavrcp_status, tvb, offset, 1, ENC_BIG_ENDIAN); status = tvb_get_guint8(tvb, offset); offset += 1; col_append_fstr(pinfo->cinfo, COL_INFO, " - Status: %s", val_to_str_const(status, status_vals, "Unknown status")); } break; }; return offset; } static gint dissect_browsing(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, gint offset, gboolean is_command) { guint pdu_id; guint status = 0x00; proto_tree_add_item(tree, hf_btavrcp_browsing_pdu_id, tvb, offset, 1, ENC_BIG_ENDIAN); pdu_id = tvb_get_guint8(tvb, offset); offset += 1; proto_tree_add_item(tree, hf_btavrcp_length, tvb, offset, 2, ENC_BIG_ENDIAN); offset += 2; if (is_command) { col_append_str(pinfo->cinfo, COL_INFO, ": Command"); } else { /* common code "status" in response */ proto_tree_add_item(tree, hf_btavrcp_status, tvb, offset, 1, ENC_BIG_ENDIAN); status = tvb_get_guint8(tvb, offset); offset += 1; col_append_fstr(pinfo->cinfo, COL_INFO, ": %s", val_to_str_const(status, status_vals, "Unknown status")); } col_append_fstr(pinfo->cinfo, COL_INFO, " - %s", val_to_str_const(pdu_id, browsing_pdu_id_vals, "Unknown opcode")); if (is_command || status == STATUS_OK) switch(pdu_id) { case PDU_SET_BROWSED_PLAYER: if (is_command) { guint player_id; proto_tree_add_item(tree, hf_btavrcp_player_id, tvb, offset, 2, ENC_BIG_ENDIAN); player_id = tvb_get_ntohs(tvb, offset); offset += 2; col_append_fstr(pinfo->cinfo, COL_INFO, " - Player ID: %u", player_id); } else { guint i_folder; guint folder_depth; guint folder_name_length; proto_item *pitem; proto_tree *ptree; const guint8 *folder_name; proto_tree_add_item(tree, hf_btavrcp_uid_counter, tvb, offset, 2, ENC_BIG_ENDIAN); offset += 2; proto_tree_add_item(tree, hf_btavrcp_number_of_items, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4; proto_tree_add_item(tree, hf_btavrcp_character_set, tvb, offset, 2, ENC_BIG_ENDIAN); offset += 2; proto_tree_add_item(tree, hf_btavrcp_folder_depth, tvb, offset, 1, ENC_BIG_ENDIAN); folder_depth = tvb_get_guint8(tvb, offset); offset += 1; pitem = proto_tree_add_none_format(tree, hf_btavrcp_currect_path, tvb, offset, tvb_reported_length_remaining(tvb, offset), "Current Path: /"); col_append_str(pinfo->cinfo, COL_INFO, "Current Path: /"); ptree = proto_item_add_subtree(pitem, ett_btavrcp_path); for (i_folder = 0; i_folder < folder_depth; ++i_folder) { proto_tree_add_item(ptree, hf_btavrcp_folder_name_length, tvb, offset, 2, ENC_BIG_ENDIAN); folder_name_length = tvb_get_ntohs(tvb, offset); offset += 2; proto_tree_add_item_ret_string(ptree, hf_btavrcp_folder_name, tvb, offset, folder_name_length, ENC_ASCII|ENC_NA, wmem_packet_scope(), &folder_name); offset += folder_name_length; proto_item_append_text(pitem, "%s/", folder_name); col_append_fstr(pinfo->cinfo, COL_INFO, "%s/", folder_name); } } break; case PDU_GET_FOLDER_ITEMS: if (is_command) { guint attribute_count; guint scope; guint start_item; guint end_item; proto_tree_add_item(tree, hf_btavrcp_scope, tvb, offset, 1, ENC_BIG_ENDIAN); scope = tvb_get_guint8(tvb, offset); offset += 1; proto_tree_add_item(tree, hf_btavrcp_start_item, tvb, offset, 4, ENC_BIG_ENDIAN); start_item = tvb_get_ntohl(tvb, offset); offset += 4; proto_tree_add_item(tree, hf_btavrcp_end_item, tvb, offset, 4, ENC_BIG_ENDIAN); end_item = tvb_get_ntohl(tvb, offset); offset += 4; proto_tree_add_item(tree, hf_btavrcp_attribute_count, tvb, offset, 1, ENC_BIG_ENDIAN); attribute_count = tvb_get_guint8(tvb, offset); offset += 1; offset = dissect_attribute_id_list(tvb, tree, offset, attribute_count); col_append_fstr(pinfo->cinfo, COL_INFO, " - Scope: %s, StartItem: 0x%04x, EndItem: 0x%04x", val_to_str_const(scope, scope_vals, "unknown"), start_item, end_item); } else { guint number_of_items; guint uid_counter; guint i_item; guint item_type; guint item_length; proto_tree_add_item(tree, hf_btavrcp_uid_counter, tvb, offset, 2, ENC_BIG_ENDIAN); uid_counter = tvb_get_ntohs(tvb, offset); offset += 2; proto_tree_add_item(tree, hf_btavrcp_number_of_items16, tvb, offset, 2, ENC_BIG_ENDIAN); number_of_items = tvb_get_ntohs(tvb, offset); offset += 2; col_append_fstr(pinfo->cinfo, COL_INFO, " - UidCounter: 0x%04x, NumberOfItems: %u", uid_counter, number_of_items); for (i_item = 0; i_item < number_of_items; ++i_item) { item_type = tvb_get_guint8(tvb, offset); item_length = tvb_get_ntohs(tvb, offset + 1); if (item_type == ITEM_MEDIAPLAYER) { dissect_item_mediaplayer(tvb, tree, offset); offset += 1 + 2 + item_length; } else if (item_type == ITEM_MEDIA_ELEMENT) { dissect_item_media_element(tvb, pinfo, tree, offset); offset += 1 + 2 + item_length; } else if (item_type == ITEM_FOLDER) { dissect_item_folder(tvb, tree, offset); offset += 1 + 2 + item_length; } else { proto_tree_add_item(tree, hf_btavrcp_data, tvb, offset, item_length, ENC_NA); offset += 1 + 2 + item_length; } } } break; case PDU_CHANGE_PATH: if (is_command) { guint64 uid; guint uid_counter; guint direction; proto_tree_add_item(tree, hf_btavrcp_uid_counter, tvb, offset, 2, ENC_BIG_ENDIAN); uid_counter = tvb_get_ntohs(tvb, offset); offset += 2; proto_tree_add_item(tree, hf_btavrcp_direction, tvb, offset, 1, ENC_BIG_ENDIAN); direction = tvb_get_guint8(tvb, offset); offset += 1; proto_tree_add_item(tree, hf_btavrcp_uid, tvb, offset, 8, ENC_BIG_ENDIAN); uid = tvb_get_ntoh64(tvb, offset); offset += 8; col_append_fstr(pinfo->cinfo, COL_INFO, " - Direction: %s, Uid: 0x%016" G_GINT64_MODIFIER "x, UidCounter: 0x%04x", val_to_str_const(direction, direction_vals, "unknown"), uid, uid_counter); } else { guint number_of_items; proto_tree_add_item(tree, hf_btavrcp_number_of_items, tvb, offset, 4, ENC_BIG_ENDIAN); number_of_items = tvb_get_ntohl(tvb, offset); offset += 4; col_append_fstr(pinfo->cinfo, COL_INFO, " - NumberOfItems: %u", number_of_items); } break; case PDU_GET_ITEM_ATTRIBUTES: if (is_command) { guint number_of_attributes; guint64 uid; guint uid_counter; guint scope; proto_tree_add_item(tree, hf_btavrcp_scope, tvb, offset, 1, ENC_BIG_ENDIAN); scope = tvb_get_guint8(tvb, offset); offset += 1; proto_tree_add_item(tree, hf_btavrcp_uid, tvb, offset, 8, ENC_BIG_ENDIAN); uid = tvb_get_ntoh64(tvb, offset); offset += 8; proto_tree_add_item(tree, hf_btavrcp_uid_counter, tvb, offset, 2, ENC_BIG_ENDIAN); uid_counter = tvb_get_ntohs(tvb, offset); offset += 2; proto_tree_add_item(tree, hf_btavrcp_number_of_attributes, tvb, offset, 1, ENC_BIG_ENDIAN); number_of_attributes = tvb_get_guint8(tvb, offset); col_append_fstr(pinfo->cinfo, COL_INFO, " - Scope: %s, Uid: 0x%016" G_GINT64_MODIFIER "x, UidCounter: 0x%04x", val_to_str_const(scope, scope_vals, "unknown"), uid, uid_counter); offset += 1; offset = dissect_attribute_id_list(tvb, tree, offset, number_of_attributes); } else { guint number_of_attributes; proto_tree_add_item(tree, hf_btavrcp_number_of_attributes, tvb, offset, 1, ENC_BIG_ENDIAN); number_of_attributes = tvb_get_guint8(tvb, offset); offset += 1; offset = dissect_attribute_entries(tvb, pinfo, tree, offset, number_of_attributes); } break; case PDU_SEARCH: if (is_command) { guint search_length; proto_tree_add_item(tree, hf_btavrcp_character_set, tvb, offset, 2, ENC_BIG_ENDIAN); offset += 2; proto_tree_add_item(tree, hf_btavrcp_search_length, tvb, offset, 2, ENC_BIG_ENDIAN); search_length = tvb_get_ntohs(tvb, offset); offset += 2; proto_tree_add_item(tree, hf_btavrcp_search, tvb, offset, search_length, ENC_NA); offset += search_length; } else { proto_tree_add_item(tree, hf_btavrcp_uid_counter, tvb, offset, 2, ENC_BIG_ENDIAN); offset += 2; proto_tree_add_item(tree, hf_btavrcp_number_of_items, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4; } break; case PDU_GENERAL_REJECT: /* implemented in common code before switch() */ break; }; return offset; } static gint dissect_btavrcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data) { proto_item *ti; proto_tree *btavrcp_tree; proto_item *pitem = NULL; gint offset = 0; guint32 opcode; guint32 op = 0; guint32 op_arg = 0; guint ctype; guint response_time; guint max_response_time; guint is_command; timing_info_t *timing_info; wmem_tree_key_t key[9]; guint32 k_op_arg; guint32 frame_number; gint previous_proto; avrcp_proto_data_t avrcp_proto_data; previous_proto = (GPOINTER_TO_INT(wmem_list_frame_data(wmem_list_frame_prev(wmem_list_tail(pinfo->layers))))); if (previous_proto == proto_btavctp) { btavctp_data_t *avctp_data; avctp_data = (btavctp_data_t *) data; avrcp_proto_data.interface_id = avctp_data->interface_id; avrcp_proto_data.adapter_id = avctp_data->adapter_id; avrcp_proto_data.chandle = avctp_data->chandle; avrcp_proto_data.channel = avctp_data->psm; is_command = !avctp_data->cr; } else { avrcp_proto_data.interface_id = HCI_INTERFACE_DEFAULT; avrcp_proto_data.adapter_id = HCI_ADAPTER_DEFAULT; avrcp_proto_data.chandle = 0; avrcp_proto_data.channel = 0; /* NOTE: There is need to allow user specify that */ is_command = (pinfo->p2p_dir == P2P_DIR_SENT); } ti = proto_tree_add_item(tree, proto_btavrcp, tvb, offset, tvb_captured_length_remaining(tvb, offset), ENC_NA); btavrcp_tree = proto_item_add_subtree(ti, ett_btavrcp); col_set_str(pinfo->cinfo, COL_PROTOCOL, "AVRCP"); switch (pinfo->p2p_dir) { case P2P_DIR_SENT: col_set_str(pinfo->cinfo, COL_INFO, "Sent "); break; case P2P_DIR_RECV: col_set_str(pinfo->cinfo, COL_INFO, "Rcvd "); break; default: col_set_str(pinfo->cinfo, COL_INFO, "UnknownDirection "); break; } if (avrcp_proto_data.channel == BTL2CAP_PSM_AVCTP_BRWS) { col_append_str(pinfo->cinfo, COL_INFO, "Browsing"); offset = dissect_browsing(tvb, pinfo, btavrcp_tree, offset, is_command); } else { proto_tree_add_item(btavrcp_tree, hf_btavrcp_reserved, tvb, offset, 1, ENC_BIG_ENDIAN); proto_tree_add_item(btavrcp_tree, hf_btavrcp_ctype, tvb, offset, 1, ENC_BIG_ENDIAN); ctype = tvb_get_guint8(tvb, offset) & 0x0F; offset += 1; proto_tree_add_item(btavrcp_tree, hf_btavrcp_subunit_type, tvb, offset, 1, ENC_BIG_ENDIAN); proto_tree_add_item(btavrcp_tree, hf_btavrcp_subunit_id, tvb, offset, 1, ENC_BIG_ENDIAN); offset += 1; proto_tree_add_item(btavrcp_tree, hf_btavrcp_opcode, tvb, offset, 1, ENC_BIG_ENDIAN); opcode = tvb_get_guint8(tvb, offset); offset += 1; col_append_fstr(pinfo->cinfo, COL_INFO, "%s: %s", val_to_str_const(opcode, opcode_vals, "Unknown opcode"), val_to_str_const(ctype, ctype_vals, "Unknown ctype")); switch(opcode) { case OPCODE_PASSTHROUGH: offset = dissect_passthrough(tvb, pinfo, btavrcp_tree, offset, is_command, &op); break; case OPCODE_UNIT: offset = dissect_unit(tvb, btavrcp_tree, offset, is_command); break; case OPCODE_SUBUNIT: offset = dissect_subunit(tvb, btavrcp_tree, offset, is_command); break; case OPCODE_VENDOR_DEPENDENT: offset = dissect_vendor_dependent(tvb, pinfo, btavrcp_tree, offset, ctype, &op, &op_arg, is_command, &avrcp_proto_data); break; }; k_op_arg = (ctype == 0x0a) ? G_MAXUINT32 : op_arg; frame_number = pinfo->num; key[0].length = 1; key[0].key = &avrcp_proto_data.interface_id; key[1].length = 1; key[1].key = &avrcp_proto_data.adapter_id; key[2].length = 1; key[2].key = &avrcp_proto_data.chandle; key[3].length = 1; key[3].key = &avrcp_proto_data.channel; key[4].length = 1; key[4].key = &opcode; key[5].length = 1; key[5].key = &op; key[6].length = 1; key[6].key = &k_op_arg; key[7].length = 1; key[7].key = &frame_number; key[8].length = 0; key[8].key = NULL; if (pinfo->fd->flags.visited == 0) { if (is_command) { if (ctype == 0x00) { /* MTC is for CONTROL */ max_response_time = 200; } else if (ctype == 0x01 || ctype == 0x03) { /* MTP is for STATUS, NOTIFY */ max_response_time = 1000; } else { /* RCP */ max_response_time = 100; } timing_info = wmem_new(wmem_file_scope(), timing_info_t); timing_info->command_frame_number = pinfo->num; timing_info->command_timestamp = pinfo->abs_ts; timing_info->response_frame_number = 0; timing_info->response_timestamp.secs = 0; timing_info->response_timestamp.nsecs = 0; timing_info->max_response_time = max_response_time; timing_info->interface_id = avrcp_proto_data.interface_id; timing_info->adapter_id = avrcp_proto_data.adapter_id; timing_info->chandle = avrcp_proto_data.chandle; timing_info->psm = avrcp_proto_data.channel; timing_info->opcode = opcode; timing_info->op = op; timing_info->op_arg = op_arg; timing_info->used = 0; wmem_tree_insert32_array(timing, key, timing_info); } else { timing_info = (timing_info_t *)wmem_tree_lookup32_array_le(timing, key); if (timing_info && timing_info->interface_id == avrcp_proto_data.interface_id && timing_info->adapter_id == avrcp_proto_data.adapter_id && timing_info->chandle == avrcp_proto_data.chandle && timing_info->psm == avrcp_proto_data.channel && timing_info->opcode == opcode && timing_info->op == op && ((ctype == 0x0a) ? 1 : (timing_info->op_arg == op_arg)) && timing_info->used == 0) { timing_info->response_frame_number = pinfo->num; timing_info->response_timestamp = pinfo->abs_ts; timing_info->used = 1; } } k_op_arg = (ctype == 0x0a) ? G_MAXUINT32 : op_arg; frame_number = pinfo->num; key[0].length = 1; key[0].key = &avrcp_proto_data.interface_id; key[1].length = 1; key[1].key = &avrcp_proto_data.adapter_id; key[2].length = 1; key[2].key = &avrcp_proto_data.chandle; key[3].length = 1; key[3].key = &avrcp_proto_data.channel; key[4].length = 1; key[4].key = &opcode; key[5].length = 1; key[5].key = &op; key[6].length = 1; key[6].key = &k_op_arg; key[7].length = 1; key[7].key = &frame_number; key[8].length = 0; key[8].key = NULL; } timing_info = (timing_info_t *)wmem_tree_lookup32_array_le(timing, key); if (timing_info && timing_info->interface_id == avrcp_proto_data.interface_id && timing_info->adapter_id == avrcp_proto_data.adapter_id && timing_info->chandle == avrcp_proto_data.chandle && timing_info->psm == avrcp_proto_data.channel && timing_info->opcode == opcode && timing_info->op == op && ((ctype == 0x0a) ? 1 : (timing_info->op_arg == op_arg))) { if (timing_info->command_timestamp.nsecs > timing_info->response_timestamp.nsecs) { response_time = timing_info->response_timestamp.nsecs + (1000000000 - timing_info->command_timestamp.nsecs); response_time /= 1000000; response_time += ((guint)timing_info->response_timestamp.secs - 1 - (guint)timing_info->command_timestamp.secs) / 1000; } else { response_time = timing_info->response_timestamp.nsecs - timing_info->command_timestamp.nsecs; response_time /= 1000000; response_time += ((guint)timing_info->response_timestamp.secs - (guint)timing_info->command_timestamp.secs) / 1000; } if (timing_info->response_frame_number == 0) { response_time = UINT_MAX; } pitem = proto_tree_add_uint(btavrcp_tree, hf_btavrcp_response_time, tvb, 0, 0, response_time); proto_item_append_text(pitem, "/%ums", timing_info->max_response_time); if (response_time > timing_info->max_response_time) { proto_item_append_text(pitem, "; TIME EXCEEDED"); } PROTO_ITEM_SET_GENERATED(pitem); if (timing_info->response_frame_number == 0) { pitem = proto_tree_add_expert(btavrcp_tree, pinfo, &ei_btavrcp_no_response, tvb, 0, 0); PROTO_ITEM_SET_GENERATED(pitem); } else { if (is_command) { pitem = proto_tree_add_uint(btavrcp_tree, hf_btavrcp_response_in_frame, tvb, 0, 0, timing_info->response_frame_number); PROTO_ITEM_SET_GENERATED(pitem); } else { pitem = proto_tree_add_uint(btavrcp_tree, hf_btavrcp_command_in_frame, tvb, 0, 0, timing_info->command_frame_number); PROTO_ITEM_SET_GENERATED(pitem); } } } } if (tvb_reported_length_remaining(tvb, offset) > 0) { pitem = proto_tree_add_item(btavrcp_tree, hf_btavrcp_data, tvb, offset, tvb_reported_length_remaining(tvb, offset), ENC_NA); expert_add_info(pinfo, pitem, &ei_btavrcp_unexpected_data); } return offset; } void proto_register_btavrcp(void) { module_t *module; expert_module_t* expert_btavrcp; static hf_register_info hf[] = { { &hf_btavrcp_reserved, { "Reserved", "btavrcp.reserved", FT_UINT8, BASE_HEX, NULL, 0xF0, NULL, HFILL } }, { &hf_btavrcp_ctype, { "Ctype", "btavrcp.ctype", FT_UINT8, BASE_HEX, VALS(ctype_vals), 0x0F, NULL, HFILL } }, { &hf_btavrcp_subunit_type, { "Subunit Type", "btavrcp.subunit_type", FT_UINT8, BASE_HEX, VALS(subunit_type_vals), 0xF8, NULL, HFILL } }, { &hf_btavrcp_subunit_id, { "Subunit ID", "btavrcp.subunit_id", FT_UINT8, BASE_HEX, NULL, 0x07, NULL, HFILL } }, { &hf_btavrcp_opcode, { "Opcode", "btavrcp.opcode", FT_UINT8, BASE_HEX, VALS(opcode_vals), 0x00, NULL, HFILL } }, { &hf_btavrcp_company_id, { "Company ID", "btavrcp.company_id", FT_UINT24, BASE_HEX, VALS(oui_vals), 0x00, NULL, HFILL } }, { &hf_btavrcp_passthrough_state, { "State", "btavrcp.passthrough.state", FT_UINT8, BASE_HEX, VALS(passthrough_state_vals), 0x80, NULL, HFILL } }, { &hf_btavrcp_passthrough_operation, { "Operation ID", "btavrcp.passthrough.operation", FT_UINT8, BASE_HEX, VALS(passthrough_operation_vals), 0x7F, NULL, HFILL } }, { &hf_btavrcp_passthrough_data_length, { "Data Length", "btavrcp.passthrough.length", FT_UINT8, BASE_HEX, NULL, 0x00, NULL, HFILL } }, { &hf_btavrcp_passthrough_vendor_unique_id, { "Vendor Unique ID", "btavrcp.passthrough.vendor_unique_id", FT_UINT16, BASE_HEX, VALS(vendor_unique_id_vals), 0x00, NULL, HFILL } }, { &hf_btavrcp_passthrough_company_id, { "Company ID", "btavrcp.passthrough.company_id", FT_UINT24, BASE_HEX, VALS(oui_vals), 0x00, NULL, HFILL } }, { &hf_btavrcp_unit_unknown, { "Unknown", "btavrcp.unit.unknown", FT_UINT8, BASE_HEX, NULL, 0xFF, NULL, HFILL } }, { &hf_btavrcp_unit_type, { "Unit Type", "btavrcp.unit.type", FT_UINT8, BASE_HEX, VALS(subunit_type_vals), 0xF8, NULL, HFILL } }, { &hf_btavrcp_unit_id, { "Subunit ID", "btavrcp.unit.id", FT_UINT8, BASE_HEX, NULL, 0x07, NULL, HFILL } }, { &hf_btavrcp_subunit_page, { "Page", "btavrcp.subunit.page", FT_UINT8, BASE_HEX, VALS(subunit_type_vals), 0xF8, NULL, HFILL } }, { &hf_btavrcp_subunit_extension_code, { "Extension Code", "btavrcp.subunit.extension_code", FT_UINT8, BASE_HEX, NULL, 0x07, NULL, HFILL } }, { &hf_btavrcp_bt_pdu_id, { "PDU ID", "btavrcp.pdu_id", FT_UINT8, BASE_HEX, VALS(pdu_id_vals), 0x00, NULL, HFILL } }, { &hf_btavrcp_pdu_id, { "PDU ID", "btavrcp.pdu_id", FT_UINT8, BASE_HEX, NULL, 0x00, NULL, HFILL } }, { &hf_btavrcp_bt_continuing_pdu_id, { "Continuing PDU ID", "btavrcp.pdu_id", FT_UINT8, BASE_HEX, VALS(pdu_id_vals), 0x00, NULL, HFILL } }, { &hf_btavrcp_browsing_pdu_id, { "PDU ID", "btavrcp.pdu_id", FT_UINT8, BASE_HEX, VALS(browsing_pdu_id_vals), 0x00, NULL, HFILL } }, { &hf_btavrcp_rfa, { "RFA", "btavrcp.rfa", FT_UINT8, BASE_HEX, NULL, 0xFC, NULL, HFILL } }, { &hf_btavrcp_packet_type, { "Packet Type", "btavrcp.packet_type", FT_UINT8, BASE_HEX, VALS(packet_type_vals), 0x03, NULL, HFILL } }, { &hf_btavrcp_length, { "Parameter Length", "btavrcp.length", FT_UINT16, BASE_DEC, NULL, 0x00, NULL, HFILL } }, { &hf_btavrcp_player_id, { "Player ID", "btavrcp.player_id", FT_UINT16, BASE_DEC, NULL, 0x00, NULL, HFILL } }, { &hf_btavrcp_status, { "Status", "btavrcp.status", FT_UINT8, BASE_HEX, VALS(status_vals), 0x00, NULL, HFILL } }, { &hf_btavrcp_uid_counter, { "UID Counter", "btavrcp.uid_counter", FT_UINT16, BASE_HEX, NULL, 0x00, NULL, HFILL } }, { &hf_btavrcp_character_set, { "Character Set", "btavrcp.character_set", FT_UINT16, BASE_DEC|BASE_EXT_STRING, &mibenum_vals_character_sets_ext, 0x00, NULL, HFILL } }, { &hf_btavrcp_number_of_items, { "Number Of Items", "btavrcp.number_of_items", FT_UINT32, BASE_DEC, NULL, 0x00, "32bit number of items", HFILL } }, { &hf_btavrcp_number_of_items16, { "Number Of Items", "btavrcp.number_of_items", FT_UINT16, BASE_DEC, NULL, 0x00, "16bit number of items", HFILL } }, { &hf_btavrcp_folder_depth, { "Folder Depth", "btavrcp.folder_depth", FT_UINT8, BASE_DEC, NULL, 0x00, NULL, HFILL } }, { &hf_btavrcp_folder_name_length, { "Folder Name Length", "btavrcp.folder_name_length", FT_UINT16, BASE_DEC, NULL, 0x00, NULL, HFILL } }, { &hf_btavrcp_folder_name, { "Folder Name", "btavrcp.folder_name", FT_STRING, BASE_NONE, NULL, 0x00, NULL, HFILL } }, { &hf_btavrcp_search_length, { "Search String Length", "btavrcp.search_length", FT_UINT16, BASE_DEC, NULL, 0x00, NULL, HFILL } }, { &hf_btavrcp_search, { "Search String", "btavrcp.search", FT_NONE, BASE_NONE, NULL, 0x00, NULL, HFILL } }, { &hf_btavrcp_number_of_attributes, { "Number of Attributes", "btavrcp.number_of_attributes", FT_UINT8, BASE_DEC|BASE_SPECIAL_VALS, VALS(unique_all_supported_attributes), 0x00, NULL, HFILL } }, { &hf_btavrcp_attribute_count, { "Attribute Count", "btavrcp.attribute_count", FT_UINT8, BASE_DEC, VALS(attribute_count_vals), 0x00, NULL, HFILL } }, { &hf_btavrcp_direction, { "Direction", "btavrcp.direction", FT_UINT8, BASE_HEX, VALS(direction_vals), 0x00, NULL, HFILL } }, { &hf_btavrcp_scope, { "Scope", "btavrcp.scope", FT_UINT8, BASE_HEX, VALS(scope_vals), 0x00, NULL, HFILL } }, { &hf_btavrcp_start_item, { "StartItem", "btavrcp.start_item", FT_UINT32, BASE_DEC, NULL, 0x00, NULL, HFILL } }, { &hf_btavrcp_end_item, { "EndItem", "btavrcp.end_item", FT_UINT32, BASE_DEC, NULL, 0x00, NULL, HFILL } }, { &hf_btavrcp_uid, { "UID", "btavrcp.uid", FT_UINT64, BASE_HEX, NULL, 0x00, NULL, HFILL } }, { &hf_btavrcp_identifier, { "Identifier", "btavrcp.identifier", FT_UINT64, BASE_HEX, NULL, 0x00, NULL, HFILL } }, { &hf_btavrcp_play_status, { "Play Status", "btavrcp.play_status", FT_UINT8, BASE_HEX, VALS(play_status_vals), 0x00, NULL, HFILL } }, { &hf_btavrcp_song_length, { "Song Length", "btavrcp.song_length", FT_UINT32, BASE_DEC, NULL, 0x00, NULL, HFILL } }, { &hf_btavrcp_song_position, { "Song Position", "btavrcp.song_position", FT_UINT32, BASE_DEC, NULL, 0x00, NULL, HFILL } }, { &hf_btavrcp_notification_interval, { "Interval", "btavrcp.notification.interval", FT_UINT32, BASE_DEC, NULL, 0x00, NULL, HFILL } }, { &hf_btavrcp_event_id, { "Event ID", "btavrcp.notification.event_id", FT_UINT8, BASE_HEX, VALS(notification_vals), 0x00, NULL, HFILL } }, { &hf_btavrcp_battery_status, { "Battery Status", "btavrcp.battery_status", FT_UINT8, BASE_HEX, VALS(battery_status_vals), 0x00, NULL, HFILL } }, { &hf_btavrcp_number_of_character_set, { "Number of Character Set", "btavrcp.number_of_character_set", FT_UINT8, BASE_DEC, NULL, 0x00, NULL, HFILL } }, { &hf_btavrcp_absolute_volume_rfa, { "RFA", "btavrcp.absoluter_volume_rfa", FT_UINT8, BASE_HEX, NULL, 0x80, NULL, HFILL } }, { &hf_btavrcp_absolute_volume, { "Volume", "btavrcp.volume", FT_UINT8, BASE_HEX, NULL, 0x7F, NULL, HFILL } }, { &hf_btavrcp_capability, { "Capability", "btavrcp.capability", FT_UINT8, BASE_HEX, VALS(capability_vals), 0x00, NULL, HFILL } }, { &hf_btavrcp_capability_count, { "Capability Count", "btavrcp.capability.count", FT_UINT8, BASE_HEX, NULL, 0x00, NULL, HFILL } }, { &hf_btavrcp_item_type, { "Item Type", "btavrcp.item.type", FT_UINT8, BASE_HEX, VALS(item_type_vals), 0x00, NULL, HFILL } }, { &hf_btavrcp_item_length, { "Item Length", "btavrcp.item.length", FT_UINT16, BASE_DEC, NULL, 0x00, NULL, HFILL } }, { &hf_btavrcp_number_of_settings, { "Number of Settings", "btavrcp.number_of_settings", FT_UINT8, BASE_DEC, NULL, 0x00, NULL, HFILL } }, { &hf_btavrcp_settings_attribute, { "Setting Attribute", "btavrcp.settings.attribute", FT_UINT8, BASE_HEX, VALS(settings_vals), 0x00, NULL, HFILL } }, { &hf_btavrcp_settings_value, { "Setting Value", "btavrcp.settings.value", FT_UINT8, BASE_HEX, NULL, 0x00, NULL, HFILL } }, { &hf_btavrcp_system_status, { "System Status", "btavrcp.system_status", FT_UINT8, BASE_HEX, VALS(system_status_vals), 0x00, NULL, HFILL } }, { &hf_btavrcp_player_item, { "Player", "btavrcp.player_item", FT_NONE, BASE_NONE, NULL, 0x00, NULL, HFILL } }, { &hf_btavrcp_folder, { "Folder", "btavrcp.folder", FT_NONE, BASE_NONE, NULL, 0x00, NULL, HFILL } }, { &hf_btavrcp_item, { "Item", "btavrcp.item", FT_NONE, BASE_NONE, NULL, 0x00, NULL, HFILL } }, { &hf_btavrcp_features, { "Features", "btavrcp.features", FT_NONE, BASE_NONE, NULL, 0x00, NULL, HFILL } }, { &hf_btavrcp_not_used_features, { "Not Used Features", "btavrcp.not_used_features", FT_NONE, BASE_NONE, NULL, 0x00, NULL, HFILL } }, { &hf_btavrcp_attribute_list, { "Attribute List", "btavrcp.attribute_list", FT_NONE, BASE_NONE, NULL, 0x00, NULL, HFILL } }, { &hf_btavrcp_attribute_item, { "Attribute", "btavrcp.attribute_item", FT_NONE, BASE_NONE, NULL, 0x00, NULL, HFILL } }, { &hf_btavrcp_attribute_entries, { "Attribute Entries", "btavrcp.attribute_entries", FT_NONE, BASE_NONE, NULL, 0x00, NULL, HFILL } }, { &hf_btavrcp_attribute, { "Attribute ID", "btavrcp.attribute", FT_UINT32, BASE_HEX, VALS(attribute_id_vals), 0x00, NULL, HFILL } }, { &hf_btavrcp_attribute_value_length, { "Value Length", "btavrcp.value.length", FT_UINT16, BASE_DEC, NULL, 0x00, NULL, HFILL } }, { &hf_btavrcp_attribute_value, { "Value", "btavrcp.value", FT_STRING, BASE_NONE, NULL, 0x00, NULL, HFILL } }, { &hf_btavrcp_displayable_name_length, { "Displayable Name Length", "btavrcp.displayable_name_length", FT_UINT16, BASE_DEC, NULL, 0x00, NULL, HFILL } }, { &hf_btavrcp_displayable_name, { "Displayable Name", "btavrcp.displayable_name", FT_STRING, BASE_NONE, NULL, 0x00, NULL, HFILL } }, { &hf_btavrcp_media_type, { "Media Type", "btavrcp.media_type", FT_UINT8, BASE_HEX, VALS(media_type_vals), 0x00, NULL, HFILL } }, { &hf_btavrcp_folder_type, { "Folder Type", "btavrcp.folder_type", FT_UINT8, BASE_HEX, VALS(folder_type_vals), 0x00, NULL, HFILL } }, { &hf_btavrcp_folder_playable, { "Folder Playable", "btavrcp.folder_playable", FT_UINT8, BASE_HEX, VALS(folder_playable_vals), 0x00, NULL, HFILL } }, { &hf_btavrcp_major_player_type, { "Major Player Type", "btavrcp.major_player_type", FT_UINT8, BASE_HEX, VALS(major_player_type_vals), 0x00, NULL, HFILL } }, { &hf_btavrcp_player_subtype, { "Player SubType", "btavrcp.player_subtype", FT_UINT8, BASE_HEX, VALS(player_subtype_vals), 0x00, NULL, HFILL } }, { &hf_btavrcp_setting_value_length, { "Value Length", "btavrcp.setting_value.length", FT_UINT8, BASE_DEC, NULL, 0x00, NULL, HFILL } }, { &hf_btavrcp_setting_value, { "Value", "btavrcp.setting_value", FT_STRING, BASE_NONE, NULL, 0x00, NULL, HFILL } }, { &hf_btavrcp_attribute_name_length, { "Value Length", "btavrcp.attribute_name.length", FT_UINT8, BASE_DEC, NULL, 0x00, NULL, HFILL } }, { &hf_btavrcp_attribute_name, { "Value", "btavrcp.attribute_name", FT_STRING, BASE_NONE, NULL, 0x00, NULL, HFILL } }, /* features */ { &hf_btavrcp_feature_reserved_0, { "Feature Reserved", "btavrcp.feature.reserved", FT_UINT8, BASE_HEX, NULL, 0x01, NULL, HFILL } }, { &hf_btavrcp_feature_reserved_1, { "Feature Reserved", "btavrcp.feature.reserved", FT_UINT8, BASE_HEX, NULL, 0x02, NULL, HFILL } }, { &hf_btavrcp_feature_reserved_2, { "Feature Reserved", "btavrcp.feature.reserved", FT_UINT8, BASE_HEX, NULL, 0x04, NULL, HFILL } }, { &hf_btavrcp_feature_reserved_3, { "Feature Reserved", "btavrcp.feature.reserved", FT_UINT8, BASE_HEX, NULL, 0x08, NULL, HFILL } }, { &hf_btavrcp_feature_reserved_4, { "Feature Reserved", "btavrcp.feature.reserved", FT_UINT8, BASE_HEX, NULL, 0x10, NULL, HFILL } }, { &hf_btavrcp_feature_reserved_5, { "Feature Reserved", "btavrcp.feature.reserved", FT_UINT8, BASE_HEX, NULL, 0x20, NULL, HFILL } }, { &hf_btavrcp_feature_reserved_6, { "Feature Reserved", "btavrcp.feature.reserved", FT_UINT8, BASE_HEX, NULL, 0x40, NULL, HFILL } }, { &hf_btavrcp_feature_reserved_7, { "Feature Reserved", "btavrcp.feature.reserved", FT_UINT8, BASE_HEX, NULL, 0x80, NULL, HFILL } }, { &hf_btavrcp_feature_passthrough_select, { "PASSTHROUGH Select", "btavrcp.feature.passthrough.select", FT_UINT8, BASE_HEX, NULL, 0x01, NULL, HFILL } }, { &hf_btavrcp_feature_passthrough_up, { "PASSTHROUGH Up", "btavrcp.feature.passthrough.up", FT_UINT8, BASE_HEX, NULL, 0x02, NULL, HFILL } }, { &hf_btavrcp_feature_passthrough_down, { "PASSTHROUGH Down", "btavrcp.feature.passthrough.down", FT_UINT8, BASE_HEX, NULL, 0x04, NULL, HFILL } }, { &hf_btavrcp_feature_passthrough_left, { "PASSTHROUGH Left", "btavrcp.feature.passthrough.left", FT_UINT8, BASE_HEX, NULL, 0x08, NULL, HFILL } }, { &hf_btavrcp_feature_passthrough_right, { "PASSTHROUGH Right", "btavrcp.feature.passthrough.right", FT_UINT8, BASE_HEX, NULL, 0x10, NULL, HFILL } }, { &hf_btavrcp_feature_passthrough_right_up, { "PASSTHROUGH Right Up", "btavrcp.feature.passthrough.right_up", FT_UINT8, BASE_HEX, NULL, 0x20, NULL, HFILL } }, { &hf_btavrcp_feature_passthrough_right_down, { "PASSTHROUGH Right Down", "btavrcp.feature.passthrough.right_down", FT_UINT8, BASE_HEX, NULL, 0x40, NULL, HFILL } }, { &hf_btavrcp_feature_passthrough_left_up, { "PASSTHROUGH Left Up", "btavrcp.feature.passthrough.left_up", FT_UINT8, BASE_HEX, NULL, 0x80, NULL, HFILL } }, { &hf_btavrcp_feature_passthrough_left_down, { "PASSTHROUGH Left Down", "btavrcp.feature.passthrough.left_down", FT_UINT8, BASE_HEX, NULL, 0x01, NULL, HFILL } }, { &hf_btavrcp_feature_passthrough_root_menu, { "PASSTHROUGH Root Menu", "btavrcp.feature.passthrough.root_menu", FT_UINT8, BASE_HEX, NULL, 0x02, NULL, HFILL } }, { &hf_btavrcp_feature_passthrough_setup_menu, { "PASSTHROUGH Setup Menu", "btavrcp.feature.passthrough.setup_menu", FT_UINT8, BASE_HEX, NULL, 0x04, NULL, HFILL } }, { &hf_btavrcp_feature_passthrough_contents_menu, { "PASSTHROUGH Contents Menu", "btavrcp.feature.passthrough.contents_menu", FT_UINT8, BASE_HEX, NULL, 0x08, NULL, HFILL } }, { &hf_btavrcp_feature_passthrough_favorite_menu, { "PASSTHROUGH Favorite Menu", "btavrcp.feature.passthrough.favorite_menu", FT_UINT8, BASE_HEX, NULL, 0x10, NULL, HFILL } }, { &hf_btavrcp_feature_passthrough_exit, { "PASSTHROUGH Exit", "btavrcp.feature.passthrough.exit", FT_UINT8, BASE_HEX, NULL, 0x20, NULL, HFILL } }, { &hf_btavrcp_feature_passthrough_0, { "PASSTHROUGH 0", "btavrcp.feature.passthrough.0", FT_UINT8, BASE_HEX, NULL, 0x40, NULL, HFILL } }, { &hf_btavrcp_feature_passthrough_1, { "PASSTHROUGH 1", "btavrcp.feature.passthrough.1", FT_UINT8, BASE_HEX, NULL, 0x80, NULL, HFILL } }, { &hf_btavrcp_feature_passthrough_2, { "PASSTHROUGH 2", "btavrcp.feature.passthrough.2", FT_UINT8, BASE_HEX, NULL, 0x01, NULL, HFILL } }, { &hf_btavrcp_feature_passthrough_3, { "PASSTHROUGH 3", "btavrcp.feature.passthrough.3", FT_UINT8, BASE_HEX, NULL, 0x02, NULL, HFILL } }, { &hf_btavrcp_feature_passthrough_4, { "PASSTHROUGH 4", "btavrcp.feature.passthrough.4", FT_UINT8, BASE_HEX, NULL, 0x04, NULL, HFILL } }, { &hf_btavrcp_feature_passthrough_5, { "PASSTHROUGH 5", "btavrcp.feature.passthrough.5", FT_UINT8, BASE_HEX, NULL, 0x08, NULL, HFILL } }, { &hf_btavrcp_feature_passthrough_6, { "PASSTHROUGH 6", "btavrcp.feature.passthrough.6", FT_UINT8, BASE_HEX, NULL, 0x10, NULL, HFILL } }, { &hf_btavrcp_feature_passthrough_7, { "PASSTHROUGH 7", "btavrcp.feature.passthrough.7", FT_UINT8, BASE_HEX, NULL, 0x20, NULL, HFILL } }, { &hf_btavrcp_feature_passthrough_8, { "PASSTHROUGH 8", "btavrcp.feature.passthrough.8", FT_UINT8, BASE_HEX, NULL, 0x40, NULL, HFILL } }, { &hf_btavrcp_feature_passthrough_9, { "PASSTHROUGH 9", "btavrcp.feature.passthrough.9", FT_UINT8, BASE_HEX, NULL, 0x80, NULL, HFILL } }, { &hf_btavrcp_feature_passthrough_dot, { "PASSTHROUGH Dot", "btavrcp.feature.passthrough.dot", FT_UINT8, BASE_HEX, NULL, 0x01, NULL, HFILL } }, { &hf_btavrcp_feature_passthrough_enter, { "PASSTHROUGH Enter", "btavrcp.feature.passthrough.enter", FT_UINT8, BASE_HEX, NULL, 0x02, NULL, HFILL } }, { &hf_btavrcp_feature_passthrough_clear, { "PASSTHROUGH Clear", "btavrcp.feature.passthrough.clear", FT_UINT8, BASE_HEX, NULL, 0x04, NULL, HFILL } }, { &hf_btavrcp_feature_passthrough_channel_up, { "PASSTHROUGH Channel Up", "btavrcp.feature.passthrough.channel_up", FT_UINT8, BASE_HEX, NULL, 0x08, NULL, HFILL } }, { &hf_btavrcp_feature_passthrough_channel_down, { "PASSTHROUGH Channel Down", "btavrcp.feature.passthrough.channel_down", FT_UINT8, BASE_HEX, NULL, 0x10, NULL, HFILL } }, { &hf_btavrcp_feature_passthrough_previous_channel, { "PASSTHROUGH Previous Channel", "btavrcp.feature.passthrough.previous_channel", FT_UINT8, BASE_HEX, NULL, 0x20, NULL, HFILL } }, { &hf_btavrcp_feature_passthrough_sound_select, { "PASSTHROUGH Sound Select", "btavrcp.feature.passthrough.sound_select", FT_UINT8, BASE_HEX, NULL, 0x40, NULL, HFILL } }, { &hf_btavrcp_feature_passthrough_input_select, { "PASSTHROUGH Input Select", "btavrcp.feature.passthrough.input_select", FT_UINT8, BASE_HEX, NULL, 0x80, NULL, HFILL } }, { &hf_btavrcp_feature_passthrough_display_information, { "PASSTHROUGH Display Information", "btavrcp.feature.passthrough.display_information", FT_UINT8, BASE_HEX, NULL, 0x01, NULL, HFILL } }, { &hf_btavrcp_feature_passthrough_help, { "PASSTHROUGH Help", "btavrcp.feature.passthrough.help", FT_UINT8, BASE_HEX, NULL, 0x02, NULL, HFILL } }, { &hf_btavrcp_feature_passthrough_page_up, { "PASSTHROUGH Page Up", "btavrcp.feature.passthrough.page_up", FT_UINT8, BASE_HEX, NULL, 0x04, NULL, HFILL } }, { &hf_btavrcp_feature_passthrough_page_down, { "PASSTHROUGH Page Down", "btavrcp.feature.passthrough.page_down", FT_UINT8, BASE_HEX, NULL, 0x08, NULL, HFILL } }, { &hf_btavrcp_feature_passthrough_power, { "PASSTHROUGH Power", "btavrcp.feature.passthrough.power", FT_UINT8, BASE_HEX, NULL, 0x10, NULL, HFILL } }, { &hf_btavrcp_feature_passthrough_volume_up, { "PASSTHROUGH Volume Up", "btavrcp.feature.passthrough.volume_up", FT_UINT8, BASE_HEX, NULL, 0x20, NULL, HFILL } }, { &hf_btavrcp_feature_passthrough_volume_down, { "PASSTHROUGH Volume Down", "btavrcp.feature.passthrough.volume_down", FT_UINT8, BASE_HEX, NULL, 0x40, NULL, HFILL } }, { &hf_btavrcp_feature_passthrough_mute, { "PASSTHROUGH Mute", "btavrcp.feature.passthrough.mute", FT_UINT8, BASE_HEX, NULL, 0x80, NULL, HFILL } }, { &hf_btavrcp_feature_passthrough_play, { "PASSTHROUGH Play", "btavrcp.feature.passthrough.play", FT_UINT8, BASE_HEX, NULL, 0x01, NULL, HFILL } }, { &hf_btavrcp_feature_passthrough_stop, { "PASSTHROUGH Stop", "btavrcp.feature.passthrough.stop", FT_UINT8, BASE_HEX, NULL, 0x02, NULL, HFILL } }, { &hf_btavrcp_feature_passthrough_pause, { "PASSTHROUGH Pause", "btavrcp.feature.passthrough.pause", FT_UINT8, BASE_HEX, NULL, 0x04, NULL, HFILL } }, { &hf_btavrcp_feature_passthrough_record, { "PASSTHROUGH Record", "btavrcp.feature.passthrough.record", FT_UINT8, BASE_HEX, NULL, 0x08, NULL, HFILL } }, { &hf_btavrcp_feature_passthrough_rewind, { "PASSTHROUGH Rewind", "btavrcp.feature.passthrough.rewind", FT_UINT8, BASE_HEX, NULL, 0x10, NULL, HFILL } }, { &hf_btavrcp_feature_passthrough_fast_forward, { "PASSTHROUGH FastForward", "btavrcp.feature.passthrough.fast_forward", FT_UINT8, BASE_HEX, NULL, 0x20, NULL, HFILL } }, { &hf_btavrcp_feature_passthrough_eject, { "PASSTHROUGH Eject", "btavrcp.feature.passthrough.eject", FT_UINT8, BASE_HEX, NULL, 0x40, NULL, HFILL } }, { &hf_btavrcp_feature_passthrough_forward, { "PASSTHROUGH Forward", "btavrcp.feature.passthrough.forward", FT_UINT8, BASE_HEX, NULL, 0x80, NULL, HFILL } }, { &hf_btavrcp_feature_passthrough_backward, { "PASSTHROUGH Backward", "btavrcp.feature.passthrough.backward", FT_UINT8, BASE_HEX, NULL, 0x01, NULL, HFILL } }, { &hf_btavrcp_feature_passthrough_angle, { "PASSTHROUGH Angle", "btavrcp.feature.passthrough.angle", FT_UINT8, BASE_HEX, NULL, 0x02, NULL, HFILL } }, { &hf_btavrcp_feature_passthrough_subpicture, { "PASSTHROUGH SubPicture", "btavrcp.feature.passthrough.subpicture", FT_UINT8, BASE_HEX, NULL, 0x04, NULL, HFILL } }, { &hf_btavrcp_feature_passthrough_f1, { "PASSTHROUGH F1", "btavrcp.feature.passthrough.f1", FT_UINT8, BASE_HEX, NULL, 0x08, NULL, HFILL } }, { &hf_btavrcp_feature_passthrough_f2, { "PASSTHROUGH F2", "btavrcp.feature.passthrough.f2", FT_UINT8, BASE_HEX, NULL, 0x10, NULL, HFILL } }, { &hf_btavrcp_feature_passthrough_f3, { "PASSTHROUGH F3", "btavrcp.feature.passthrough.f3", FT_UINT8, BASE_HEX, NULL, 0x20, NULL, HFILL } }, { &hf_btavrcp_feature_passthrough_f4, { "PASSTHROUGH F4", "btavrcp.feature.passthrough.f4", FT_UINT8, BASE_HEX, NULL, 0x40, NULL, HFILL } }, { &hf_btavrcp_feature_passthrough_f5, { "PASSTHROUGH F5", "btavrcp.feature.passthrough.f5", FT_UINT8, BASE_HEX, NULL, 0x80, NULL, HFILL } }, { &hf_btavrcp_feature_vendor_unique, { "Vendor Unique", "btavrcp.feature.vendor_unique", FT_UINT8, BASE_HEX, NULL, 0x01, NULL, HFILL } }, { &hf_btavrcp_feature_basic_group_navigation, { "Basic Group Navigation", "btavrcp.feature.basic_group_navigation", FT_UINT8, BASE_HEX, NULL, 0x02, NULL, HFILL } }, { &hf_btavrcp_feature_advanced_control_player, { "Advanced Control Player", "btavrcp.feature.advanced_control_player", FT_UINT8, BASE_HEX, NULL, 0x04, NULL, HFILL } }, { &hf_btavrcp_feature_browsing, { "Browsing", "btavrcp.feature.browsing", FT_UINT8, BASE_HEX, NULL, 0x08, NULL, HFILL } }, { &hf_btavrcp_feature_searching, { "Searching", "btavrcp.feature.searching", FT_UINT8, BASE_HEX, NULL, 0x10, NULL, HFILL } }, { &hf_btavrcp_feature_addtonowplayer, { "AddToNowPlaying", "btavrcp.feature.addtonowplaying", FT_UINT8, BASE_HEX, NULL, 0x20, NULL, HFILL } }, { &hf_btavrcp_feature_uid_unique, { "UID Unique", "btavrcp.feature.uid_unique", FT_UINT8, BASE_HEX, NULL, 0x40, NULL, HFILL } }, { &hf_btavrcp_feature_only_browsable_when_addressed, { "Only Browsable When Addressed", "btavrcp.feature.only_browsable_when_addressed", FT_UINT8, BASE_HEX, NULL, 0x80, NULL, HFILL } }, { &hf_btavrcp_feature_only_searchable_when_addressed, { "Only Searchable When Addressed", "btavrcp.feature.only_searchable_when_addressed", FT_UINT8, BASE_HEX, NULL, 0x01, NULL, HFILL } }, { &hf_btavrcp_feature_nowplaying, { "Nowplaying", "btavrcp.feature.nowplaying", FT_UINT8, BASE_HEX, NULL, 0x02, NULL, HFILL } }, { &hf_btavrcp_feature_uid_persistency, { "UID Persistency", "btavrcp.feature.uid_persistency", FT_UINT8, BASE_HEX, NULL, 0x04, NULL, HFILL } }, /* end of features */ { &hf_btavrcp_currect_path, { "Currect Path", "btavrcp.currect_path", FT_NONE, BASE_NONE, NULL, 0x00, NULL, HFILL } }, { &hf_btavrcp_reassembled, { "Reassembled", "btavrcp.reassembled", FT_NONE, BASE_NONE, NULL, 0x00, NULL, HFILL } }, { &hf_btavrcp_response_time, { "Response Time", "btavrcp.response_time", FT_UINT32, BASE_DEC, NULL, 0x00, NULL, HFILL } }, { &hf_btavrcp_command_in_frame, { "Command in frame", "btavrcp.command_in_frame", FT_FRAMENUM, BASE_NONE, NULL, 0x00, NULL, HFILL } }, { &hf_btavrcp_response_in_frame, { "Response in frame", "btavrcp.response_in_frame", FT_FRAMENUM, BASE_NONE, NULL, 0x00, NULL, HFILL } }, { &hf_btavrcp_data, { "Data", "btavrcp.data", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL } } }; static gint *ett[] = { &ett_btavrcp, &ett_btavrcp_attribute_list, &ett_btavrcp_attribute_entry, &ett_btavrcp_attribute_entries, &ett_btavrcp_element, &ett_btavrcp_player, &ett_btavrcp_folder, &ett_btavrcp_path, }; static ei_register_info ei[] = { { &ei_btavrcp_item_length_bad, { "btavrcp.item.length.bad", PI_PROTOCOL, PI_WARN, "Item length does not correspond to sum of length of attributes", EXPFILL }}, { &ei_btavrcp_unexpected_data, { "btavrcp.unexpected_data", PI_PROTOCOL, PI_WARN, "Unexpected data", EXPFILL }}, { &ei_btavrcp_no_response, { "btavrcp.no_response", PI_PROTOCOL, PI_WARN, "No response", EXPFILL }}, }; reassembling = wmem_tree_new_autoreset(wmem_epan_scope(), wmem_file_scope()); timing = wmem_tree_new_autoreset(wmem_epan_scope(), wmem_file_scope()); btavrcp_song_positions = wmem_tree_new_autoreset(wmem_epan_scope(), wmem_file_scope()); proto_btavrcp = proto_register_protocol("Bluetooth AVRCP Profile", "BT AVRCP", "btavrcp"); btavrcp_handle = register_dissector("btavrcp", dissect_btavrcp, proto_btavrcp); proto_register_field_array(proto_btavrcp, hf, array_length(hf)); proto_register_subtree_array(ett, array_length(ett)); expert_btavrcp = expert_register_protocol(proto_btavrcp); expert_register_field_array(expert_btavrcp, ei, array_length(ei)); module = prefs_register_protocol(proto_btavrcp, NULL); prefs_register_static_text_preference(module, "avrcp.version", "Bluetooth Profile AVRCP version: 1.5", "Version of profile supported by this dissector."); } void proto_reg_handoff_btavrcp(void) { dissector_add_string("bluetooth.uuid", "110c", btavrcp_handle); dissector_add_string("bluetooth.uuid", "110e", btavrcp_handle); dissector_add_string("bluetooth.uuid", "110f", btavrcp_handle); } /* * Editor modelines - http://www.wireshark.org/tools/modelines.html * * Local variables: * c-basic-offset: 4 * tab-width: 8 * indent-tabs-mode: nil * End: * * vi: set shiftwidth=4 tabstop=8 expandtab: * :indentSize=4:tabSize=8:noTabs=true: */