diff options
author | Alan Stern <stern@rowland.harvard.edu> | 2014-09-02 11:39:15 -0400 |
---|---|---|
committer | Jiri Kosina <jkosina@suse.cz> | 2014-09-03 17:37:38 -0400 |
commit | 8f507ef522d55a6e2f9e11a1c1163a92756da044 (patch) | |
tree | 7f053757c34528cbf56aac2ffc49fe7eb48623eb /drivers/hid/usbhid | |
parent | 79346d620e9de87912de73337f6df8b7f9a46888 (diff) |
HID: usbhid: improve handling of Clear-Halt and reset
This patch changes the way usbhid carries out Clear-Halt and reset.
Currently, after a Clear-Halt on the interrupt-IN endpoint, the driver
immediately restarts the interrupt URB, even if the Clear-Halt failed.
This doesn't work out well when the reason for the failure was that
the device was disconnected (when a low- or full-speed device is
connected through a hub to an EHCI controller, transfer errors caused
by disconnection are reported as stalls by the hub). Instead now the
driver will attempt a reset after a failed Clear-Halt.
The way resets are carried out is also changed. Now the driver will
call usb_queue_reset_device() instead of calling usb_reset_device()
directly. This avoids a deadlock that would arise when a device is
unplugged: The hid_reset() routine runs as a workqueue item, a reset
attempt after the device has been unplugged will fail, failure will
cause usbhid to be unbound, and the disconnect routine will try to do
cancel_work_sync(). The usb_queue_reset_device() implementation is
carefully written to handle scenarios like this one properly.
Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
Diffstat (limited to 'drivers/hid/usbhid')
-rw-r--r-- | drivers/hid/usbhid/hid-core.c | 34 |
1 files changed, 9 insertions, 25 deletions
diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index 79cf503e37bf..80c50763b3f8 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c | |||
@@ -116,40 +116,24 @@ static void hid_reset(struct work_struct *work) | |||
116 | struct usbhid_device *usbhid = | 116 | struct usbhid_device *usbhid = |
117 | container_of(work, struct usbhid_device, reset_work); | 117 | container_of(work, struct usbhid_device, reset_work); |
118 | struct hid_device *hid = usbhid->hid; | 118 | struct hid_device *hid = usbhid->hid; |
119 | int rc = 0; | 119 | int rc; |
120 | 120 | ||
121 | if (test_bit(HID_CLEAR_HALT, &usbhid->iofl)) { | 121 | if (test_bit(HID_CLEAR_HALT, &usbhid->iofl)) { |
122 | dev_dbg(&usbhid->intf->dev, "clear halt\n"); | 122 | dev_dbg(&usbhid->intf->dev, "clear halt\n"); |
123 | rc = usb_clear_halt(hid_to_usb_dev(hid), usbhid->urbin->pipe); | 123 | rc = usb_clear_halt(hid_to_usb_dev(hid), usbhid->urbin->pipe); |
124 | clear_bit(HID_CLEAR_HALT, &usbhid->iofl); | 124 | clear_bit(HID_CLEAR_HALT, &usbhid->iofl); |
125 | hid_start_in(hid); | ||
126 | } | ||
127 | |||
128 | else if (test_bit(HID_RESET_PENDING, &usbhid->iofl)) { | ||
129 | dev_dbg(&usbhid->intf->dev, "resetting device\n"); | ||
130 | rc = usb_lock_device_for_reset(hid_to_usb_dev(hid), usbhid->intf); | ||
131 | if (rc == 0) { | 125 | if (rc == 0) { |
132 | rc = usb_reset_device(hid_to_usb_dev(hid)); | 126 | hid_start_in(hid); |
133 | usb_unlock_device(hid_to_usb_dev(hid)); | 127 | } else { |
128 | dev_dbg(&usbhid->intf->dev, | ||
129 | "clear-halt failed: %d\n", rc); | ||
130 | set_bit(HID_RESET_PENDING, &usbhid->iofl); | ||
134 | } | 131 | } |
135 | clear_bit(HID_RESET_PENDING, &usbhid->iofl); | ||
136 | } | 132 | } |
137 | 133 | ||
138 | switch (rc) { | 134 | if (test_bit(HID_RESET_PENDING, &usbhid->iofl)) { |
139 | case 0: | 135 | dev_dbg(&usbhid->intf->dev, "resetting device\n"); |
140 | if (!test_bit(HID_IN_RUNNING, &usbhid->iofl)) | 136 | usb_queue_reset_device(usbhid->intf); |
141 | hid_io_error(hid); | ||
142 | break; | ||
143 | default: | ||
144 | hid_err(hid, "can't reset device, %s-%s/input%d, status %d\n", | ||
145 | hid_to_usb_dev(hid)->bus->bus_name, | ||
146 | hid_to_usb_dev(hid)->devpath, | ||
147 | usbhid->ifnum, rc); | ||
148 | /* FALLTHROUGH */ | ||
149 | case -EHOSTUNREACH: | ||
150 | case -ENODEV: | ||
151 | case -EINTR: | ||
152 | break; | ||
153 | } | 137 | } |
154 | } | 138 | } |
155 | 139 | ||