On Mon, 14 Mar 2016, Oliver Neukum wrote:

> On Mon, 2016-03-14 at 11:03 -0400, Alan Stern wrote:
> > On Mon, 14 Mar 2016, Oliver Neukum wrote:
> 
> > > It seems to me that hid_resume_common() needs to be split into three
> > > parts
> > > 
> > > a) doing the stuff for pending resets
> > > b) conditionally restarting IO
> > > c) reset the flag 
> > 
> > Daniel's problem involved hid_post_reset(), not hid_resume_common().  
> > Is that what you meant?  Maybe I'm wrong, but it looks like 
> > hid_post_reset() just needs to check whether I/O should be started 
> > before trying to restart it.
> 
> Yes. That means that all that hid_post_reset() does before calling
> hid_start_in() must be left unchanged and we can put the change into
> a common code path.
> 
> > (It also looks like hid_resume() needs to clear the HID_SUSPENDED flag 
> > even when HID_STARTED isn't set.)
> 
> Exactly. Thus hid_resume_common() needs to be split. The code paths
> for reset_resume(), post_reset() and resume() diverge too much. You will
> see that they can be easily be reused if you split them into
> those three parts.

All right, I have taken Oliver's suggestion.  The patch below refactors 
the code to consolidate the common activities in a new function, 
hid_restart_io().

Daniel, can you please test this patch?

Alan Stern



Index: usb-4.4/drivers/hid/usbhid/hid-core.c
===================================================================
--- usb-4.4.orig/drivers/hid/usbhid/hid-core.c
+++ usb-4.4/drivers/hid/usbhid/hid-core.c
@@ -951,14 +951,6 @@ static int usbhid_output_report(struct h
        return ret;
 }
 
-static void usbhid_restart_queues(struct usbhid_device *usbhid)
-{
-       if (usbhid->urbout && !test_bit(HID_OUT_RUNNING, &usbhid->iofl))
-               usbhid_restart_out_queue(usbhid);
-       if (!test_bit(HID_CTRL_RUNNING, &usbhid->iofl))
-               usbhid_restart_ctrl_queue(usbhid);
-}
-
 static void hid_free_buffers(struct usb_device *dev, struct hid_device *hid)
 {
        struct usbhid_device *usbhid = hid->driver_data;
@@ -1404,6 +1396,33 @@ static void hid_cease_io(struct usbhid_d
        usb_kill_urb(usbhid->urbout);
 }
 
+static void hid_restart_io(struct hid_device *hid)
+{
+       struct usbhid_device *usbhid = hid->driver_data;
+
+       spin_lock_irq(&usbhid->lock);
+       clear_bit(HID_SUSPENDED, &usbhid->iofl);
+       usbhid_mark_busy(usbhid);
+
+       if (test_bit(HID_CLEAR_HALT, &usbhid->iofl) ||
+                       test_bit(HID_RESET_PENDING, &usbhid->iofl))
+               schedule_work(&usbhid->reset_work);
+       usbhid->retry_delay = 0;
+       spin_unlock_irq(&usbhid->lock);
+
+       if (test_bit(HID_STARTED, &usbhid->iofl)) {
+               if (hid_start_in(hid) < 0)
+                       hid_io_error(hid);
+
+               spin_lock_irq(&usbhid->lock);
+               if (usbhid->urbout && !test_bit(HID_OUT_RUNNING, &usbhid->iofl))
+                       usbhid_restart_out_queue(usbhid);
+               if (!test_bit(HID_CTRL_RUNNING, &usbhid->iofl))
+                       usbhid_restart_ctrl_queue(usbhid);
+               spin_unlock_irq(&usbhid->lock);
+       }
+}
+
 /* Treat USB reset pretty much the same as suspend/resume */
 static int hid_pre_reset(struct usb_interface *intf)
 {
@@ -1457,10 +1476,8 @@ static int hid_post_reset(struct usb_int
        clear_bit(HID_RESET_PENDING, &usbhid->iofl);
        spin_unlock_irq(&usbhid->lock);
        hid_set_idle(dev, intf->cur_altsetting->desc.bInterfaceNumber, 0, 0);
-       status = hid_start_in(hid);
-       if (status < 0)
-               hid_io_error(hid);
-       usbhid_restart_queues(usbhid);
+
+       hid_restart_io(hid);
 
        return 0;
 }
@@ -1483,25 +1500,9 @@ void usbhid_put_power(struct hid_device
 #ifdef CONFIG_PM
 static int hid_resume_common(struct hid_device *hid, bool driver_suspended)
 {
-       struct usbhid_device *usbhid = hid->driver_data;
-       int status;
-
-       spin_lock_irq(&usbhid->lock);
-       clear_bit(HID_SUSPENDED, &usbhid->iofl);
-       usbhid_mark_busy(usbhid);
-
-       if (test_bit(HID_CLEAR_HALT, &usbhid->iofl) ||
-                       test_bit(HID_RESET_PENDING, &usbhid->iofl))
-               schedule_work(&usbhid->reset_work);
-       usbhid->retry_delay = 0;
-
-       usbhid_restart_queues(usbhid);
-       spin_unlock_irq(&usbhid->lock);
-
-       status = hid_start_in(hid);
-       if (status < 0)
-               hid_io_error(hid);
+       int status = 0;
 
+       hid_restart_io(hid);
        if (driver_suspended && hid->driver && hid->driver->resume)
                status = hid->driver->resume(hid);
        return status;
@@ -1570,12 +1571,8 @@ static int hid_suspend(struct usb_interf
 static int hid_resume(struct usb_interface *intf)
 {
        struct hid_device *hid = usb_get_intfdata (intf);
-       struct usbhid_device *usbhid = hid->driver_data;
        int status;
 
-       if (!test_bit(HID_STARTED, &usbhid->iofl))
-               return 0;
-
        status = hid_resume_common(hid, true);
        dev_dbg(&intf->dev, "resume status %d\n", status);
        return 0;
@@ -1584,10 +1581,8 @@ static int hid_resume(struct usb_interfa
 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_SUSPENDED, &usbhid->iofl);
        status = hid_post_reset(intf);
        if (status >= 0 && hid->driver && hid->driver->reset_resume) {
                int ret = hid->driver->reset_resume(hid);

--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to