aboutsummaryrefslogtreecommitdiffstats
path: root/epan/dissectors/packet-usb-hid.c
diff options
context:
space:
mode:
authorFilipe Laíns <lains@archlinux.org>2020-07-11 18:25:18 +0100
committerAnders Broman <a.broman58@gmail.com>2020-08-20 08:16:04 +0000
commitbef04c21b4664f4318705ec3ccb48919407f0484 (patch)
treeb9ff879cce1cf4c820bd7612de839c6e7852543f /epan/dissectors/packet-usb-hid.c
parent4215a55c7d601393a90e6f271158cee80ac797e6 (diff)
USB HID: separate HID items into input and output
Change-Id: I36c2eddb8ae227b28a26c86c87120e393d9a62b0 Signed-off-by: Filipe Laíns <lains@archlinux.org> Reviewed-on: https://code.wireshark.org/review/37828 Reviewed-by: Anders Broman <a.broman58@gmail.com>
Diffstat (limited to 'epan/dissectors/packet-usb-hid.c')
-rw-r--r--epan/dissectors/packet-usb-hid.c133
1 files changed, 87 insertions, 46 deletions
diff --git a/epan/dissectors/packet-usb-hid.c b/epan/dissectors/packet-usb-hid.c
index 8a43b79d3e..5207e50d98 100644
--- a/epan/dissectors/packet-usb-hid.c
+++ b/epan/dissectors/packet-usb-hid.c
@@ -148,7 +148,6 @@ struct usb_hid_global_state {
static wmem_tree_t *report_descriptors = NULL;
-#define HID_FIELD_SET(field, val) ((field).defined |= val)
/* local items */
#define HID_USAGE_MIN (1 << 0)
@@ -160,7 +159,7 @@ static wmem_tree_t *report_descriptors = NULL;
#define HID_REPORT_SIZE (1 << 4)
#define HID_LOGICAL_MIN (1 << 5)
#define HID_LOGICAL_MAX (1 << 6)
-#define HID_USAGE_PAGE (1 << 7) /* non optional - defined for use in parse_report_descriptor */
+#define HID_USAGE_PAGE (1 << 7)
/* main items */
#define HID_INPUT (1 << 8)
@@ -168,6 +167,7 @@ static wmem_tree_t *report_descriptors = NULL;
#define HID_FEATURE (1 << 10)
/* masks */
+
#define HID_GLOBAL_MASK (HID_REPORT_ID | \
HID_REPORT_COUNT | \
HID_REPORT_SIZE | \
@@ -175,6 +175,11 @@ static wmem_tree_t *report_descriptors = NULL;
HID_LOGICAL_MAX | \
HID_USAGE_PAGE)
+#define HID_REQUIRED_MASK (HID_REPORT_COUNT | \
+ HID_REPORT_SIZE | \
+ HID_LOGICAL_MIN | \
+ HID_LOGICAL_MAX)
+
#define HID_MAIN_CONSTANT (1 << 0) /* data / constant */
#define HID_MAIN_TYPE (1 << 1) /* array / variable */
@@ -186,28 +191,23 @@ static wmem_tree_t *report_descriptors = NULL;
#define HID_MAIN_BUFFERED_BYTES (1 << 8) /* bit field / buferred bytes */
-typedef struct _hid_field hid_field_t;
-
-struct _hid_field {
- guint32 usage_page;
- guint32 usage;
+#define HID_USAGE_UNSET 0
+#define HID_USAGE_SINGLE 1
+#define HID_USAGE_RANGE 2
- /* the following items are optional */
- /* supersedes usage if set */
- guint32 usage_min;
- guint32 usage_max;
+typedef struct _hid_field hid_field_t;
- guint32 report_id;
- guint32 report_count;
- guint32 report_size;
- gint32 logical_min;
- gint32 logical_max;
- guint32 input;
- guint32 output;
- guint32 feature;
+struct _hid_field {
+ guint32 usage_page;
+ wmem_array_t *usages;
- guint32 defined; /* bitmap of the defined items */
+ guint32 report_id; /* optional */
+ guint32 report_count;
+ guint32 report_size;
+ gint32 logical_min;
+ gint32 logical_max;
+ guint32 properties;
hid_field_t *next;
};
@@ -222,7 +222,9 @@ struct _report_descriptor {
guint8 *desc_body;
gboolean uses_report_id;
- wmem_array_t *fields;
+ wmem_array_t *fields_in;
+ wmem_array_t *fields_out;
+ /* TODO: features */
report_descriptor_t *next;
};
@@ -3295,10 +3297,13 @@ parse_report_descriptor(report_descriptor_t *rdesc)
guint8 *data = rdesc->desc_body;
unsigned int tag, type, size;
guint8 prefix;
+ guint32 defined = 0, usage = 0, usage_min = 0, usage_max = 0;
wmem_allocator_t *scope = wmem_file_scope();
memset(&field, 0, sizeof(field));
- rdesc->fields = wmem_array_new(scope, sizeof(hid_field_t));
+ field.usages = wmem_array_new(scope, sizeof(guint32));
+ rdesc->fields_in = wmem_array_new(scope, sizeof(hid_field_t));
+ rdesc->fields_out = wmem_array_new(scope, sizeof(hid_field_t));
int i = 0;
while (i < rdesc->desc_length)
@@ -3317,21 +3322,45 @@ parse_report_descriptor(report_descriptor_t *rdesc)
switch (tag)
{
case USBHID_MAINITEM_TAG_INPUT:
- field.input = hid_unpack_value(data, i, size);
- HID_FIELD_SET(field, HID_INPUT);
+ field.properties = hid_unpack_value(data, i, size);
+
+ if ((defined & HID_REQUIRED_MASK) != HID_REQUIRED_MASK)
+ goto err;
+
+ /* new field */
+ wmem_array_append_one(rdesc->fields_in, field);
+
+ field.usages = wmem_array_new(scope, sizeof(guint32));
+
+ /* only keep the global items */
+ defined &= HID_GLOBAL_MASK;
break;
case USBHID_MAINITEM_TAG_OUTPUT:
- field.output = hid_unpack_value(data, i, size);
- HID_FIELD_SET(field, HID_OUTPUT);
+ field.properties = hid_unpack_value(data, i, size);
+
+ if ((defined & HID_REQUIRED_MASK) != HID_REQUIRED_MASK)
+ goto err;
+
+ wmem_array_append_one(rdesc->fields_out, field);
+
+ defined &= HID_GLOBAL_MASK;
break;
case USBHID_MAINITEM_TAG_FEATURE:
- field.feature = hid_unpack_value(data, i, size);
- HID_FIELD_SET(field, HID_FEATURE);
+ /*
+ field.properties = hid_unpack_value(data, i, size);
+ TODO
+ */
break;
- default: /* we don't care about collections starting/ending */
+ case USBHID_MAINITEM_TAG_COLLECTION:
+ /* clear usages */
+ wmem_free(scope, field.usages);
+ field.usages = wmem_array_new(scope, sizeof(guint32));
+ break;
+
+ default:
break;
}
break;
@@ -3341,35 +3370,35 @@ parse_report_descriptor(report_descriptor_t *rdesc)
{
case USBHID_GLOBALITEM_TAG_USAGE_PAGE:
field.usage_page = hid_unpack_value(data, i, size);
- HID_FIELD_SET(field, HID_USAGE_PAGE);
+ defined |= HID_USAGE_PAGE;
break;
case USBHID_GLOBALITEM_TAG_LOG_MIN:
if (hid_unpack_signed(data, i, size, &field.logical_min))
goto err;
- HID_FIELD_SET(field, HID_LOGICAL_MIN);
+ defined |= HID_LOGICAL_MIN;
break;
case USBHID_GLOBALITEM_TAG_LOG_MAX:
if (hid_unpack_signed(data, i, size, &field.logical_max))
goto err;
- HID_FIELD_SET(field, HID_LOGICAL_MAX);
+ defined |= HID_LOGICAL_MAX;
break;
case USBHID_GLOBALITEM_TAG_REPORT_SIZE:
field.report_size = hid_unpack_value(data, i, size);
- HID_FIELD_SET(field, HID_REPORT_SIZE);
+ defined |= HID_REPORT_SIZE;
break;
case USBHID_GLOBALITEM_TAG_REPORT_ID:
rdesc->uses_report_id = TRUE;
field.report_id = hid_unpack_value(data, i, size);
- HID_FIELD_SET(field, HID_REPORT_ID);
+ defined |= HID_REPORT_ID;
break;
case USBHID_GLOBALITEM_TAG_REPORT_COUNT:
field.report_count = hid_unpack_value(data, i, size);
- HID_FIELD_SET(field, HID_REPORT_COUNT);
+ defined |= HID_REPORT_COUNT;
break;
case USBHID_GLOBALITEM_TAG_PUSH:
@@ -3386,25 +3415,30 @@ parse_report_descriptor(report_descriptor_t *rdesc)
switch (tag)
{
case USBHID_LOCALITEM_TAG_USAGE:
- /* invalid - every item must have a usage page */
- if (!(field.defined & HID_USAGE_PAGE))
+ if (!(defined & HID_USAGE_PAGE))
return FALSE;
- /* new field */
- wmem_array_append_one(rdesc->fields, field);
+ usage = hid_unpack_value(data, i, size);
- /* only keep the global items */
- field.defined &= HID_GLOBAL_MASK;
+ wmem_array_append_one(field.usages, usage);
break;
case USBHID_LOCALITEM_TAG_USAGE_MIN:
- field.usage_min = hid_unpack_value(data, i, size);
- HID_FIELD_SET(field, HID_USAGE_MIN);
+ usage_min = hid_unpack_value(data, i, size);
+ defined |= HID_USAGE_MIN;
break;
case USBHID_LOCALITEM_TAG_USAGE_MAX:
- field.usage_max = hid_unpack_value(data, i, size);
- HID_FIELD_SET(field, HID_USAGE_MAX);
+ if (!(defined & HID_USAGE_MIN))
+ return FALSE;
+
+ usage_max = hid_unpack_value(data, i, size);
+
+ wmem_array_grow(field.usages, usage_max - usage_min);
+ for (guint32 j = usage_min; j < usage_max; j++)
+ wmem_array_append_one(field.usages, j);
+
+ defined ^= HID_USAGE_MIN;
break;
default: /* TODO */
@@ -3422,7 +3456,14 @@ parse_report_descriptor(report_descriptor_t *rdesc)
return TRUE;
err:
- wmem_free(scope, rdesc->fields);
+ for (unsigned int j = 0; j < wmem_array_get_count(rdesc->fields_in); j++)
+ wmem_free(scope, ((hid_field_t*) wmem_array_index(rdesc->fields_in, j))->usages);
+
+ for (unsigned int j = 0; j < wmem_array_get_count(rdesc->fields_out); j++)
+ wmem_free(scope, ((hid_field_t*) wmem_array_index(rdesc->fields_out, j))->usages);
+
+ wmem_free(scope, rdesc->fields_in);
+ wmem_free(scope, rdesc->fields_out);
return FALSE;
}