summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/net/wireless/mwl8k.c207
1 files changed, 178 insertions, 29 deletions
diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c
index e5b062c3bd56..081bb6c848d9 100644
--- a/drivers/net/wireless/mwl8k.c
+++ b/drivers/net/wireless/mwl8k.c
@@ -224,6 +224,12 @@ struct mwl8k_priv {
* the firmware image is swapped.
*/
struct ieee80211_tx_queue_params wmm_params[MWL8K_TX_QUEUES];
+
+ /* async firmware loading state */
+ unsigned fw_state;
+ char *fw_pref;
+ char *fw_alt;
+ struct completion firmware_loading_complete;
};
/* Per interface specific private data */
@@ -403,34 +409,66 @@ static void mwl8k_release_firmware(struct mwl8k_priv *priv)
mwl8k_release_fw(&priv->fw_helper);
}
+/* states for asynchronous f/w loading */
+static void mwl8k_fw_state_machine(const struct firmware *fw, void *context);
+enum {
+ FW_STATE_INIT = 0,
+ FW_STATE_LOADING_PREF,
+ FW_STATE_LOADING_ALT,
+ FW_STATE_ERROR,
+};
+
/* Request fw image */
static int mwl8k_request_fw(struct mwl8k_priv *priv,
- const char *fname, struct firmware **fw)
+ const char *fname, struct firmware **fw,
+ bool nowait)
{
/* release current image */
if (*fw != NULL)
mwl8k_release_fw(fw);
- return request_firmware((const struct firmware **)fw,
- fname, &priv->pdev->dev);
+ if (nowait)
+ return request_firmware_nowait(THIS_MODULE, 1, fname,
+ &priv->pdev->dev, GFP_KERNEL,
+ priv, mwl8k_fw_state_machine);
+ else
+ return request_firmware((const struct firmware **)fw,
+ fname, &priv->pdev->dev);
}
-static int mwl8k_request_firmware(struct mwl8k_priv *priv, char *fw_image)
+static int mwl8k_request_firmware(struct mwl8k_priv *priv, char *fw_image,
+ bool nowait)
{
struct mwl8k_device_info *di = priv->device_info;
int rc;
if (di->helper_image != NULL) {
- rc = mwl8k_request_fw(priv, di->helper_image, &priv->fw_helper);
- if (rc) {
- printk(KERN_ERR "%s: Error requesting helper "
- "firmware file %s\n", pci_name(priv->pdev),
- di->helper_image);
+ if (nowait)
+ rc = mwl8k_request_fw(priv, di->helper_image,
+ &priv->fw_helper, true);
+ else
+ rc = mwl8k_request_fw(priv, di->helper_image,
+ &priv->fw_helper, false);
+ if (rc)
+ printk(KERN_ERR "%s: Error requesting helper fw %s\n",
+ pci_name(priv->pdev), di->helper_image);
+
+ if (rc || nowait)
return rc;
- }
}
- rc = mwl8k_request_fw(priv, fw_image, &priv->fw_ucode);
+ if (nowait) {
+ /*
+ * if we get here, no helper image is needed. Skip the
+ * FW_STATE_INIT state.
+ */
+ priv->fw_state = FW_STATE_LOADING_PREF;
+ rc = mwl8k_request_fw(priv, fw_image,
+ &priv->fw_ucode,
+ true);
+ } else
+ rc = mwl8k_request_fw(priv, fw_image,
+ &priv->fw_ucode, false);
if (rc) {
printk(KERN_ERR "%s: Error requesting firmware file %s\n",
pci_name(priv->pdev), fw_image);
@@ -3998,7 +4036,99 @@ static DEFINE_PCI_DEVICE_TABLE(mwl8k_pci_id_table) = {
};
MODULE_DEVICE_TABLE(pci, mwl8k_pci_id_table);
-static int mwl8k_init_firmware(struct ieee80211_hw *hw, char *fw_image)
+static int mwl8k_request_alt_fw(struct mwl8k_priv *priv)
+{
+ int rc;
+ printk(KERN_ERR "%s: Error requesting preferred fw %s.\n"
+ "Trying alternative firmware %s\n", pci_name(priv->pdev),
+ priv->fw_pref, priv->fw_alt);
+ rc = mwl8k_request_fw(priv, priv->fw_alt, &priv->fw_ucode, true);
+ if (rc) {
+ printk(KERN_ERR "%s: Error requesting alt fw %s\n",
+ pci_name(priv->pdev), priv->fw_alt);
+ return rc;
+ }
+ return 0;
+}
+
+static int mwl8k_firmware_load_success(struct mwl8k_priv *priv);
+static void mwl8k_fw_state_machine(const struct firmware *fw, void *context)
+{
+ struct mwl8k_priv *priv = context;
+ struct mwl8k_device_info *di = priv->device_info;
+ int rc;
+
+ switch (priv->fw_state) {
+ case FW_STATE_INIT:
+ if (!fw) {
+ printk(KERN_ERR "%s: Error requesting helper fw %s\n",
+ pci_name(priv->pdev), di->helper_image);
+ goto fail;
+ }
+ priv->fw_helper = fw;
+ rc = mwl8k_request_fw(priv, priv->fw_pref, &priv->fw_ucode,
+ true);
+ if (rc && priv->fw_alt) {
+ rc = mwl8k_request_alt_fw(priv);
+ if (rc)
+ goto fail;
+ priv->fw_state = FW_STATE_LOADING_ALT;
+ } else if (rc)
+ goto fail;
+ else
+ priv->fw_state = FW_STATE_LOADING_PREF;
+ break;
+
+ case FW_STATE_LOADING_PREF:
+ if (!fw) {
+ if (priv->fw_alt) {
+ rc = mwl8k_request_alt_fw(priv);
+ if (rc)
+ goto fail;
+ priv->fw_state = FW_STATE_LOADING_ALT;
+ } else
+ goto fail;
+ } else {
+ priv->fw_ucode = fw;
+ rc = mwl8k_firmware_load_success(priv);
+ if (rc)
+ goto fail;
+ else
+ complete(&priv->firmware_loading_complete);
+ }
+ break;
+
+ case FW_STATE_LOADING_ALT:
+ if (!fw) {
+ printk(KERN_ERR "%s: Error requesting alt fw %s\n",
+ pci_name(priv->pdev), di->helper_image);
+ goto fail;
+ }
+ priv->fw_ucode = fw;
+ rc = mwl8k_firmware_load_success(priv);
+ if (rc)
+ goto fail;
+ else
+ complete(&priv->firmware_loading_complete);
+ break;
+
+ default:
+ printk(KERN_ERR "%s: Unexpected firmware loading state: %d\n",
+ MWL8K_NAME, priv->fw_state);
+ BUG_ON(1);
+ }
+
+ return;
+
+fail:
+ priv->fw_state = FW_STATE_ERROR;
+ complete(&priv->firmware_loading_complete);
+ device_release_driver(&priv->pdev->dev);
+ mwl8k_release_firmware(priv);
+}
+
+static int mwl8k_init_firmware(struct ieee80211_hw *hw, char *fw_image,
+ bool nowait)
{
struct mwl8k_priv *priv = hw->priv;
int rc;
@@ -4007,12 +4137,15 @@ static int mwl8k_init_firmware(struct ieee80211_hw *hw, char *fw_image)
mwl8k_hw_reset(priv);
/* Ask userland hotplug daemon for the device firmware */
- rc = mwl8k_request_firmware(priv, fw_image);
+ rc = mwl8k_request_firmware(priv, fw_image, nowait);
if (rc) {
wiphy_err(hw->wiphy, "Firmware files not found\n");
return rc;
}
+ if (nowait)
+ return rc;
+
/* Load firmware into hardware */
rc = mwl8k_load_firmware(hw);
if (rc)
@@ -4147,7 +4280,7 @@ static int mwl8k_reload_firmware(struct ieee80211_hw *hw, char *fw_image)
for (i = 0; i < MWL8K_TX_QUEUES; i++)
mwl8k_txq_deinit(hw, i);
- rc = mwl8k_init_firmware(hw, fw_image);
+ rc = mwl8k_init_firmware(hw, fw_image, false);
if (rc)
goto fail;
@@ -4181,6 +4314,13 @@ static int mwl8k_firmware_load_success(struct mwl8k_priv *priv)
struct ieee80211_hw *hw = priv->hw;
int i, rc;
+ rc = mwl8k_load_firmware(hw);
+ mwl8k_release_firmware(priv);
+ if (rc) {
+ wiphy_err(hw->wiphy, "Cannot start firmware\n");
+ return rc;
+ }
+
/*
* Extra headroom is the size of the required DMA header
* minus the size of the smallest 802.11 frame (CTS frame).
@@ -4325,28 +4465,29 @@ static int __devinit mwl8k_probe(struct pci_dev *pdev,
}
/*
- * Choose the initial fw image depending on user input and availability
- * of images.
+ * Choose the initial fw image depending on user input. If a second
+ * image is available, make it the alternative image that will be
+ * loaded if the first one fails.
*/
+ init_completion(&priv->firmware_loading_complete);
di = priv->device_info;
- if (ap_mode_default && di->fw_image_ap)
- rc = mwl8k_init_firmware(hw, di->fw_image_ap);
- else if (!ap_mode_default && di->fw_image_sta)
- rc = mwl8k_init_firmware(hw, di->fw_image_sta);
- else if (ap_mode_default && !di->fw_image_ap && di->fw_image_sta) {
+ if (ap_mode_default && di->fw_image_ap) {
+ priv->fw_pref = di->fw_image_ap;
+ priv->fw_alt = di->fw_image_sta;
+ } else if (!ap_mode_default && di->fw_image_sta) {
+ priv->fw_pref = di->fw_image_sta;
+ priv->fw_alt = di->fw_image_ap;
+ } else if (ap_mode_default && !di->fw_image_ap && di->fw_image_sta) {
printk(KERN_WARNING "AP fw is unavailable. Using STA fw.");
- rc = mwl8k_init_firmware(hw, di->fw_image_sta);
+ priv->fw_pref = di->fw_image_sta;
} else if (!ap_mode_default && !di->fw_image_sta && di->fw_image_ap) {
printk(KERN_WARNING "STA fw is unavailable. Using AP fw.");
- rc = mwl8k_init_firmware(hw, di->fw_image_ap);
- } else
- rc = mwl8k_init_firmware(hw, di->fw_image_sta);
+ priv->fw_pref = di->fw_image_ap;
+ }
+ rc = mwl8k_init_firmware(hw, priv->fw_pref, true);
if (rc)
goto err_stop_firmware;
-
- rc = mwl8k_firmware_load_success(priv);
- if (!rc)
- return rc;
+ return rc;
err_stop_firmware:
mwl8k_hw_reset(priv);
@@ -4385,6 +4526,13 @@ static void __devexit mwl8k_remove(struct pci_dev *pdev)
return;
priv = hw->priv;
+ wait_for_completion(&priv->firmware_loading_complete);
+
+ if (priv->fw_state == FW_STATE_ERROR) {
+ mwl8k_hw_reset(priv);
+ goto unmap;
+ }
+
ieee80211_stop_queues(hw);
ieee80211_unregister_hw(hw);
@@ -4407,6 +4555,7 @@ static void __devexit mwl8k_remove(struct pci_dev *pdev)
pci_free_consistent(priv->pdev, 4, priv->cookie, priv->cookie_dma);
+unmap:
pci_iounmap(pdev, priv->regs);
pci_iounmap(pdev, priv->sram);
pci_set_drvdata(pdev, NULL);