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: Larry Finger <Larry.Finger@lwfinger.net> Signed-off-by: Christian Lamparter <chunkeey@googlemail.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
This commit is contained in:
Родитель
a9b9361dd5
Коммит
5612a508d1
|
@ -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)
|
||||||
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)
|
||||||
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,72 +853,139 @@ fail:
|
||||||
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int p54u_start_ops(struct p54u_priv *priv)
|
||||||
|
{
|
||||||
|
struct ieee80211_hw *dev = priv->common.hw;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = p54_parse_firmware(dev, priv->fw);
|
||||||
|
if (ret)
|
||||||
|
goto err_out;
|
||||||
|
|
||||||
|
ret = p54_find_type(priv);
|
||||||
|
if (ret < 0)
|
||||||
|
goto err_out;
|
||||||
|
|
||||||
|
if (priv->common.fw_interface != p54u_fwlist[ret].intf) {
|
||||||
|
dev_err(&priv->udev->dev, "wrong firmware, please get "
|
||||||
|
"a firmware for \"%s\" and try again.\n",
|
||||||
|
p54u_fwlist[ret].hw);
|
||||||
|
ret = -ENODEV;
|
||||||
|
goto err_out;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = priv->upload_fw(dev);
|
||||||
|
if (ret)
|
||||||
|
goto err_out;
|
||||||
|
|
||||||
|
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 void p54u_load_firmware_cb(const struct firmware *firmware,
|
||||||
|
void *context)
|
||||||
|
{
|
||||||
|
struct p54u_priv *priv = context;
|
||||||
|
struct usb_device *udev = priv->udev;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
complete(&priv->fw_wait_load);
|
||||||
|
if (firmware) {
|
||||||
|
priv->fw = firmware;
|
||||||
|
err = p54u_start_ops(priv);
|
||||||
|
} else {
|
||||||
|
err = -ENOENT;
|
||||||
|
dev_err(&udev->dev, "Firmware not found.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (err) {
|
||||||
|
struct device *parent = priv->udev->dev.parent;
|
||||||
|
|
||||||
|
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 int p54u_load_firmware(struct ieee80211_hw *dev,
|
||||||
|
struct usb_interface *intf)
|
||||||
|
{
|
||||||
|
struct usb_device *udev = interface_to_usbdev(intf);
|
||||||
|
struct p54u_priv *priv = dev->priv;
|
||||||
|
struct device *device = &udev->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) {
|
if (err) {
|
||||||
dev_err(&priv->udev->dev, "(p54usb) cannot load firmware %s "
|
dev_err(&priv->udev->dev, "(p54usb) cannot load firmware %s "
|
||||||
"(%d)!\n", p54u_fwlist[i].fw, err);
|
"(%d)!\n", p54u_fwlist[i].fw, err);
|
||||||
|
|
||||||
err = request_firmware(&priv->fw, p54u_fwlist[i].fw_legacy,
|
|
||||||
&priv->udev->dev);
|
|
||||||
if (err)
|
|
||||||
return err;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
err = p54_parse_firmware(dev, priv->fw);
|
|
||||||
if (err)
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
if (priv->common.fw_interface != p54u_fwlist[i].intf) {
|
|
||||||
dev_err(&priv->udev->dev, "wrong firmware, please get "
|
|
||||||
"a firmware for \"%s\" and try again.\n",
|
|
||||||
p54u_fwlist[i].hw);
|
|
||||||
err = -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
out:
|
|
||||||
if (err)
|
|
||||||
release_firmware(priv->fw);
|
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int p54u_open(struct ieee80211_hw *dev)
|
|
||||||
{
|
|
||||||
struct p54u_priv *priv = dev->priv;
|
|
||||||
int err;
|
|
||||||
|
|
||||||
err = p54u_init_urbs(dev);
|
|
||||||
if (err) {
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
priv->common.open = p54u_init_urbs;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
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 __devinit p54u_probe(struct usb_interface *intf,
|
static int __devinit p54u_probe(struct usb_interface *intf,
|
||||||
const struct usb_device_id *id)
|
const struct usb_device_id *id)
|
||||||
{
|
{
|
||||||
|
@ -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)
|
||||||
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 {
|
||||||
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 */
|
||||||
|
|
Загрузка…
Ссылка в новой задаче