Skip to content

Commit

Permalink
HID: usbhid: introduce timeout for stuck ctrl/out URBs
Browse files Browse the repository at this point in the history
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 <oliver@neukum.org>
Signed-off-by: David Fries <david@fries.net>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
  • Loading branch information
Oliver Neukum authored and Jiri Kosina committed Feb 12, 2010
1 parent c8a8602 commit 858155f
Show file tree
Hide file tree
Showing 2 changed files with 28 additions and 2 deletions.
28 changes: 26 additions & 2 deletions drivers/hid/usbhid/hid-core.c
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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;
}

Expand All @@ -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)
Expand Down
2 changes: 2 additions & 0 deletions drivers/hid/usbhid/usbhid.h
Original file line number Diff line number Diff line change
Expand Up @@ -80,12 +80,14 @@ struct usbhid_device {
unsigned char ctrlhead, ctrltail; /* Control fifo head & tail */
char *ctrlbuf; /* Control buffer */
dma_addr_t ctrlbuf_dma; /* Control buffer dma */
unsigned long last_ctrl; /* record of last output for timeouts */

struct urb *urbout; /* Output URB */
struct hid_output_fifo out[HID_CONTROL_FIFO_SIZE]; /* Output pipe fifo */
unsigned char outhead, outtail; /* Output pipe fifo head & tail */
char *outbuf; /* Output buffer */
dma_addr_t outbuf_dma; /* Output buffer dma */
unsigned long last_out; /* record of last output for timeouts */

spinlock_t lock; /* fifo spinlock */
unsigned long iofl; /* I/O flags (CTRL_RUNNING, OUT_RUNNING) */
Expand Down

0 comments on commit 858155f

Please sign in to comment.