aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/hid
diff options
context:
space:
mode:
authorDaniel Kurtz <djkurtz@chromium.org>2011-11-17 06:23:49 -0500
committerJiri Kosina <jkosina@suse.cz>2011-12-21 05:18:35 -0500
commitf0befcd64bc57e6a0b7a96c37c55f79e6b999af7 (patch)
treef6847dbd4cf25e0f8ae21707bb365424ba5e1816 /drivers/hid
parentede6a8b239736acd55ad8a219b2bd2ae7f551fb7 (diff)
HID: usbhid: hid-core: submit queued urbs before suspend
If any userspace program has opened a keyboard device, the input core de-activates the keyboard's LEDs upon suspend(). It does this by sending individual EV_LED[LED_X]=0 events to the underlying device driver by directly calling the driver's registered event() handler. The usb-hid driver event() handler processes each request by immediately attempting to submit a CTRL URB to turn off the LED. USB URB submission is asynchronous. First the URB is added to the head of the ctrl queue. Then, if the CTRL_RUNNING flag is false, the URB is submitted immediately (and CTRL_RUNNING is set). If the CTRL_RUNNING flag was already true, then the newly queued URB is submitted in the ctrl completion handler when all previously submitted URBs have completed. When all queued URBs have been submitted, the completion handler clears the CTRL_RUNNING flag. In the 2-LED suspend case, at input suspend(), 2 LED event CTRL URBs get queued, with only the first actually submitted. Soon after input suspend() handler finishes, the usb-hid suspend() handler gets called. Since this is NOT a PM_EVENT_AUTO suspend, the handler sets REPORTED_IDLE, then waits for io to complete. Unfortunately, this usually happens while the first LED request is actually still being processed. Thus when the completion handler tries to submit the second LED request it fails, since REPORTED_IDLE is already set! This REPORTED_IDLE check failure causes the completion handler to complete, however without clearing the CTRL_RUNNING flag. This, in turn, means that the suspend() handler's wait_io() condition is never satisfied, and instead it times out after 10 seconds, aborting the original system suspend. This patch changes the behavior to the following: (1) allow completion handler to finish submitting all queued URBs, even if REPORTED_IDLE is set. This guarantees that all URBs queued before the hid-core suspend() call will be submitted before the system is suspended. (2) if REPORTED_IDLE is set and the URB queue is empty, queue, but don't submit, new URB submission requests. These queued requests get submitted when resume() flushes the URB queue. This is similar to the existing behavior, however, any requests that arrive while the queue is not yet empty will still get submitted before suspend. (3) set the RUNNING flag when flushing the URB queue in resume(). This keeps URBs that were queued in (2) from colliding with any new URBs that are being submitted during the resume process. The new URB submission requests upon resume get properly queued behind the ones being flushed instead of the current situation where they collide, causing memory corruption and oopses. Signed-off-by: Daniel Kurtz <djkurtz@chromium.org> Acked-by: Oliver Neukum <oneukum@suse.de> Signed-off-by: Jiri Kosina <jkosina@suse.cz>
Diffstat (limited to 'drivers/hid')
-rw-r--r--drivers/hid/usbhid/hid-core.c184
1 files changed, 105 insertions, 79 deletions
diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c
index 66061349be87..719f6b02fab1 100644
--- a/drivers/hid/usbhid/hid-core.c
+++ b/drivers/hid/usbhid/hid-core.c
@@ -197,16 +197,24 @@ static int usbhid_restart_out_queue(struct usbhid_device *usbhid)
197{ 197{
198 struct hid_device *hid = usb_get_intfdata(usbhid->intf); 198 struct hid_device *hid = usb_get_intfdata(usbhid->intf);
199 int kicked; 199 int kicked;
200 int r;
200 201
201 if (!hid) 202 if (!hid)
202 return 0; 203 return 0;
203 204
204 if ((kicked = (usbhid->outhead != usbhid->outtail))) { 205 if ((kicked = (usbhid->outhead != usbhid->outtail))) {
205 dbg("Kicking head %d tail %d", usbhid->outhead, usbhid->outtail); 206 dbg("Kicking head %d tail %d", usbhid->outhead, usbhid->outtail);
207
208 r = usb_autopm_get_interface_async(usbhid->intf);
209 if (r < 0)
210 return r;
211 /* Asynchronously flush queue. */
212 set_bit(HID_OUT_RUNNING, &usbhid->iofl);
206 if (hid_submit_out(hid)) { 213 if (hid_submit_out(hid)) {
207 clear_bit(HID_OUT_RUNNING, &usbhid->iofl); 214 clear_bit(HID_OUT_RUNNING, &usbhid->iofl);
208 wake_up(&usbhid->wait); 215 usb_autopm_put_interface_async(usbhid->intf);
209 } 216 }
217 wake_up(&usbhid->wait);
210 } 218 }
211 return kicked; 219 return kicked;
212} 220}
@@ -215,6 +223,7 @@ static int usbhid_restart_ctrl_queue(struct usbhid_device *usbhid)
215{ 223{
216 struct hid_device *hid = usb_get_intfdata(usbhid->intf); 224 struct hid_device *hid = usb_get_intfdata(usbhid->intf);
217 int kicked; 225 int kicked;
226 int r;
218 227
219 WARN_ON(hid == NULL); 228 WARN_ON(hid == NULL);
220 if (!hid) 229 if (!hid)
@@ -222,10 +231,17 @@ static int usbhid_restart_ctrl_queue(struct usbhid_device *usbhid)
222 231
223 if ((kicked = (usbhid->ctrlhead != usbhid->ctrltail))) { 232 if ((kicked = (usbhid->ctrlhead != usbhid->ctrltail))) {
224 dbg("Kicking head %d tail %d", usbhid->ctrlhead, usbhid->ctrltail); 233 dbg("Kicking head %d tail %d", usbhid->ctrlhead, usbhid->ctrltail);
234
235 r = usb_autopm_get_interface_async(usbhid->intf);
236 if (r < 0)
237 return r;
238 /* Asynchronously flush queue. */
239 set_bit(HID_CTRL_RUNNING, &usbhid->iofl);
225 if (hid_submit_ctrl(hid)) { 240 if (hid_submit_ctrl(hid)) {
226 clear_bit(HID_CTRL_RUNNING, &usbhid->iofl); 241 clear_bit(HID_CTRL_RUNNING, &usbhid->iofl);
227 wake_up(&usbhid->wait); 242 usb_autopm_put_interface_async(usbhid->intf);
228 } 243 }
244 wake_up(&usbhid->wait);
229 } 245 }
230 return kicked; 246 return kicked;
231} 247}
@@ -304,30 +320,21 @@ static int hid_submit_out(struct hid_device *hid)
304 report = usbhid->out[usbhid->outtail].report; 320 report = usbhid->out[usbhid->outtail].report;
305 raw_report = usbhid->out[usbhid->outtail].raw_report; 321 raw_report = usbhid->out[usbhid->outtail].raw_report;
306 322
307 r = usb_autopm_get_interface_async(usbhid->intf); 323 usbhid->urbout->transfer_buffer_length = ((report->size - 1) >> 3) +
308 if (r < 0) 324 1 + (report->id > 0);
309 return -1; 325 usbhid->urbout->dev = hid_to_usb_dev(hid);
326 memcpy(usbhid->outbuf, raw_report,
327 usbhid->urbout->transfer_buffer_length);
328 kfree(raw_report);
310 329
311 /* 330 dbg_hid("submitting out urb\n");
312 * if the device hasn't been woken, we leave the output
313 * to resume()
314 */
315 if (!test_bit(HID_REPORTED_IDLE, &usbhid->iofl)) {
316 usbhid->urbout->transfer_buffer_length = ((report->size - 1) >> 3) + 1 + (report->id > 0);
317 usbhid->urbout->dev = hid_to_usb_dev(hid);
318 memcpy(usbhid->outbuf, raw_report, usbhid->urbout->transfer_buffer_length);
319 kfree(raw_report);
320
321 dbg_hid("submitting out urb\n");
322 331
323 if (usb_submit_urb(usbhid->urbout, GFP_ATOMIC)) { 332 r = usb_submit_urb(usbhid->urbout, GFP_ATOMIC);
324 hid_err(hid, "usb_submit_urb(out) failed\n"); 333 if (r < 0) {
325 usb_autopm_put_interface_async(usbhid->intf); 334 hid_err(hid, "usb_submit_urb(out) failed: %d\n", r);
326 return -1; 335 return r;
327 }
328 usbhid->last_out = jiffies;
329 } 336 }
330 337 usbhid->last_out = jiffies;
331 return 0; 338 return 0;
332} 339}
333 340
@@ -343,50 +350,48 @@ static int hid_submit_ctrl(struct hid_device *hid)
343 raw_report = usbhid->ctrl[usbhid->ctrltail].raw_report; 350 raw_report = usbhid->ctrl[usbhid->ctrltail].raw_report;
344 dir = usbhid->ctrl[usbhid->ctrltail].dir; 351 dir = usbhid->ctrl[usbhid->ctrltail].dir;
345 352
346 r = usb_autopm_get_interface_async(usbhid->intf); 353 len = ((report->size - 1) >> 3) + 1 + (report->id > 0);
347 if (r < 0) 354 if (dir == USB_DIR_OUT) {
348 return -1; 355 usbhid->urbctrl->pipe = usb_sndctrlpipe(hid_to_usb_dev(hid), 0);
349 if (!test_bit(HID_REPORTED_IDLE, &usbhid->iofl)) { 356 usbhid->urbctrl->transfer_buffer_length = len;
350 len = ((report->size - 1) >> 3) + 1 + (report->id > 0); 357 memcpy(usbhid->ctrlbuf, raw_report, len);
351 if (dir == USB_DIR_OUT) { 358 kfree(raw_report);
352 usbhid->urbctrl->pipe = usb_sndctrlpipe(hid_to_usb_dev(hid), 0); 359 } else {
353 usbhid->urbctrl->transfer_buffer_length = len; 360 int maxpacket, padlen;
354 memcpy(usbhid->ctrlbuf, raw_report, len); 361
355 kfree(raw_report); 362 usbhid->urbctrl->pipe = usb_rcvctrlpipe(hid_to_usb_dev(hid), 0);
356 } else { 363 maxpacket = usb_maxpacket(hid_to_usb_dev(hid),
357 int maxpacket, padlen; 364 usbhid->urbctrl->pipe, 0);
358 365 if (maxpacket > 0) {
359 usbhid->urbctrl->pipe = usb_rcvctrlpipe(hid_to_usb_dev(hid), 0); 366 padlen = DIV_ROUND_UP(len, maxpacket);
360 maxpacket = usb_maxpacket(hid_to_usb_dev(hid), usbhid->urbctrl->pipe, 0); 367 padlen *= maxpacket;
361 if (maxpacket > 0) { 368 if (padlen > usbhid->bufsize)
362 padlen = DIV_ROUND_UP(len, maxpacket); 369 padlen = usbhid->bufsize;
363 padlen *= maxpacket; 370 } else
364 if (padlen > usbhid->bufsize) 371 padlen = 0;
365 padlen = usbhid->bufsize; 372 usbhid->urbctrl->transfer_buffer_length = padlen;
366 } else
367 padlen = 0;
368 usbhid->urbctrl->transfer_buffer_length = padlen;
369 }
370 usbhid->urbctrl->dev = hid_to_usb_dev(hid);
371
372 usbhid->cr->bRequestType = USB_TYPE_CLASS | USB_RECIP_INTERFACE | dir;
373 usbhid->cr->bRequest = (dir == USB_DIR_OUT) ? HID_REQ_SET_REPORT : HID_REQ_GET_REPORT;
374 usbhid->cr->wValue = cpu_to_le16(((report->type + 1) << 8) | report->id);
375 usbhid->cr->wIndex = cpu_to_le16(usbhid->ifnum);
376 usbhid->cr->wLength = cpu_to_le16(len);
377
378 dbg_hid("submitting ctrl urb: %s wValue=0x%04x wIndex=0x%04x wLength=%u\n",
379 usbhid->cr->bRequest == HID_REQ_SET_REPORT ? "Set_Report" : "Get_Report",
380 usbhid->cr->wValue, usbhid->cr->wIndex, usbhid->cr->wLength);
381
382 if (usb_submit_urb(usbhid->urbctrl, GFP_ATOMIC)) {
383 usb_autopm_put_interface_async(usbhid->intf);
384 hid_err(hid, "usb_submit_urb(ctrl) failed\n");
385 return -1;
386 }
387 usbhid->last_ctrl = jiffies;
388 } 373 }
389 374 usbhid->urbctrl->dev = hid_to_usb_dev(hid);
375
376 usbhid->cr->bRequestType = USB_TYPE_CLASS | USB_RECIP_INTERFACE | dir;
377 usbhid->cr->bRequest = (dir == USB_DIR_OUT) ? HID_REQ_SET_REPORT :
378 HID_REQ_GET_REPORT;
379 usbhid->cr->wValue = cpu_to_le16(((report->type + 1) << 8) |
380 report->id);
381 usbhid->cr->wIndex = cpu_to_le16(usbhid->ifnum);
382 usbhid->cr->wLength = cpu_to_le16(len);
383
384 dbg_hid("submitting ctrl urb: %s wValue=0x%04x wIndex=0x%04x wLength=%u\n",
385 usbhid->cr->bRequest == HID_REQ_SET_REPORT ? "Set_Report" :
386 "Get_Report",
387 usbhid->cr->wValue, usbhid->cr->wIndex, usbhid->cr->wLength);
388
389 r = usb_submit_urb(usbhid->urbctrl, GFP_ATOMIC);
390 if (r < 0) {
391 hid_err(hid, "usb_submit_urb(ctrl) failed: %d\n", r);
392 return r;
393 }
394 usbhid->last_ctrl = jiffies;
390 return 0; 395 return 0;
391} 396}
392 397
@@ -423,11 +428,8 @@ static void hid_irq_out(struct urb *urb)
423 else 428 else
424 usbhid->outtail = (usbhid->outtail + 1) & (HID_OUTPUT_FIFO_SIZE - 1); 429 usbhid->outtail = (usbhid->outtail + 1) & (HID_OUTPUT_FIFO_SIZE - 1);
425 430
426 if (usbhid->outhead != usbhid->outtail) { 431 if (usbhid->outhead != usbhid->outtail && !hid_submit_out(hid)) {
427 if (hid_submit_out(hid)) { 432 /* Successfully submitted next urb in queue */
428 clear_bit(HID_OUT_RUNNING, &usbhid->iofl);
429 wake_up(&usbhid->wait);
430 }
431 spin_unlock_irqrestore(&usbhid->lock, flags); 433 spin_unlock_irqrestore(&usbhid->lock, flags);
432 return; 434 return;
433 } 435 }
@@ -474,13 +476,9 @@ static void hid_ctrl(struct urb *urb)
474 else 476 else
475 usbhid->ctrltail = (usbhid->ctrltail + 1) & (HID_CONTROL_FIFO_SIZE - 1); 477 usbhid->ctrltail = (usbhid->ctrltail + 1) & (HID_CONTROL_FIFO_SIZE - 1);
476 478
477 if (usbhid->ctrlhead != usbhid->ctrltail) { 479 if (usbhid->ctrlhead != usbhid->ctrltail && !hid_submit_ctrl(hid)) {
478 if (hid_submit_ctrl(hid)) { 480 /* Successfully submitted next urb in queue */
479 clear_bit(HID_CTRL_RUNNING, &usbhid->iofl);
480 wake_up(&usbhid->wait);
481 }
482 spin_unlock(&usbhid->lock); 481 spin_unlock(&usbhid->lock);
483 usb_autopm_put_interface_async(usbhid->intf);
484 return; 482 return;
485 } 483 }
486 484
@@ -515,9 +513,23 @@ static void __usbhid_submit_report(struct hid_device *hid, struct hid_report *re
515 usbhid->out[usbhid->outhead].report = report; 513 usbhid->out[usbhid->outhead].report = report;
516 usbhid->outhead = head; 514 usbhid->outhead = head;
517 515
516 /* Try to awake from autosuspend... */
517 if (usb_autopm_get_interface_async(usbhid->intf) < 0)
518 return;
519
520 /*
521 * But if still suspended, leave urb enqueued, don't submit.
522 * Submission will occur if/when resume() drains the queue.
523 */
524 if (test_bit(HID_REPORTED_IDLE, &usbhid->iofl))
525 return;
526
518 if (!test_and_set_bit(HID_OUT_RUNNING, &usbhid->iofl)) { 527 if (!test_and_set_bit(HID_OUT_RUNNING, &usbhid->iofl)) {
519 if (hid_submit_out(hid)) 528 if (hid_submit_out(hid)) {
520 clear_bit(HID_OUT_RUNNING, &usbhid->iofl); 529 clear_bit(HID_OUT_RUNNING, &usbhid->iofl);
530 usb_autopm_put_interface_async(usbhid->intf);
531 }
532 wake_up(&usbhid->wait);
521 } else { 533 } else {
522 /* 534 /*
523 * the queue is known to run 535 * the queue is known to run
@@ -549,9 +561,23 @@ static void __usbhid_submit_report(struct hid_device *hid, struct hid_report *re
549 usbhid->ctrl[usbhid->ctrlhead].dir = dir; 561 usbhid->ctrl[usbhid->ctrlhead].dir = dir;
550 usbhid->ctrlhead = head; 562 usbhid->ctrlhead = head;
551 563
564 /* Try to awake from autosuspend... */
565 if (usb_autopm_get_interface_async(usbhid->intf) < 0)
566 return;
567
568 /*
569 * If already suspended, leave urb enqueued, but don't submit.
570 * Submission will occur if/when resume() drains the queue.
571 */
572 if (test_bit(HID_REPORTED_IDLE, &usbhid->iofl))
573 return;
574
552 if (!test_and_set_bit(HID_CTRL_RUNNING, &usbhid->iofl)) { 575 if (!test_and_set_bit(HID_CTRL_RUNNING, &usbhid->iofl)) {
553 if (hid_submit_ctrl(hid)) 576 if (hid_submit_ctrl(hid)) {
554 clear_bit(HID_CTRL_RUNNING, &usbhid->iofl); 577 clear_bit(HID_CTRL_RUNNING, &usbhid->iofl);
578 usb_autopm_put_interface_async(usbhid->intf);
579 }
580 wake_up(&usbhid->wait);
555 } else { 581 } else {
556 /* 582 /*
557 * the queue is known to run 583 * the queue is known to run