/* packet-btavrcp.c
 * Routines for Bluetooth AVRCP dissection
 *
 * Copyright 2012, Michal Labedzki for Tieto Corporation
 *
 * Wireshark - Network traffic analyzer
 * By Gerald Combs <gerald@wireshark.org>
 * Copyright 1998 Gerald Combs
 *
 * SPDX-License-Identifier: GPL-2.0-or-later
 */

#include "config.h"

#include <epan/packet.h>
#include <epan/prefs.h>
#include <epan/expert.h>
#include <epan/oui.h>
#include <epan/iana_charsets.h>
#include "packet-btl2cap.h"
#include "packet-btsdp.h"
#include "packet-btavctp.h"
#include "packet-btavrcp.h"

static int proto_btavrcp;

static int hf_btavrcp_rfa;
static int hf_btavrcp_ctype;
static int hf_btavrcp_subunit_type;
static int hf_btavrcp_subunit_id;
static int hf_btavrcp_opcode;
static int hf_btavrcp_company_id;
static int hf_btavrcp_length;
static int hf_btavrcp_pdu_id;
static int hf_btavrcp_bt_pdu_id;
static int hf_btavrcp_bt_continuing_pdu_id;
static int hf_btavrcp_browsing_pdu_id;
static int hf_btavrcp_reserved;
static int hf_btavrcp_packet_type;
static int hf_btavrcp_passthrough_state;
static int hf_btavrcp_passthrough_operation;
static int hf_btavrcp_passthrough_data_length;
static int hf_btavrcp_passthrough_company_id;
static int hf_btavrcp_passthrough_vendor_unique_id;
static int hf_btavrcp_unit_unknown;
static int hf_btavrcp_unit_type;
static int hf_btavrcp_unit_id;
static int hf_btavrcp_subunit_page;
static int hf_btavrcp_subunit_extension_code;
static int hf_btavrcp_item;
static int hf_btavrcp_folder;
static int hf_btavrcp_player_id;
static int hf_btavrcp_status;
static int hf_btavrcp_uid_counter;
static int hf_btavrcp_number_of_items;
static int hf_btavrcp_number_of_items16;
static int hf_btavrcp_character_set;
static int hf_btavrcp_folder_depth;
static int hf_btavrcp_folder_name_length;
static int hf_btavrcp_folder_name;
static int hf_btavrcp_search;
static int hf_btavrcp_search_length;
static int hf_btavrcp_number_of_attributes;
static int hf_btavrcp_uid;
static int hf_btavrcp_scope;
static int hf_btavrcp_start_item;
static int hf_btavrcp_end_item;
static int hf_btavrcp_direction;
static int hf_btavrcp_identifier;
static int hf_btavrcp_song_length;
static int hf_btavrcp_song_position;
static int hf_btavrcp_play_status;
static int hf_btavrcp_notification_interval;
static int hf_btavrcp_event_id;
static int hf_btavrcp_battery_status;
static int hf_btavrcp_number_of_character_set;
static int hf_btavrcp_absolute_volume_rfa;
static int hf_btavrcp_absolute_volume;
static int hf_btavrcp_capability;
static int hf_btavrcp_capability_count;
static int hf_btavrcp_item_type;
static int hf_btavrcp_item_length;
static int hf_btavrcp_system_status;
static int hf_btavrcp_number_of_settings;
static int hf_btavrcp_settings_attribute;
static int hf_btavrcp_settings_value;
static int hf_btavrcp_displayable_name;
static int hf_btavrcp_displayable_name_length;
static int hf_btavrcp_media_type;
static int hf_btavrcp_folder_type;
static int hf_btavrcp_folder_playable;
static int hf_btavrcp_major_player_type;
static int hf_btavrcp_player_subtype;
static int hf_btavrcp_player_item;
static int hf_btavrcp_attribute;
static int hf_btavrcp_attribute_count;
static int hf_btavrcp_attribute_value;
static int hf_btavrcp_attribute_value_length;
static int hf_btavrcp_attribute_item;
static int hf_btavrcp_attribute_list;
static int hf_btavrcp_attribute_entries;
static int hf_btavrcp_attribute_name_length;
static int hf_btavrcp_attribute_name;
static int hf_btavrcp_setting_value_length;
static int hf_btavrcp_setting_value;
static int hf_btavrcp_features;
static int hf_btavrcp_not_used_features;
static int hf_btavrcp_feature_reserved_0;
static int hf_btavrcp_feature_reserved_1;
static int hf_btavrcp_feature_reserved_2;
static int hf_btavrcp_feature_reserved_3;
static int hf_btavrcp_feature_reserved_4;
static int hf_btavrcp_feature_reserved_5;
static int hf_btavrcp_feature_reserved_6;
static int hf_btavrcp_feature_reserved_7;
static int hf_btavrcp_feature_passthrough_select;
static int hf_btavrcp_feature_passthrough_up;
static int hf_btavrcp_feature_passthrough_down;
static int hf_btavrcp_feature_passthrough_left;
static int hf_btavrcp_feature_passthrough_right;
static int hf_btavrcp_feature_passthrough_right_up;
static int hf_btavrcp_feature_passthrough_right_down;
static int hf_btavrcp_feature_passthrough_left_up;
static int hf_btavrcp_feature_passthrough_left_down;
static int hf_btavrcp_feature_passthrough_root_menu;
static int hf_btavrcp_feature_passthrough_setup_menu;
static int hf_btavrcp_feature_passthrough_contents_menu;
static int hf_btavrcp_feature_passthrough_favorite_menu;
static int hf_btavrcp_feature_passthrough_exit;
static int hf_btavrcp_feature_passthrough_0;
static int hf_btavrcp_feature_passthrough_1;
static int hf_btavrcp_feature_passthrough_2;
static int hf_btavrcp_feature_passthrough_3;
static int hf_btavrcp_feature_passthrough_4;
static int hf_btavrcp_feature_passthrough_5;
static int hf_btavrcp_feature_passthrough_6;
static int hf_btavrcp_feature_passthrough_7;
static int hf_btavrcp_feature_passthrough_8;
static int hf_btavrcp_feature_passthrough_9;
static int hf_btavrcp_feature_passthrough_dot;
static int hf_btavrcp_feature_passthrough_enter;
static int hf_btavrcp_feature_passthrough_clear;
static int hf_btavrcp_feature_passthrough_channel_up;
static int hf_btavrcp_feature_passthrough_channel_down;
static int hf_btavrcp_feature_passthrough_previous_channel;
static int hf_btavrcp_feature_passthrough_sound_select;
static int hf_btavrcp_feature_passthrough_input_select;
static int hf_btavrcp_feature_passthrough_display_information;
static int hf_btavrcp_feature_passthrough_help;
static int hf_btavrcp_feature_passthrough_page_up;
static int hf_btavrcp_feature_passthrough_page_down;
static int hf_btavrcp_feature_passthrough_power;
static int hf_btavrcp_feature_passthrough_volume_up;
static int hf_btavrcp_feature_passthrough_volume_down;
static int hf_btavrcp_feature_passthrough_mute;
static int hf_btavrcp_feature_passthrough_play;
static int hf_btavrcp_feature_passthrough_stop;
static int hf_btavrcp_feature_passthrough_pause;
static int hf_btavrcp_feature_passthrough_record;
static int hf_btavrcp_feature_passthrough_rewind;
static int hf_btavrcp_feature_passthrough_fast_forward;
static int hf_btavrcp_feature_passthrough_eject;
static int hf_btavrcp_feature_passthrough_forward;
static int hf_btavrcp_feature_passthrough_backward;
static int hf_btavrcp_feature_passthrough_angle;
static int hf_btavrcp_feature_passthrough_subpicture;
static int hf_btavrcp_feature_passthrough_f1;
static int hf_btavrcp_feature_passthrough_f2;
static int hf_btavrcp_feature_passthrough_f3;
static int hf_btavrcp_feature_passthrough_f4;
static int hf_btavrcp_feature_passthrough_f5;
static int hf_btavrcp_feature_vendor_unique;
static int hf_btavrcp_feature_basic_group_navigation;
static int hf_btavrcp_feature_advanced_control_player;
static int hf_btavrcp_feature_browsing;
static int hf_btavrcp_feature_searching;
static int hf_btavrcp_feature_addtonowplayer;
static int hf_btavrcp_feature_uid_unique;
static int hf_btavrcp_feature_only_browsable_when_addressed;
static int hf_btavrcp_feature_only_searchable_when_addressed;
static int hf_btavrcp_feature_nowplaying;
static int hf_btavrcp_feature_uid_persistency;
static int hf_btavrcp_feature_number_of_items;
static int hf_btavrcp_feature_cover_art;
static int hf_btavrcp_reassembled;
static int hf_btavrcp_current_path;
static int hf_btavrcp_response_time;
static int hf_btavrcp_command_in_frame;
static int hf_btavrcp_response_in_frame;
static int hf_btavrcp_data;

static int ett_btavrcp;
static int ett_btavrcp_attribute_list;
static int ett_btavrcp_attribute_entry;
static int ett_btavrcp_attribute_entries;
static int ett_btavrcp_element;
static int ett_btavrcp_folder;
static int ett_btavrcp_player;
static int ett_btavrcp_features;
static int ett_btavrcp_features_not_used;
static int ett_btavrcp_path;

static expert_field ei_btavrcp_no_response;
static expert_field ei_btavrcp_item_length_bad;
static expert_field ei_btavrcp_unexpected_data;

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;
static wmem_tree_t *timing;
       wmem_tree_t *btavrcp_song_positions = NULL;

typedef struct _avrcp_proto_data_t {
    uint32_t interface_id;
    uint32_t adapter_id;
    uint32_t chandle;
    uint32_t channel;
} avrcp_proto_data_t;

typedef struct _fragment {
    unsigned     start_frame_number;
    unsigned     end_frame_number;
    uint32_t     interface_id;
    uint32_t     adapter_id;
    uint32_t     chandle;
    uint32_t     psm;
    uint32_t     op;
    unsigned     state;
    uint32_t     count;
    wmem_tree_t  *fragments;
    } fragment_t;

typedef struct _data_fragment_t {
    uint32_t  length;
    uint8_t   *data;
} data_fragment_t;

typedef struct _timing_info {
    unsigned  command_frame_number;
    nstime_t  command_timestamp;
    unsigned  response_frame_number;
    nstime_t  response_timestamp;
    unsigned  max_response_time;
    unsigned  used;
    uint32_t  interface_id;
    uint32_t  adapter_id;
    uint32_t  chandle;
    uint32_t  psm;
    uint32_t  opcode;
    uint32_t  op;
    uint32_t  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,   "Reverse 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[] = {
    { 0x01,   "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  int
dissect_attribute_id_list(tvbuff_t *tvb, proto_tree *tree, int offset,
                          unsigned count)
{
    unsigned    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 int
dissect_attribute_entries(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
                          int offset, unsigned count)
{
    unsigned    i_entry;
    unsigned    attribute_id;
    unsigned    value_length;
    unsigned    length;
    uint8_t    *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(pinfo->pool, 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);
        offset += value_length;
    }

    return offset;
}


static int
dissect_item_mediaplayer(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset)
{
    unsigned    displayable_name_length;
    unsigned    item_length;
    unsigned    feature_octet;
    unsigned    i_feature;
    uint8_t    *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 + 2 + 1 + 4 + 16 + 1 + 2);
    displayable_name = tvb_get_string_enc(pinfo->pool, tvb, offset + 1 + 2 + 2 + 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, 2, ENC_BIG_ENDIAN);
    offset += 2;

    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;

    proto_tree_add_item(ptree, hf_btavrcp_play_status, tvb, offset, 1, ENC_BIG_ENDIAN);
    offset += 1;

    /* 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_uint8(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_uint8(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_uint8(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_uint8(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_uint8(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_uint8(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_uint8(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_uint8(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_uint8(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);
    proto_tree_add_item((feature_octet & (1 << 3)) ? features_tree : features_not_set_tree, hf_btavrcp_feature_number_of_items, tvb, offset + 8, 1, ENC_BIG_ENDIAN);
    proto_tree_add_item((feature_octet & (1 << 4)) ? features_tree : features_not_set_tree, hf_btavrcp_feature_cover_art, tvb, offset + 8, 1, ENC_BIG_ENDIAN);

    /* reserved */
    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_uint8(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_character_set, tvb, offset, 2, ENC_BIG_ENDIAN);
    offset += 2;

    proto_tree_add_item_ret_uint(ptree, hf_btavrcp_displayable_name_length, tvb, offset, 2, ENC_BIG_ENDIAN, &displayable_name_length);
    offset += 2;

    proto_tree_add_item(ptree, hf_btavrcp_displayable_name, tvb, offset, displayable_name_length, ENC_UTF_8);
    offset += displayable_name_length;

    return offset;
}


static int
dissect_item_media_element(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
                           int offset)
{
    unsigned    number_of_attributes;
    unsigned    displayable_name_length;
    unsigned    item_length;
    uint8_t    *displayable_name;
    proto_item *pitem;
    proto_tree *ptree;
    int         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(pinfo->pool, 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_ret_uint(ptree, hf_btavrcp_displayable_name_length, tvb, offset, 2, ENC_BIG_ENDIAN, &displayable_name_length);
    offset += 2;

    proto_tree_add_item(ptree, hf_btavrcp_displayable_name, tvb, offset, displayable_name_length, ENC_UTF_8);
    offset += displayable_name_length;

    proto_tree_add_item_ret_uint(ptree, hf_btavrcp_number_of_attributes, tvb, offset, 1, ENC_BIG_ENDIAN, &number_of_attributes);
    offset += 1;

    offset = dissect_attribute_entries(tvb, pinfo, ptree, offset, number_of_attributes);

    if ( item_length != (unsigned) offset - offset_in) {
        expert_add_info(pinfo, pitem, &ei_btavrcp_item_length_bad);
    }

    return offset;
}


static int
dissect_item_folder(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset)
{
    unsigned    displayable_name_length;
    unsigned    item_length;
    uint8_t    *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(pinfo->pool, 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_ret_uint(ptree, hf_btavrcp_displayable_name_length, tvb, offset, 2, ENC_BIG_ENDIAN, &displayable_name_length);
    offset += 2;

    proto_tree_add_item(ptree, hf_btavrcp_displayable_name, tvb, offset, displayable_name_length, ENC_UTF_8);
    offset += displayable_name_length;

    return offset;
}


static int
dissect_passthrough(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
                    int offset, bool is_command, uint32_t *op)
{
    unsigned operation;
    unsigned 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_uint8(tvb, offset);
    operation = tvb_get_uint8(tvb, offset) & 0x7F;
    state = (tvb_get_uint8(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 int
dissect_unit(tvbuff_t *tvb, proto_tree *tree, int offset, bool 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 int
dissect_subunit(tvbuff_t *tvb, proto_tree *tree, int offset, bool 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 int
dissect_vendor_dependent(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
                         int offset, unsigned ctype, uint32_t *op, uint32_t *op_arg,
                         bool is_command, avrcp_proto_data_t *avrcp_proto_data)
{
    proto_item      *pitem;
    unsigned         pdu_id;
    unsigned         company_id;
    unsigned         event_id;
    unsigned         packet_type;
    unsigned         parameter_length;
    unsigned         length;
    wmem_tree_key_t  key[7];
    uint32_t         k_op;
    uint32_t         frame_number;
    unsigned         volume;
    unsigned         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_uint8(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_ret_uint(tree, hf_btavrcp_packet_type, tvb, offset, 1, ENC_BIG_ENDIAN, &packet_type);
    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->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 = (uint8_t *) 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->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 = (uint8_t *) 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) {
        unsigned         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->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 = (uint8_t *) 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) {
                unsigned    i_length = 0;
                uint8_t    *reassembled;

                for (i_frame = 1; i_frame <= fragment->count; ++i_frame) {
                    data_fragment = (data_fragment_t *)wmem_tree_lookup32_le(fragment->fragments, i_frame);
                    if (data_fragment)
                        length += data_fragment->length;
                }

                reassembled = (uint8_t *) 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);
                    if (data_fragment) {
                        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 */
        unsigned status;
        proto_tree_add_item_ret_uint(tree, hf_btavrcp_status, tvb, offset, 1, ENC_BIG_ENDIAN, &status);
        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)  {
                unsigned capability;

                proto_tree_add_item_ret_uint(tree, hf_btavrcp_capability, tvb, offset, 1, ENC_BIG_ENDIAN, &capability);
                *op_arg = capability;
                col_append_fstr(pinfo->cinfo, COL_INFO, "(%s)",
                        val_to_str_const(capability, capability_vals, "unknown"));
                offset += 1;
            } else {
                unsigned capability;
                unsigned capability_count;
                unsigned i_capability;

                proto_tree_add_item_ret_uint(tree, hf_btavrcp_capability, tvb, offset, 1, ENC_BIG_ENDIAN, &capability);
                *op_arg = capability;
                offset += 1;
                proto_tree_add_item_ret_uint(tree, hf_btavrcp_capability_count, tvb, offset, 1, ENC_BIG_ENDIAN, &capability_count);
                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 {
                unsigned number_of_attributes;
                unsigned i_attribute;

                proto_tree_add_item(tree, hf_btavrcp_number_of_settings, tvb, offset, 1, ENC_BIG_ENDIAN);
                number_of_attributes = tvb_get_uint8(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 {
                unsigned number_of_values;
                unsigned i_value;

                proto_tree_add_item(tree, hf_btavrcp_number_of_settings, tvb, offset, 1, ENC_BIG_ENDIAN);
                number_of_values = tvb_get_uint8(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)  {
                unsigned number_of_attributes;
                unsigned i_attribute;

                proto_tree_add_item(tree, hf_btavrcp_number_of_settings, tvb, offset, 1, ENC_BIG_ENDIAN);
                number_of_attributes = tvb_get_uint8(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 {
                unsigned number_of_settings;
                unsigned i_setting;

                proto_tree_add_item_ret_uint(tree, hf_btavrcp_number_of_settings, tvb, offset, 1, ENC_BIG_ENDIAN, &number_of_settings);
                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)  {
                unsigned number_of_settings;
                unsigned i_setting;

                proto_tree_add_item_ret_uint(tree, hf_btavrcp_number_of_settings, tvb, offset, 1, ENC_BIG_ENDIAN, &number_of_settings);
                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)  {
                unsigned number_of_attributes;
                unsigned i_attribute;

                proto_tree_add_item(tree, hf_btavrcp_number_of_settings, tvb, offset, 1, ENC_BIG_ENDIAN);
                number_of_attributes = tvb_get_uint8(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 {
                unsigned number_of_attributes;
                unsigned i_attribute;
                unsigned attribute_name_length;

                proto_tree_add_item(tree, hf_btavrcp_number_of_settings, tvb, offset, 1, ENC_BIG_ENDIAN);
                number_of_attributes = tvb_get_uint8(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_ret_uint(tree, hf_btavrcp_attribute_name_length, tvb, offset, 1, ENC_BIG_ENDIAN, &attribute_name_length);
                    offset += 1;

                    proto_tree_add_item(tree, hf_btavrcp_attribute_name, tvb, offset, attribute_name_length, ENC_UTF_8);
                    offset += attribute_name_length;
                }
            }
            break;
        case PDU_GET_PLAYER_APPLICATION_SETTING_VALUE_TEXT:
            if (is_command)  {
                unsigned number_of_values;
                unsigned 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_uint8(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 {
                unsigned number_of_values;
                unsigned i_attribute;
                unsigned attribute_value_length;

                proto_tree_add_item(tree, hf_btavrcp_number_of_settings, tvb, offset, 1, ENC_BIG_ENDIAN);
                number_of_values = tvb_get_uint8(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_ret_uint(tree, hf_btavrcp_attribute_value_length, tvb, offset, 1, ENC_BIG_ENDIAN, &attribute_value_length);
                    offset += 1;

                    proto_tree_add_item(tree, hf_btavrcp_attribute_value, tvb, offset, attribute_value_length, ENC_UTF_8);
                    offset += attribute_value_length;
                }
            }
            break;
        case PDU_INFORM_DISPLAYABLE_CHARACTER_SET:
            if (is_command)  {
                unsigned number_of_character_set;
                unsigned i_character_set;

                proto_tree_add_item_ret_uint(tree, hf_btavrcp_number_of_character_set, tvb, offset, 1, ENC_BIG_ENDIAN, &number_of_character_set);
                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)  {
                unsigned battery_status;

                proto_tree_add_item_ret_uint(tree, hf_btavrcp_battery_status, tvb, offset, 1, ENC_BIG_ENDIAN, &battery_status);
                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)  {
                unsigned  number_of_attributes;
                uint64_t      identifier;

                proto_tree_add_item_ret_uint64(tree, hf_btavrcp_identifier, tvb, offset, 8, ENC_BIG_ENDIAN, &identifier);
                offset += 8;

                col_append_fstr(pinfo->cinfo, COL_INFO, " - 0x%08X%08X", (unsigned) (identifier >> 32), (unsigned) (identifier & 0xFFFFFFFF));
                if (identifier == 0x00) col_append_str(pinfo->cinfo, COL_INFO, " (PLAYING)");

                proto_tree_add_item_ret_uint(tree, hf_btavrcp_number_of_attributes, tvb, offset, 1, ENC_BIG_ENDIAN, &number_of_attributes);
                offset += 1;
                offset = dissect_attribute_id_list(tvb, tree, offset, number_of_attributes);
            } else {
                unsigned number_of_attributes;

                proto_tree_add_item_ret_uint(tree, hf_btavrcp_number_of_attributes, tvb, offset, 1, ENC_BIG_ENDIAN, &number_of_attributes);
                offset += 1;
                offset = dissect_attribute_entries(tvb, pinfo, tree, offset, number_of_attributes);
            }
            break;
        case PDU_GET_PLAY_STATUS:
            if (is_command)  {
                /* non */
            } else {
                unsigned  song_length;
                unsigned  song_position;
                unsigned  play_status;

                proto_tree_add_item_ret_uint(tree, hf_btavrcp_song_length, tvb, offset, 4, ENC_BIG_ENDIAN, &song_length);
                offset += 4;
                proto_tree_add_item_ret_uint(tree, hf_btavrcp_song_position, tvb, offset, 4, ENC_BIG_ENDIAN, &song_position);
                offset += 4;
                proto_tree_add_item_ret_uint(tree, hf_btavrcp_play_status, tvb, offset, 1, ENC_BIG_ENDIAN, &play_status);
                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_uint8(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 {
                unsigned    number_of_settings;
                unsigned    i_setting;
                uint64_t    identifier;
                unsigned    play_status;
                unsigned    song_position;
                unsigned    battery_status;
                unsigned    uid_counter;
                unsigned    player_id;
                unsigned    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_ret_uint(tree, hf_btavrcp_play_status, tvb, offset, 1, ENC_BIG_ENDIAN, &play_status);
                        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_ret_uint64(tree, hf_btavrcp_identifier, tvb, offset, 8, ENC_BIG_ENDIAN, &identifier);
                        offset += 8;

                        col_append_fstr(pinfo->cinfo, COL_INFO, " - 0x%08X%08X", (unsigned) (identifier >> 32), (unsigned) (identifier & 0xFFFFFFFF));
                        if (identifier == UINT64_C(0x0000000000000000)) {
                            col_append_str(pinfo->cinfo, COL_INFO, " (SELECTED)");
                            proto_item_append_text(pitem, " (SELECTED)");
                        } else if (identifier == UINT64_C(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_ret_uint(tree, hf_btavrcp_song_position, tvb, offset, 4, ENC_BIG_ENDIAN, &song_position);
                        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->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_ret_uint(tree, hf_btavrcp_battery_status, tvb, offset, 1, ENC_BIG_ENDIAN, &battery_status);
                        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_ret_uint(tree, hf_btavrcp_system_status, tvb, offset, 1, ENC_BIG_ENDIAN, &system_status);
                        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_ret_uint(tree, hf_btavrcp_number_of_settings, tvb, offset, 1, ENC_BIG_ENDIAN, &number_of_settings);
                        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_ret_uint(tree, hf_btavrcp_player_id, tvb, offset, 2, ENC_BIG_ENDIAN, &player_id);
                        offset += 2;
                        proto_tree_add_item_ret_uint(tree, hf_btavrcp_uid_counter, tvb, offset, 2, ENC_BIG_ENDIAN, &uid_counter);
                        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_ret_uint(tree, hf_btavrcp_uid_counter, tvb, offset, 2, ENC_BIG_ENDIAN, &uid_counter);
                        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_ret_uint(tree, hf_btavrcp_absolute_volume, tvb, offset, 1, ENC_BIG_ENDIAN, &volume);
                        volume_percent = (unsigned) ((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)  {
                unsigned continuing_op;

                proto_tree_add_item(tree, hf_btavrcp_bt_continuing_pdu_id, tvb, offset, 1, ENC_BIG_ENDIAN);
                continuing_op = tvb_get_uint8(tvb, offset) | (company_id << 8);
                col_append_fstr(pinfo->cinfo, COL_INFO, " - %s",
                        val_to_str_const(tvb_get_uint8(tvb, offset), pdu_id_vals, "Unknown opcode"));
                offset += 1;

                if (pinfo->fd->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)  {
                unsigned continuing_op;

                proto_tree_add_item(tree, hf_btavrcp_bt_continuing_pdu_id, tvb, offset, 1, ENC_BIG_ENDIAN);
                continuing_op = tvb_get_uint8(tvb, offset) | (company_id << 8);
                col_append_fstr(pinfo->cinfo, COL_INFO, " - %s",
                        val_to_str_const(tvb_get_uint8(tvb, offset), pdu_id_vals, "Unknown opcode"));
                offset += 1;

                if (pinfo->fd->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_ret_uint(tree, hf_btavrcp_absolute_volume, tvb, offset, 1, ENC_BIG_ENDIAN, &volume);
            volume_percent = (unsigned) ((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)  {
                unsigned player_id;

                proto_tree_add_item_ret_uint(tree, hf_btavrcp_player_id, tvb, offset, 2, ENC_BIG_ENDIAN, &player_id);
                offset += 2;

                col_append_fstr(pinfo->cinfo, COL_INFO, " - Player ID: %u", player_id);
            } else {
                unsigned status;

                proto_tree_add_item_ret_uint(tree, hf_btavrcp_status, tvb, offset, 1, ENC_BIG_ENDIAN, &status);
                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)  {
                unsigned scope;
                uint64_t uid;
                unsigned uid_counter;

                proto_tree_add_item_ret_uint(tree, hf_btavrcp_scope, tvb, offset, 1, ENC_BIG_ENDIAN, &scope);
                offset += 1;
                proto_tree_add_item_ret_uint64(tree, hf_btavrcp_uid, tvb, offset, 8, ENC_BIG_ENDIAN, &uid);
                offset += 8;
                proto_tree_add_item_ret_uint(tree, hf_btavrcp_uid_counter, tvb, offset, 2, ENC_BIG_ENDIAN, &uid_counter);
                offset += 2;

                col_append_fstr(pinfo->cinfo, COL_INFO, " - Scope: %s, Uid: 0x%016" PRIx64 ", UidCounter: 0x%04x",
                        val_to_str_const(scope, scope_vals, "unknown"), uid, uid_counter);
            } else {
                unsigned status;

                proto_tree_add_item_ret_uint(tree, hf_btavrcp_status, tvb, offset, 1, ENC_BIG_ENDIAN, &status);
                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)  {
                unsigned scope;
                uint64_t uid;
                unsigned uid_counter;

                proto_tree_add_item_ret_uint(tree, hf_btavrcp_scope, tvb, offset, 1, ENC_BIG_ENDIAN, &scope);
                offset += 1;
                proto_tree_add_item_ret_uint64(tree, hf_btavrcp_uid, tvb, offset, 8, ENC_BIG_ENDIAN, &uid);
                offset += 8;
                proto_tree_add_item_ret_uint(tree, hf_btavrcp_uid_counter, tvb, offset, 2, ENC_BIG_ENDIAN, &uid_counter);
                offset += 2;

                col_append_fstr(pinfo->cinfo, COL_INFO, " - Scope: %s, Uid: 0x%016" PRIx64 ", UidCounter: 0x%04x",
                        val_to_str_const(scope, scope_vals, "unknown"), uid, uid_counter);
            } else {
                unsigned status;

                proto_tree_add_item_ret_uint(tree, hf_btavrcp_status, tvb, offset, 1, ENC_BIG_ENDIAN, &status);
                offset += 1;
                col_append_fstr(pinfo->cinfo, COL_INFO, " - Status: %s",
                        val_to_str_const(status, status_vals, "Unknown status"));
            }
            break;
    };

    return offset;
}


static int
dissect_browsing(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
                 int offset, bool is_command)
{
    unsigned pdu_id;
    unsigned status = 0x00;

    proto_tree_add_item_ret_uint(tree, hf_btavrcp_browsing_pdu_id, tvb, offset, 1, ENC_BIG_ENDIAN, &pdu_id);
    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_ret_uint(tree, hf_btavrcp_status, tvb, offset, 1, ENC_BIG_ENDIAN, &status);
        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)  {
                unsigned player_id;

                proto_tree_add_item_ret_uint(tree, hf_btavrcp_player_id, tvb, offset, 2, ENC_BIG_ENDIAN, &player_id);
                offset += 2;

                col_append_fstr(pinfo->cinfo, COL_INFO, " - Player ID: %u", player_id);
            } else {
                unsigned      i_folder;
                unsigned      folder_depth;
                unsigned      folder_name_length;
                proto_item    *pitem;
                proto_tree    *ptree;
                const uint8_t *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_ret_uint(tree, hf_btavrcp_folder_depth, tvb, offset, 1, ENC_BIG_ENDIAN, &folder_depth);
                offset += 1;

                pitem = proto_tree_add_none_format(tree, hf_btavrcp_current_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_ret_uint(ptree, hf_btavrcp_folder_name_length, tvb, offset, 2, ENC_BIG_ENDIAN, &folder_name_length);
                    offset += 2;
                    proto_tree_add_item_ret_string(ptree, hf_btavrcp_folder_name, tvb, offset, folder_name_length, ENC_ASCII|ENC_NA, pinfo->pool, &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)  {
                unsigned attribute_count;
                unsigned scope;
                unsigned start_item;
                unsigned end_item;

                proto_tree_add_item_ret_uint(tree, hf_btavrcp_scope, tvb, offset, 1, ENC_BIG_ENDIAN, &scope);
                offset += 1;
                proto_tree_add_item_ret_uint(tree, hf_btavrcp_start_item, tvb, offset, 4, ENC_BIG_ENDIAN, &start_item);
                offset += 4;
                proto_tree_add_item_ret_uint(tree, hf_btavrcp_end_item, tvb, offset, 4, ENC_BIG_ENDIAN, &end_item);
                offset += 4;
                proto_tree_add_item_ret_uint(tree, hf_btavrcp_attribute_count, tvb, offset, 1, ENC_BIG_ENDIAN, &attribute_count);
                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 {
                unsigned number_of_items;
                unsigned uid_counter;
                unsigned i_item;
                unsigned item_type;
                unsigned item_length;

                proto_tree_add_item_ret_uint(tree, hf_btavrcp_uid_counter, tvb, offset, 2, ENC_BIG_ENDIAN, &uid_counter);
                offset += 2;
                proto_tree_add_item_ret_uint(tree, hf_btavrcp_number_of_items16, tvb, offset, 2, ENC_BIG_ENDIAN, &number_of_items);
                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_uint8(tvb, offset);
                    item_length = tvb_get_ntohs(tvb, offset + 1);

                    if (item_type == ITEM_MEDIAPLAYER) {
                        dissect_item_mediaplayer(tvb, pinfo, 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, pinfo, 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)  {
                uint64_t uid;
                unsigned uid_counter;
                unsigned direction;

                proto_tree_add_item_ret_uint(tree, hf_btavrcp_uid_counter, tvb, offset, 2, ENC_BIG_ENDIAN, &uid_counter);
                offset += 2;
                proto_tree_add_item_ret_uint(tree, hf_btavrcp_direction, tvb, offset, 1, ENC_BIG_ENDIAN, &direction);
                offset += 1;
                proto_tree_add_item_ret_uint64(tree, hf_btavrcp_uid, tvb, offset, 8, ENC_BIG_ENDIAN, &uid);
                offset += 8;

                col_append_fstr(pinfo->cinfo, COL_INFO, " - Direction: %s, Uid: 0x%016" PRIx64 ", UidCounter: 0x%04x",
                        val_to_str_const(direction, direction_vals, "unknown"), uid, uid_counter);
            } else {
                unsigned number_of_items;

                proto_tree_add_item_ret_uint(tree, hf_btavrcp_number_of_items, tvb, offset, 4, ENC_BIG_ENDIAN, &number_of_items);
                offset += 4;
                col_append_fstr(pinfo->cinfo, COL_INFO, " - NumberOfItems: %u", number_of_items);
            }
            break;
        case PDU_GET_ITEM_ATTRIBUTES:
            if (is_command)  {
                unsigned    number_of_attributes;
                uint64_t    uid;
                unsigned    uid_counter;
                unsigned    scope;

                proto_tree_add_item_ret_uint(tree, hf_btavrcp_scope, tvb, offset, 1, ENC_BIG_ENDIAN, &scope);
                offset += 1;
                proto_tree_add_item_ret_uint64(tree, hf_btavrcp_uid, tvb, offset, 8, ENC_BIG_ENDIAN, &uid);
                offset += 8;
                proto_tree_add_item_ret_uint(tree, hf_btavrcp_uid_counter, tvb, offset, 2, ENC_BIG_ENDIAN, &uid_counter);
                offset += 2;
                proto_tree_add_item_ret_uint(tree, hf_btavrcp_number_of_attributes, tvb, offset, 1, ENC_BIG_ENDIAN, &number_of_attributes);

                col_append_fstr(pinfo->cinfo, COL_INFO, " - Scope: %s, Uid: 0x%016" PRIx64 ", 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 {
                unsigned number_of_attributes;

                proto_tree_add_item_ret_uint(tree, hf_btavrcp_number_of_attributes, tvb, offset, 1, ENC_BIG_ENDIAN, &number_of_attributes);
                offset += 1;
                offset = dissect_attribute_entries(tvb, pinfo, tree, offset, number_of_attributes);
            }
            break;
        case PDU_SEARCH:
            if (is_command)  {
                unsigned search_length;

                proto_tree_add_item(tree, hf_btavrcp_character_set, tvb, offset, 2, ENC_BIG_ENDIAN);
                offset += 2;
                proto_tree_add_item_ret_uint(tree, hf_btavrcp_search_length, tvb, offset, 2, ENC_BIG_ENDIAN, &search_length);
                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 int
dissect_btavrcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
{
    proto_item      *ti;
    proto_tree      *btavrcp_tree;
    proto_item      *pitem  = NULL;
    int              offset = 0;
    uint32_t         opcode;
    uint32_t         op     = 0;
    uint32_t         op_arg = 0;
    unsigned         ctype;
    unsigned         response_time;
    unsigned         max_response_time;
    unsigned         is_command;
    timing_info_t   *timing_info;
    wmem_tree_key_t  key[9];
    uint32_t         k_op_arg;
    uint32_t         frame_number;
    int              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_ret_uint(btavrcp_tree, hf_btavrcp_ctype, tvb, offset, 1, ENC_BIG_ENDIAN, &ctype);
        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_ret_uint(btavrcp_tree, hf_btavrcp_opcode, tvb, offset, 1, ENC_BIG_ENDIAN, &opcode);
        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) ? UINT32_MAX : 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->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) ? UINT32_MAX : 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 += ((unsigned)timing_info->response_timestamp.secs - 1 - (unsigned)timing_info->command_timestamp.secs) / 1000;
            } else {
                response_time = timing_info->response_timestamp.nsecs - timing_info->command_timestamp.nsecs;
                response_time /= 1000000;
                response_time += ((unsigned)timing_info->response_timestamp.secs - (unsigned)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_OUI, NULL, 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_OUI, NULL, 0x00,
            NULL, HFILL }
        },
        { &hf_btavrcp_unit_unknown,
            { "Unknown",                         "btavrcp.unit.unknown",
            FT_UINT8, BASE_HEX, NULL, 0x0,
            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_UINT32, BASE_HEX, VALS(player_subtype_vals), 0x00,
            NULL, HFILL }
        },
        { &hf_btavrcp_setting_value_length,
            { "Value Length",                    "btavrcp.setting_value.length",
            FT_UINT16, 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,
            { "Name Length",                    "btavrcp.attribute_name.length",
            FT_UINT8, BASE_DEC, NULL, 0x00,
            NULL, HFILL }
        },
        { &hf_btavrcp_attribute_name,
            { "Name",                           "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 }
        },
        { &hf_btavrcp_feature_number_of_items,
            { "Number of Items",                 "btavrcp.feature.number_of_items",
            FT_UINT8, BASE_HEX, NULL, 0x08,
            NULL, HFILL }
        },
        { &hf_btavrcp_feature_cover_art,
            { "Cover Art",                       "btavrcp.feature.cover_art",
            FT_UINT8, BASE_HEX, NULL, 0x10,
            NULL, HFILL }
        },
        /* end of features */
        { &hf_btavrcp_current_path,
            { "Current Path",                     "btavrcp.current_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, FRAMENUM_TYPE(FT_FRAMENUM_REQUEST), 0x00,
            NULL, HFILL }
        },
        { &hf_btavrcp_response_in_frame,
            { "Response in frame",               "btavrcp.response_in_frame",
            FT_FRAMENUM, BASE_NONE, FRAMENUM_TYPE(FT_FRAMENUM_RESPONSE), 0x00,
            NULL, HFILL }
        },
        { &hf_btavrcp_data,
            { "Data",                            "btavrcp.data",
            FT_NONE, BASE_NONE, NULL, 0x0,
            NULL, HFILL }
        }
    };

    static int *ett[] = {
        &ett_btavrcp,
        &ett_btavrcp_attribute_list,
        &ett_btavrcp_attribute_entry,
        &ett_btavrcp_attribute_entries,
        &ett_btavrcp_element,
        &ett_btavrcp_player,
        &ett_btavrcp_features,
        &ett_btavrcp_features_not_used,
        &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_subtree("Bluetooth", 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  -  https://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:
 */
