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 <oliver@neukum.org> Signed-off-by: David Fries <david@fries.net> Signed-off-by: Jiri Kosina <jkosina@suse.cz>
This commit is contained in:
parent
c8a8602b76
commit
858155fbcc
@ -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)
|
||||
|
@ -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) */
|
||||
|
Loading…
Reference in New Issue
Block a user