HID: usbhid: introduce timeout for stuck ctrl/out URBs
[deliverable/linux.git] / drivers / hid / usbhid / hid-core.c
index 0258289f3b3e649f04e5447f0d53221d1df07222..74bd3ca220d3e7979442aacac5aa609869c21536 100644 (file)
@@ -5,7 +5,7 @@
  *  Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
  *  Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
  *  Copyright (c) 2007-2008 Oliver Neukum
- *  Copyright (c) 2006-2009 Jiri Kosina
+ *  Copyright (c) 2006-2010 Jiri Kosina
  */
 
 /*
@@ -316,6 +316,7 @@ static int hid_submit_out(struct hid_device *hid)
                        err_hid("usb_submit_urb(out) failed");
                        return -1;
                }
+               usbhid->last_out = jiffies;
        } else {
                /*
                 * queue work to wake up the device.
@@ -377,6 +378,7 @@ static int hid_submit_ctrl(struct hid_device *hid)
                        err_hid("usb_submit_urb(ctrl) failed");
                        return -1;
                }
+               usbhid->last_ctrl = jiffies;
        } else {
                /*
                 * queue work to wake up the device.
@@ -512,9 +514,20 @@ static void __usbhid_submit_report(struct hid_device *hid, struct hid_report *re
                usbhid->out[usbhid->outhead].report = report;
                usbhid->outhead = head;
 
-               if (!test_and_set_bit(HID_OUT_RUNNING, &usbhid->iofl))
+               if (!test_and_set_bit(HID_OUT_RUNNING, &usbhid->iofl)) {
                        if (hid_submit_out(hid))
                                clear_bit(HID_OUT_RUNNING, &usbhid->iofl);
+               } else {
+                       /*
+                        * the queue is known to run
+                        * but an earlier request may be stuck
+                        * we may need to time out
+                        * no race because this is called under
+                        * spinlock
+                        */
+                       if (time_after(jiffies, usbhid->last_out + HZ * 5))
+                               usb_unlink_urb(usbhid->urbout);
+               }
                return;
        }
 
@@ -535,9 +548,20 @@ static void __usbhid_submit_report(struct hid_device *hid, struct hid_report *re
        usbhid->ctrl[usbhid->ctrlhead].dir = dir;
        usbhid->ctrlhead = head;
 
-       if (!test_and_set_bit(HID_CTRL_RUNNING, &usbhid->iofl))
+       if (!test_and_set_bit(HID_CTRL_RUNNING, &usbhid->iofl)) {
                if (hid_submit_ctrl(hid))
                        clear_bit(HID_CTRL_RUNNING, &usbhid->iofl);
+       } else {
+               /*
+                * the queue is known to run
+                * but an earlier request may be stuck
+                * we may need to time out
+                * no race because this is called under
+                * spinlock
+                */
+               if (time_after(jiffies, usbhid->last_ctrl + HZ * 5))
+                       usb_unlink_urb(usbhid->urbctrl);
+       }
 }
 
 void usbhid_submit_report(struct hid_device *hid, struct hid_report *report, unsigned char dir)
@@ -1253,10 +1277,9 @@ static int hid_suspend(struct usb_interface *intf, pm_message_t message)
 {
        struct hid_device *hid = usb_get_intfdata(intf);
        struct usbhid_device *usbhid = hid->driver_data;
-       struct usb_device *udev = interface_to_usbdev(intf);
        int status;
 
-       if (udev->auto_pm) {
+       if (message.event & PM_EVENT_AUTO) {
                spin_lock_irq(&usbhid->lock);   /* Sync with error handler */
                if (!test_bit(HID_RESET_PENDING, &usbhid->iofl)
                    && !test_bit(HID_CLEAR_HALT, &usbhid->iofl)
@@ -1281,7 +1304,7 @@ static int hid_suspend(struct usb_interface *intf, pm_message_t message)
                        return -EIO;
        }
 
-       if (!ignoreled && udev->auto_pm) {
+       if (!ignoreled && (message.event & PM_EVENT_AUTO)) {
                spin_lock_irq(&usbhid->lock);
                if (test_bit(HID_LED_ON, &usbhid->iofl)) {
                        spin_unlock_irq(&usbhid->lock);
@@ -1294,7 +1317,8 @@ static int hid_suspend(struct usb_interface *intf, pm_message_t message)
        hid_cancel_delayed_stuff(usbhid);
        hid_cease_io(usbhid);
 
-       if (udev->auto_pm && test_bit(HID_KEYS_PRESSED, &usbhid->iofl)) {
+       if ((message.event & PM_EVENT_AUTO) &&
+                       test_bit(HID_KEYS_PRESSED, &usbhid->iofl)) {
                /* lost race against keypresses */
                status = hid_start_in(hid);
                if (status < 0)
@@ -1342,7 +1366,7 @@ static int hid_reset_resume(struct usb_interface *intf)
 
 #endif /* CONFIG_PM */
 
-static struct usb_device_id hid_usb_ids [] = {
+static const struct usb_device_id hid_usb_ids[] = {
        { .match_flags = USB_DEVICE_ID_MATCH_INT_CLASS,
                .bInterfaceClass = USB_INTERFACE_CLASS_HID },
        { }                                             /* Terminating entry */
This page took 0.028085 seconds and 5 git commands to generate.