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); |
