wlcore: Load the NVS file asynchronously
authorIdo Yariv <ido@wizery.com>
Sat, 1 Sep 2012 22:32:47 +0000 (01:32 +0300)
committerLuciano Coelho <luca@coelho.fi>
Thu, 27 Sep 2012 09:13:54 +0000 (12:13 +0300)
The NVS file is loaded by the device's probe callback with the help of
request_firmware(). Since request_firmware() relies on udevd, the
modules cannot be loaded before hotplug events are handled.

Fix this by loading the NVS file asynchronously and continue
initialization only after the firmware request is over.

Signed-off-by: Ido Yariv <ido@wizery.com>
Signed-off-by: Luciano Coelho <luca@coelho.fi>
drivers/net/wireless/ti/wlcore/main.c
drivers/net/wireless/ti/wlcore/wlcore.h

index 7db6384a245723dec3342f3f6426b6e09efeb145..6ada018fe4a43982412936d17433fec5769a832e 100644 (file)
@@ -744,32 +744,6 @@ out:
        return ret;
 }
 
-static void wl1271_fetch_nvs(struct wl1271 *wl)
-{
-       const struct firmware *fw;
-       int ret;
-
-       ret = request_firmware(&fw, WL12XX_NVS_NAME, wl->dev);
-
-       if (ret < 0) {
-               wl1271_debug(DEBUG_BOOT, "could not get nvs file %s: %d",
-                            WL12XX_NVS_NAME, ret);
-               return;
-       }
-
-       wl->nvs = kmemdup(fw->data, fw->size, GFP_KERNEL);
-
-       if (!wl->nvs) {
-               wl1271_error("could not allocate memory for the nvs file");
-               goto out;
-       }
-
-       wl->nvs_len = fw->size;
-
-out:
-       release_firmware(fw);
-}
-
 void wl12xx_queue_recovery_work(struct wl1271 *wl)
 {
        WARN_ON(!test_bit(WL1271_FLAG_INTENDED_FW_RECOVERY, &wl->flags));
@@ -5153,8 +5127,7 @@ static int wl1271_register_hw(struct wl1271 *wl)
        if (wl->mac80211_registered)
                return 0;
 
-       wl1271_fetch_nvs(wl);
-       if (wl->nvs != NULL) {
+       if (wl->nvs_len >= 12) {
                /* NOTE: The wl->nvs->nvs element must be first, in
                 * order to simplify the casting, we assume it is at
                 * the beginning of the wl->nvs structure.
@@ -5419,6 +5392,7 @@ struct ieee80211_hw *wlcore_alloc_hw(size_t priv_size, u32 aggr_buf_size)
        wl->fw_type = WL12XX_FW_TYPE_NONE;
        mutex_init(&wl->mutex);
        mutex_init(&wl->flush_mutex);
+       init_completion(&wl->nvs_loading_complete);
 
        order = get_order(aggr_buf_size);
        wl->aggr_buf = (u8 *)__get_free_pages(GFP_KERNEL, order);
@@ -5539,24 +5513,31 @@ static irqreturn_t wl12xx_hardirq(int irq, void *cookie)
        return IRQ_WAKE_THREAD;
 }
 
-int __devinit wlcore_probe(struct wl1271 *wl, struct platform_device *pdev)
+static void wlcore_nvs_cb(const struct firmware *fw, void *context)
 {
+       struct wl1271 *wl = context;
+       struct platform_device *pdev = wl->pdev;
        struct wl12xx_platform_data *pdata = pdev->dev.platform_data;
        unsigned long irqflags;
        int ret;
 
-       if (!wl->ops || !wl->ptable) {
-               ret = -EINVAL;
-               goto out;
+       if (fw) {
+               wl->nvs = kmemdup(fw->data, fw->size, GFP_KERNEL);
+               if (!wl->nvs) {
+                       wl1271_error("Could not allocate nvs data");
+                       goto out;
+               }
+               wl->nvs_len = fw->size;
+       } else {
+               wl1271_debug(DEBUG_BOOT, "Could not get nvs file %s",
+                            WL12XX_NVS_NAME);
+               wl->nvs = NULL;
+               wl->nvs_len = 0;
        }
 
-       wl->dev = &pdev->dev;
-       wl->pdev = pdev;
-       platform_set_drvdata(pdev, wl);
-
        ret = wl->ops->setup(wl);
        if (ret < 0)
-               goto out;
+               goto out_free_nvs;
 
        BUG_ON(wl->num_tx_desc > WLCORE_MAX_TX_DESCRIPTORS);
 
@@ -5578,7 +5559,7 @@ int __devinit wlcore_probe(struct wl1271 *wl, struct platform_device *pdev)
                                   pdev->name, wl);
        if (ret < 0) {
                wl1271_error("request_irq() failed: %d", ret);
-               goto out;
+               goto out_free_nvs;
        }
 
 #ifdef CONFIG_PM
@@ -5637,6 +5618,7 @@ int __devinit wlcore_probe(struct wl1271 *wl, struct platform_device *pdev)
                goto out_hw_pg_ver;
        }
 
+       wl->initialized = true;
        goto out;
 
 out_hw_pg_ver:
@@ -5651,7 +5633,33 @@ out_unreg:
 out_irq:
        free_irq(wl->irq, wl);
 
+out_free_nvs:
+       kfree(wl->nvs);
+
 out:
+       release_firmware(fw);
+       complete_all(&wl->nvs_loading_complete);
+}
+
+int __devinit wlcore_probe(struct wl1271 *wl, struct platform_device *pdev)
+{
+       int ret;
+
+       if (!wl->ops || !wl->ptable)
+               return -EINVAL;
+
+       wl->dev = &pdev->dev;
+       wl->pdev = pdev;
+       platform_set_drvdata(pdev, wl);
+
+       ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG,
+                                     WL12XX_NVS_NAME, &pdev->dev, GFP_KERNEL,
+                                     wl, wlcore_nvs_cb);
+       if (ret < 0) {
+               wl1271_error("request_firmware_nowait failed: %d", ret);
+               complete_all(&wl->nvs_loading_complete);
+       }
+
        return ret;
 }
 EXPORT_SYMBOL_GPL(wlcore_probe);
@@ -5660,6 +5668,10 @@ int __devexit wlcore_remove(struct platform_device *pdev)
 {
        struct wl1271 *wl = platform_get_drvdata(pdev);
 
+       wait_for_completion(&wl->nvs_loading_complete);
+       if (!wl->initialized)
+               return 0;
+
        if (wl->irq_wake_enabled) {
                device_init_wakeup(wl->dev, 0);
                disable_irq_wake(wl->irq);
index 6a0455616289dbdccd2d16d99b656c0dd03d00d1..68584aa0f2b0f731e86b7e1a0c0ea973b3a89ebf 100644 (file)
@@ -146,6 +146,7 @@ struct wl1271_stats {
 };
 
 struct wl1271 {
+       bool initialized;
        struct ieee80211_hw *hw;
        bool mac80211_registered;
 
@@ -409,6 +410,8 @@ struct wl1271 {
 
        /* the minimum FW version required for the driver to work */
        unsigned int min_fw_ver[NUM_FW_VER];
+
+       struct completion nvs_loading_complete;
 };
 
 int __devinit wlcore_probe(struct wl1271 *wl, struct platform_device *pdev);