From 858155fbcc0cd713f6382c527bb1c3abc0ed6d00 Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Fri, 12 Feb 2010 13:02:28 +0100 Subject: HID: usbhid: introduce timeout for stuck ctrl/out URBs Some devices do not react to a control request (seen on APC UPS's) resulting in a slow stream of messages, "generic-usb ... control queue full". Therefore request needs a timeout. Cc: stable@kernel.org Signed-off-by: Oliver Neukum Signed-off-by: David Fries Signed-off-by: Jiri Kosina --- drivers/hid/usbhid/hid-core.c | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) (limited to 'drivers/hid/usbhid/hid-core.c') diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index 54060741d45b..74bd3ca220d3 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c @@ -316,6 +316,7 @@ static int hid_submit_out(struct hid_device *hid) err_hid("usb_submit_urb(out) failed"); return -1; } + usbhid->last_out = jiffies; } else { /* * queue work to wake up the device. @@ -377,6 +378,7 @@ static int hid_submit_ctrl(struct hid_device *hid) err_hid("usb_submit_urb(ctrl) failed"); return -1; } + usbhid->last_ctrl = jiffies; } else { /* * queue work to wake up the device. @@ -512,9 +514,20 @@ static void __usbhid_submit_report(struct hid_device *hid, struct hid_report *re usbhid->out[usbhid->outhead].report = report; usbhid->outhead = head; - if (!test_and_set_bit(HID_OUT_RUNNING, &usbhid->iofl)) + if (!test_and_set_bit(HID_OUT_RUNNING, &usbhid->iofl)) { if (hid_submit_out(hid)) clear_bit(HID_OUT_RUNNING, &usbhid->iofl); + } else { + /* + * 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 + * spinlock + */ + if (time_after(jiffies, usbhid->last_out + HZ * 5)) + usb_unlink_urb(usbhid->urbout); + } return; } @@ -535,9 +548,20 @@ static void __usbhid_submit_report(struct hid_device *hid, struct hid_report *re usbhid->ctrl[usbhid->ctrlhead].dir = dir; usbhid->ctrlhead = head; - if (!test_and_set_bit(HID_CTRL_RUNNING, &usbhid->iofl)) + if (!test_and_set_bit(HID_CTRL_RUNNING, &usbhid->iofl)) { if (hid_submit_ctrl(hid)) clear_bit(HID_CTRL_RUNNING, &usbhid->iofl); + } else { + /* + * 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 + * spinlock + */ + if (time_after(jiffies, usbhid->last_ctrl + HZ * 5)) + usb_unlink_urb(usbhid->urbctrl); + } } void usbhid_submit_report(struct hid_device *hid, struct hid_report *report, unsigned char dir) -- cgit v1.2.3