|
@@ -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,70 +853,137 @@ 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 err, i;
|
|
|
|
-
|
|
|
|
- BUILD_BUG_ON(ARRAY_SIZE(p54u_fwlist) != __NUM_P54U_HWTYPES);
|
|
|
|
|
|
+ int i;
|
|
|
|
|
|
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);
|
|
|
|
- if (err) {
|
|
|
|
- dev_err(&priv->udev->dev, "(p54usb) cannot load firmware %s "
|
|
|
|
- "(%d)!\n", p54u_fwlist[i].fw, err);
|
|
|
|
|
|
+ return i;
|
|
|
|
+}
|
|
|
|
|
|
- err = request_firmware(&priv->fw, p54u_fwlist[i].fw_legacy,
|
|
|
|
- &priv->udev->dev);
|
|
|
|
- if (err)
|
|
|
|
- return err;
|
|
|
|
- }
|
|
|
|
|
|
+static int p54u_start_ops(struct p54u_priv *priv)
|
|
|
|
+{
|
|
|
|
+ struct ieee80211_hw *dev = priv->common.hw;
|
|
|
|
+ int ret;
|
|
|
|
|
|
- err = p54_parse_firmware(dev, priv->fw);
|
|
|
|
- if (err)
|
|
|
|
- goto out;
|
|
|
|
|
|
+ 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[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);
|
|
|
|
- err = -EINVAL;
|
|
|
|
|
|
+ p54u_fwlist[ret].hw);
|
|
|
|
+ ret = -ENODEV;
|
|
|
|
+ goto err_out;
|
|
}
|
|
}
|
|
|
|
|
|
-out:
|
|
|
|
- if (err)
|
|
|
|
- release_firmware(priv->fw);
|
|
|
|
|
|
+ ret = priv->upload_fw(dev);
|
|
|
|
+ if (ret)
|
|
|
|
+ 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);
|
|
|
|
- if (err) {
|
|
|
|
- return 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");
|
|
}
|
|
}
|
|
|
|
|
|
- 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
|
|
|
|
- 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);
|
|
|
|
|
|
+ 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) {
|
|
|
|
+ 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,
|
|
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);
|
|
|
|
- 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);
|
|
|
|
|
|
+ err = p54u_load_firmware(dev, intf);
|
|
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);
|