diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2010-05-21 17:51:03 (GMT) |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2010-05-21 17:51:03 (GMT) |
commit | 8b108c609adefd98577c35f0a41497a610041a6c (patch) | |
tree | f1552fdc5bf0ebcc484a88f01cd3864113adf25c /drivers/hid/usbhid | |
parent | 7ce1418f95e918cfc5ad36e3ec3431145c768cd0 (diff) | |
parent | 73d5e8f77e88a4d3a154dfdbb4ed2cf461b7bf21 (diff) | |
download | linux-8b108c609adefd98577c35f0a41497a610041a6c.tar.xz |
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jikos/hid
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jikos/hid: (59 commits)
HID: fix up 'EMBEDDED' mess in Kconfig
HID: roccat: cleanup preprocessor macros
HID: roccat: refactor special event handling
HID: roccat: fix special button support
HID: roccat: Correctly mark init and exit functions
HID: hidraw: Use Interrupt Endpoint for OUT Transfers if Available
HID: hid-samsung: remove redundant key mappings
HID: add omitted hid-zydacron.c file
HID: hid-samsung: add support for Creative Desktop Wireless 6000
HID: picolcd: Eliminate use after free
HID: Zydacron Remote Control driver
HID: Use kmemdup
HID: magicmouse: fix input registration
HID: make Prodikeys driver standalone config option
HID: Prodikeys PC-MIDI HID Driver
HID: hidraw: fix indentation
HID: ntrig: add filtering module parameters
HID: ntrig: add sysfs access to filter parameters
HID: ntrig: add sensitivity and responsiveness support
HID: add multi-input quirk for eGalax Touchcontroller
...
Diffstat (limited to 'drivers/hid/usbhid')
-rw-r--r-- | drivers/hid/usbhid/hid-core.c | 73 | ||||
-rw-r--r-- | drivers/hid/usbhid/hid-quirks.c | 1 | ||||
-rw-r--r-- | drivers/hid/usbhid/hiddev.c | 19 | ||||
-rw-r--r-- | drivers/hid/usbhid/usbkbd.c | 1 |
4 files changed, 79 insertions, 15 deletions
diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index df61975..1ebd324 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c @@ -623,6 +623,7 @@ int usbhid_wait_io(struct hid_device *hid) return 0; } +EXPORT_SYMBOL_GPL(usbhid_wait_io); static int hid_set_idle(struct usb_device *dev, int ifnum, int report, int idle) { @@ -806,16 +807,36 @@ static int usbhid_output_raw_report(struct hid_device *hid, __u8 *buf, size_t co struct usb_host_interface *interface = intf->cur_altsetting; int ret; - ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), - HID_REQ_SET_REPORT, - USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE, - ((report_type + 1) << 8) | *buf, - interface->desc.bInterfaceNumber, buf + 1, count - 1, - USB_CTRL_SET_TIMEOUT); - - /* count also the report id */ - if (ret > 0) - ret++; + if (usbhid->urbout) { + int actual_length; + int skipped_report_id = 0; + if (buf[0] == 0x0) { + /* Don't send the Report ID */ + buf++; + count--; + skipped_report_id = 1; + } + ret = usb_interrupt_msg(dev, usbhid->urbout->pipe, + buf, count, &actual_length, + USB_CTRL_SET_TIMEOUT); + /* return the number of bytes transferred */ + if (ret == 0) { + ret = actual_length; + /* count also the report id */ + if (skipped_report_id) + ret++; + } + } else { + ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + HID_REQ_SET_REPORT, + USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE, + ((report_type + 1) << 8) | *buf, + interface->desc.bInterfaceNumber, buf + 1, count - 1, + USB_CTRL_SET_TIMEOUT); + /* count also the report id */ + if (ret > 0) + ret++; + } return ret; } @@ -1017,12 +1038,15 @@ static int usbhid_start(struct hid_device *hid) /* Some keyboards don't work until their LEDs have been set. * Since BIOSes do set the LEDs, it must be safe for any device * that supports the keyboard boot protocol. + * In addition, enable remote wakeup by default for all keyboard + * devices supporting the boot protocol. */ if (interface->desc.bInterfaceSubClass == USB_INTERFACE_SUBCLASS_BOOT && interface->desc.bInterfaceProtocol == - USB_INTERFACE_PROTOCOL_KEYBOARD) + USB_INTERFACE_PROTOCOL_KEYBOARD) { usbhid_set_leds(hid); - + device_set_wakeup_enable(&dev->dev, 1); + } return 0; fail: @@ -1131,6 +1155,7 @@ static int usbhid_probe(struct usb_interface *intf, const struct usb_device_id * hid->vendor = le16_to_cpu(dev->descriptor.idVendor); hid->product = le16_to_cpu(dev->descriptor.idProduct); hid->name[0] = 0; + hid->quirks = usbhid_lookup_quirk(hid->vendor, hid->product); if (intf->cur_altsetting->desc.bInterfaceProtocol == USB_INTERFACE_PROTOCOL_MOUSE) hid->type = HID_TYPE_USBMOUSE; @@ -1287,6 +1312,11 @@ static int hid_suspend(struct usb_interface *intf, pm_message_t message) { set_bit(HID_REPORTED_IDLE, &usbhid->iofl); spin_unlock_irq(&usbhid->lock); + if (hid->driver && hid->driver->suspend) { + status = hid->driver->suspend(hid, message); + if (status < 0) + return status; + } } else { usbhid_mark_busy(usbhid); spin_unlock_irq(&usbhid->lock); @@ -1294,6 +1324,11 @@ static int hid_suspend(struct usb_interface *intf, pm_message_t message) } } else { + if (hid->driver && hid->driver->suspend) { + status = hid->driver->suspend(hid, message); + if (status < 0) + return status; + } spin_lock_irq(&usbhid->lock); set_bit(HID_REPORTED_IDLE, &usbhid->iofl); spin_unlock_irq(&usbhid->lock); @@ -1348,6 +1383,11 @@ static int hid_resume(struct usb_interface *intf) hid_io_error(hid); usbhid_restart_queues(usbhid); + if (status >= 0 && hid->driver && hid->driver->resume) { + int ret = hid->driver->resume(hid); + if (ret < 0) + status = ret; + } dev_dbg(&intf->dev, "resume status %d\n", status); return 0; } @@ -1356,9 +1396,16 @@ static int hid_reset_resume(struct usb_interface *intf) { struct hid_device *hid = usb_get_intfdata(intf); struct usbhid_device *usbhid = hid->driver_data; + int status; clear_bit(HID_REPORTED_IDLE, &usbhid->iofl); - return hid_post_reset(intf); + status = hid_post_reset(intf); + if (status >= 0 && hid->driver && hid->driver->reset_resume) { + int ret = hid->driver->reset_resume(hid); + if (ret < 0) + status = ret; + } + return status; } #endif /* CONFIG_PM */ diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c index 1152f9b..5ff8d32 100644 --- a/drivers/hid/usbhid/hid-quirks.c +++ b/drivers/hid/usbhid/hid-quirks.c @@ -33,6 +33,7 @@ static const struct hid_blacklist { { 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_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH, HID_QUIRK_MULTI_INPUT }, { USB_VENDOR_ID_HAPP, USB_DEVICE_ID_UGCI_DRIVING, HID_QUIRK_BADPAD | HID_QUIRK_MULTI_INPUT }, { USB_VENDOR_ID_HAPP, USB_DEVICE_ID_UGCI_FLYING, HID_QUIRK_BADPAD | HID_QUIRK_MULTI_INPUT }, { USB_VENDOR_ID_HAPP, USB_DEVICE_ID_UGCI_FIGHTING, HID_QUIRK_BADPAD | HID_QUIRK_MULTI_INPUT }, diff --git a/drivers/hid/usbhid/hiddev.c b/drivers/hid/usbhid/hiddev.c index 433602a..c24d2fa 100644 --- a/drivers/hid/usbhid/hiddev.c +++ b/drivers/hid/usbhid/hiddev.c @@ -267,6 +267,7 @@ static int hiddev_open(struct inode *inode, struct file *file) struct hiddev_list *list; int res, i; + /* See comment in hiddev_connect() for BKL explanation */ lock_kernel(); i = iminor(inode) - HIDDEV_MINOR_BASE; @@ -894,8 +895,22 @@ int hiddev_connect(struct hid_device *hid, unsigned int force) hiddev->hid = hid; hiddev->exist = 1; - /* when lock_kernel() usage is fixed in usb_open(), - * we could also fix it here */ + /* + * BKL here is used to avoid race after usb_register_dev(). + * Once the device node has been created, open() could happen on it. + * The code below will then fail, as hiddev_table hasn't been + * updated. + * + * The obvious fix -- introducing mutex to guard hiddev_table[] + * doesn't work, as usb_open() and usb_register_dev() both take + * minor_rwsem, thus we'll have ABBA deadlock. + * + * Before BKL pushdown, usb_open() had been acquiring it in right + * order, so _open() was safe to use it to protect from this race. + * Now the order is different, but AB-BA deadlock still doesn't occur + * as BKL is dropped on schedule() (i.e. while sleeping on + * minor_rwsem). Fugly. + */ lock_kernel(); retval = usb_register_dev(usbhid->intf, &hiddev_class); if (retval) { diff --git a/drivers/hid/usbhid/usbkbd.c b/drivers/hid/usbhid/usbkbd.c index bb14c82..a948605 100644 --- a/drivers/hid/usbhid/usbkbd.c +++ b/drivers/hid/usbhid/usbkbd.c @@ -311,6 +311,7 @@ static int usb_kbd_probe(struct usb_interface *iface, goto fail2; usb_set_intfdata(iface, kbd); + device_set_wakeup_enable(&dev->dev, 1); return 0; fail2: |