summaryrefslogtreecommitdiff
path: root/drivers/usb/input/hid-core.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/input/hid-core.c')
-rw-r--r--drivers/usb/input/hid-core.c132
1 files changed, 97 insertions, 35 deletions
diff --git a/drivers/usb/input/hid-core.c b/drivers/usb/input/hid-core.c
index ca7b3f198390..6f82975e4838 100644
--- a/drivers/usb/input/hid-core.c
+++ b/drivers/usb/input/hid-core.c
@@ -127,18 +127,41 @@ static int open_collection(struct hid_parser *parser, unsigned type)
usage = parser->local.usage[0];
- if (type == HID_COLLECTION_APPLICATION
- && parser->device->maxapplication < HID_MAX_APPLICATIONS)
- parser->device->application[parser->device->maxapplication++] = usage;
-
if (parser->collection_stack_ptr == HID_COLLECTION_STACK_SIZE) {
dbg("collection stack overflow");
return -1;
}
- collection = parser->collection_stack + parser->collection_stack_ptr++;
+ if (parser->device->maxcollection == parser->device->collection_size) {
+ collection = kmalloc(sizeof(struct hid_collection) *
+ parser->device->collection_size * 2,
+ GFP_KERNEL);
+ if (collection == NULL) {
+ dbg("failed to reallocate collection array");
+ return -1;
+ }
+ memcpy(collection, parser->device->collection,
+ sizeof(struct hid_collection) *
+ parser->device->collection_size);
+ memset(collection + parser->device->collection_size, 0,
+ sizeof(struct hid_collection) *
+ parser->device->collection_size);
+ kfree(parser->device->collection);
+ parser->device->collection = collection;
+ parser->device->collection_size *= 2;
+ }
+
+ parser->collection_stack[parser->collection_stack_ptr++] =
+ parser->device->maxcollection;
+
+ collection = parser->device->collection +
+ parser->device->maxcollection++;
collection->type = type;
collection->usage = usage;
+ collection->level = parser->collection_stack_ptr - 1;
+
+ if (type == HID_COLLECTION_APPLICATION)
+ parser->device->maxapplication++;
return 0;
}
@@ -166,8 +189,8 @@ static unsigned hid_lookup_collection(struct hid_parser *parser, unsigned type)
{
int n;
for (n = parser->collection_stack_ptr - 1; n >= 0; n--)
- if (parser->collection_stack[n].type == type)
- return parser->collection_stack[n].usage;
+ if (parser->device->collection[parser->collection_stack[n]].type == type)
+ return parser->device->collection[parser->collection_stack[n]].usage;
return 0; /* we know nothing about this usage type */
}
@@ -181,7 +204,11 @@ static int hid_add_usage(struct hid_parser *parser, unsigned usage)
dbg("usage index exceeded");
return -1;
}
- parser->local.usage[parser->local.usage_index++] = usage;
+ parser->local.usage[parser->local.usage_index] = usage;
+ parser->local.collection_index[parser->local.usage_index] =
+ parser->collection_stack_ptr ?
+ parser->collection_stack[parser->collection_stack_ptr - 1] : 0;
+ parser->local.usage_index++;
return 0;
}
@@ -221,8 +248,11 @@ static int hid_add_field(struct hid_parser *parser, unsigned report_type, unsign
field->logical = hid_lookup_collection(parser, HID_COLLECTION_LOGICAL);
field->application = hid_lookup_collection(parser, HID_COLLECTION_APPLICATION);
- for (i = 0; i < usages; i++)
+ for (i = 0; i < usages; i++) {
field->usage[i].hid = parser->local.usage[i];
+ field->usage[i].collection_index =
+ parser->local.collection_index[i];
+ }
field->maxusage = usages;
field->flags = flags;
@@ -460,7 +490,7 @@ static int hid_parser_main(struct hid_parser *parser, struct hid_item *item)
switch (item->tag) {
case HID_MAIN_ITEM_TAG_BEGIN_COLLECTION:
- ret = open_collection(parser, data & 3);
+ ret = open_collection(parser, data & 0xff);
break;
case HID_MAIN_ITEM_TAG_END_COLLECTION:
ret = close_collection(parser);
@@ -621,17 +651,30 @@ static struct hid_device *hid_parse_report(__u8 *start, unsigned size)
return NULL;
memset(device, 0, sizeof(struct hid_device));
+ if (!(device->collection = kmalloc(sizeof(struct hid_collection) *
+ HID_DEFAULT_NUM_COLLECTIONS,
+ GFP_KERNEL))) {
+ kfree(device);
+ return NULL;
+ }
+ memset(device->collection, 0, sizeof(struct hid_collection) *
+ HID_DEFAULT_NUM_COLLECTIONS);
+ device->collection_size = HID_DEFAULT_NUM_COLLECTIONS;
+
for (i = 0; i < HID_REPORT_TYPES; i++)
INIT_LIST_HEAD(&device->report_enum[i].report_list);
if (!(device->rdesc = (__u8 *)kmalloc(size, GFP_KERNEL))) {
+ kfree(device->collection);
kfree(device);
return NULL;
}
memcpy(device->rdesc, start, size);
+ device->rsize = size;
if (!(parser = kmalloc(sizeof(struct hid_parser), GFP_KERNEL))) {
kfree(device->rdesc);
+ kfree(device->collection);
kfree(device);
return NULL;
}
@@ -643,6 +686,8 @@ static struct hid_device *hid_parse_report(__u8 *start, unsigned size)
if (item.format != HID_ITEM_FORMAT_SHORT) {
dbg("unexpected long global item");
+ kfree(device->rdesc);
+ kfree(device->collection);
hid_free_device(device);
kfree(parser);
return NULL;
@@ -651,6 +696,8 @@ static struct hid_device *hid_parse_report(__u8 *start, unsigned size)
if (dispatch_type[item.type](parser, &item)) {
dbg("item %u %u %u %u parsing failed\n",
item.format, (unsigned)item.size, (unsigned)item.type, (unsigned)item.tag);
+ kfree(device->rdesc);
+ kfree(device->collection);
hid_free_device(device);
kfree(parser);
return NULL;
@@ -659,12 +706,16 @@ static struct hid_device *hid_parse_report(__u8 *start, unsigned size)
if (start == end) {
if (parser->collection_stack_ptr) {
dbg("unbalanced collection at end of report description");
+ kfree(device->rdesc);
+ kfree(device->collection);
hid_free_device(device);
kfree(parser);
return NULL;
}
if (parser->local.delimiter_depth) {
dbg("unbalanced delimiter at end of report description");
+ kfree(device->rdesc);
+ kfree(device->collection);
hid_free_device(device);
kfree(parser);
return NULL;
@@ -675,6 +726,8 @@ static struct hid_device *hid_parse_report(__u8 *start, unsigned size)
}
dbg("item fetching failed at offset %d\n", (int)(end - start));
+ kfree(device->rdesc);
+ kfree(device->collection);
hid_free_device(device);
kfree(parser);
return NULL;
@@ -1284,6 +1337,10 @@ void hid_init_reports(struct hid_device *hid)
#define USB_DEVICE_ID_ATEN_2PORTKVM 0x2204
#define USB_DEVICE_ID_ATEN_4PORTKVM 0x2205
+#define USB_VENDOR_ID_MGE 0x0463
+#define USB_DEVICE_ID_MGE_UPS 0xffff
+#define USB_DEVICE_ID_MGE_UPS1 0x0001
+
struct hid_blacklist {
__u16 idVendor;
__u16 idProduct;
@@ -1301,6 +1358,8 @@ struct hid_blacklist {
{ USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_CS124U, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_2PORTKVM, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_4PORTKVM, HID_QUIRK_NOGET },
+ { USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS, HID_QUIRK_HIDDEV },
+ { USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS1, HID_QUIRK_HIDDEV },
{ 0, 0 }
};
@@ -1438,6 +1497,27 @@ fail:
return NULL;
}
+static void hid_disconnect(struct usb_device *dev, void *ptr)
+{
+ struct hid_device *hid = ptr;
+
+ usb_unlink_urb(hid->urbin);
+ usb_unlink_urb(hid->urbout);
+ usb_unlink_urb(hid->urbctrl);
+
+ if (hid->claimed & HID_CLAIMED_INPUT)
+ hidinput_disconnect(hid);
+ if (hid->claimed & HID_CLAIMED_HIDDEV)
+ hiddev_disconnect(hid);
+
+ usb_free_urb(hid->urbin);
+ usb_free_urb(hid->urbctrl);
+ if (hid->urbout)
+ usb_free_urb(hid->urbout);
+
+ hid_free_device(hid);
+}
+
static void* hid_probe(struct usb_device *dev, unsigned int ifnum,
const struct usb_device_id *id)
{
@@ -1462,7 +1542,7 @@ static void* hid_probe(struct usb_device *dev, unsigned int ifnum,
hid->claimed |= HID_CLAIMED_HIDDEV;
if (!hid->claimed) {
- hid_free_device(hid);
+ hid_disconnect(dev, hid);
return NULL;
}
@@ -1476,11 +1556,14 @@ static void* hid_probe(struct usb_device *dev, unsigned int ifnum,
printk("hiddev%d", hid->minor);
c = "Device";
- for (i = 0; i < hid->maxapplication; i++)
- if ((hid->application[i] & 0xffff) < ARRAY_SIZE(hid_types)) {
- c = hid_types[hid->application[i] & 0xffff];
+ for (i = 0; i < hid->maxcollection; i++) {
+ if (hid->collection[i].type == HID_COLLECTION_APPLICATION &&
+ (hid->collection[i].usage & HID_USAGE_PAGE) == HID_UP_GENDESK &&
+ (hid->collection[i].usage & 0xffff) < ARRAY_SIZE(hid_types)) {
+ c = hid_types[hid->collection[i].usage & 0xffff];
break;
}
+ }
usb_make_path(dev, path, 63);
@@ -1490,27 +1573,6 @@ static void* hid_probe(struct usb_device *dev, unsigned int ifnum,
return hid;
}
-static void hid_disconnect(struct usb_device *dev, void *ptr)
-{
- struct hid_device *hid = ptr;
-
- usb_unlink_urb(hid->urbin);
- usb_unlink_urb(hid->urbout);
- usb_unlink_urb(hid->urbctrl);
-
- if (hid->claimed & HID_CLAIMED_INPUT)
- hidinput_disconnect(hid);
- if (hid->claimed & HID_CLAIMED_HIDDEV)
- hiddev_disconnect(hid);
-
- usb_free_urb(hid->urbin);
- usb_free_urb(hid->urbctrl);
- if (hid->urbout)
- usb_free_urb(hid->urbout);
-
- hid_free_device(hid);
-}
-
static struct usb_device_id hid_usb_ids [] = {
{ match_flags: USB_DEVICE_ID_MATCH_INT_CLASS,
bInterfaceClass: USB_INTERFACE_CLASS_HID },