Commit 5612a508 authored by Larry Finger's avatar Larry Finger Committed by John W. Linville

p54usb: Load firmware asynchronously

Drivers that load firmware from their probe routine have problems with
the latest versions of udev as they get timeouts while waiting for user
space to start. The problem is fixed by using request_firmware_nowait()
and delaying the start of mac80211 until the firmware is loaded.

To prevent the possibility of the driver being unloaded while the
firmware loading callback is still active, a completion queue entry
is used.

Also, to simplify the firmware loading procedure, this patch removes
the old, unofficial and confusing fallback firmware names. However,
they are still supported! So any user - who is still using them -
is hereby advised to link/rename their old firmware filenames:
	isl3890usb to isl3886usb
	isl3887usb_bare to isl3887usb
Signed-off-by: default avatarLarry Finger <Larry.Finger@lwfinger.net>
Signed-off-by: default avatarChristian Lamparter <chunkeey@googlemail.com>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent a9b9361d
...@@ -117,21 +117,18 @@ static const struct { ...@@ -117,21 +117,18 @@ static const struct {
u32 intf; u32 intf;
enum p54u_hw_type type; enum p54u_hw_type type;
const char *fw; const char *fw;
const char *fw_legacy;
char hw[20]; char hw[20];
} p54u_fwlist[__NUM_P54U_HWTYPES] = { } p54u_fwlist[__NUM_P54U_HWTYPES] = {
{ {
.type = P54U_NET2280, .type = P54U_NET2280,
.intf = FW_LM86, .intf = FW_LM86,
.fw = "isl3886usb", .fw = "isl3886usb",
.fw_legacy = "isl3890usb",
.hw = "ISL3886 + net2280", .hw = "ISL3886 + net2280",
}, },
{ {
.type = P54U_3887, .type = P54U_3887,
.intf = FW_LM87, .intf = FW_LM87,
.fw = "isl3887usb", .fw = "isl3887usb",
.fw_legacy = "isl3887usb_bare",
.hw = "ISL3887", .hw = "ISL3887",
}, },
}; };
...@@ -208,6 +205,16 @@ static void p54u_free_urbs(struct ieee80211_hw *dev) ...@@ -208,6 +205,16 @@ static void p54u_free_urbs(struct ieee80211_hw *dev)
usb_kill_anchored_urbs(&priv->submitted); usb_kill_anchored_urbs(&priv->submitted);
} }
static void p54u_stop(struct ieee80211_hw *dev)
{
/*
* TODO: figure out how to reliably stop the 3887 and net2280 so
* the hardware is still usable next time we want to start it.
* until then, we just stop listening to the hardware..
*/
p54u_free_urbs(dev);
}
static int p54u_init_urbs(struct ieee80211_hw *dev) static int p54u_init_urbs(struct ieee80211_hw *dev)
{ {
struct p54u_priv *priv = dev->priv; struct p54u_priv *priv = dev->priv;
...@@ -257,6 +264,16 @@ static int p54u_init_urbs(struct ieee80211_hw *dev) ...@@ -257,6 +264,16 @@ static int p54u_init_urbs(struct ieee80211_hw *dev)
return ret; return ret;
} }
static int p54u_open(struct ieee80211_hw *dev)
{
/*
* TODO: Because we don't know how to reliably stop the 3887 and
* the isl3886+net2280, other than brutally cut off all
* communications. We have to reinitialize the urbs on every start.
*/
return p54u_init_urbs(dev);
}
static __le32 p54u_lm87_chksum(const __le32 *data, size_t length) static __le32 p54u_lm87_chksum(const __le32 *data, size_t length)
{ {
u32 chk = 0; u32 chk = 0;
...@@ -836,70 +853,137 @@ static int p54u_upload_firmware_net2280(struct ieee80211_hw *dev) ...@@ -836,70 +853,137 @@ static int p54u_upload_firmware_net2280(struct ieee80211_hw *dev)
return err; return err;
} }
static int p54u_load_firmware(struct ieee80211_hw *dev) static int p54_find_type(struct p54u_priv *priv)
{ {
struct p54u_priv *priv = dev->priv; int i;
int err, i;
BUILD_BUG_ON(ARRAY_SIZE(p54u_fwlist) != __NUM_P54U_HWTYPES);
for (i = 0; i < __NUM_P54U_HWTYPES; i++) for (i = 0; i < __NUM_P54U_HWTYPES; i++)
if (p54u_fwlist[i].type == priv->hw_type) if (p54u_fwlist[i].type == priv->hw_type)
break; break;
if (i == __NUM_P54U_HWTYPES) if (i == __NUM_P54U_HWTYPES)
return -EOPNOTSUPP; return -EOPNOTSUPP;
err = request_firmware(&priv->fw, p54u_fwlist[i].fw, &priv->udev->dev); return i;
if (err) { }
dev_err(&priv->udev->dev, "(p54usb) cannot load firmware %s "
"(%d)!\n", p54u_fwlist[i].fw, err);
err = request_firmware(&priv->fw, p54u_fwlist[i].fw_legacy, static int p54u_start_ops(struct p54u_priv *priv)
&priv->udev->dev); {
if (err) struct ieee80211_hw *dev = priv->common.hw;
return err; int ret;
}
err = p54_parse_firmware(dev, priv->fw); ret = p54_parse_firmware(dev, priv->fw);
if (err) if (ret)
goto out; goto err_out;
ret = p54_find_type(priv);
if (ret < 0)
goto err_out;
if (priv->common.fw_interface != p54u_fwlist[i].intf) { if (priv->common.fw_interface != p54u_fwlist[ret].intf) {
dev_err(&priv->udev->dev, "wrong firmware, please get " dev_err(&priv->udev->dev, "wrong firmware, please get "
"a firmware for \"%s\" and try again.\n", "a firmware for \"%s\" and try again.\n",
p54u_fwlist[i].hw); p54u_fwlist[ret].hw);
err = -EINVAL; ret = -ENODEV;
goto err_out;
} }
out: ret = priv->upload_fw(dev);
if (err) if (ret)
release_firmware(priv->fw); goto err_out;
return err; ret = p54u_open(dev);
if (ret)
goto err_out;
ret = p54_read_eeprom(dev);
if (ret)
goto err_stop;
p54u_stop(dev);
ret = p54_register_common(dev, &priv->udev->dev);
if (ret)
goto err_stop;
return 0;
err_stop:
p54u_stop(dev);
err_out:
/*
* p54u_disconnect will do the rest of the
* cleanup
*/
return ret;
} }
static int p54u_open(struct ieee80211_hw *dev) static void p54u_load_firmware_cb(const struct firmware *firmware,
void *context)
{ {
struct p54u_priv *priv = dev->priv; struct p54u_priv *priv = context;
struct usb_device *udev = priv->udev;
int err; int err;
err = p54u_init_urbs(dev); complete(&priv->fw_wait_load);
if (err) { if (firmware) {
return err; priv->fw = firmware;
err = p54u_start_ops(priv);
} else {
err = -ENOENT;
dev_err(&udev->dev, "Firmware not found.\n");
} }
priv->common.open = p54u_init_urbs; if (err) {
struct device *parent = priv->udev->dev.parent;
return 0; dev_err(&udev->dev, "failed to initialize device (%d)\n", err);
if (parent)
device_lock(parent);
device_release_driver(&udev->dev);
/*
* At this point p54u_disconnect has already freed
* the "priv" context. Do not use it anymore!
*/
priv = NULL;
if (parent)
device_unlock(parent);
}
usb_put_dev(udev);
} }
static void p54u_stop(struct ieee80211_hw *dev) static int p54u_load_firmware(struct ieee80211_hw *dev,
struct usb_interface *intf)
{ {
/* TODO: figure out how to reliably stop the 3887 and net2280 so struct usb_device *udev = interface_to_usbdev(intf);
the hardware is still usable next time we want to start it. struct p54u_priv *priv = dev->priv;
until then, we just stop listening to the hardware.. */ struct device *device = &udev->dev;
p54u_free_urbs(dev); int err, i;
BUILD_BUG_ON(ARRAY_SIZE(p54u_fwlist) != __NUM_P54U_HWTYPES);
init_completion(&priv->fw_wait_load);
i = p54_find_type(priv);
if (i < 0)
return i;
dev_info(&priv->udev->dev, "Loading firmware file %s\n",
p54u_fwlist[i].fw);
usb_get_dev(udev);
err = request_firmware_nowait(THIS_MODULE, 1, p54u_fwlist[i].fw,
device, GFP_KERNEL, priv,
p54u_load_firmware_cb);
if (err) {
dev_err(&priv->udev->dev, "(p54usb) cannot load firmware %s "
"(%d)!\n", p54u_fwlist[i].fw, err);
}
return err;
} }
static int __devinit p54u_probe(struct usb_interface *intf, static int __devinit p54u_probe(struct usb_interface *intf,
...@@ -969,33 +1053,7 @@ static int __devinit p54u_probe(struct usb_interface *intf, ...@@ -969,33 +1053,7 @@ static int __devinit p54u_probe(struct usb_interface *intf,
priv->common.tx = p54u_tx_net2280; priv->common.tx = p54u_tx_net2280;
priv->upload_fw = p54u_upload_firmware_net2280; priv->upload_fw = p54u_upload_firmware_net2280;
} }
err = p54u_load_firmware(dev); err = p54u_load_firmware(dev, intf);
if (err)
goto err_free_dev;
err = priv->upload_fw(dev);
if (err)
goto err_free_fw;
p54u_open(dev);
err = p54_read_eeprom(dev);
p54u_stop(dev);
if (err)
goto err_free_fw;
err = p54_register_common(dev, &udev->dev);
if (err)
goto err_free_fw;
return 0;
err_free_fw:
release_firmware(priv->fw);
err_free_dev:
p54_free_common(dev);
usb_set_intfdata(intf, NULL);
usb_put_dev(udev);
return err; return err;
} }
...@@ -1007,9 +1065,10 @@ static void __devexit p54u_disconnect(struct usb_interface *intf) ...@@ -1007,9 +1065,10 @@ static void __devexit p54u_disconnect(struct usb_interface *intf)
if (!dev) if (!dev)
return; return;
priv = dev->priv;
wait_for_completion(&priv->fw_wait_load);
p54_unregister_common(dev); p54_unregister_common(dev);
priv = dev->priv;
usb_put_dev(interface_to_usbdev(intf)); usb_put_dev(interface_to_usbdev(intf));
release_firmware(priv->fw); release_firmware(priv->fw);
p54_free_common(dev); p54_free_common(dev);
......
...@@ -143,6 +143,9 @@ struct p54u_priv { ...@@ -143,6 +143,9 @@ struct p54u_priv {
struct sk_buff_head rx_queue; struct sk_buff_head rx_queue;
struct usb_anchor submitted; struct usb_anchor submitted;
const struct firmware *fw; const struct firmware *fw;
/* asynchronous firmware callback */
struct completion fw_wait_load;
}; };
#endif /* P54USB_H */ #endif /* P54USB_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment