diff options
author | Oliver Neukum <oliver@neukum.org> | 2008-07-03 12:02:03 -0400 |
---|---|---|
committer | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2008-07-07 09:01:49 -0400 |
commit | b4ecda3e965a87881a94017cb0cd484d65799261 (patch) | |
tree | bfd5ebf739f1fd2296dad27a8726634abc72716c /drivers/input | |
parent | 4ad88901dd675acb9c8d1eca1f083c3d22cbbd4d (diff) |
Input: yealink - reliably kill urbs
Yealink uses two URBs that submit each other. This arrangement
cannot be reliably killed with usb_kill_urb() alone, as there's
a window during which the wrong URB may be killed. The fix is
to introduce a flag.
[dtor@mail.ru: remove spinlock, flag alone should be enough]
Signed-off-by: Oliver Neukum <oneukum@suse.de>
Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
Diffstat (limited to 'drivers/input')
-rw-r--r-- | drivers/input/misc/yealink.c | 44 |
1 files changed, 29 insertions, 15 deletions
diff --git a/drivers/input/misc/yealink.c b/drivers/input/misc/yealink.c index 8a949e7d8f4e..facefd3dba29 100644 --- a/drivers/input/misc/yealink.c +++ b/drivers/input/misc/yealink.c | |||
@@ -119,6 +119,8 @@ struct yealink_dev { | |||
119 | u8 lcdMap[ARRAY_SIZE(lcdMap)]; /* state of LCD, LED ... */ | 119 | u8 lcdMap[ARRAY_SIZE(lcdMap)]; /* state of LCD, LED ... */ |
120 | int key_code; /* last reported key */ | 120 | int key_code; /* last reported key */ |
121 | 121 | ||
122 | unsigned int shutdown:1; | ||
123 | |||
122 | int stat_ix; | 124 | int stat_ix; |
123 | union { | 125 | union { |
124 | struct yld_status s; | 126 | struct yld_status s; |
@@ -424,10 +426,10 @@ send_update: | |||
424 | static void urb_irq_callback(struct urb *urb) | 426 | static void urb_irq_callback(struct urb *urb) |
425 | { | 427 | { |
426 | struct yealink_dev *yld = urb->context; | 428 | struct yealink_dev *yld = urb->context; |
427 | int ret; | 429 | int ret, status = urb->status; |
428 | 430 | ||
429 | if (urb->status) | 431 | if (status) |
430 | err("%s - urb status %d", __func__, urb->status); | 432 | err("%s - urb status %d", __func__, status); |
431 | 433 | ||
432 | switch (yld->irq_data->cmd) { | 434 | switch (yld->irq_data->cmd) { |
433 | case CMD_KEYPRESS: | 435 | case CMD_KEYPRESS: |
@@ -447,32 +449,37 @@ static void urb_irq_callback(struct urb *urb) | |||
447 | 449 | ||
448 | yealink_do_idle_tasks(yld); | 450 | yealink_do_idle_tasks(yld); |
449 | 451 | ||
450 | ret = usb_submit_urb(yld->urb_ctl, GFP_ATOMIC); | 452 | if (!yld->shutdown) { |
451 | if (ret) | 453 | ret = usb_submit_urb(yld->urb_ctl, GFP_ATOMIC); |
452 | err("%s - usb_submit_urb failed %d", __func__, ret); | 454 | if (ret && ret != -EPERM) |
455 | err("%s - usb_submit_urb failed %d", __func__, ret); | ||
456 | } | ||
453 | } | 457 | } |
454 | 458 | ||
455 | static void urb_ctl_callback(struct urb *urb) | 459 | static void urb_ctl_callback(struct urb *urb) |
456 | { | 460 | { |
457 | struct yealink_dev *yld = urb->context; | 461 | struct yealink_dev *yld = urb->context; |
458 | int ret; | 462 | int ret = 0, status = urb->status; |
459 | 463 | ||
460 | if (urb->status) | 464 | if (status) |
461 | err("%s - urb status %d", __func__, urb->status); | 465 | err("%s - urb status %d", __func__, status); |
462 | 466 | ||
463 | switch (yld->ctl_data->cmd) { | 467 | switch (yld->ctl_data->cmd) { |
464 | case CMD_KEYPRESS: | 468 | case CMD_KEYPRESS: |
465 | case CMD_SCANCODE: | 469 | case CMD_SCANCODE: |
466 | /* ask for a response */ | 470 | /* ask for a response */ |
467 | ret = usb_submit_urb(yld->urb_irq, GFP_ATOMIC); | 471 | if (!yld->shutdown) |
472 | ret = usb_submit_urb(yld->urb_irq, GFP_ATOMIC); | ||
468 | break; | 473 | break; |
469 | default: | 474 | default: |
470 | /* send new command */ | 475 | /* send new command */ |
471 | yealink_do_idle_tasks(yld); | 476 | yealink_do_idle_tasks(yld); |
472 | ret = usb_submit_urb(yld->urb_ctl, GFP_ATOMIC); | 477 | if (!yld->shutdown) |
478 | ret = usb_submit_urb(yld->urb_ctl, GFP_ATOMIC); | ||
479 | break; | ||
473 | } | 480 | } |
474 | 481 | ||
475 | if (ret) | 482 | if (ret && ret != -EPERM) |
476 | err("%s - usb_submit_urb failed %d", __func__, ret); | 483 | err("%s - usb_submit_urb failed %d", __func__, ret); |
477 | } | 484 | } |
478 | 485 | ||
@@ -531,8 +538,18 @@ static void input_close(struct input_dev *dev) | |||
531 | { | 538 | { |
532 | struct yealink_dev *yld = input_get_drvdata(dev); | 539 | struct yealink_dev *yld = input_get_drvdata(dev); |
533 | 540 | ||
541 | yld->shutdown = 1; | ||
542 | /* | ||
543 | * Make sure the flag is seen by other CPUs before we start | ||
544 | * killing URBs so new URBs won't be submitted | ||
545 | */ | ||
546 | smp_wmb(); | ||
547 | |||
534 | usb_kill_urb(yld->urb_ctl); | 548 | usb_kill_urb(yld->urb_ctl); |
535 | usb_kill_urb(yld->urb_irq); | 549 | usb_kill_urb(yld->urb_irq); |
550 | |||
551 | yld->shutdown = 0; | ||
552 | smp_wmb(); | ||
536 | } | 553 | } |
537 | 554 | ||
538 | /******************************************************************************* | 555 | /******************************************************************************* |
@@ -809,9 +826,6 @@ static int usb_cleanup(struct yealink_dev *yld, int err) | |||
809 | if (yld == NULL) | 826 | if (yld == NULL) |
810 | return err; | 827 | return err; |
811 | 828 | ||
812 | usb_kill_urb(yld->urb_irq); /* parameter validation in core/urb */ | ||
813 | usb_kill_urb(yld->urb_ctl); /* parameter validation in core/urb */ | ||
814 | |||
815 | if (yld->idev) { | 829 | if (yld->idev) { |
816 | if (err) | 830 | if (err) |
817 | input_free_device(yld->idev); | 831 | input_free_device(yld->idev); |