Skip to content

Commit

Permalink
ar9170usb: fix hang on resume
Browse files Browse the repository at this point in the history
This patch fixes a hang on resume when the filesystem is not
available and request_firmware blocks.

However, the device does not accept the firmware on resume.
and it will exit with:

> firmware part 1 upload failed (-71).
> device is in a bad state. please reconnect it!

Reported-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: Christian Lamparter <chunkeey@web.de>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
  • Loading branch information
Christian Lamparter authored and John W. Linville committed Apr 20, 2009
1 parent 18aaab1 commit e10a9df
Showing 1 changed file with 90 additions and 20 deletions.
110 changes: 90 additions & 20 deletions drivers/net/wireless/ar9170/usb.c
Original file line number Diff line number Diff line change
Expand Up @@ -623,6 +623,39 @@ static int ar9170_usb_open(struct ar9170 *ar)
return 0;
}

static int ar9170_usb_init_device(struct ar9170_usb *aru)
{
int err;

err = ar9170_usb_alloc_rx_irq_urb(aru);
if (err)
goto err_out;

err = ar9170_usb_alloc_rx_bulk_urbs(aru);
if (err)
goto err_unrx;

err = ar9170_usb_upload_firmware(aru);
if (err) {
err = ar9170_echo_test(&aru->common, 0x60d43110);
if (err) {
/* force user invention, by disabling the device */
err = usb_driver_set_configuration(aru->udev, -1);
dev_err(&aru->udev->dev, "device is in a bad state. "
"please reconnect it!\n");
goto err_unrx;
}
}

return 0;

err_unrx:
ar9170_usb_cancel_urbs(aru);

err_out:
return err;
}

static int ar9170_usb_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
Expand Down Expand Up @@ -658,32 +691,16 @@ static int ar9170_usb_probe(struct usb_interface *intf,

err = ar9170_usb_reset(aru);
if (err)
goto err_unlock;
goto err_freehw;

err = ar9170_usb_request_firmware(aru);
if (err)
goto err_unlock;
goto err_freehw;

err = ar9170_usb_alloc_rx_irq_urb(aru);
err = ar9170_usb_init_device(aru);
if (err)
goto err_freefw;

err = ar9170_usb_alloc_rx_bulk_urbs(aru);
if (err)
goto err_unrx;

err = ar9170_usb_upload_firmware(aru);
if (err) {
err = ar9170_echo_test(&aru->common, 0x60d43110);
if (err) {
/* force user invention, by disabling the device */
err = usb_driver_set_configuration(aru->udev, -1);
dev_err(&aru->udev->dev, "device is in a bad state. "
"please reconnect it!\n");
goto err_unrx;
}
}

err = ar9170_usb_open(ar);
if (err)
goto err_unrx;
Expand All @@ -703,7 +720,7 @@ static int ar9170_usb_probe(struct usb_interface *intf,
release_firmware(aru->init_values);
release_firmware(aru->firmware);

err_unlock:
err_freehw:
usb_set_intfdata(intf, NULL);
usb_put_dev(udev);
ieee80211_free_hw(ar->hw);
Expand All @@ -730,12 +747,65 @@ static void ar9170_usb_disconnect(struct usb_interface *intf)
ieee80211_free_hw(aru->common.hw);
}

#ifdef CONFIG_PM
static int ar9170_suspend(struct usb_interface *intf,
pm_message_t message)
{
struct ar9170_usb *aru = usb_get_intfdata(intf);

if (!aru)
return -ENODEV;

aru->common.state = AR9170_IDLE;
ar9170_usb_cancel_urbs(aru);

return 0;
}

static int ar9170_resume(struct usb_interface *intf)
{
struct ar9170_usb *aru = usb_get_intfdata(intf);
int err;

if (!aru)
return -ENODEV;

usb_unpoison_anchored_urbs(&aru->rx_submitted);
usb_unpoison_anchored_urbs(&aru->tx_submitted);

/*
* FIXME: firmware upload will fail on resume.
* but this is better than a hang!
*/

err = ar9170_usb_init_device(aru);
if (err)
goto err_unrx;

err = ar9170_usb_open(&aru->common);
if (err)
goto err_unrx;

return 0;

err_unrx:
aru->common.state = AR9170_IDLE;
ar9170_usb_cancel_urbs(aru);

return err;
}
#endif /* CONFIG_PM */

static struct usb_driver ar9170_driver = {
.name = "ar9170usb",
.probe = ar9170_usb_probe,
.disconnect = ar9170_usb_disconnect,
.id_table = ar9170_usb_ids,
.soft_unbind = 1,
#ifdef CONFIG_PM
.suspend = ar9170_suspend,
.resume = ar9170_resume,
#endif /* CONFIG_PM */
};

static int __init ar9170_init(void)
Expand Down

0 comments on commit e10a9df

Please sign in to comment.