diff options
author | Oliver Neukum <oliver@neukum.org> | 2010-02-12 07:02:28 -0500 |
---|---|---|
committer | Jiri Kosina <jkosina@suse.cz> | 2010-02-12 07:07:51 -0500 |
commit | 858155fbcc0cd713f6382c527bb1c3abc0ed6d00 (patch) | |
tree | 1b7c93ba35985e96b23b3ba0977e3fdd976fc45d /drivers/hid | |
parent | c8a8602b76b6703df1243e31be01cf0e4451e4a6 (diff) |
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>
Diffstat (limited to 'drivers/hid')
-rw-r--r-- | drivers/hid/usbhid/hid-core.c | 28 | ||||
-rw-r--r-- | drivers/hid/usbhid/usbhid.h | 2 |
2 files changed, 28 insertions, 2 deletions
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) | |||
316 | err_hid("usb_submit_urb(out) failed"); | 316 | err_hid("usb_submit_urb(out) failed"); |
317 | return -1; | 317 | return -1; |
318 | } | 318 | } |
319 | usbhid->last_out = jiffies; | ||
319 | } else { | 320 | } else { |
320 | /* | 321 | /* |
321 | * queue work to wake up the device. | 322 | * queue work to wake up the device. |
@@ -377,6 +378,7 @@ static int hid_submit_ctrl(struct hid_device *hid) | |||
377 | err_hid("usb_submit_urb(ctrl) failed"); | 378 | err_hid("usb_submit_urb(ctrl) failed"); |
378 | return -1; | 379 | return -1; |
379 | } | 380 | } |
381 | usbhid->last_ctrl = jiffies; | ||
380 | } else { | 382 | } else { |
381 | /* | 383 | /* |
382 | * queue work to wake up the device. | 384 | * queue work to wake up the device. |
@@ -512,9 +514,20 @@ static void __usbhid_submit_report(struct hid_device *hid, struct hid_report *re | |||
512 | usbhid->out[usbhid->outhead].report = report; | 514 | usbhid->out[usbhid->outhead].report = report; |
513 | usbhid->outhead = head; | 515 | usbhid->outhead = head; |
514 | 516 | ||
515 | if (!test_and_set_bit(HID_OUT_RUNNING, &usbhid->iofl)) | 517 | if (!test_and_set_bit(HID_OUT_RUNNING, &usbhid->iofl)) { |
516 | if (hid_submit_out(hid)) | 518 | if (hid_submit_out(hid)) |
517 | clear_bit(HID_OUT_RUNNING, &usbhid->iofl); | 519 | clear_bit(HID_OUT_RUNNING, &usbhid->iofl); |
520 | } else { | ||
521 | /* | ||
522 | * the queue is known to run | ||
523 | * but an earlier request may be stuck | ||
524 | * we may need to time out | ||
525 | * no race because this is called under | ||
526 | * spinlock | ||
527 | */ | ||
528 | if (time_after(jiffies, usbhid->last_out + HZ * 5)) | ||
529 | usb_unlink_urb(usbhid->urbout); | ||
530 | } | ||
518 | return; | 531 | return; |
519 | } | 532 | } |
520 | 533 | ||
@@ -535,9 +548,20 @@ static void __usbhid_submit_report(struct hid_device *hid, struct hid_report *re | |||
535 | usbhid->ctrl[usbhid->ctrlhead].dir = dir; | 548 | usbhid->ctrl[usbhid->ctrlhead].dir = dir; |
536 | usbhid->ctrlhead = head; | 549 | usbhid->ctrlhead = head; |
537 | 550 | ||
538 | if (!test_and_set_bit(HID_CTRL_RUNNING, &usbhid->iofl)) | 551 | if (!test_and_set_bit(HID_CTRL_RUNNING, &usbhid->iofl)) { |
539 | if (hid_submit_ctrl(hid)) | 552 | if (hid_submit_ctrl(hid)) |
540 | clear_bit(HID_CTRL_RUNNING, &usbhid->iofl); | 553 | clear_bit(HID_CTRL_RUNNING, &usbhid->iofl); |
554 | } else { | ||
555 | /* | ||
556 | * the queue is known to run | ||
557 | * but an earlier request may be stuck | ||
558 | * we may need to time out | ||
559 | * no race because this is called under | ||
560 | * spinlock | ||
561 | */ | ||
562 | if (time_after(jiffies, usbhid->last_ctrl + HZ * 5)) | ||
563 | usb_unlink_urb(usbhid->urbctrl); | ||
564 | } | ||
541 | } | 565 | } |
542 | 566 | ||
543 | void usbhid_submit_report(struct hid_device *hid, struct hid_report *report, unsigned char dir) | 567 | void usbhid_submit_report(struct hid_device *hid, struct hid_report *report, unsigned char dir) |
diff --git a/drivers/hid/usbhid/usbhid.h b/drivers/hid/usbhid/usbhid.h index 08f505ca2e3d..ec20400c7f29 100644 --- a/drivers/hid/usbhid/usbhid.h +++ b/drivers/hid/usbhid/usbhid.h | |||
@@ -80,12 +80,14 @@ struct usbhid_device { | |||
80 | unsigned char ctrlhead, ctrltail; /* Control fifo head & tail */ | 80 | unsigned char ctrlhead, ctrltail; /* Control fifo head & tail */ |
81 | char *ctrlbuf; /* Control buffer */ | 81 | char *ctrlbuf; /* Control buffer */ |
82 | dma_addr_t ctrlbuf_dma; /* Control buffer dma */ | 82 | dma_addr_t ctrlbuf_dma; /* Control buffer dma */ |
83 | unsigned long last_ctrl; /* record of last output for timeouts */ | ||
83 | 84 | ||
84 | struct urb *urbout; /* Output URB */ | 85 | struct urb *urbout; /* Output URB */ |
85 | struct hid_output_fifo out[HID_CONTROL_FIFO_SIZE]; /* Output pipe fifo */ | 86 | struct hid_output_fifo out[HID_CONTROL_FIFO_SIZE]; /* Output pipe fifo */ |
86 | unsigned char outhead, outtail; /* Output pipe fifo head & tail */ | 87 | unsigned char outhead, outtail; /* Output pipe fifo head & tail */ |
87 | char *outbuf; /* Output buffer */ | 88 | char *outbuf; /* Output buffer */ |
88 | dma_addr_t outbuf_dma; /* Output buffer dma */ | 89 | dma_addr_t outbuf_dma; /* Output buffer dma */ |
90 | unsigned long last_out; /* record of last output for timeouts */ | ||
89 | 91 | ||
90 | spinlock_t lock; /* fifo spinlock */ | 92 | spinlock_t lock; /* fifo spinlock */ |
91 | unsigned long iofl; /* I/O flags (CTRL_RUNNING, OUT_RUNNING) */ | 93 | unsigned long iofl; /* I/O flags (CTRL_RUNNING, OUT_RUNNING) */ |