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>
This commit is contained in:
Oliver Neukum 2008-07-03 12:02:03 -04:00 committed by Dmitry Torokhov
parent 4ad88901dd
commit b4ecda3e96
1 changed files with 29 additions and 15 deletions

View File

@ -119,6 +119,8 @@ struct yealink_dev {
u8 lcdMap[ARRAY_SIZE(lcdMap)]; /* state of LCD, LED ... */ u8 lcdMap[ARRAY_SIZE(lcdMap)]; /* state of LCD, LED ... */
int key_code; /* last reported key */ int key_code; /* last reported key */
unsigned int shutdown:1;
int stat_ix; int stat_ix;
union { union {
struct yld_status s; struct yld_status s;
@ -424,10 +426,10 @@ send_update:
static void urb_irq_callback(struct urb *urb) static void urb_irq_callback(struct urb *urb)
{ {
struct yealink_dev *yld = urb->context; struct yealink_dev *yld = urb->context;
int ret; int ret, status = urb->status;
if (urb->status) if (status)
err("%s - urb status %d", __func__, urb->status); err("%s - urb status %d", __func__, status);
switch (yld->irq_data->cmd) { switch (yld->irq_data->cmd) {
case CMD_KEYPRESS: case CMD_KEYPRESS:
@ -447,32 +449,37 @@ static void urb_irq_callback(struct urb *urb)
yealink_do_idle_tasks(yld); yealink_do_idle_tasks(yld);
if (!yld->shutdown) {
ret = usb_submit_urb(yld->urb_ctl, GFP_ATOMIC); ret = usb_submit_urb(yld->urb_ctl, GFP_ATOMIC);
if (ret) if (ret && ret != -EPERM)
err("%s - usb_submit_urb failed %d", __func__, ret); err("%s - usb_submit_urb failed %d", __func__, ret);
} }
}
static void urb_ctl_callback(struct urb *urb) static void urb_ctl_callback(struct urb *urb)
{ {
struct yealink_dev *yld = urb->context; struct yealink_dev *yld = urb->context;
int ret; int ret = 0, status = urb->status;
if (urb->status) if (status)
err("%s - urb status %d", __func__, urb->status); err("%s - urb status %d", __func__, status);
switch (yld->ctl_data->cmd) { switch (yld->ctl_data->cmd) {
case CMD_KEYPRESS: case CMD_KEYPRESS:
case CMD_SCANCODE: case CMD_SCANCODE:
/* ask for a response */ /* ask for a response */
if (!yld->shutdown)
ret = usb_submit_urb(yld->urb_irq, GFP_ATOMIC); ret = usb_submit_urb(yld->urb_irq, GFP_ATOMIC);
break; break;
default: default:
/* send new command */ /* send new command */
yealink_do_idle_tasks(yld); yealink_do_idle_tasks(yld);
if (!yld->shutdown)
ret = usb_submit_urb(yld->urb_ctl, GFP_ATOMIC); ret = usb_submit_urb(yld->urb_ctl, GFP_ATOMIC);
break;
} }
if (ret) if (ret && ret != -EPERM)
err("%s - usb_submit_urb failed %d", __func__, ret); err("%s - usb_submit_urb failed %d", __func__, ret);
} }
@ -531,8 +538,18 @@ static void input_close(struct input_dev *dev)
{ {
struct yealink_dev *yld = input_get_drvdata(dev); struct yealink_dev *yld = input_get_drvdata(dev);
yld->shutdown = 1;
/*
* Make sure the flag is seen by other CPUs before we start
* killing URBs so new URBs won't be submitted
*/
smp_wmb();
usb_kill_urb(yld->urb_ctl); usb_kill_urb(yld->urb_ctl);
usb_kill_urb(yld->urb_irq); usb_kill_urb(yld->urb_irq);
yld->shutdown = 0;
smp_wmb();
} }
/******************************************************************************* /*******************************************************************************
@ -809,9 +826,6 @@ static int usb_cleanup(struct yealink_dev *yld, int err)
if (yld == NULL) if (yld == NULL)
return err; return err;
usb_kill_urb(yld->urb_irq); /* parameter validation in core/urb */
usb_kill_urb(yld->urb_ctl); /* parameter validation in core/urb */
if (yld->idev) { if (yld->idev) {
if (err) if (err)
input_free_device(yld->idev); input_free_device(yld->idev);