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 */