Thread (11 messages) 11 messages, 4 authors, 2014-08-25

Re: [PATCH 1/1] HID: usbhid: add usb_clear_halt determination for next hid_start_in

From: vichy <hidden>
Date: 2014-08-22 17:38:31
Subsystem: hid core layer, the rest, usb hid/hidbp drivers (usb keyboards, mice, remote controls, ...) · Maintainers: Jiri Kosina, Benjamin Tissoires, Linus Torvalds

hi alan and all:
quoted hunk ↗ jump to hunk
I recently posted (but did not submit) a more comprehensive solution to
this and other related problems.  For example, HID devices typically
run at low speed or full speed.  When attached through a hub to an EHCI
controller (very common on modern systems), unplugging the device
causes -EPIPE errors, so the driver calls usb_clear_halt and restarts
the interrupt pipe.  Of course, the same failure occurs again, so
there's a lengthy flurry of useless error messages.

This patch changes the driver so that after usb_clear_halt fails, it
will try to do a port reset.  And if that fails, the driver will be
unbound from the device.  This avoids infinite loops.

What do you think?

Alan Stern



Index: usb-3.16/drivers/hid/usbhid/hid-core.c
===================================================================
--- usb-3.16.orig/drivers/hid/usbhid/hid-core.c
+++ usb-3.16/drivers/hid/usbhid/hid-core.c
@@ -116,40 +116,24 @@ static void hid_reset(struct work_struct
        struct usbhid_device *usbhid =
                container_of(work, struct usbhid_device, reset_work);
        struct hid_device *hid = usbhid->hid;
-       int rc = 0;
+       int rc;

        if (test_bit(HID_CLEAR_HALT, &usbhid->iofl)) {
                dev_dbg(&usbhid->intf->dev, "clear halt\n");
                rc = usb_clear_halt(hid_to_usb_dev(hid), usbhid->urbin->pipe);
                clear_bit(HID_CLEAR_HALT, &usbhid->iofl);
-               hid_start_in(hid);
-       }
-
-       else if (test_bit(HID_RESET_PENDING, &usbhid->iofl)) {
-               dev_dbg(&usbhid->intf->dev, "resetting device\n");
-               rc = usb_lock_device_for_reset(hid_to_usb_dev(hid), usbhid->intf);
                if (rc == 0) {
-                       rc = usb_reset_device(hid_to_usb_dev(hid));
-                       usb_unlock_device(hid_to_usb_dev(hid));
+                       hid_start_in(hid);
+               } else {
+                       dev_dbg(&usbhid->intf->dev,
+                                       "clear-halt failed: %d\n", rc);
+                       set_bit(HID_RESET_PENDING, &usbhid->iofl);
                }
-               clear_bit(HID_RESET_PENDING, &usbhid->iofl);
        }

-       switch (rc) {
-       case 0:
-               if (!test_bit(HID_IN_RUNNING, &usbhid->iofl))
-                       hid_io_error(hid);
-               break;
-       default:
-               hid_err(hid, "can't reset device, %s-%s/input%d, status %d\n",
-                       hid_to_usb_dev(hid)->bus->bus_name,
-                       hid_to_usb_dev(hid)->devpath,
-                       usbhid->ifnum, rc);
-               /* FALLTHROUGH */
-       case -EHOSTUNREACH:
-       case -ENODEV:
-       case -EINTR:
-               break;
+       if (test_bit(HID_RESET_PENDING, &usbhid->iofl)) {
+               dev_dbg(&usbhid->intf->dev, "resetting device\n");
+               usb_queue_reset_device(usbhid->intf);
        }
 }
from your patch, I have some questions:
a. in Alan's version, if both HID_CLEAR_HALT and HID_RESET_PENDING are
set, hid_reset will both "clear ep halt" and "reset devcie".
But in original one, even HID_CLEAR_HALT and HID_RESET_PENDING are
both set, hid_reset only do one of them.
is there any special reason in original hid_reset to use below flow?
    if (test_bit(HID_CLEAR_HALT, &usbhid->iofl)) {
            xxxxx
    } else if (test_bit(HID_RESET_PENDING, &usbhid->iofl)) {
            xxxxxx
    }

b. in original hid_reset, if "Clear halt ep" or "Rest device" success
or none of those flags raise up, it will call hid_io_error for later
resending the urb. Shall we need to call "hid_io_error(hid);" in the
end if "clear halt" or "reset device" success?

c. why we chose to use usb_queue_reset_device instead of usb_reset_device()?

d. shall we "useusb_lock_device_for_reset(hid_to_usb_dev(hid),
usbhid->intf)" or "spin_lock_irq(&usbhid->lock)" before calling
usb_queue_reset_device?

I append patch for explaining my questions.
Appreciate your kind help,
diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c
index 256b102..aa321f9 100644
--- a/drivers/hid/usbhid/hid-core.c
+++ b/drivers/hid/usbhid/hid-core.c
@@ -116,18 +116,22 @@ static void hid_reset(struct work_struct *work)
        struct usbhid_device *usbhid =
                container_of(work, struct usbhid_device, reset_work);
        struct hid_device *hid = usbhid->hid;
-       int rc = 0;
+       int rc;

        if (test_bit(HID_CLEAR_HALT, &usbhid->iofl)) {
                dev_dbg(&usbhid->intf->dev, "clear halt\n");
                rc = usb_clear_halt(hid_to_usb_dev(hid), usbhid->urbin->pipe);
                clear_bit(HID_CLEAR_HALT, &usbhid->iofl);
-               if (rc == 0)
+               if (rc == 0) {
                        hid_start_in(hid);
+               } else {
+                       dev_dbg(&usbhid->intf->dev,
+                                       "clear-halt failed: %d\n", rc);
+                       set_bit(HID_RESET_PENDING, &usbhid->iofl);
+               }
        }

-       else if (test_bit(HID_RESET_PENDING, &usbhid->iofl)) {
-               dev_dbg(&usbhid->intf->dev, "resetting device\n");
+       if (test_bit(HID_RESET_PENDING, &usbhid->iofl)) {
                rc = usb_lock_device_for_reset(hid_to_usb_dev(hid),
usbhid->intf);
                if (rc == 0) {
                        rc = usb_reset_device(hid_to_usb_dev(hid));
@@ -136,22 +140,8 @@ static void hid_reset(struct work_struct *work)
                clear_bit(HID_RESET_PENDING, &usbhid->iofl);
        }

-       switch (rc) {
-       case 0:
-               if (!test_bit(HID_IN_RUNNING, &usbhid->iofl))
-                       hid_io_error(hid);
-               break;
-       default:
-               hid_err(hid, "can't reset device, %s-%s/input%d, status %d\n",
-                       hid_to_usb_dev(hid)->bus->bus_name,
-                       hid_to_usb_dev(hid)->devpath,
-                       usbhid->ifnum, rc);
-               /* FALLTHROUGH */
-       case -EHOSTUNREACH:
-       case -ENODEV:
-       case -EINTR:
-               break;
-       }
+       if (!test_bit(HID_IN_RUNNING, &usbhid->iofl) || (rc == 0))
+               hid_io_error(hid);
 }

 /* Main I/O error handler */
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help