wireshark/epan/dissectors/packet-usb-hid.c

1059 lines
39 KiB
C

/* packet-usb-hid.c
*
* $Id$
*
* USB HID dissector
* By Adam Nielsen <a.nielsen@shikadi.net> 2009
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "config.h"
#include <glib.h>
#include <epan/packet.h>
#include <string.h>
#include "packet-usb.h"
#include "packet-usb-hid.h"
/* protocols and header fields */
static int proto_usb_hid = -1;
static int hf_usb_hid_item_bSize = -1;
static int hf_usb_hid_item_bType = -1;
static int hf_usb_hid_mainitem_bTag = -1;
static int hf_usb_hid_globalitem_bTag = -1;
static int hf_usb_hid_localitem_bTag = -1;
static int hf_usb_hid_longitem_bTag = -1;
static int hf_usb_hid_item_bDataSize = -1;
static int hf_usb_hid_item_bLongItemTag = -1;
static int hf_usb_hid_item_unk_data = -1;
static int hf_usb_hid_mainitem_bit0 = -1;
static int hf_usb_hid_mainitem_bit1 = -1;
static int hf_usb_hid_mainitem_bit2 = -1;
static int hf_usb_hid_mainitem_bit3 = -1;
static int hf_usb_hid_mainitem_bit4 = -1;
static int hf_usb_hid_mainitem_bit5 = -1;
static int hf_usb_hid_mainitem_bit6 = -1;
static int hf_usb_hid_mainitem_bit7 = -1;
static int hf_usb_hid_mainitem_bit7_input = -1;
static int hf_usb_hid_mainitem_bit8 = -1;
static int hf_usb_hid_mainitem_colltype = -1;
static int hf_usb_hid_globalitem_usage = -1;
static int hf_usb_hid_globalitem_log_min = -1;
static int hf_usb_hid_globalitem_log_max = -1;
static int hf_usb_hid_globalitem_phy_min = -1;
static int hf_usb_hid_globalitem_phy_max = -1;
static int hf_usb_hid_globalitem_unit_exp = -1;
static int hf_usb_hid_globalitem_unit_sys = -1;
static int hf_usb_hid_globalitem_unit_len = -1;
static int hf_usb_hid_globalitem_unit_mass = -1;
static int hf_usb_hid_globalitem_unit_time = -1;
static int hf_usb_hid_globalitem_unit_temp = -1;
static int hf_usb_hid_globalitem_unit_current = -1;
static int hf_usb_hid_globalitem_unit_brightness = -1;
static int hf_usb_hid_globalitem_report_size = -1;
static int hf_usb_hid_globalitem_report_id = -1;
static int hf_usb_hid_globalitem_report_count = -1;
static int hf_usb_hid_globalitem_push = -1;
static int hf_usb_hid_globalitem_pop = -1;
static int hf_usb_hid_localitem_usage = -1;
static int hf_usb_hid_localitem_usage_min = -1;
static int hf_usb_hid_localitem_usage_max = -1;
static int hf_usb_hid_localitem_desig_index = -1;
static int hf_usb_hid_localitem_desig_min = -1;
static int hf_usb_hid_localitem_desig_max = -1;
static int hf_usb_hid_localitem_string_index = -1;
static int hf_usb_hid_localitem_string_min = -1;
static int hf_usb_hid_localitem_string_max = -1;
static int hf_usb_hid_localitem_delimiter = -1;
static gint ett_usb_hid_report = -1;
static gint ett_usb_hid_item_header = -1;
static gint ett_usb_hid_wValue = -1;
static int hf_usb_hid_request = -1;
static int hf_usb_hid_value = -1;
static int hf_usb_hid_index = -1;
static int hf_usb_hid_length = -1;
static int hf_usb_hid_report_type = -1;
static int hf_usb_hid_report_id = -1;
static int hf_usb_hid_duration = -1;
static int hf_usb_hid_zero = -1;
static const true_false_string tfs_mainitem_bit0 = {"Data", "Constant"};
static const true_false_string tfs_mainitem_bit1 = {"Array", "Variable"};
static const true_false_string tfs_mainitem_bit2 = {"Absolute", "Relative"};
static const true_false_string tfs_mainitem_bit3 = {"No wrap", "Wrap"};
static const true_false_string tfs_mainitem_bit4 = {"Linear", "Nonlinear"};
static const true_false_string tfs_mainitem_bit5 = {"Preferred state", "No preferred state"};
static const true_false_string tfs_mainitem_bit6 = {"No null position", "Null state"};
static const true_false_string tfs_mainitem_bit7 = {"Non-volatile", "Volatile"};
static const true_false_string tfs_mainitem_bit8 = {"Bit field", "Buffered bytes"};
struct usb_hid_global_state {
unsigned int usage_page;
};
#define USBHID_SIZE_MASK 0x03
#define USBHID_TYPE_MASK 0x0C
#define USBHID_TAG_MASK 0xF0
static const value_string usb_hid_item_bSize_vals[] = {
{0, "0 bytes"},
{1, "1 byte"},
{2, "2 bytes"},
{3, "4 bytes"},
{0, NULL}
};
#define USBHID_ITEMTYPE_MAIN 0
#define USBHID_ITEMTYPE_GLOBAL 1
#define USBHID_ITEMTYPE_LOCAL 2
#define USBHID_ITEMTYPE_LONG 3
static const value_string usb_hid_item_bType_vals[] = {
{USBHID_ITEMTYPE_MAIN, "Main"},
{USBHID_ITEMTYPE_GLOBAL, "Global"},
{USBHID_ITEMTYPE_LOCAL, "Local"},
{USBHID_ITEMTYPE_LONG, "Long item"},
{0, NULL}
};
#define USBHID_MAINITEM_TAG_INPUT 8
#define USBHID_MAINITEM_TAG_OUTPUT 9
#define USBHID_MAINITEM_TAG_FEATURE 11
#define USBHID_MAINITEM_TAG_COLLECTION 10
#define USBHID_MAINITEM_TAG_ENDCOLLECTION 12
static const value_string usb_hid_mainitem_bTag_vals[] = {
{USBHID_MAINITEM_TAG_INPUT, "Input"},
{USBHID_MAINITEM_TAG_OUTPUT, "Output"},
{USBHID_MAINITEM_TAG_FEATURE, "Feature"},
{USBHID_MAINITEM_TAG_COLLECTION, "Collection"},
{USBHID_MAINITEM_TAG_ENDCOLLECTION, "End collection"},
{0, NULL}
};
#define USBHID_GLOBALITEM_TAG_USAGE_PAGE 0
#define USBHID_GLOBALITEM_TAG_LOG_MIN 1
#define USBHID_GLOBALITEM_TAG_LOG_MAX 2
#define USBHID_GLOBALITEM_TAG_PHY_MIN 3
#define USBHID_GLOBALITEM_TAG_PHY_MAX 4
#define USBHID_GLOBALITEM_TAG_UNIT_EXP 5
#define USBHID_GLOBALITEM_TAG_UNIT 6
#define USBHID_GLOBALITEM_TAG_REPORT_SIZE 7
#define USBHID_GLOBALITEM_TAG_REPORT_ID 8
#define USBHID_GLOBALITEM_TAG_REPORT_COUNT 9
#define USBHID_GLOBALITEM_TAG_PUSH 10
#define USBHID_GLOBALITEM_TAG_POP 11
static const value_string usb_hid_globalitem_bTag_vals[] = {
{USBHID_GLOBALITEM_TAG_USAGE_PAGE, "Usage"},
{USBHID_GLOBALITEM_TAG_LOG_MIN, "Logical minimum"},
{USBHID_GLOBALITEM_TAG_LOG_MAX, "Logical maximum"},
{USBHID_GLOBALITEM_TAG_PHY_MIN, "Physical minimum"},
{USBHID_GLOBALITEM_TAG_PHY_MAX, "Physical maximum"},
{USBHID_GLOBALITEM_TAG_UNIT_EXP, "Unit exponent"},
{USBHID_GLOBALITEM_TAG_UNIT, "Units"},
{USBHID_GLOBALITEM_TAG_REPORT_SIZE, "Report size"},
{USBHID_GLOBALITEM_TAG_REPORT_ID, "Report ID"},
{USBHID_GLOBALITEM_TAG_REPORT_COUNT, "Report count"},
{USBHID_GLOBALITEM_TAG_PUSH, "Push"},
{USBHID_GLOBALITEM_TAG_POP, "Pop"},
{12, "[Reserved]"},
{13, "[Reserved]"},
{14, "[Reserved]"},
{15, "[Reserved]"},
{0, NULL}
};
#define USBHID_LOCALITEM_TAG_USAGE_PAGE 0
#define USBHID_LOCALITEM_TAG_USAGE_MIN 1
#define USBHID_LOCALITEM_TAG_USAGE_MAX 2
#define USBHID_LOCALITEM_TAG_DESIG_INDEX 3
#define USBHID_LOCALITEM_TAG_DESIG_MIN 4
#define USBHID_LOCALITEM_TAG_DESIG_MAX 5
/* No 6 in spec */
#define USBHID_LOCALITEM_TAG_STRING_INDEX 7
#define USBHID_LOCALITEM_TAG_STRING_MIN 8
#define USBHID_LOCALITEM_TAG_STRING_MAX 9
#define USBHID_LOCALITEM_TAG_DELIMITER 10 /* Also listed as reserved in spec! */
static const value_string usb_hid_localitem_bTag_vals[] = {
{USBHID_LOCALITEM_TAG_USAGE_PAGE, "Usage"},
{USBHID_LOCALITEM_TAG_USAGE_MIN, "Usage minimum"},
{USBHID_LOCALITEM_TAG_USAGE_MAX, "Usage maximum"},
{USBHID_LOCALITEM_TAG_DESIG_INDEX, "Designator index"},
{USBHID_LOCALITEM_TAG_DESIG_MIN, "Designator minimum"},
{USBHID_LOCALITEM_TAG_DESIG_MAX, "Designator maximum"},
{USBHID_LOCALITEM_TAG_STRING_INDEX, "String index"},
{USBHID_LOCALITEM_TAG_STRING_MIN, "String minimum"},
{USBHID_LOCALITEM_TAG_STRING_MAX, "String maximum"},
{USBHID_LOCALITEM_TAG_DELIMITER, "Delimiter"},
{11, "[Reserved]"},
{12, "[Reserved]"},
{13, "[Reserved]"},
{14, "[Reserved]"},
{15, "[Reserved]"},
{0, NULL}
};
static const value_string usb_hid_longitem_bTag_vals[] = {
{15, "Long item"},
{0, NULL}
};
static const range_string usb_hid_mainitem_colltype_vals[] = {
{0x00, 0x00, "Physical"},
{0x01, 0x01, "Application"},
{0x02, 0x02, "Logical"},
{0x03, 0x03, "Report"},
{0x04, 0x04, "Named array"},
{0x05, 0x05, "Usage switch"},
{0x06, 0x06, "Usage modifier"},
{0x07, 0x7F, "[Reserved]"},
{0x80, 0xFF, "[Vendor-defined]"},
{0, 0, NULL}
};
static const value_string usb_hid_globalitem_unit_exp_vals[] = {
{0x0, "n^0"},
{0x1, "n^1"},
{0x2, "n^2"},
{0x3, "n^3"},
{0x4, "n^4"},
{0x5, "n^5"},
{0x6, "n^6"},
{0x7, "n^7"},
{0x8, "n^-8"},
{0x9, "n^-7"},
{0xA, "n^-6"},
{0xB, "n^-5"},
{0xC, "n^-4"},
{0xD, "n^-3"},
{0xE, "n^-2"},
{0xF, "n^-1"},
{0, NULL}
};
static const range_string usb_hid_item_usage_vals[] = {
{0x00, 0x00, "Undefined"},
{0x01, 0x01, "Generic desktop controls"},
{0x02, 0x02, "Simulation controls"},
{0x03, 0x03, "VR controls"},
{0x04, 0x04, "Sport controls"},
{0x05, 0x05, "Game controls"},
{0x06, 0x06, "Generic device controls"},
{0x07, 0x07, "Keyboard/keypad"},
{0x08, 0x08, "LEDs"},
{0x09, 0x09, "Button"},
{0x0A, 0x0A, "Ordinal"},
{0x0B, 0x0B, "Telephony"},
{0x0C, 0x0C, "Consumer"},
{0x0D, 0x0D, "Digitizer"},
{0x0E, 0x0E, "[Reserved]"},
{0x0F, 0x0F, "Physical Interface Device (PID) page"},
{0x10, 0x10, "Unicode"},
{0x11, 0x13, "[Reserved]"},
{0x14, 0x14, "Alphanumeric display"},
{0x15, 0x3F, "[Reserved]"},
{0x40, 0x40, "Medical instruments"},
{0x41, 0x7F, "[Reserved]"},
{0x80, 0x83, "Monitor page"},
{0x84, 0x87, "Power page"},
{0x88, 0x8B, "[Reserved]"},
{0x8C, 0x8C, "Bar code scanner page"},
{0x8D, 0x8D, "Scale page"},
{0x8E, 0x8E, "Magnetic Stripe Reading (MSR) devices"},
{0x8F, 0x8F, "[Reserved Point of Sale page]"},
{0x90, 0x90, "Camera control page"},
{0x91, 0x91, "Arcade page"},
{0x92, 0xFEFF, "[Reserved]"},
{0xFF00, 0xFFFF, "[Vendor-defined]"},
{0, 0, NULL}
};
/* Dissector for the data in a HID main report. */
static int
dissect_usb_hid_report_mainitem_data(packet_info *pinfo _U_, proto_tree *tree, tvbuff_t *tvb, int offset, unsigned int bSize, unsigned int bTag)
{
switch (bTag) {
case USBHID_MAINITEM_TAG_INPUT:
case USBHID_MAINITEM_TAG_OUTPUT:
case USBHID_MAINITEM_TAG_FEATURE:
proto_tree_add_item(tree, hf_usb_hid_mainitem_bit0, tvb, offset, bSize, ENC_LITTLE_ENDIAN);
proto_tree_add_item(tree, hf_usb_hid_mainitem_bit1, tvb, offset, bSize, ENC_LITTLE_ENDIAN);
proto_tree_add_item(tree, hf_usb_hid_mainitem_bit2, tvb, offset, bSize, ENC_LITTLE_ENDIAN);
proto_tree_add_item(tree, hf_usb_hid_mainitem_bit3, tvb, offset, bSize, ENC_LITTLE_ENDIAN);
proto_tree_add_item(tree, hf_usb_hid_mainitem_bit4, tvb, offset, bSize, ENC_LITTLE_ENDIAN);
proto_tree_add_item(tree, hf_usb_hid_mainitem_bit5, tvb, offset, bSize, ENC_LITTLE_ENDIAN);
proto_tree_add_item(tree, hf_usb_hid_mainitem_bit6, tvb, offset, bSize, ENC_LITTLE_ENDIAN);
if (bTag == USBHID_MAINITEM_TAG_INPUT) {
proto_tree_add_item(tree, hf_usb_hid_mainitem_bit7_input, tvb, offset, bSize, ENC_LITTLE_ENDIAN);
} else {
proto_tree_add_item(tree, hf_usb_hid_mainitem_bit7, tvb, offset, bSize, ENC_LITTLE_ENDIAN);
}
if (bSize > 1) {
proto_tree_add_item(tree, hf_usb_hid_mainitem_bit8, tvb, offset, bSize, ENC_LITTLE_ENDIAN);
} else {
proto_tree_add_text(tree, tvb, offset, 0, "Bits or bytes: Buffered bytes (default, no second byte present)");
}
break;
case USBHID_MAINITEM_TAG_COLLECTION:
proto_tree_add_item(tree, hf_usb_hid_mainitem_colltype, tvb, offset, 1, ENC_LITTLE_ENDIAN);
break;
case USBHID_MAINITEM_TAG_ENDCOLLECTION:
/* No item data */
break;
default:
proto_tree_add_item(tree, hf_usb_hid_item_unk_data, tvb, offset, bSize, ENC_NA);
break;
}
offset += bSize;
return offset;
}
/* Dissector for the data in a HID main report. */
static int
dissect_usb_hid_report_globalitem_data(packet_info *pinfo _U_, proto_tree *tree, tvbuff_t *tvb, int offset, unsigned int bSize, unsigned int bTag, struct usb_hid_global_state *global)
{
switch (bTag) {
case USBHID_GLOBALITEM_TAG_USAGE_PAGE:
switch (bSize) {
case 1: global->usage_page = tvb_get_guint8(tvb, offset); break;
case 2: global->usage_page = tvb_get_letohs(tvb, offset); break;
case 3: global->usage_page = tvb_get_letoh24(tvb, offset); break;
case 4: global->usage_page = tvb_get_letohl(tvb, offset); break;
default: global->usage_page = 0; break;
}
proto_tree_add_item(tree, hf_usb_hid_globalitem_usage, tvb, offset, bSize, ENC_LITTLE_ENDIAN);
break;
case USBHID_GLOBALITEM_TAG_LOG_MIN:
proto_tree_add_item(tree, hf_usb_hid_globalitem_log_min, tvb, offset, bSize, ENC_LITTLE_ENDIAN);
break;
case USBHID_GLOBALITEM_TAG_LOG_MAX:
proto_tree_add_item(tree, hf_usb_hid_globalitem_log_max, tvb, offset, bSize, ENC_LITTLE_ENDIAN);
break;
case USBHID_GLOBALITEM_TAG_PHY_MIN:
proto_tree_add_item(tree, hf_usb_hid_globalitem_phy_min, tvb, offset, bSize, ENC_LITTLE_ENDIAN);
break;
case USBHID_GLOBALITEM_TAG_PHY_MAX:
proto_tree_add_item(tree, hf_usb_hid_globalitem_phy_max, tvb, offset, bSize, ENC_LITTLE_ENDIAN);
break;
case USBHID_GLOBALITEM_TAG_UNIT_EXP:
proto_tree_add_item(tree, hf_usb_hid_globalitem_unit_exp, tvb, offset, bSize, ENC_LITTLE_ENDIAN);
break;
case USBHID_GLOBALITEM_TAG_UNIT:
proto_tree_add_item(tree, hf_usb_hid_globalitem_unit_sys, tvb, offset, bSize, ENC_LITTLE_ENDIAN);
proto_tree_add_item(tree, hf_usb_hid_globalitem_unit_len, tvb, offset, bSize, ENC_LITTLE_ENDIAN);
proto_tree_add_item(tree, hf_usb_hid_globalitem_unit_mass, tvb, offset, bSize, ENC_LITTLE_ENDIAN);
proto_tree_add_item(tree, hf_usb_hid_globalitem_unit_time, tvb, offset, bSize, ENC_LITTLE_ENDIAN);
proto_tree_add_item(tree, hf_usb_hid_globalitem_unit_temp, tvb, offset, bSize, ENC_LITTLE_ENDIAN);
proto_tree_add_item(tree, hf_usb_hid_globalitem_unit_current, tvb, offset, bSize, ENC_LITTLE_ENDIAN);
proto_tree_add_item(tree, hf_usb_hid_globalitem_unit_brightness, tvb, offset, bSize, ENC_LITTLE_ENDIAN);
break;
case USBHID_GLOBALITEM_TAG_REPORT_SIZE:
proto_tree_add_item(tree, hf_usb_hid_globalitem_report_size, tvb, offset, bSize, ENC_LITTLE_ENDIAN);
break;
case USBHID_GLOBALITEM_TAG_REPORT_ID:
proto_tree_add_item(tree, hf_usb_hid_globalitem_report_id, tvb, offset, bSize, ENC_LITTLE_ENDIAN);
break;
case USBHID_GLOBALITEM_TAG_REPORT_COUNT:
proto_tree_add_item(tree, hf_usb_hid_globalitem_report_count, tvb, offset, bSize, ENC_LITTLE_ENDIAN);
break;
case USBHID_GLOBALITEM_TAG_PUSH:
proto_tree_add_item(tree, hf_usb_hid_globalitem_push, tvb, offset, bSize, ENC_LITTLE_ENDIAN);
break;
case USBHID_GLOBALITEM_TAG_POP:
proto_tree_add_item(tree, hf_usb_hid_globalitem_pop, tvb, offset, bSize, ENC_LITTLE_ENDIAN);
break;
default:
proto_tree_add_item(tree, hf_usb_hid_item_unk_data, tvb, offset, bSize, ENC_NA);
break;
}
offset += bSize;
return offset;
}
/* Dissector for the data in a HID main report. */
static int
dissect_usb_hid_report_localitem_data(packet_info *pinfo _U_, proto_tree *tree, tvbuff_t *tvb, int offset, unsigned int bSize, unsigned int bTag, struct usb_hid_global_state *global)
{
unsigned int usage_page = 0xffffffff; /* in case bSize == 0 */
switch (bTag) {
case USBHID_LOCALITEM_TAG_USAGE_PAGE:
if (bSize > 2) {
/* Full page ID */
proto_tree_add_item(tree, hf_usb_hid_localitem_usage, tvb, offset, bSize, ENC_LITTLE_ENDIAN);
} else {
/* Only lower few bits given, need to combine with last global ID */
if (bSize == 1)
usage_page = (global->usage_page & 0xFFFFFF00) | tvb_get_guint8(tvb, offset);
else if (bSize == 2)
usage_page = (global->usage_page & 0xFFFF0000) | tvb_get_ntohs(tvb, offset);
proto_tree_add_text(tree, tvb, offset, bSize, "Usage: %s (0x%08x)",
rval_to_str(usage_page, usb_hid_item_usage_vals, "[Unknown page!]"),
usage_page);
}
break;
case USBHID_LOCALITEM_TAG_USAGE_MIN:
proto_tree_add_item(tree, hf_usb_hid_localitem_usage_min, tvb, offset, bSize, ENC_LITTLE_ENDIAN);
break;
case USBHID_LOCALITEM_TAG_USAGE_MAX:
proto_tree_add_item(tree, hf_usb_hid_localitem_usage, tvb, offset, bSize, ENC_LITTLE_ENDIAN);
break;
case USBHID_LOCALITEM_TAG_DESIG_INDEX:
proto_tree_add_item(tree, hf_usb_hid_localitem_desig_index, tvb, offset, bSize, ENC_LITTLE_ENDIAN);
break;
case USBHID_LOCALITEM_TAG_DESIG_MIN:
proto_tree_add_item(tree, hf_usb_hid_localitem_desig_min, tvb, offset, bSize, ENC_LITTLE_ENDIAN);
break;
case USBHID_LOCALITEM_TAG_DESIG_MAX:
proto_tree_add_item(tree, hf_usb_hid_localitem_desig_max, tvb, offset, bSize, ENC_LITTLE_ENDIAN);
break;
case USBHID_LOCALITEM_TAG_STRING_INDEX:
proto_tree_add_item(tree, hf_usb_hid_localitem_string_index, tvb, offset, bSize, ENC_LITTLE_ENDIAN);
break;
case USBHID_LOCALITEM_TAG_STRING_MIN:
proto_tree_add_item(tree, hf_usb_hid_localitem_string_min, tvb, offset, bSize, ENC_LITTLE_ENDIAN);
break;
case USBHID_LOCALITEM_TAG_STRING_MAX:
proto_tree_add_item(tree, hf_usb_hid_localitem_string_max, tvb, offset, bSize, ENC_LITTLE_ENDIAN);
break;
case USBHID_LOCALITEM_TAG_DELIMITER:
proto_tree_add_item(tree, hf_usb_hid_localitem_delimiter, tvb, offset, bSize, ENC_LITTLE_ENDIAN);
break;
default:
proto_tree_add_item(tree, hf_usb_hid_item_unk_data, tvb, offset, bSize, ENC_NA);
break;
}
offset += bSize;
return offset;
}
/* Dissector for individual HID report items. Recursive. */
static int
dissect_usb_hid_report_item(packet_info *pinfo _U_, proto_tree *parent_tree, tvbuff_t *tvb, int offset, usb_trans_info_t *usb_trans_info _U_, usb_conv_info_t *usb_conv_info _U_, const struct usb_hid_global_state *global)
{
proto_item *item=NULL, *subitem=NULL;
proto_tree *tree=NULL, *subtree=NULL;
int old_offset;
unsigned int tmp;
unsigned int bSize, bType, bTag;
const value_string *usb_hid_cur_bTag_vals;
int hf_usb_hid_curitem_bTag;
struct usb_hid_global_state cur_global;
memcpy(&cur_global, global, sizeof(struct usb_hid_global_state));
while (tvb_reported_length_remaining(tvb, offset))
{
old_offset=offset;
tmp = tvb_get_guint8(tvb, offset);
bSize = tmp & USBHID_SIZE_MASK;
if (bSize == 3) bSize++; /* 3 == four bytes */
bType = (tmp & USBHID_TYPE_MASK) >> 2;
bTag = (tmp & USBHID_TAG_MASK) >> 4;
switch (bType) {
case USBHID_ITEMTYPE_MAIN:
hf_usb_hid_curitem_bTag = hf_usb_hid_mainitem_bTag;
usb_hid_cur_bTag_vals = usb_hid_mainitem_bTag_vals;
break;
case USBHID_ITEMTYPE_GLOBAL:
hf_usb_hid_curitem_bTag = hf_usb_hid_globalitem_bTag;
usb_hid_cur_bTag_vals = usb_hid_globalitem_bTag_vals;
break;
case USBHID_ITEMTYPE_LOCAL:
hf_usb_hid_curitem_bTag = hf_usb_hid_localitem_bTag;
usb_hid_cur_bTag_vals = usb_hid_localitem_bTag_vals;
break;
default: /* Only USBHID_ITEMTYPE_LONG, but keep compiler happy */
hf_usb_hid_curitem_bTag = hf_usb_hid_longitem_bTag;
usb_hid_cur_bTag_vals = usb_hid_longitem_bTag_vals;
break;
}
subitem = proto_tree_add_text(parent_tree, tvb, offset, bSize + 1, "%s item (%s)",
val_to_str(bType, usb_hid_item_bType_vals, "Unknown/%u"),
val_to_str(bTag, usb_hid_cur_bTag_vals, "Unknown/%u tag")
);
subtree = proto_item_add_subtree(subitem, ett_usb_hid_item_header);
item = proto_tree_add_text(subtree, tvb, offset, 1, "Header");
tree = proto_item_add_subtree(item, ett_usb_hid_item_header);
proto_tree_add_item(tree, hf_usb_hid_item_bSize, tvb, offset, 1, ENC_LITTLE_ENDIAN);
proto_tree_add_item(tree, hf_usb_hid_item_bType, tvb, offset, 1, ENC_LITTLE_ENDIAN);
proto_tree_add_item(tree, hf_usb_hid_curitem_bTag, tvb, offset, 1, ENC_LITTLE_ENDIAN);
offset++;
if ((bType == 3) && (bTag == 16)) {
/* Long item */
bSize = tvb_get_guint8(tvb, offset);
proto_tree_add_item(subtree, hf_usb_hid_item_bDataSize, tvb, offset, 1, ENC_LITTLE_ENDIAN);
offset++;
proto_tree_add_item(subtree, hf_usb_hid_item_bLongItemTag, tvb, offset, 1, ENC_LITTLE_ENDIAN);
offset++;
proto_tree_add_item(subtree, hf_usb_hid_item_unk_data, tvb, offset, bSize, ENC_NA);
offset += bSize;
} else {
/* Short item */
switch (bType) {
case USBHID_ITEMTYPE_MAIN:
offset = dissect_usb_hid_report_mainitem_data(pinfo, subtree, tvb, offset, bSize, bTag);
break;
case USBHID_ITEMTYPE_GLOBAL:
offset = dissect_usb_hid_report_globalitem_data(pinfo, subtree, tvb, offset, bSize, bTag, &cur_global);
break;
case USBHID_ITEMTYPE_LOCAL:
offset = dissect_usb_hid_report_localitem_data(pinfo, subtree, tvb, offset, bSize, bTag, &cur_global);
break;
default: /* Only USBHID_ITEMTYPE_LONG, but keep compiler happy */
proto_tree_add_item(subtree, hf_usb_hid_item_unk_data, tvb, offset, bSize, ENC_NA);
offset += bSize;
break;
}
}
if (bType == USBHID_ITEMTYPE_MAIN) {
if (bTag == USBHID_MAINITEM_TAG_COLLECTION) {
/* Begin collection, nest following elements under us */
offset = dissect_usb_hid_report_item(pinfo, subtree, tvb, offset, usb_trans_info, usb_conv_info, &cur_global);
proto_item_set_len(subitem, offset-old_offset);
} else if (bTag == USBHID_MAINITEM_TAG_ENDCOLLECTION) {
/* End collection, break out to parent tree item */
break;
}
}
}
return offset;
}
/* Dissector for HID "GET DESCRIPTOR" subtype. */
int
dissect_usb_hid_get_report_descriptor(packet_info *pinfo _U_, proto_tree *parent_tree, tvbuff_t *tvb, int offset, usb_trans_info_t *usb_trans_info _U_, usb_conv_info_t *usb_conv_info _U_)
{
proto_item *item=NULL;
proto_tree *tree=NULL;
int old_offset=offset;
struct usb_hid_global_state initial_global;
memset(&initial_global, 0, sizeof(struct usb_hid_global_state));
if (parent_tree) {
item = proto_tree_add_protocol_format(parent_tree, proto_usb_hid, tvb, offset,
-1, "HID Report");
tree = proto_item_add_subtree(item, ett_usb_hid_report);
offset = dissect_usb_hid_report_item(pinfo, tree, tvb, offset, usb_trans_info, usb_conv_info, &initial_global);
proto_item_set_len(item, offset-old_offset);
}
return offset;
}
/* Dissector for HID GET_REPORT request. See USBHID 1.11, Chapter 7.2.1 Get_Report Request */
static void
dissect_usb_hid_get_report(packet_info *pinfo _U_, proto_tree *tree, tvbuff_t *tvb, int offset, gboolean is_request, usb_trans_info_t *usb_trans_info _U_, usb_conv_info_t *usb_conv_info _U_)
{
proto_item *item = NULL;
proto_tree *subtree = NULL;
if (is_request) {
item = proto_tree_add_item(tree, hf_usb_hid_value, tvb, offset, 2, ENC_LITTLE_ENDIAN);
subtree = proto_item_add_subtree(item, ett_usb_hid_wValue);
/* Report Type in the high byte, Report ID in the low byte */
proto_tree_add_item(subtree, hf_usb_hid_report_id, tvb, offset, 1, ENC_LITTLE_ENDIAN);
offset++;
proto_tree_add_item(subtree, hf_usb_hid_report_type, tvb, offset, 1, ENC_LITTLE_ENDIAN);
offset++;
proto_tree_add_item(tree, hf_usb_hid_index, tvb, offset, 2, ENC_LITTLE_ENDIAN);
offset += 2;
proto_tree_add_item(tree, hf_usb_hid_length, tvb, offset, 2, ENC_LITTLE_ENDIAN);
/*offset += 2;*/
} else {
}
}
/* Dissector for HID SET_REPORT request. See USBHID 1.11, Chapter 7.2.2 Set_Report Request */
static void
dissect_usb_hid_set_report(packet_info *pinfo _U_, proto_tree *tree, tvbuff_t *tvb, int offset, gboolean is_request, usb_trans_info_t *usb_trans_info _U_, usb_conv_info_t *usb_conv_info _U_)
{
proto_item *item = NULL;
proto_tree *subtree = NULL;
if (is_request) {
item = proto_tree_add_item(tree, hf_usb_hid_value, tvb, offset, 2, ENC_LITTLE_ENDIAN);
subtree = proto_item_add_subtree(item, ett_usb_hid_wValue);
proto_tree_add_item(subtree, hf_usb_hid_report_id, tvb, offset, 1, ENC_LITTLE_ENDIAN);
offset++;
proto_tree_add_item(subtree, hf_usb_hid_report_type, tvb, offset, 1, ENC_LITTLE_ENDIAN);
offset++;
proto_tree_add_item(tree, hf_usb_hid_index, tvb, offset, 2, ENC_LITTLE_ENDIAN);
offset += 2;
proto_tree_add_item(tree, hf_usb_hid_length, tvb, offset, 2, ENC_LITTLE_ENDIAN);
/*offset += 2;*/
} else {
}
}
/* Dissector for HID GET_IDLE request. See USBHID 1.11, Chapter 7.2.3 Get_Idle Request */
static void
dissect_usb_hid_get_idle(packet_info *pinfo _U_, proto_tree *tree, tvbuff_t *tvb, int offset, gboolean is_request, usb_trans_info_t *usb_trans_info _U_, usb_conv_info_t *usb_conv_info _U_)
{
proto_item *item = NULL;
proto_tree *subtree = NULL;
if (is_request) {
item = proto_tree_add_item(tree, hf_usb_hid_value, tvb, offset, 2, ENC_LITTLE_ENDIAN);
subtree = proto_item_add_subtree(item, ett_usb_hid_wValue);
proto_tree_add_item(subtree, hf_usb_hid_report_id, tvb, offset, 1, ENC_LITTLE_ENDIAN);
offset++;
proto_tree_add_item(subtree, hf_usb_hid_zero, tvb, offset, 1, ENC_LITTLE_ENDIAN);
offset++;
proto_tree_add_item(tree, hf_usb_hid_index, tvb, offset, 2, ENC_LITTLE_ENDIAN);
offset += 2;
proto_tree_add_item(tree, hf_usb_hid_length, tvb, offset, 2, ENC_LITTLE_ENDIAN);
/*offset += 2;*/
} else {
}
}
/* Dissector for HID SET_IDLE request. See USBHID 1.11, Chapter 7.2.4 Set_Idle Request */
static void
dissect_usb_hid_set_idle(packet_info *pinfo _U_, proto_tree *tree, tvbuff_t *tvb, int offset, gboolean is_request, usb_trans_info_t *usb_trans_info _U_, usb_conv_info_t *usb_conv_info _U_)
{
proto_item *item = NULL;
proto_tree *subtree = NULL;
if (is_request) {
item = proto_tree_add_item(tree, hf_usb_hid_value, tvb, offset, 2, ENC_LITTLE_ENDIAN);
subtree = proto_item_add_subtree(item, ett_usb_hid_wValue);
/* Duration in the high byte, Report ID in the low byte */
proto_tree_add_item(subtree, hf_usb_hid_report_id, tvb, offset, 1, ENC_LITTLE_ENDIAN);
offset++;
proto_tree_add_item(subtree, hf_usb_hid_duration, tvb, offset, 1, ENC_LITTLE_ENDIAN);
offset++;
proto_tree_add_item(tree, hf_usb_hid_index, tvb, offset, 2, ENC_LITTLE_ENDIAN);
offset += 2;
proto_tree_add_item(tree, hf_usb_hid_length, tvb, offset, 2, ENC_LITTLE_ENDIAN);
/*offset += 2;*/
} else {
}
}
/* Dissector for HID GET_PROTOCOL request. See USBHID 1.11, Chapter 7.2.5 Get_Protocol Request */
static void
dissect_usb_hid_get_protocol(packet_info *pinfo _U_, proto_tree *tree, tvbuff_t *tvb, int offset, gboolean is_request, usb_trans_info_t *usb_trans_info _U_, usb_conv_info_t *usb_conv_info _U_)
{
if (is_request) {
proto_tree_add_item(tree, hf_usb_hid_value, tvb, offset, 2, ENC_LITTLE_ENDIAN);
offset += 2;
proto_tree_add_item(tree, hf_usb_hid_index, tvb, offset, 2, ENC_LITTLE_ENDIAN);
offset += 2;
proto_tree_add_item(tree, hf_usb_hid_length, tvb, offset, 2, ENC_LITTLE_ENDIAN);
/*offset += 2;*/
} else {
}
}
/* Dissector for HID SET_PROTOCOL request. See USBHID 1.11, Chapter 7.2.6 Set_Protocol Request */
static void
dissect_usb_hid_set_protocol(packet_info *pinfo _U_, proto_tree *tree, tvbuff_t *tvb, int offset, gboolean is_request, usb_trans_info_t *usb_trans_info _U_, usb_conv_info_t *usb_conv_info _U_)
{
if (is_request) {
proto_tree_add_item(tree, hf_usb_hid_value, tvb, offset, 2, ENC_LITTLE_ENDIAN);
offset += 2;
proto_tree_add_item(tree, hf_usb_hid_index, tvb, offset, 2, ENC_LITTLE_ENDIAN);
offset += 2;
proto_tree_add_item(tree, hf_usb_hid_length, tvb, offset, 2, ENC_LITTLE_ENDIAN);
/*offset += 2;*/
} else {
}
}
typedef void (*usb_setup_dissector)(packet_info *pinfo, proto_tree *tree, tvbuff_t *tvb, int offset, gboolean is_request, usb_trans_info_t *usb_trans_info, usb_conv_info_t *usb_conv_info);
typedef struct _usb_setup_dissector_table_t {
guint8 request;
usb_setup_dissector dissector;
} usb_setup_dissector_table_t;
/* USBHID 1.11, Chapter 7.2 Class-Specific Requests */
#define USB_HID_SETUP_GET_REPORT 0x01
#define USB_HID_SETUP_GET_IDLE 0x02
#define USB_HID_SETUP_GET_PROTOCOL 0x03
/* 0x04..0x08: Reserved */
#define USB_HID_SETUP_SET_REPORT 0x09
#define USB_HID_SETUP_SET_IDLE 0x0A
#define USB_HID_SETUP_SET_PROTOCOL 0x0B
static const usb_setup_dissector_table_t setup_dissectors[] = {
{ USB_HID_SETUP_GET_REPORT, dissect_usb_hid_get_report },
{ USB_HID_SETUP_GET_IDLE, dissect_usb_hid_get_idle },
{ USB_HID_SETUP_GET_PROTOCOL, dissect_usb_hid_get_protocol },
{ USB_HID_SETUP_SET_REPORT, dissect_usb_hid_set_report },
{ USB_HID_SETUP_SET_IDLE, dissect_usb_hid_set_idle },
{ USB_HID_SETUP_SET_PROTOCOL, dissect_usb_hid_set_protocol },
{ 0, NULL }
};
static const value_string setup_request_names_vals[] = {
{ USB_HID_SETUP_GET_REPORT, "GET_REPORT" },
{ USB_HID_SETUP_GET_IDLE, "GET_IDLE" },
{ USB_HID_SETUP_GET_PROTOCOL, "GET_PROTOCOL" },
{ USB_HID_SETUP_SET_REPORT, "SET_REPORT" },
{ USB_HID_SETUP_SET_IDLE, "SET_IDLE" },
{ USB_HID_SETUP_SET_PROTOCOL, "SET_PROTOCOL" },
{ 0, NULL }
};
static const value_string usb_hid_report_type_vals[] = {
{ 1, "Input" },
{ 2, "Output" },
{ 3, "Feature" },
{ 0, NULL }
};
/* Dissector for HID class-specific control request as defined in
* USBHID 1.11, Chapter 7.2.
* Returns TRUE if a class specific dissector was found
* and FALSE otherwise.
*/
static gint
dissect_usb_hid_control(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
{
gboolean is_request;
usb_conv_info_t *usb_conv_info;
usb_trans_info_t *usb_trans_info;
int offset = 0;
usb_setup_dissector dissector;
const usb_setup_dissector_table_t *tmp;
is_request = (pinfo->srcport==NO_ENDPOINT);
usb_conv_info = pinfo->usb_conv_info;
usb_trans_info = usb_conv_info->usb_trans_info;
/* See if we can find a class specific dissector for this request */
dissector = NULL;
/* Check valid values for bmRequestType. See Chapter 7.2 in USBHID 1.11 */
if ((usb_trans_info->setup.requesttype & 0x7F) ==
((RQT_SETUP_TYPE_CLASS << 5) | RQT_SETUP_RECIPIENT_INTERFACE)) {
for (tmp = setup_dissectors; tmp->dissector; tmp++) {
if (tmp->request == usb_trans_info->setup.request) {
dissector = tmp->dissector;
break;
}
}
}
/* No, we could not find any class specific dissector for this request
* return FALSE and let USB try any of the standard requests.
*/
if (!dissector) {
return FALSE;
}
col_set_str(pinfo->cinfo, COL_PROTOCOL, "USBHID");
if (check_col(pinfo->cinfo, COL_INFO)) {
col_add_fstr(pinfo->cinfo, COL_INFO, "%s %s",
val_to_str(usb_trans_info->setup.request, setup_request_names_vals, "Unknown type %x"),
is_request ? "Request" : "Response");
}
if (is_request) {
proto_tree_add_item(tree, hf_usb_hid_request, tvb, offset, 1, ENC_LITTLE_ENDIAN);
offset += 1;
}
dissector(pinfo, tree, tvb, offset, is_request, usb_trans_info, usb_conv_info);
return TRUE;
}
void
proto_register_usb_hid(void)
{
static hf_register_info hf[] = {
{ &hf_usb_hid_item_bSize,
{ "bSize", "usbhid.item.bSize", FT_UINT8, BASE_DEC,
VALS(usb_hid_item_bSize_vals), USBHID_SIZE_MASK, NULL, HFILL }},
{ &hf_usb_hid_item_bType,
{ "bType", "usbhid.item.bType", FT_UINT8, BASE_DEC,
VALS(usb_hid_item_bType_vals), USBHID_TYPE_MASK, NULL, HFILL }},
{ &hf_usb_hid_mainitem_bTag,
{ "bTag", "usbhid.item.bTag", FT_UINT8, BASE_HEX,
VALS(usb_hid_mainitem_bTag_vals), USBHID_TAG_MASK, NULL, HFILL }},
{ &hf_usb_hid_globalitem_bTag,
{ "bTag", "usbhid.item.bTag", FT_UINT8, BASE_HEX,
VALS(usb_hid_globalitem_bTag_vals), USBHID_TAG_MASK, NULL, HFILL }},
{ &hf_usb_hid_localitem_bTag,
{ "bTag", "usbhid.item.bTag", FT_UINT8, BASE_HEX,
VALS(usb_hid_localitem_bTag_vals), USBHID_TAG_MASK, NULL, HFILL }},
{ &hf_usb_hid_longitem_bTag,
{ "bTag", "usbhid.item.bTag", FT_UINT8, BASE_HEX,
VALS(usb_hid_longitem_bTag_vals), USBHID_TAG_MASK, NULL, HFILL }},
{ &hf_usb_hid_item_bDataSize,
{ "bDataSize", "usbhid.item.bDataSize", FT_UINT8, BASE_DEC,
NULL, 0, NULL, HFILL }},
{ &hf_usb_hid_item_bLongItemTag,
{ "bTag", "usbhid.item.bLongItemTag", FT_UINT8, BASE_HEX,
NULL, 0, NULL, HFILL }},
/* Main-report item data */
{ &hf_usb_hid_mainitem_bit0,
{ "Data/constant", "usbhid.item.main.readonly", FT_BOOLEAN, 9,
TFS(&tfs_mainitem_bit0), 1<<0, NULL, HFILL }},
{ &hf_usb_hid_mainitem_bit1,
{ "Data type", "usbhid.item.main.variable", FT_BOOLEAN, 9,
TFS(&tfs_mainitem_bit1), 1<<1, NULL, HFILL }},
{ &hf_usb_hid_mainitem_bit2,
{ "Coordinates", "usbhid.item.main.relative", FT_BOOLEAN, 9,
TFS(&tfs_mainitem_bit2), 1<<2, NULL, HFILL }},
{ &hf_usb_hid_mainitem_bit3,
{ "Min/max wraparound", "usbhid.item.main.wrap", FT_BOOLEAN, 9,
TFS(&tfs_mainitem_bit3), 1<<3, NULL, HFILL }},
{ &hf_usb_hid_mainitem_bit4,
{ "Physical relationship to data", "usbhid.item.main.nonlinear", FT_BOOLEAN, 9,
TFS(&tfs_mainitem_bit4), 1<<4, NULL, HFILL }},
{ &hf_usb_hid_mainitem_bit5,
{ "Preferred state", "usbhid.item.main.no_preferred_state", FT_BOOLEAN, 9,
TFS(&tfs_mainitem_bit5), 1<<5, NULL, HFILL }},
{ &hf_usb_hid_mainitem_bit6,
{ "Has null position", "usbhid.item.main.nullstate", FT_BOOLEAN, 9,
TFS(&tfs_mainitem_bit6), 1<<6, NULL, HFILL }},
{ &hf_usb_hid_mainitem_bit7,
{ "(Non)-volatile", "usbhid.item.main.volatile", FT_BOOLEAN, 9,
TFS(&tfs_mainitem_bit7), 1<<7, NULL, HFILL }},
{ &hf_usb_hid_mainitem_bit7_input,
{ "[Reserved]", "usbhid.item.main.volatile", FT_BOOLEAN, 9,
NULL, 1<<7, NULL, HFILL }},
{ &hf_usb_hid_mainitem_bit8,
{ "Bits or bytes", "usbhid.item.main.buffered_bytes", FT_BOOLEAN, 9,
TFS(&tfs_mainitem_bit8), 1<<8, NULL, HFILL }},
{ &hf_usb_hid_mainitem_colltype,
{ "Collection type", "usbhid.item.main.colltype", FT_UINT8, BASE_RANGE_STRING|BASE_HEX,
RVALS(usb_hid_mainitem_colltype_vals), 0, NULL, HFILL }},
/* Global-report item data */
{ &hf_usb_hid_globalitem_usage,
{ "Usage page", "usbhid.item.global.usage", FT_UINT8, BASE_RANGE_STRING|BASE_HEX,
RVALS(usb_hid_item_usage_vals), 0, NULL, HFILL }},
{ &hf_usb_hid_globalitem_log_min,
{ "Logical minimum", "usbhid.item.global.log_min", FT_UINT8, BASE_DEC,
NULL, 0, NULL, HFILL }},
{ &hf_usb_hid_globalitem_log_max,
{ "Logical maximum", "usbhid.item.global.log_max", FT_UINT8, BASE_DEC,
NULL, 0, NULL, HFILL }},
{ &hf_usb_hid_globalitem_phy_min,
{ "Physical minimum", "usbhid.item.global.phy_min", FT_UINT8, BASE_DEC,
NULL, 0, NULL, HFILL }},
{ &hf_usb_hid_globalitem_phy_max,
{ "Physical maximum", "usbhid.item.global.phy_max", FT_UINT8, BASE_DEC,
NULL, 0, NULL, HFILL }},
{ &hf_usb_hid_globalitem_unit_exp,
{ "Unit exponent", "usbhid.item.global.unit_exp", FT_UINT8, BASE_DEC,
NULL, 0, NULL, HFILL }},
{ &hf_usb_hid_globalitem_unit_sys,
{ "System", "usbhid.item.global.unit.system", FT_UINT32, BASE_HEX,
VALS(usb_hid_globalitem_unit_exp_vals), 0x0000000F, NULL, HFILL }},
{ &hf_usb_hid_globalitem_unit_len,
{ "Length", "usbhid.item.global.unit.length", FT_UINT32, BASE_HEX,
VALS(usb_hid_globalitem_unit_exp_vals), 0x000000F0, NULL, HFILL }},
{ &hf_usb_hid_globalitem_unit_mass,
{ "Mass", "usbhid.item.global.unit.mass", FT_UINT32, BASE_HEX,
VALS(usb_hid_globalitem_unit_exp_vals), 0x00000F00, NULL, HFILL }},
{ &hf_usb_hid_globalitem_unit_time,
{ "Time", "usbhid.item.global.unit.time", FT_UINT32, BASE_HEX,
VALS(usb_hid_globalitem_unit_exp_vals), 0x0000F000, NULL, HFILL }},
{ &hf_usb_hid_globalitem_unit_temp,
{ "Temperature", "usbhid.item.global.unit.temperature", FT_UINT32, BASE_HEX,
VALS(usb_hid_globalitem_unit_exp_vals), 0x000F0000, NULL, HFILL }},
{ &hf_usb_hid_globalitem_unit_current,
{ "Current", "usbhid.item.global.unit.current", FT_UINT32, BASE_HEX,
VALS(usb_hid_globalitem_unit_exp_vals), 0x00F00000, NULL, HFILL }},
{ &hf_usb_hid_globalitem_unit_brightness,
{ "Luminous intensity", "usbhid.item.global.unit.brightness", FT_UINT32, BASE_HEX,
VALS(usb_hid_globalitem_unit_exp_vals), 0x0F000000, NULL, HFILL }},
{ &hf_usb_hid_globalitem_report_size,
{ "Report size", "usbhid.item.global.report_size", FT_UINT8, BASE_DEC,
NULL, 0, NULL, HFILL }},
{ &hf_usb_hid_globalitem_report_id,
{ "Report ID", "usbhid.item.global.report_id", FT_UINT8, BASE_HEX,
NULL, 0, NULL, HFILL }},
{ &hf_usb_hid_globalitem_report_count,
{ "Report count", "usbhid.item.global.report_count", FT_UINT8, BASE_DEC,
NULL, 0, NULL, HFILL }},
{ &hf_usb_hid_globalitem_push,
{ "Push", "usbhid.item.global.push", FT_UINT8, BASE_HEX,
NULL, 0, NULL, HFILL }},
{ &hf_usb_hid_globalitem_pop,
{ "Pop", "usbhid.item.global.pop", FT_UINT8, BASE_HEX,
NULL, 0, NULL, HFILL }},
/* Local-report item data */
{ &hf_usb_hid_localitem_usage,
{ "Usage", "usbhid.item.local.usage", FT_UINT8, BASE_RANGE_STRING|BASE_HEX,
RVALS(usb_hid_item_usage_vals), 0, NULL, HFILL }},
{ &hf_usb_hid_localitem_usage_min,
{ "Usage minimum", "usbhid.item.local.usage_min", FT_UINT8, BASE_HEX,
NULL, 0, NULL, HFILL }},
{ &hf_usb_hid_localitem_usage_max,
{ "Usage maximum", "usbhid.item.local.usage_max", FT_UINT8, BASE_HEX,
NULL, 0, NULL, HFILL }},
{ &hf_usb_hid_localitem_desig_index,
{ "Designator index", "usbhid.item.local.desig_index", FT_UINT8, BASE_HEX,
NULL, 0, NULL, HFILL }},
{ &hf_usb_hid_localitem_desig_min,
{ "Designator minimum", "usbhid.item.local.desig_min", FT_UINT8, BASE_HEX,
NULL, 0, NULL, HFILL }},
{ &hf_usb_hid_localitem_desig_max,
{ "Designator maximum", "usbhid.item.local.desig_max", FT_UINT8, BASE_HEX,
NULL, 0, NULL, HFILL }},
{ &hf_usb_hid_localitem_string_index,
{ "String index", "usbhid.item.local.string_index", FT_UINT8, BASE_HEX,
NULL, 0, NULL, HFILL }},
{ &hf_usb_hid_localitem_string_min,
{ "String minimum", "usbhid.item.local.string_min", FT_UINT8, BASE_HEX,
NULL, 0, NULL, HFILL }},
{ &hf_usb_hid_localitem_string_max,
{ "String maximum", "usbhid.item.local.string_max", FT_UINT8, BASE_HEX,
NULL, 0, NULL, HFILL }},
{ &hf_usb_hid_localitem_delimiter,
{ "Delimiter", "usbhid.item.local.delimiter", FT_UINT8, BASE_HEX,
NULL, 0, NULL, HFILL }},
{ &hf_usb_hid_item_unk_data,
{ "Item data", "usbhid.item.data", FT_BYTES, BASE_NONE,
NULL, 0, NULL, HFILL }},
/* USB HID specific requests */
{ &hf_usb_hid_request,
{ "bRequest", "usbhid.setup.bRequest", FT_UINT8, BASE_HEX, VALS(setup_request_names_vals), 0x0,
NULL, HFILL }},
{ &hf_usb_hid_value,
{ "wValue", "usbhid.setup.wValue", FT_UINT16, BASE_HEX, NULL, 0x0,
NULL, HFILL }},
{ &hf_usb_hid_index,
{ "wIndex", "usbhid.setup.wIndex", FT_UINT16, BASE_DEC, NULL, 0x0,
NULL, HFILL }},
{ &hf_usb_hid_length,
{ "wLength", "usbhid.setup.wLength", FT_UINT16, BASE_DEC, NULL, 0x0,
NULL, HFILL }},
{ &hf_usb_hid_report_type,
{ "ReportType", "usbhid.setup.ReportType", FT_UINT8, BASE_DEC,
VALS(usb_hid_report_type_vals), 0x0,
NULL, HFILL }},
{ &hf_usb_hid_report_id,
{ "ReportID", "usbhid.setup.ReportID", FT_UINT8, BASE_DEC, NULL, 0x0,
NULL, HFILL }},
{ &hf_usb_hid_duration,
{ "Duration", "usbhid.setup.Duration", FT_UINT8, BASE_DEC, NULL, 0x0,
NULL, HFILL }},
{ &hf_usb_hid_zero,
{ "(zero)", "usbhid.setup.zero", FT_UINT8, BASE_DEC, NULL, 0x0,
NULL, HFILL }}
};
static gint *usb_hid_subtrees[] = {
&ett_usb_hid_report,
&ett_usb_hid_item_header,
&ett_usb_hid_wValue
};
proto_usb_hid = proto_register_protocol("USB HID", "USBHID", "usbhid");
proto_register_field_array(proto_usb_hid, hf, array_length(hf));
proto_register_subtree_array(usb_hid_subtrees, array_length(usb_hid_subtrees));
}
void
proto_reg_handoff_usb_hid(void) {
dissector_handle_t usb_hid_control_handle;
usb_hid_control_handle = new_create_dissector_handle(dissect_usb_hid_control, proto_usb_hid);
dissector_add_uint("usb.control", IF_CLASS_HID, usb_hid_control_handle);
}