* USB HID support for Linux
*
* Copyright (c) 1999 Andreas Gal
- * Copyright (c) 2000-2001 Vojtech Pavlik <vojtech@suse.cz>
+ * Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
+ * Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
*/
/*
#include <asm/unaligned.h>
#include <asm/byteorder.h>
#include <linux/input.h>
+#include <linux/wait.h>
#undef DEBUG
#undef DEBUG_DATA
* Version Information
*/
-#define DRIVER_VERSION "v2.0"
+#define DRIVER_VERSION "v2.6"
#define DRIVER_AUTHOR "Andreas Gal, Vojtech Pavlik"
#define DRIVER_DESC "USB HID core driver"
#define DRIVER_LICENSE "GPL"
static char *hid_types[] = {"Device", "Pointer", "Mouse", "Device", "Joystick",
"Gamepad", "Keyboard", "Keypad", "Multi-Axis Controller"};
+/*
+ * Module parameters.
+ */
+
+static unsigned int hid_mousepoll_interval;
+module_param_named(mousepoll, hid_mousepoll_interval, uint, 0644);
+MODULE_PARM_DESC(mousepoll, "Polling interval of mice");
/*
* Register a new report for a device.
if (report_enum->report_id_hash[id])
return report_enum->report_id_hash[id];
- if (!(report = kmalloc(sizeof(struct hid_report), GFP_KERNEL)))
+ if (!(report = kzalloc(sizeof(struct hid_report), GFP_KERNEL)))
return NULL;
- memset(report, 0, sizeof(struct hid_report));
if (id != 0)
report_enum->numbered = 1;
return NULL;
}
- if (!(field = kmalloc(sizeof(struct hid_field) + usages * sizeof(struct hid_usage)
+ if (!(field = kzalloc(sizeof(struct hid_field) + usages * sizeof(struct hid_usage)
+ values * sizeof(unsigned), GFP_KERNEL))) return NULL;
- memset(field, 0, sizeof(struct hid_field) + usages * sizeof(struct hid_usage)
- + values * sizeof(unsigned));
-
- report->field[report->maxfield++] = field;
+ field->index = report->maxfield++;
+ report->field[field->index] = field;
field->usage = (struct hid_usage *)(field + 1);
field->value = (unsigned *)(field->usage + usages);
field->report = report;
if (parser->device->maxcollection == parser->device->collection_size) {
collection = kmalloc(sizeof(struct hid_collection) *
- parser->device->collection_size * 2,
- GFP_KERNEL);
+ 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);
+ sizeof(struct hid_collection) *
+ parser->device->collection_size);
memset(collection + parser->device->collection_size, 0,
- sizeof(struct hid_collection) *
- parser->device->collection_size);
+ 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 +
+ 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++;
}
parser->local.usage[parser->local.usage_index] = usage;
parser->local.collection_index[parser->local.usage_index] =
- parser->collection_stack_ptr ?
+ parser->collection_stack_ptr ?
parser->collection_stack[parser->collection_stack_ptr - 1] : 0;
parser->local.usage_index++;
return 0;
dbg("logical range invalid %d %d", parser->global.logical_minimum, parser->global.logical_maximum);
return -1;
}
- usages = parser->local.usage_index;
offset = report->size;
report->size += parser->global.report_size * parser->global.report_count;
- if (usages < parser->global.report_count)
- usages = parser->global.report_count;
+ if (!parser->local.usage_index) /* Ignore padding fields */
+ return 0;
- if (usages == 0)
- return 0; /* ignore padding fields */
+ usages = max_t(int, parser->local.usage_index, parser->global.report_count);
if ((field = hid_register_field(report, usages, parser->global.report_count)) == NULL)
return 0;
}
}
- if (device->rdesc)
- kfree(device->rdesc);
+ kfree(device->rdesc);
kfree(device);
}
item->size = *start++;
item->tag = *start++;
- if ((end - start) < item->size)
+ if ((end - start) < item->size)
return NULL;
item->data.longdata = start;
start += item->size;
return start;
- }
+ }
item->format = HID_ITEM_FORMAT_SHORT;
item->size = b & 3;
return start;
case 2:
- if ((end - start) < 2)
+ if ((end - start) < 2)
return NULL;
- item->data.u16 = le16_to_cpu(get_unaligned((__u16*)start));
- start = (__u8 *)((__u16 *)start + 1);
+ item->data.u16 = le16_to_cpu(get_unaligned((__le16*)start));
+ start = (__u8 *)((__le16 *)start + 1);
return start;
case 3:
item->size++;
if ((end - start) < 4)
return NULL;
- item->data.u32 = le32_to_cpu(get_unaligned((__u32*)start));
- start = (__u8 *)((__u32 *)start + 1);
+ item->data.u32 = le32_to_cpu(get_unaligned((__le32*)start));
+ start = (__u8 *)((__le32 *)start + 1);
return start;
}
hid_parser_reserved
};
- if (!(device = kmalloc(sizeof(struct hid_device), GFP_KERNEL)))
+ if (!(device = kzalloc(sizeof(struct hid_device), GFP_KERNEL)))
return NULL;
- memset(device, 0, sizeof(struct hid_device));
- if (!(device->collection =kmalloc(sizeof(struct hid_collection) *
+ if (!(device->collection = kzalloc(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++)
memcpy(device->rdesc, start, size);
device->rsize = size;
- if (!(parser = kmalloc(sizeof(struct hid_parser), GFP_KERNEL))) {
+ if (!(parser = kzalloc(sizeof(struct hid_parser), GFP_KERNEL))) {
kfree(device->rdesc);
kfree(device->collection);
kfree(device);
return NULL;
}
- memset(parser, 0, sizeof(struct hid_parser));
parser->device = device;
end = start + size;
- while ((start = fetch_item(start, end, &item)) != 0) {
+ while ((start = fetch_item(start, end, &item)) != NULL) {
if (item.format != HID_ITEM_FORMAT_SHORT) {
dbg("unexpected long global item");
static __inline__ __u32 extract(__u8 *report, unsigned offset, unsigned n)
{
report += (offset >> 5) << 2; offset &= 31;
- return (le64_to_cpu(get_unaligned((__u64*)report)) >> offset) & ((1 << n) - 1);
+ return (le64_to_cpu(get_unaligned((__le64*)report)) >> offset) & ((1ULL << n) - 1);
}
static __inline__ void implement(__u8 *report, unsigned offset, unsigned n, __u32 value)
{
report += (offset >> 5) << 2; offset &= 31;
- put_unaligned((get_unaligned((__u64*)report)
+ put_unaligned((get_unaligned((__le64*)report)
& cpu_to_le64(~((((__u64) 1 << n) - 1) << offset)))
- | cpu_to_le64((__u64)value << offset), (__u64*)report);
+ | cpu_to_le64((__u64)value << offset), (__le64*)report);
}
/*
return -1;
}
-static void hid_process_event(struct hid_device *hid, struct hid_field *field, struct hid_usage *usage, __s32 value, struct pt_regs *regs)
+static void hid_process_event(struct hid_device *hid, struct hid_field *field, struct hid_usage *usage, __s32 value, int interrupt, struct pt_regs *regs)
{
hid_dump_input(usage, value);
if (hid->claimed & HID_CLAIMED_INPUT)
hidinput_hid_event(hid, field, usage, value, regs);
- if (hid->claimed & HID_CLAIMED_HIDDEV)
+ if (hid->claimed & HID_CLAIMED_HIDDEV && interrupt)
hiddev_hid_event(hid, field, usage, value, regs);
}
* reporting to the layer).
*/
-static void hid_input_field(struct hid_device *hid, struct hid_field *field, __u8 *data, struct pt_regs *regs)
+static void hid_input_field(struct hid_device *hid, struct hid_field *field, __u8 *data, int interrupt, struct pt_regs *regs)
{
unsigned n;
unsigned count = field->report_count;
unsigned size = field->report_size;
__s32 min = field->logical_minimum;
__s32 max = field->logical_maximum;
- __s32 value[count]; /* WARNING: gcc specific */
+ __s32 *value;
+
+ if (!(value = kmalloc(sizeof(__s32) * count, GFP_ATOMIC)))
+ return;
for (n = 0; n < count; n++) {
if (!(field->flags & HID_MAIN_ITEM_VARIABLE) /* Ignore report if ErrorRollOver */
&& value[n] >= min && value[n] <= max
&& field->usage[value[n] - min].hid == HID_UP_KEYBOARD + 1)
- return;
+ goto exit;
}
for (n = 0; n < count; n++) {
if (HID_MAIN_ITEM_VARIABLE & field->flags) {
-
- if (field->flags & HID_MAIN_ITEM_RELATIVE) {
- if (!value[n])
- continue;
- } else {
- if (value[n] == field->value[n])
- continue;
- }
- hid_process_event(hid, field, &field->usage[n], value[n], regs);
+ hid_process_event(hid, field, &field->usage[n], value[n], interrupt, regs);
continue;
}
if (field->value[n] >= min && field->value[n] <= max
&& field->usage[field->value[n] - min].hid
&& search(value, field->value[n], count))
- hid_process_event(hid, field, &field->usage[field->value[n] - min], 0, regs);
+ hid_process_event(hid, field, &field->usage[field->value[n] - min], 0, interrupt, regs);
if (value[n] >= min && value[n] <= max
&& field->usage[value[n] - min].hid
&& search(field->value, value[n], count))
- hid_process_event(hid, field, &field->usage[value[n] - min], 1, regs);
+ hid_process_event(hid, field, &field->usage[value[n] - min], 1, interrupt, regs);
}
memcpy(field->value, value, count * sizeof(__s32));
+exit:
+ kfree(value);
}
-static int hid_input_report(int type, struct urb *urb, struct pt_regs *regs)
+static int hid_input_report(int type, struct urb *urb, int interrupt, struct pt_regs *regs)
{
struct hid_device *hid = urb->context;
struct hid_report_enum *report_enum = hid->report_enum + type;
if (len < size) {
dbg("report %d is too short, (%d < %d)", report->id, len, size);
- return -1;
+ memset(data + len, 0, size - len);
}
if (hid->claimed & HID_CLAIMED_HIDDEV)
hiddev_report_event(hid, report);
for (n = 0; n < report->maxfield; n++)
- hid_input_field(hid, report->field[n], data, regs);
+ hid_input_field(hid, report->field[n], data, interrupt, regs);
if (hid->claimed & HID_CLAIMED_INPUT)
hidinput_report_event(hid, report);
return 0;
}
+/*
+ * Input submission and I/O error handler.
+ */
+
+static void hid_io_error(struct hid_device *hid);
+
+/* Start up the input URB */
+static int hid_start_in(struct hid_device *hid)
+{
+ unsigned long flags;
+ int rc = 0;
+
+ spin_lock_irqsave(&hid->inlock, flags);
+ if (hid->open > 0 && !test_bit(HID_SUSPENDED, &hid->iofl) &&
+ !test_and_set_bit(HID_IN_RUNNING, &hid->iofl)) {
+ rc = usb_submit_urb(hid->urbin, GFP_ATOMIC);
+ if (rc != 0)
+ clear_bit(HID_IN_RUNNING, &hid->iofl);
+ }
+ spin_unlock_irqrestore(&hid->inlock, flags);
+ return rc;
+}
+
+/* I/O retry timer routine */
+static void hid_retry_timeout(unsigned long _hid)
+{
+ struct hid_device *hid = (struct hid_device *) _hid;
+
+ dev_dbg(&hid->intf->dev, "retrying intr urb\n");
+ if (hid_start_in(hid))
+ hid_io_error(hid);
+}
+
+/* Workqueue routine to reset the device */
+static void hid_reset(void *_hid)
+{
+ struct hid_device *hid = (struct hid_device *) _hid;
+ int rc_lock, rc;
+
+ dev_dbg(&hid->intf->dev, "resetting device\n");
+ rc = rc_lock = usb_lock_device_for_reset(hid->dev, hid->intf);
+ if (rc_lock >= 0) {
+ rc = usb_reset_device(hid->dev);
+ if (rc_lock)
+ usb_unlock_device(hid->dev);
+ }
+ clear_bit(HID_RESET_PENDING, &hid->iofl);
+
+ if (rc == 0) {
+ hid->retry_delay = 0;
+ if (hid_start_in(hid))
+ hid_io_error(hid);
+ } else if (!(rc == -ENODEV || rc == -EHOSTUNREACH || rc == -EINTR))
+ err("can't reset device, %s-%s/input%d, status %d",
+ hid->dev->bus->bus_name,
+ hid->dev->devpath,
+ hid->ifnum, rc);
+}
+
+/* Main I/O error handler */
+static void hid_io_error(struct hid_device *hid)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&hid->inlock, flags);
+
+ /* Stop when disconnected */
+ if (usb_get_intfdata(hid->intf) == NULL)
+ goto done;
+
+ /* When an error occurs, retry at increasing intervals */
+ if (hid->retry_delay == 0) {
+ hid->retry_delay = 13; /* Then 26, 52, 104, 104, ... */
+ hid->stop_retry = jiffies + msecs_to_jiffies(1000);
+ } else if (hid->retry_delay < 100)
+ hid->retry_delay *= 2;
+
+ if (time_after(jiffies, hid->stop_retry)) {
+
+ /* Retries failed, so do a port reset */
+ if (!test_and_set_bit(HID_RESET_PENDING, &hid->iofl)) {
+ if (schedule_work(&hid->reset_work))
+ goto done;
+ clear_bit(HID_RESET_PENDING, &hid->iofl);
+ }
+ }
+
+ mod_timer(&hid->io_retry,
+ jiffies + msecs_to_jiffies(hid->retry_delay));
+done:
+ spin_unlock_irqrestore(&hid->inlock, flags);
+}
+
/*
* Input interrupt completion handler.
*/
int status;
switch (urb->status) {
- case 0: /* success */
- hid_input_report(HID_INPUT_REPORT, urb, regs);
- break;
- case -ECONNRESET: /* unlink */
- case -ENOENT:
- case -ESHUTDOWN:
- return;
- default: /* error */
- dbg("nonzero status in input irq %d", urb->status);
+ case 0: /* success */
+ hid->retry_delay = 0;
+ hid_input_report(HID_INPUT_REPORT, urb, 1, regs);
+ break;
+ case -ECONNRESET: /* unlink */
+ case -ENOENT:
+ case -ESHUTDOWN: /* unplug */
+ clear_bit(HID_IN_RUNNING, &hid->iofl);
+ return;
+ case -EILSEQ: /* protocol error or unplug */
+ case -EPROTO: /* protocol error or unplug */
+ case -ETIMEDOUT: /* NAK */
+ clear_bit(HID_IN_RUNNING, &hid->iofl);
+ hid_io_error(hid);
+ return;
+ default: /* error */
+ warn("input irq status %d received", urb->status);
+ }
+
+ status = usb_submit_urb(urb, SLAB_ATOMIC);
+ if (status) {
+ clear_bit(HID_IN_RUNNING, &hid->iofl);
+ if (status != -EPERM) {
+ err("can't resubmit intr, %s-%s/input%d, status %d",
+ hid->dev->bus->bus_name,
+ hid->dev->devpath,
+ hid->ifnum, status);
+ hid_io_error(hid);
+ }
}
-
- status = usb_submit_urb (urb, SLAB_ATOMIC);
- if (status)
- err ("can't resubmit intr, %s-%s/input%d, status %d",
- hid->dev->bus->bus_name, hid->dev->devpath,
- hid->ifnum, status);
}
/*
for (n = 0; n < count; n++) {
if (field->logical_minimum < 0) /* signed values */
implement(data, offset + n * size, size, s32ton(field->value[n], size));
- else /* unsigned values */
+ else /* unsigned values */
implement(data, offset + n * size, size, field->value[n]);
}
}
return 0;
}
-int hid_find_field(struct hid_device *hid, unsigned int type, unsigned int code, struct hid_field **field)
-{
- struct hid_report_enum *report_enum = hid->report_enum + HID_OUTPUT_REPORT;
- struct list_head *list = report_enum->report_list.next;
- int i, j;
-
- while (list != &report_enum->report_list) {
- struct hid_report *report = (struct hid_report *) list;
- list = list->next;
- for (i = 0; i < report->maxfield; i++) {
- *field = report->field[i];
- for (j = 0; j < (*field)->maxusage; j++)
- if ((*field)->usage[j].type == type && (*field)->usage[j].code == code)
- return j;
- }
- }
- return -1;
-}
-
/*
- * Find a report with a specified HID usage.
+ * Find a report field with a specified HID usage.
*/
-int hid_find_report_by_usage(struct hid_device *hid, __u32 wanted_usage, struct hid_report **report, int type)
-{
- struct hid_report_enum *report_enum = hid->report_enum + type;
- struct list_head *list = report_enum->report_list.next;
- int i, j;
-
- while (list != &report_enum->report_list) {
- *report = (struct hid_report *) list;
- list = list->next;
- for (i = 0; i < (*report)->maxfield; i++) {
- struct hid_field *field = (*report)->field[i];
- for (j = 0; j < field->maxusage; j++)
- if (field->logical == wanted_usage)
- return j;
- }
- }
- return -1;
-}
-
-#if 0
-static int hid_find_field_in_report(struct hid_report *report, __u32 wanted_usage, struct hid_field **field)
+struct hid_field *hid_find_field_by_usage(struct hid_device *hid, __u32 wanted_usage, int type)
{
- int i, j;
-
- for (i = 0; i < report->maxfield; i++) {
- *field = report->field[i];
- for (j = 0; j < (*field)->maxusage; j++)
- if ((*field)->usage[j].hid == wanted_usage)
- return j;
- }
+ struct hid_report *report;
+ int i;
- return -1;
+ list_for_each_entry(report, &hid->report_enum[type].report_list, list)
+ for (i = 0; i < report->maxfield; i++)
+ if (report->field[i]->logical == wanted_usage)
+ return report->field[i];
+ return NULL;
}
-#endif
static int hid_submit_out(struct hid_device *hid)
{
if (maxpacket > 0) {
padlen = (len + maxpacket - 1) / maxpacket;
padlen *= maxpacket;
- if (padlen > HID_BUFFER_SIZE)
- padlen = HID_BUFFER_SIZE;
+ if (padlen > hid->bufsize)
+ padlen = hid->bufsize;
} else
padlen = 0;
hid->urbctrl->transfer_buffer_length = padlen;
hid->cr->wLength = cpu_to_le16(len);
dbg("submitting ctrl urb: %s wValue=0x%04x wIndex=0x%04x wLength=%u",
- hid->cr->bRequest == HID_REQ_SET_REPORT ? "Set_Report" : "Get_Report",
- hid->cr->wValue, hid->cr->wIndex, hid->cr->wLength);
+ hid->cr->bRequest == HID_REQ_SET_REPORT ? "Set_Report" : "Get_Report",
+ hid->cr->wValue, hid->cr->wIndex, hid->cr->wLength);
if (usb_submit_urb(hid->urbctrl, GFP_ATOMIC)) {
err("usb_submit_urb(ctrl) failed");
{
struct hid_device *hid = urb->context;
unsigned long flags;
+ int unplug = 0;
- if (urb->status)
- warn("output irq status %d received", urb->status);
+ switch (urb->status) {
+ case 0: /* success */
+ break;
+ case -ESHUTDOWN: /* unplug */
+ unplug = 1;
+ case -EILSEQ: /* protocol error or unplug */
+ case -EPROTO: /* protocol error or unplug */
+ case -ECONNRESET: /* unlink */
+ case -ENOENT:
+ break;
+ default: /* error */
+ warn("output irq status %d received", urb->status);
+ }
spin_lock_irqsave(&hid->outlock, flags);
- hid->outtail = (hid->outtail + 1) & (HID_OUTPUT_FIFO_SIZE - 1);
+ if (unplug)
+ hid->outtail = hid->outhead;
+ else
+ hid->outtail = (hid->outtail + 1) & (HID_OUTPUT_FIFO_SIZE - 1);
if (hid->outhead != hid->outtail) {
- hid_submit_out(hid);
+ if (hid_submit_out(hid)) {
+ clear_bit(HID_OUT_RUNNING, &hid->iofl);
+ wake_up(&hid->wait);
+ }
spin_unlock_irqrestore(&hid->outlock, flags);
return;
}
clear_bit(HID_OUT_RUNNING, &hid->iofl);
-
spin_unlock_irqrestore(&hid->outlock, flags);
-
wake_up(&hid->wait);
}
{
struct hid_device *hid = urb->context;
unsigned long flags;
-
- if (urb->status)
- warn("ctrl urb status %d received", urb->status);
+ int unplug = 0;
spin_lock_irqsave(&hid->ctrllock, flags);
- if (hid->ctrl[hid->ctrltail].dir == USB_DIR_IN)
- hid_input_report(hid->ctrl[hid->ctrltail].report->type, urb, regs);
+ switch (urb->status) {
+ case 0: /* success */
+ if (hid->ctrl[hid->ctrltail].dir == USB_DIR_IN)
+ hid_input_report(hid->ctrl[hid->ctrltail].report->type, urb, 0, regs);
+ break;
+ case -ESHUTDOWN: /* unplug */
+ unplug = 1;
+ case -EILSEQ: /* protocol error or unplug */
+ case -EPROTO: /* protocol error or unplug */
+ case -ECONNRESET: /* unlink */
+ case -ENOENT:
+ case -EPIPE: /* report not available */
+ break;
+ default: /* error */
+ warn("ctrl urb status %d received", urb->status);
+ }
- hid->ctrltail = (hid->ctrltail + 1) & (HID_CONTROL_FIFO_SIZE - 1);
+ if (unplug)
+ hid->ctrltail = hid->ctrlhead;
+ else
+ hid->ctrltail = (hid->ctrltail + 1) & (HID_CONTROL_FIFO_SIZE - 1);
if (hid->ctrlhead != hid->ctrltail) {
- hid_submit_ctrl(hid);
+ if (hid_submit_ctrl(hid)) {
+ clear_bit(HID_CTRL_RUNNING, &hid->iofl);
+ wake_up(&hid->wait);
+ }
spin_unlock_irqrestore(&hid->ctrllock, flags);
return;
}
clear_bit(HID_CTRL_RUNNING, &hid->iofl);
-
spin_unlock_irqrestore(&hid->ctrllock, flags);
-
wake_up(&hid->wait);
}
hid->outhead = head;
if (!test_and_set_bit(HID_OUT_RUNNING, &hid->iofl))
- hid_submit_out(hid);
+ if (hid_submit_out(hid))
+ clear_bit(HID_OUT_RUNNING, &hid->iofl);
spin_unlock_irqrestore(&hid->outlock, flags);
return;
hid->ctrlhead = head;
if (!test_and_set_bit(HID_CTRL_RUNNING, &hid->iofl))
- hid_submit_ctrl(hid);
+ if (hid_submit_ctrl(hid))
+ clear_bit(HID_CTRL_RUNNING, &hid->iofl);
spin_unlock_irqrestore(&hid->ctrllock, flags);
}
int hid_wait_io(struct hid_device *hid)
{
- DECLARE_WAITQUEUE(wait, current);
- int timeout = 10*HZ;
-
- set_current_state(TASK_UNINTERRUPTIBLE);
- add_wait_queue(&hid->wait, &wait);
-
- while (timeout && (test_bit(HID_CTRL_RUNNING, &hid->iofl) ||
- test_bit(HID_OUT_RUNNING, &hid->iofl)))
- timeout = schedule_timeout(timeout);
-
- set_current_state(TASK_RUNNING);
- remove_wait_queue(&hid->wait, &wait);
-
- if (!timeout) {
+ if (!wait_event_timeout(hid->wait, (!test_bit(HID_CTRL_RUNNING, &hid->iofl) &&
+ !test_bit(HID_OUT_RUNNING, &hid->iofl)),
+ 10*HZ)) {
dbg("timeout waiting for ctrl or out queue to clear");
return -1;
}
return 0;
}
-static int hid_get_class_descriptor(struct usb_device *dev, int ifnum,
- unsigned char type, void *buf, int size)
+static int hid_set_idle(struct usb_device *dev, int ifnum, int report, int idle)
{
- return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
- USB_REQ_GET_DESCRIPTOR, USB_RECIP_INTERFACE | USB_DIR_IN,
- (type << 8), ifnum, buf, size, HZ * USB_CTRL_GET_TIMEOUT);
+ return usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+ HID_REQ_SET_IDLE, USB_TYPE_CLASS | USB_RECIP_INTERFACE, (idle << 8) | report,
+ ifnum, NULL, 0, USB_CTRL_SET_TIMEOUT);
}
-int hid_open(struct hid_device *hid)
+static int hid_get_class_descriptor(struct usb_device *dev, int ifnum,
+ unsigned char type, void *buf, int size)
{
- if (hid->open++)
- return 0;
+ int result, retries = 4;
- hid->urbin->dev = hid->dev;
+ memset(buf,0,size); // Make sure we parse really received data
- if (usb_submit_urb(hid->urbin, GFP_KERNEL))
- return -EIO;
+ do {
+ result = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+ USB_REQ_GET_DESCRIPTOR, USB_RECIP_INTERFACE | USB_DIR_IN,
+ (type << 8), ifnum, buf, size, USB_CTRL_GET_TIMEOUT);
+ retries--;
+ } while (result < size && retries);
+ return result;
+}
+int hid_open(struct hid_device *hid)
+{
+ ++hid->open;
+ if (hid_start_in(hid))
+ hid_io_error(hid);
return 0;
}
void hid_close(struct hid_device *hid)
{
if (!--hid->open)
- usb_unlink_urb(hid->urbin);
+ usb_kill_urb(hid->urbin);
}
+#define USB_VENDOR_ID_PANJIT 0x134c
+
+#define USB_VENDOR_ID_SILVERCREST 0x062a
+#define USB_DEVICE_ID_SILVERCREST_KB 0x0201
+
/*
* Initialize all reports
*/
void hid_init_reports(struct hid_device *hid)
{
- struct hid_report_enum *report_enum;
struct hid_report *report;
- struct list_head *list;
int err, ret;
- /*
- * The Set_Idle request is supposed to affect only the
- * "Interrupt In" pipe. Unfortunately, buggy devices such as
- * the BTC keyboard (ID 046e:5303) the request also affects
- * Get_Report requests on the control pipe. In the worst
- * case, if the device was put on idle for an indefinite
- * amount of time (as we do below) and there are no input
- * events to report, the Get_Report requests will just hang
- * until we get a USB timeout. To avoid this, we temporarily
- * establish a minimal idle time of 1ms. This shouldn't hurt
- * bugfree devices and will cause a worst-case extra delay of
- * 1ms for buggy ones.
- */
- usb_control_msg(hid->dev, usb_sndctrlpipe(hid->dev, 0),
- HID_REQ_SET_IDLE, USB_TYPE_CLASS | USB_RECIP_INTERFACE, (1 << 8),
- hid->ifnum, NULL, 0, HZ * USB_CTRL_SET_TIMEOUT);
-
- report_enum = hid->report_enum + HID_INPUT_REPORT;
- list = report_enum->report_list.next;
- while (list != &report_enum->report_list) {
- report = (struct hid_report *) list;
+ list_for_each_entry(report, &hid->report_enum[HID_INPUT_REPORT].report_list, list)
hid_submit_report(hid, report, USB_DIR_IN);
- list = list->next;
- }
- report_enum = hid->report_enum + HID_FEATURE_REPORT;
- list = report_enum->report_list.next;
- while (list != &report_enum->report_list) {
- report = (struct hid_report *) list;
+ list_for_each_entry(report, &hid->report_enum[HID_FEATURE_REPORT].report_list, list)
hid_submit_report(hid, report, USB_DIR_IN);
- list = list->next;
- }
err = 0;
ret = hid_wait_io(hid);
while (ret) {
err |= ret;
if (test_bit(HID_CTRL_RUNNING, &hid->iofl))
- usb_unlink_urb(hid->urbctrl);
+ usb_kill_urb(hid->urbctrl);
if (test_bit(HID_OUT_RUNNING, &hid->iofl))
- usb_unlink_urb(hid->urbout);
+ usb_kill_urb(hid->urbout);
ret = hid_wait_io(hid);
}
if (err)
- warn("timeout initializing reports\n");
-
- report_enum = hid->report_enum + HID_INPUT_REPORT;
- list = report_enum->report_list.next;
- while (list != &report_enum->report_list) {
- report = (struct hid_report *) list;
- usb_control_msg(hid->dev, usb_sndctrlpipe(hid->dev, 0),
- HID_REQ_SET_IDLE, USB_TYPE_CLASS | USB_RECIP_INTERFACE, report->id,
- hid->ifnum, NULL, 0, HZ * USB_CTRL_SET_TIMEOUT);
- list = list->next;
- }
+ warn("timeout initializing reports");
}
#define USB_VENDOR_ID_WACOM 0x056a
#define USB_DEVICE_ID_WACOM_INTUOS 0x0020
#define USB_DEVICE_ID_WACOM_PL 0x0030
#define USB_DEVICE_ID_WACOM_INTUOS2 0x0040
-#define USB_DEVICE_ID_WACOM_VOLITO 0x0060
-#define USB_DEVICE_ID_WACOM_PTU 0x0003
+#define USB_DEVICE_ID_WACOM_VOLITO 0x0060
+#define USB_DEVICE_ID_WACOM_PTU 0x0003
+#define USB_DEVICE_ID_WACOM_INTUOS3 0x00B0
+#define USB_DEVICE_ID_WACOM_CINTIQ 0x003F
+#define USB_DEVICE_ID_WACOM_DTF 0x00C0
-#define USB_VENDOR_ID_KBGEAR 0x084e
-#define USB_DEVICE_ID_KBGEAR_JAMSTUDIO 0x1001
+#define USB_VENDOR_ID_ACECAD 0x0460
+#define USB_DEVICE_ID_ACECAD_FLAIR 0x0004
+#define USB_DEVICE_ID_ACECAD_302 0x0008
+
+#define USB_VENDOR_ID_KBGEAR 0x084e
+#define USB_DEVICE_ID_KBGEAR_JAMSTUDIO 0x1001
#define USB_VENDOR_ID_AIPTEK 0x08ca
#define USB_DEVICE_ID_AIPTEK_01 0x0001
#define USB_DEVICE_ID_POWERMATE 0x0410
#define USB_DEVICE_ID_SOUNDKNOB 0x04AA
-#define USB_VENDOR_ID_ATEN 0x0557
-#define USB_DEVICE_ID_ATEN_UC100KM 0x2004
-#define USB_DEVICE_ID_ATEN_CS124U 0x2202
-#define USB_DEVICE_ID_ATEN_2PORTKVM 0x2204
-#define USB_DEVICE_ID_ATEN_4PORTKVM 0x2205
-#define USB_DEVICE_ID_ATEN_4PORTKVMC 0x2208
+#define USB_VENDOR_ID_ATEN 0x0557
+#define USB_DEVICE_ID_ATEN_UC100KM 0x2004
+#define USB_DEVICE_ID_ATEN_CS124U 0x2202
+#define USB_DEVICE_ID_ATEN_2PORTKVM 0x2204
+#define USB_DEVICE_ID_ATEN_4PORTKVM 0x2205
+#define USB_DEVICE_ID_ATEN_4PORTKVMC 0x2208
-#define USB_VENDOR_ID_TOPMAX 0x0663
-#define USB_DEVICE_ID_TOPMAX_COBRAPAD 0x0103
+#define USB_VENDOR_ID_TOPMAX 0x0663
+#define USB_DEVICE_ID_TOPMAX_COBRAPAD 0x0103
-#define USB_VENDOR_ID_HAPP 0x078b
-#define USB_DEVICE_ID_UGCI_DRIVING 0x0010
-#define USB_DEVICE_ID_UGCI_FLYING 0x0020
-#define USB_DEVICE_ID_UGCI_FIGHTING 0x0030
+#define USB_VENDOR_ID_HAPP 0x078b
+#define USB_DEVICE_ID_UGCI_DRIVING 0x0010
+#define USB_DEVICE_ID_UGCI_FLYING 0x0020
+#define USB_DEVICE_ID_UGCI_FIGHTING 0x0030
-#define USB_VENDOR_ID_MGE 0x0463
-#define USB_DEVICE_ID_MGE_UPS 0xffff
-#define USB_DEVICE_ID_MGE_UPS1 0x0001
+#define USB_VENDOR_ID_MGE 0x0463
+#define USB_DEVICE_ID_MGE_UPS 0xffff
+#define USB_DEVICE_ID_MGE_UPS1 0x0001
#define USB_VENDOR_ID_ONTRAK 0x0a07
#define USB_DEVICE_ID_ONTRAK_ADU100 0x0064
-#define USB_VENDOR_ID_TANGTOP 0x0d3d
-#define USB_DEVICE_ID_TANGTOP_USBPS2 0x0001
+#define USB_VENDOR_ID_TANGTOP 0x0d3d
+#define USB_DEVICE_ID_TANGTOP_USBPS2 0x0001
#define USB_VENDOR_ID_ESSENTIAL_REALITY 0x0d7f
-#define USB_DEVICE_ID_ESSENTIAL_REALITY_P5 0x0100
+#define USB_DEVICE_ID_ESSENTIAL_REALITY_P5 0x0100
-#define USB_VENDOR_ID_A4TECH 0x09DA
+#define USB_VENDOR_ID_A4TECH 0x09da
#define USB_DEVICE_ID_A4TECH_WCP32PU 0x0006
+#define USB_VENDOR_ID_AASHIMA 0x06d6
+#define USB_DEVICE_ID_AASHIMA_GAMEPAD 0x0025
+#define USB_DEVICE_ID_AASHIMA_PREDATOR 0x0026
+
#define USB_VENDOR_ID_CYPRESS 0x04b4
#define USB_DEVICE_ID_CYPRESS_MOUSE 0x0001
+#define USB_DEVICE_ID_CYPRESS_HIDCOM 0x5500
+#define USB_DEVICE_ID_CYPRESS_ULTRAMOUSE 0x7417
#define USB_VENDOR_ID_BERKSHIRE 0x0c98
#define USB_DEVICE_ID_BERKSHIRE_PCWD 0x1140
#define USB_VENDOR_ID_GLAB 0x06c2
#define USB_DEVICE_ID_4_PHIDGETSERVO_30 0x0038
#define USB_DEVICE_ID_1_PHIDGETSERVO_30 0x0039
+#define USB_DEVICE_ID_8_8_8_IF_KIT 0x0045
+#define USB_DEVICE_ID_0_0_4_IF_KIT 0x0040
+#define USB_DEVICE_ID_0_8_8_IF_KIT 0x0053
#define USB_VENDOR_ID_WISEGROUP 0x0925
#define USB_DEVICE_ID_1_PHIDGETSERVO_20 0x8101
#define USB_DEVICE_ID_4_PHIDGETSERVO_20 0x8104
+#define USB_DEVICE_ID_DUAL_USB_JOYPAD 0x8866
+
+#define USB_VENDOR_ID_CODEMERCS 0x07c0
+#define USB_DEVICE_ID_CODEMERCS_IOW40 0x1500
+#define USB_DEVICE_ID_CODEMERCS_IOW24 0x1501
+#define USB_DEVICE_ID_CODEMERCS_IOW48 0x1502
+#define USB_DEVICE_ID_CODEMERCS_IOW28 0x1503
+
+#define USB_VENDOR_ID_DELORME 0x1163
+#define USB_DEVICE_ID_DELORME_EARTHMATE 0x0100
+#define USB_DEVICE_ID_DELORME_EM_LT20 0x0200
+
+#define USB_VENDOR_ID_MCC 0x09db
+#define USB_DEVICE_ID_MCC_PMD1024LS 0x0076
+#define USB_DEVICE_ID_MCC_PMD1208LS 0x007a
+
+#define USB_VENDOR_ID_CHICONY 0x04f2
+#define USB_DEVICE_ID_CHICONY_USBHUB_KB 0x0100
+
+#define USB_VENDOR_ID_BTC 0x046e
+#define USB_DEVICE_ID_BTC_KEYBOARD 0x5303
+
+#define USB_VENDOR_ID_VERNIER 0x08f7
+#define USB_DEVICE_ID_VERNIER_LABPRO 0x0001
+#define USB_DEVICE_ID_VERNIER_GOTEMP 0x0002
+#define USB_DEVICE_ID_VERNIER_SKIP 0x0003
+#define USB_DEVICE_ID_VERNIER_CYCLOPS 0x0004
+
+#define USB_VENDOR_ID_LD 0x0f11
+#define USB_DEVICE_ID_LD_CASSY 0x1000
+#define USB_DEVICE_ID_LD_POCKETCASSY 0x1010
+#define USB_DEVICE_ID_LD_MOBILECASSY 0x1020
+#define USB_DEVICE_ID_LD_JWM 0x1080
+#define USB_DEVICE_ID_LD_DMMP 0x1081
+#define USB_DEVICE_ID_LD_UMIP 0x1090
+#define USB_DEVICE_ID_LD_XRAY1 0x1100
+#define USB_DEVICE_ID_LD_XRAY2 0x1101
+#define USB_DEVICE_ID_LD_VIDEOCOM 0x1200
+#define USB_DEVICE_ID_LD_COM3LAB 0x2000
+#define USB_DEVICE_ID_LD_TELEPORT 0x2010
+#define USB_DEVICE_ID_LD_NETWORKANALYSER 0x2020
+#define USB_DEVICE_ID_LD_POWERCONTROL 0x2030
+#define USB_DEVICE_ID_LD_MACHINETEST 0x2040
+
+#define USB_VENDOR_ID_APPLE 0x05ac
+#define USB_DEVICE_ID_APPLE_POWERMOUSE 0x0304
+
+#define USB_VENDOR_ID_CHERRY 0x046a
+#define USB_DEVICE_ID_CHERRY_CYMOTION 0x0023
+
+#define USB_VENDOR_ID_HP 0x03f0
+#define USB_DEVICE_ID_HP_USBHUB_KB 0x020c
+
+#define USB_VENDOR_ID_IBM 0x04b3
+#define USB_DEVICE_ID_IBM_USBHUB_KB 0x3005
+
+#define USB_VENDOR_ID_CREATIVELABS 0x062a
+#define USB_DEVICE_ID_CREATIVELABS_SILVERCREST 0x0201
-static struct hid_blacklist {
+/*
+ * Alphabetically sorted blacklist by quirk type.
+ */
+
+static const struct hid_blacklist {
__u16 idVendor;
__u16 idProduct;
unsigned quirks;
{ USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_23, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_24, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_BERKSHIRE, USB_DEVICE_ID_BERKSHIRE_PCWD, HID_QUIRK_IGNORE },
+ { USB_VENDOR_ID_CODEMERCS, USB_DEVICE_ID_CODEMERCS_IOW40, HID_QUIRK_IGNORE },
+ { USB_VENDOR_ID_CODEMERCS, USB_DEVICE_ID_CODEMERCS_IOW24, HID_QUIRK_IGNORE },
+ { USB_VENDOR_ID_CODEMERCS, USB_DEVICE_ID_CODEMERCS_IOW48, HID_QUIRK_IGNORE },
+ { USB_VENDOR_ID_CODEMERCS, USB_DEVICE_ID_CODEMERCS_IOW28, HID_QUIRK_IGNORE },
+ { USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_HIDCOM, HID_QUIRK_IGNORE },
+ { USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_ULTRAMOUSE, HID_QUIRK_IGNORE },
+ { USB_VENDOR_ID_DELORME, USB_DEVICE_ID_DELORME_EARTHMATE, HID_QUIRK_IGNORE },
+ { USB_VENDOR_ID_DELORME, USB_DEVICE_ID_DELORME_EM_LT20, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_ESSENTIAL_REALITY, USB_DEVICE_ID_ESSENTIAL_REALITY_P5, HID_QUIRK_IGNORE },
- { USB_VENDOR_ID_KBGEAR, USB_DEVICE_ID_KBGEAR_JAMSTUDIO, HID_QUIRK_IGNORE },
+ { USB_VENDOR_ID_GLAB, USB_DEVICE_ID_4_PHIDGETSERVO_30, HID_QUIRK_IGNORE },
+ { USB_VENDOR_ID_GLAB, USB_DEVICE_ID_1_PHIDGETSERVO_30, HID_QUIRK_IGNORE },
+ { USB_VENDOR_ID_GLAB, USB_DEVICE_ID_8_8_8_IF_KIT, HID_QUIRK_IGNORE },
+ { USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_0_4_IF_KIT, HID_QUIRK_IGNORE },
+ { USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_8_8_IF_KIT, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_GRIFFIN, USB_DEVICE_ID_POWERMATE, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_GRIFFIN, USB_DEVICE_ID_SOUNDKNOB, HID_QUIRK_IGNORE },
+ { USB_VENDOR_ID_KBGEAR, USB_DEVICE_ID_KBGEAR_JAMSTUDIO, HID_QUIRK_IGNORE },
+ { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_CASSY, HID_QUIRK_IGNORE },
+ { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_POCKETCASSY, HID_QUIRK_IGNORE },
+ { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MOBILECASSY, HID_QUIRK_IGNORE },
+ { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_JWM, HID_QUIRK_IGNORE },
+ { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_DMMP, HID_QUIRK_IGNORE },
+ { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_UMIP, HID_QUIRK_IGNORE },
+ { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_XRAY1, HID_QUIRK_IGNORE },
+ { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_XRAY2, HID_QUIRK_IGNORE },
+ { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_VIDEOCOM, HID_QUIRK_IGNORE },
+ { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_COM3LAB, HID_QUIRK_IGNORE },
+ { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_TELEPORT, HID_QUIRK_IGNORE },
+ { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_NETWORKANALYSER, HID_QUIRK_IGNORE },
+ { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_POWERCONTROL, HID_QUIRK_IGNORE },
+ { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MACHINETEST, HID_QUIRK_IGNORE },
+ { USB_VENDOR_ID_MCC, USB_DEVICE_ID_MCC_PMD1024LS, HID_QUIRK_IGNORE },
+ { USB_VENDOR_ID_MCC, USB_DEVICE_ID_MCC_PMD1208LS, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS1, HID_QUIRK_IGNORE },
-
{ USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 100, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 200, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 300, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 400, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 500, HID_QUIRK_IGNORE },
-
+ { USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_LABPRO, HID_QUIRK_IGNORE },
+ { USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_GOTEMP, HID_QUIRK_IGNORE },
+ { USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_SKIP, HID_QUIRK_IGNORE },
+ { USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_CYCLOPS, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_PENPARTNER, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_GRAPHIRE, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_GRAPHIRE + 1, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_PL + 3, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_PL + 4, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_PL + 5, HID_QUIRK_IGNORE },
+ { USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_PL + 7, HID_QUIRK_IGNORE },
+ { USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_PL + 8, HID_QUIRK_IGNORE },
+ { USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_PL + 9, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_INTUOS2 + 1, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_INTUOS2 + 2, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_INTUOS2 + 3, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_INTUOS2 + 5, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_INTUOS2 + 7, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_VOLITO, HID_QUIRK_IGNORE },
+ { USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_VOLITO + 1, HID_QUIRK_IGNORE },
+ { USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_VOLITO + 2, HID_QUIRK_IGNORE },
+ { USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_VOLITO + 3, HID_QUIRK_IGNORE },
+ { USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_VOLITO + 4, HID_QUIRK_IGNORE },
+ { USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_GRAPHIRE + 5, HID_QUIRK_IGNORE },
+ { USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_GRAPHIRE + 6, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_PTU, HID_QUIRK_IGNORE },
- { USB_VENDOR_ID_GLAB, USB_DEVICE_ID_4_PHIDGETSERVO_30, HID_QUIRK_IGNORE },
- { USB_VENDOR_ID_GLAB, USB_DEVICE_ID_1_PHIDGETSERVO_30, HID_QUIRK_IGNORE },
+ { USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_INTUOS3, HID_QUIRK_IGNORE },
+ { USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_INTUOS3 + 1, HID_QUIRK_IGNORE },
+ { USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_INTUOS3 + 2, HID_QUIRK_IGNORE },
+ { USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_INTUOS3 + 3, HID_QUIRK_IGNORE },
+ { USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_INTUOS3 + 4, HID_QUIRK_IGNORE },
+ { USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_INTUOS3 + 5, HID_QUIRK_IGNORE },
+ { USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_CINTIQ, HID_QUIRK_IGNORE },
+ { USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_DTF, HID_QUIRK_IGNORE },
+ { USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_DTF + 3, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_4_PHIDGETSERVO_20, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_1_PHIDGETSERVO_20, HID_QUIRK_IGNORE },
+ { USB_VENDOR_ID_ACECAD, USB_DEVICE_ID_ACECAD_FLAIR, HID_QUIRK_IGNORE },
+ { USB_VENDOR_ID_ACECAD, USB_DEVICE_ID_ACECAD_302, HID_QUIRK_IGNORE },
+
{ USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_UC100KM, HID_QUIRK_NOGET },
{ 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_ATEN, USB_DEVICE_ID_ATEN_4PORTKVMC, HID_QUIRK_NOGET },
+ { USB_VENDOR_ID_BTC, USB_DEVICE_ID_BTC_KEYBOARD, HID_QUIRK_NOGET},
+ { USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_USBHUB_KB, HID_QUIRK_NOGET},
+ { USB_VENDOR_ID_CREATIVELABS, USB_DEVICE_ID_CREATIVELABS_SILVERCREST, HID_QUIRK_NOGET },
+ { USB_VENDOR_ID_HP, USB_DEVICE_ID_HP_USBHUB_KB, HID_QUIRK_NOGET },
+ { USB_VENDOR_ID_IBM, USB_DEVICE_ID_IBM_USBHUB_KB, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_TANGTOP, USB_DEVICE_ID_TANGTOP_USBPS2, HID_QUIRK_NOGET },
+ { USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_DUAL_USB_JOYPAD, HID_QUIRK_NOGET | HID_QUIRK_MULTI_INPUT },
+ { USB_VENDOR_ID_SILVERCREST, USB_DEVICE_ID_SILVERCREST_KB, HID_QUIRK_NOGET },
- { USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_WCP32PU, HID_QUIRK_2WHEEL_MOUSE_HACK_BACK },
- { USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_MOUSE, HID_QUIRK_2WHEEL_MOUSE_HACK_EXTRA },
+ { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_POWERMOUSE, HID_QUIRK_2WHEEL_POWERMOUSE },
+ { USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_WCP32PU, HID_QUIRK_2WHEEL_MOUSE_HACK_7 },
+ { USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_MOUSE, HID_QUIRK_2WHEEL_MOUSE_HACK_5 },
+ { USB_VENDOR_ID_AASHIMA, USB_DEVICE_ID_AASHIMA_GAMEPAD, HID_QUIRK_BADPAD },
+ { USB_VENDOR_ID_AASHIMA, USB_DEVICE_ID_AASHIMA_PREDATOR, HID_QUIRK_BADPAD },
{ USB_VENDOR_ID_ALPS, USB_DEVICE_ID_IBM_GAMEPAD, HID_QUIRK_BADPAD },
{ USB_VENDOR_ID_CHIC, USB_DEVICE_ID_CHIC_GAMEPAD, HID_QUIRK_BADPAD },
{ USB_VENDOR_ID_HAPP, USB_DEVICE_ID_UGCI_DRIVING, HID_QUIRK_BADPAD | HID_QUIRK_MULTI_INPUT },
{ USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_RUMBLEPAD, HID_QUIRK_BADPAD },
{ USB_VENDOR_ID_TOPMAX, USB_DEVICE_ID_TOPMAX_COBRAPAD, HID_QUIRK_BADPAD },
+ { USB_VENDOR_ID_CHERRY, USB_DEVICE_ID_CHERRY_CYMOTION, HID_QUIRK_CYMOTION },
+
+ { USB_VENDOR_ID_APPLE, 0x020E, HID_QUIRK_POWERBOOK_HAS_FN },
+ { USB_VENDOR_ID_APPLE, 0x020F, HID_QUIRK_POWERBOOK_HAS_FN },
+ { USB_VENDOR_ID_APPLE, 0x0214, HID_QUIRK_POWERBOOK_HAS_FN },
+ { USB_VENDOR_ID_APPLE, 0x0215, HID_QUIRK_POWERBOOK_HAS_FN },
+ { USB_VENDOR_ID_APPLE, 0x0216, HID_QUIRK_POWERBOOK_HAS_FN },
+ { USB_VENDOR_ID_APPLE, 0x030A, HID_QUIRK_POWERBOOK_HAS_FN },
+ { USB_VENDOR_ID_APPLE, 0x030B, HID_QUIRK_POWERBOOK_HAS_FN },
+
+ { USB_VENDOR_ID_PANJIT, 0x0001, HID_QUIRK_IGNORE },
+ { USB_VENDOR_ID_PANJIT, 0x0002, HID_QUIRK_IGNORE },
+ { USB_VENDOR_ID_PANJIT, 0x0003, HID_QUIRK_IGNORE },
+ { USB_VENDOR_ID_PANJIT, 0x0004, HID_QUIRK_IGNORE },
+
{ 0, 0 }
};
+/*
+ * Traverse the supplied list of reports and find the longest
+ */
+static void hid_find_max_report(struct hid_device *hid, unsigned int type, int *max)
+{
+ struct hid_report *report;
+ int size;
+
+ list_for_each_entry(report, &hid->report_enum[type].report_list, list) {
+ size = ((report->size - 1) >> 3) + 1;
+ if (type == HID_INPUT_REPORT && hid->report_enum[type].numbered)
+ size++;
+ if (*max < size)
+ *max = size;
+ }
+}
+
static int hid_alloc_buffers(struct usb_device *dev, struct hid_device *hid)
{
- if (!(hid->inbuf = usb_buffer_alloc(dev, HID_BUFFER_SIZE, SLAB_ATOMIC, &hid->inbuf_dma)))
+ if (!(hid->inbuf = usb_buffer_alloc(dev, hid->bufsize, SLAB_ATOMIC, &hid->inbuf_dma)))
return -1;
- if (!(hid->outbuf = usb_buffer_alloc(dev, HID_BUFFER_SIZE, SLAB_ATOMIC, &hid->outbuf_dma)))
+ if (!(hid->outbuf = usb_buffer_alloc(dev, hid->bufsize, SLAB_ATOMIC, &hid->outbuf_dma)))
return -1;
if (!(hid->cr = usb_buffer_alloc(dev, sizeof(*(hid->cr)), SLAB_ATOMIC, &hid->cr_dma)))
return -1;
- if (!(hid->ctrlbuf = usb_buffer_alloc(dev, HID_BUFFER_SIZE, SLAB_ATOMIC, &hid->ctrlbuf_dma)))
+ if (!(hid->ctrlbuf = usb_buffer_alloc(dev, hid->bufsize, SLAB_ATOMIC, &hid->ctrlbuf_dma)))
return -1;
return 0;
static void hid_free_buffers(struct usb_device *dev, struct hid_device *hid)
{
if (hid->inbuf)
- usb_buffer_free(dev, HID_BUFFER_SIZE, hid->inbuf, hid->inbuf_dma);
+ usb_buffer_free(dev, hid->bufsize, hid->inbuf, hid->inbuf_dma);
if (hid->outbuf)
- usb_buffer_free(dev, HID_BUFFER_SIZE, hid->outbuf, hid->outbuf_dma);
+ usb_buffer_free(dev, hid->bufsize, hid->outbuf, hid->outbuf_dma);
if (hid->cr)
usb_buffer_free(dev, sizeof(*(hid->cr)), hid->cr, hid->cr_dma);
if (hid->ctrlbuf)
- usb_buffer_free(dev, HID_BUFFER_SIZE, hid->ctrlbuf, hid->ctrlbuf_dma);
+ usb_buffer_free(dev, hid->bufsize, hid->ctrlbuf, hid->ctrlbuf_dma);
+}
+
+/*
+ * Cherry Cymotion keyboard have an invalid HID report descriptor,
+ * that needs fixing before we can parse it.
+ */
+
+static void hid_fixup_cymotion_descriptor(char *rdesc, int rsize)
+{
+ if (rsize >= 17 && rdesc[11] == 0x3c && rdesc[12] == 0x02) {
+ info("Fixing up Cherry Cymotion report descriptor");
+ rdesc[11] = rdesc[16] = 0xff;
+ rdesc[12] = rdesc[17] = 0x03;
+ }
}
static struct hid_device *usb_hid_configure(struct usb_interface *intf)
struct hid_descriptor *hdesc;
struct hid_device *hid;
unsigned quirks = 0, rsize = 0;
- char *buf, *rdesc;
- int n;
+ char *rdesc;
+ int n, len, insize = 0;
for (n = 0; hid_blacklist[n].idVendor; n++)
- if ((hid_blacklist[n].idVendor == dev->descriptor.idVendor) &&
- (hid_blacklist[n].idProduct == dev->descriptor.idProduct))
+ if ((hid_blacklist[n].idVendor == le16_to_cpu(dev->descriptor.idVendor)) &&
+ (hid_blacklist[n].idProduct == le16_to_cpu(dev->descriptor.idProduct)))
quirks = hid_blacklist[n].quirks;
if (quirks & HID_QUIRK_IGNORE)
return NULL;
- if (usb_get_extra_descriptor(interface, HID_DT_HID, &hdesc) && ((!interface->desc.bNumEndpoints) ||
- usb_get_extra_descriptor(&interface->endpoint[0], HID_DT_HID, &hdesc))) {
- dbg("class descriptor not present\n");
- return NULL;
+ if (usb_get_extra_descriptor(interface, HID_DT_HID, &hdesc) &&
+ (!interface->desc.bNumEndpoints ||
+ usb_get_extra_descriptor(&interface->endpoint[0], HID_DT_HID, &hdesc))) {
+ dbg("class descriptor not present\n");
+ return NULL;
}
for (n = 0; n < hdesc->bNumDescriptors; n++)
return NULL;
}
+ hid_set_idle(dev, interface->desc.bInterfaceNumber, 0, 0);
+
if ((n = hid_get_class_descriptor(dev, interface->desc.bInterfaceNumber, HID_DT_REPORT, rdesc, rsize)) < 0) {
dbg("reading report descriptor failed");
kfree(rdesc);
return NULL;
}
+ if ((quirks & HID_QUIRK_CYMOTION))
+ hid_fixup_cymotion_descriptor(rdesc, rsize);
+
#ifdef DEBUG_DATA
printk(KERN_DEBUG __FILE__ ": report descriptor (size %u, read %d) = ", rsize, n);
for (n = 0; n < rsize; n++)
printk("\n");
#endif
- if (!(hid = hid_parse_report(rdesc, rsize))) {
+ if (!(hid = hid_parse_report(rdesc, n))) {
dbg("parsing report descriptor failed");
kfree(rdesc);
return NULL;
kfree(rdesc);
hid->quirks = quirks;
+ hid->bufsize = HID_MIN_BUFFER_SIZE;
+ hid_find_max_report(hid, HID_INPUT_REPORT, &hid->bufsize);
+ hid_find_max_report(hid, HID_OUTPUT_REPORT, &hid->bufsize);
+ hid_find_max_report(hid, HID_FEATURE_REPORT, &hid->bufsize);
+
+ if (hid->bufsize > HID_MAX_BUFFER_SIZE)
+ hid->bufsize = HID_MAX_BUFFER_SIZE;
+
+ hid_find_max_report(hid, HID_INPUT_REPORT, &insize);
+
+ if (insize > HID_MAX_BUFFER_SIZE)
+ insize = HID_MAX_BUFFER_SIZE;
+
if (hid_alloc_buffers(dev, hid)) {
hid_free_buffers(dev, hid);
goto fail;
struct usb_endpoint_descriptor *endpoint;
int pipe;
+ int interval;
endpoint = &interface->endpoint[n].desc;
if ((endpoint->bmAttributes & 3) != 3) /* Not an interrupt endpoint */
continue;
- if (endpoint->bEndpointAddress & USB_DIR_IN) {
- int len;
+ interval = endpoint->bInterval;
+
+ /* Change the polling interval of mice. */
+ if (hid->collection->usage == HID_GD_MOUSE && hid_mousepoll_interval > 0)
+ interval = hid_mousepoll_interval;
+ if (endpoint->bEndpointAddress & USB_DIR_IN) {
if (hid->urbin)
continue;
if (!(hid->urbin = usb_alloc_urb(0, GFP_KERNEL)))
goto fail;
pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
- len = usb_maxpacket(dev, pipe, 0);
- if (len > HID_BUFFER_SIZE)
- len = HID_BUFFER_SIZE;
- usb_fill_int_urb(hid->urbin, dev, pipe, hid->inbuf, len,
- hid_irq_in, hid, endpoint->bInterval);
+ usb_fill_int_urb(hid->urbin, dev, pipe, hid->inbuf, insize,
+ hid_irq_in, hid, interval);
hid->urbin->transfer_dma = hid->inbuf_dma;
hid->urbin->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
} else {
goto fail;
pipe = usb_sndintpipe(dev, endpoint->bEndpointAddress);
usb_fill_int_urb(hid->urbout, dev, pipe, hid->outbuf, 0,
- hid_irq_out, hid, 1);
+ hid_irq_out, hid, interval);
hid->urbout->transfer_dma = hid->outbuf_dma;
hid->urbout->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
}
}
init_waitqueue_head(&hid->wait);
-
- hid->outlock = SPIN_LOCK_UNLOCKED;
- hid->ctrllock = SPIN_LOCK_UNLOCKED;
+
+ INIT_WORK(&hid->reset_work, hid_reset, hid);
+ setup_timer(&hid->io_retry, hid_retry_timeout, (unsigned long) hid);
+
+ spin_lock_init(&hid->inlock);
+ spin_lock_init(&hid->outlock);
+ spin_lock_init(&hid->ctrllock);
hid->version = le16_to_cpu(hdesc->bcdHID);
hid->country = hdesc->bCountryCode;
hid->name[0] = 0;
- if (!(buf = kmalloc(64, GFP_KERNEL)))
- goto fail;
+ if (dev->manufacturer)
+ strlcpy(hid->name, dev->manufacturer, sizeof(hid->name));
- if (usb_string(dev, dev->descriptor.iManufacturer, buf, 64) > 0) {
- strcat(hid->name, buf);
- if (usb_string(dev, dev->descriptor.iProduct, buf, 64) > 0)
- snprintf(hid->name, 64, "%s %s", hid->name, buf);
- } else if (usb_string(dev, dev->descriptor.iProduct, buf, 128) > 0) {
- snprintf(hid->name, 128, "%s", buf);
- } else
- snprintf(hid->name, 128, "%04x:%04x", dev->descriptor.idVendor, dev->descriptor.idProduct);
+ if (dev->product) {
+ if (dev->manufacturer)
+ strlcat(hid->name, " ", sizeof(hid->name));
+ strlcat(hid->name, dev->product, sizeof(hid->name));
+ }
- usb_make_path(dev, buf, 64);
- snprintf(hid->phys, 64, "%s/input%d", buf,
- intf->altsetting[0].desc.bInterfaceNumber);
+ if (!strlen(hid->name))
+ snprintf(hid->name, sizeof(hid->name), "HID %04x:%04x",
+ le16_to_cpu(dev->descriptor.idVendor),
+ le16_to_cpu(dev->descriptor.idProduct));
+
+ usb_make_path(dev, hid->phys, sizeof(hid->phys));
+ strlcat(hid->phys, "/input", sizeof(hid->phys));
+ len = strlen(hid->phys);
+ if (len < sizeof(hid->phys) - 1)
+ snprintf(hid->phys + len, sizeof(hid->phys) - len,
+ "%d", intf->altsetting[0].desc.bInterfaceNumber);
if (usb_string(dev, dev->descriptor.iSerialNumber, hid->uniq, 64) <= 0)
hid->uniq[0] = 0;
- kfree(buf);
-
hid->urbctrl = usb_alloc_urb(0, GFP_KERNEL);
if (!hid->urbctrl)
goto fail;
+
usb_fill_control_urb(hid->urbctrl, dev, 0, (void *) hid->cr,
hid->ctrlbuf, 1, hid_ctrl, hid);
hid->urbctrl->setup_dma = hid->cr_dma;
hid->urbctrl->transfer_dma = hid->ctrlbuf_dma;
- hid->urbctrl->transfer_flags |= (URB_NO_TRANSFER_DMA_MAP
- | URB_NO_SETUP_DMA_MAP);
+ hid->urbctrl->transfer_flags |= (URB_NO_TRANSFER_DMA_MAP | URB_NO_SETUP_DMA_MAP);
return hid;
if (!hid)
return;
+ spin_lock_irq(&hid->inlock); /* Sync with error handler */
usb_set_intfdata(intf, NULL);
- usb_unlink_urb(hid->urbin);
- usb_unlink_urb(hid->urbout);
- usb_unlink_urb(hid->urbctrl);
+ spin_unlock_irq(&hid->inlock);
+ usb_kill_urb(hid->urbin);
+ usb_kill_urb(hid->urbout);
+ usb_kill_urb(hid->urbctrl);
+
+ del_timer_sync(&hid->io_retry);
+ flush_scheduled_work();
if (hid->claimed & HID_CLAIMED_INPUT)
hidinput_disconnect(hid);
hid_free_device(hid);
}
-static int hid_probe (struct usb_interface *intf, const struct usb_device_id *id)
+static int hid_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
struct hid_device *hid;
char path[64];
intf->altsetting->desc.bInterfaceNumber);
if (!(hid = usb_hid_configure(intf)))
- return -EIO;
+ return -ENODEV;
hid_init_reports(hid);
hid_dump_device(hid);
if (!hid->claimed) {
printk ("HID device not claimed by input or hiddev\n");
hid_disconnect(intf);
- return -EIO;
+ return -ENODEV;
}
printk(KERN_INFO);
return 0;
}
+static int hid_suspend(struct usb_interface *intf, pm_message_t message)
+{
+ struct hid_device *hid = usb_get_intfdata (intf);
+
+ spin_lock_irq(&hid->inlock); /* Sync with error handler */
+ set_bit(HID_SUSPENDED, &hid->iofl);
+ spin_unlock_irq(&hid->inlock);
+ del_timer(&hid->io_retry);
+ usb_kill_urb(hid->urbin);
+ dev_dbg(&intf->dev, "suspend\n");
+ return 0;
+}
+
+static int hid_resume(struct usb_interface *intf)
+{
+ struct hid_device *hid = usb_get_intfdata (intf);
+ int status;
+
+ clear_bit(HID_SUSPENDED, &hid->iofl);
+ status = hid_start_in(hid);
+ dev_dbg(&intf->dev, "resume status %d\n", status);
+ return status;
+}
+
static struct usb_device_id hid_usb_ids [] = {
{ .match_flags = USB_DEVICE_ID_MATCH_INT_CLASS,
- .bInterfaceClass = USB_INTERFACE_CLASS_HID },
+ .bInterfaceClass = USB_INTERFACE_CLASS_HID },
{ } /* Terminating entry */
};
MODULE_DEVICE_TABLE (usb, hid_usb_ids);
static struct usb_driver hid_driver = {
- .owner = THIS_MODULE,
.name = "usbhid",
.probe = hid_probe,
.disconnect = hid_disconnect,
+ .suspend = hid_suspend,
+ .resume = hid_resume,
.id_table = hid_usb_ids,
};
static void __exit hid_exit(void)
{
- hiddev_exit();
usb_deregister(&hid_driver);
+ hiddev_exit();
}
module_init(hid_init);