Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jikos/hid
[linux-3.10.git] / drivers / hid / usbhid / hid-core.c
index df3789f..482f936 100644 (file)
@@ -211,7 +211,7 @@ static int usbhid_restart_out_queue(struct usbhid_device *usbhid)
                return 0;
 
        if ((kicked = (usbhid->outhead != usbhid->outtail))) {
-               dbg("Kicking head %d tail %d", usbhid->outhead, usbhid->outtail);
+               hid_dbg(hid, "Kicking head %d tail %d", usbhid->outhead, usbhid->outtail);
 
                r = usb_autopm_get_interface_async(usbhid->intf);
                if (r < 0)
@@ -238,7 +238,7 @@ static int usbhid_restart_ctrl_queue(struct usbhid_device *usbhid)
                return 0;
 
        if ((kicked = (usbhid->ctrlhead != usbhid->ctrltail))) {
-               dbg("Kicking head %d tail %d", usbhid->ctrlhead, usbhid->ctrltail);
+               hid_dbg(hid, "Kicking head %d tail %d", usbhid->ctrlhead, usbhid->ctrltail);
 
                r = usb_autopm_get_interface_async(usbhid->intf);
                if (r < 0)
@@ -407,6 +407,16 @@ static int hid_submit_ctrl(struct hid_device *hid)
  * Output interrupt completion handler.
  */
 
+static int irq_out_pump_restart(struct hid_device *hid)
+{
+       struct usbhid_device *usbhid = hid->driver_data;
+
+       if (usbhid->outhead != usbhid->outtail)
+               return hid_submit_out(hid);
+       else
+               return -1;
+}
+
 static void hid_irq_out(struct urb *urb)
 {
        struct hid_device *hid = urb->context;
@@ -436,7 +446,7 @@ static void hid_irq_out(struct urb *urb)
        else
                usbhid->outtail = (usbhid->outtail + 1) & (HID_OUTPUT_FIFO_SIZE - 1);
 
-       if (usbhid->outhead != usbhid->outtail && !hid_submit_out(hid)) {
+       if (!irq_out_pump_restart(hid)) {
                /* Successfully submitted next urb in queue */
                spin_unlock_irqrestore(&usbhid->lock, flags);
                return;
@@ -451,6 +461,15 @@ static void hid_irq_out(struct urb *urb)
 /*
  * Control pipe completion handler.
  */
+static int ctrl_pump_restart(struct hid_device *hid)
+{
+       struct usbhid_device *usbhid = hid->driver_data;
+
+       if (usbhid->ctrlhead != usbhid->ctrltail)
+               return hid_submit_ctrl(hid);
+       else
+               return -1;
+}
 
 static void hid_ctrl(struct urb *urb)
 {
@@ -484,7 +503,7 @@ static void hid_ctrl(struct urb *urb)
        else
                usbhid->ctrltail = (usbhid->ctrltail + 1) & (HID_CONTROL_FIFO_SIZE - 1);
 
-       if (usbhid->ctrlhead != usbhid->ctrltail && !hid_submit_ctrl(hid)) {
+       if (!ctrl_pump_restart(hid)) {
                /* Successfully submitted next urb in queue */
                spin_unlock(&usbhid->lock);
                return;
@@ -543,11 +562,27 @@ static void __usbhid_submit_report(struct hid_device *hid, struct hid_report *re
                         * 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
+                        * no race because the URB is blocked under
                         * spinlock
                         */
-                       if (time_after(jiffies, usbhid->last_out + HZ * 5))
+                       if (time_after(jiffies, usbhid->last_out + HZ * 5)) {
+                               usb_block_urb(usbhid->urbout);
+                               /* drop lock to not deadlock if the callback is called */
+                               spin_unlock(&usbhid->lock);
                                usb_unlink_urb(usbhid->urbout);
+                               spin_lock(&usbhid->lock);
+                               usb_unblock_urb(usbhid->urbout);
+                               /*
+                                * if the unlinking has already completed
+                                * the pump will have been stopped
+                                * it must be restarted now
+                                */
+                               if (!test_bit(HID_OUT_RUNNING, &usbhid->iofl))
+                                       if (!irq_out_pump_restart(hid))
+                                               set_bit(HID_OUT_RUNNING, &usbhid->iofl);
+
+
+                       }
                }
                return;
        }
@@ -591,11 +626,25 @@ static void __usbhid_submit_report(struct hid_device *hid, struct hid_report *re
                 * 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
+                * no race because the URB is blocked under
                 * spinlock
                 */
-               if (time_after(jiffies, usbhid->last_ctrl + HZ * 5))
+               if (time_after(jiffies, usbhid->last_ctrl + HZ * 5)) {
+                       usb_block_urb(usbhid->urbctrl);
+                       /* drop lock to not deadlock if the callback is called */
+                       spin_unlock(&usbhid->lock);
                        usb_unlink_urb(usbhid->urbctrl);
+                       spin_lock(&usbhid->lock);
+                       usb_unblock_urb(usbhid->urbctrl);
+                       /*
+                        * if the unlinking has already completed
+                        * the pump will have been stopped
+                        * it must be restarted now
+                        */
+                       if (!test_bit(HID_CTRL_RUNNING, &usbhid->iofl))
+                               if (!ctrl_pump_restart(hid))
+                                       set_bit(HID_CTRL_RUNNING, &usbhid->iofl);
+               }
        }
 }