wlcore: Load the NVS file asynchronously

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>
This commit is contained in:
Ido Yariv 2012-09-02 01:32:47 +03:00 committed by Luciano Coelho
parent 3992eb2bf2
commit 6f8d6b20bb
2 changed files with 53 additions and 38 deletions

View File

@ -744,32 +744,6 @@ out:
return ret; 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) void wl12xx_queue_recovery_work(struct wl1271 *wl)
{ {
WARN_ON(!test_bit(WL1271_FLAG_INTENDED_FW_RECOVERY, &wl->flags)); 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) if (wl->mac80211_registered)
return 0; return 0;
wl1271_fetch_nvs(wl); if (wl->nvs_len >= 12) {
if (wl->nvs != NULL) {
/* NOTE: The wl->nvs->nvs element must be first, in /* NOTE: The wl->nvs->nvs element must be first, in
* order to simplify the casting, we assume it is at * order to simplify the casting, we assume it is at
* the beginning of the wl->nvs structure. * 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; wl->fw_type = WL12XX_FW_TYPE_NONE;
mutex_init(&wl->mutex); mutex_init(&wl->mutex);
mutex_init(&wl->flush_mutex); mutex_init(&wl->flush_mutex);
init_completion(&wl->nvs_loading_complete);
order = get_order(aggr_buf_size); order = get_order(aggr_buf_size);
wl->aggr_buf = (u8 *)__get_free_pages(GFP_KERNEL, order); 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; 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; struct wl12xx_platform_data *pdata = pdev->dev.platform_data;
unsigned long irqflags; unsigned long irqflags;
int ret; int ret;
if (!wl->ops || !wl->ptable) { if (fw) {
ret = -EINVAL; wl->nvs = kmemdup(fw->data, fw->size, GFP_KERNEL);
if (!wl->nvs) {
wl1271_error("Could not allocate nvs data");
goto out; goto out;
} }
wl->nvs_len = fw->size;
wl->dev = &pdev->dev; } else {
wl->pdev = pdev; wl1271_debug(DEBUG_BOOT, "Could not get nvs file %s",
platform_set_drvdata(pdev, wl); WL12XX_NVS_NAME);
wl->nvs = NULL;
wl->nvs_len = 0;
}
ret = wl->ops->setup(wl); ret = wl->ops->setup(wl);
if (ret < 0) if (ret < 0)
goto out; goto out_free_nvs;
BUG_ON(wl->num_tx_desc > WLCORE_MAX_TX_DESCRIPTORS); 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); pdev->name, wl);
if (ret < 0) { if (ret < 0) {
wl1271_error("request_irq() failed: %d", ret); wl1271_error("request_irq() failed: %d", ret);
goto out; goto out_free_nvs;
} }
#ifdef CONFIG_PM #ifdef CONFIG_PM
@ -5637,6 +5618,7 @@ int __devinit wlcore_probe(struct wl1271 *wl, struct platform_device *pdev)
goto out_hw_pg_ver; goto out_hw_pg_ver;
} }
wl->initialized = true;
goto out; goto out;
out_hw_pg_ver: out_hw_pg_ver:
@ -5651,7 +5633,33 @@ out_unreg:
out_irq: out_irq:
free_irq(wl->irq, wl); free_irq(wl->irq, wl);
out_free_nvs:
kfree(wl->nvs);
out: 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; return ret;
} }
EXPORT_SYMBOL_GPL(wlcore_probe); EXPORT_SYMBOL_GPL(wlcore_probe);
@ -5660,6 +5668,10 @@ int __devexit wlcore_remove(struct platform_device *pdev)
{ {
struct wl1271 *wl = platform_get_drvdata(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) { if (wl->irq_wake_enabled) {
device_init_wakeup(wl->dev, 0); device_init_wakeup(wl->dev, 0);
disable_irq_wake(wl->irq); disable_irq_wake(wl->irq);

View File

@ -146,6 +146,7 @@ struct wl1271_stats {
}; };
struct wl1271 { struct wl1271 {
bool initialized;
struct ieee80211_hw *hw; struct ieee80211_hw *hw;
bool mac80211_registered; bool mac80211_registered;
@ -409,6 +410,8 @@ struct wl1271 {
/* the minimum FW version required for the driver to work */ /* the minimum FW version required for the driver to work */
unsigned int min_fw_ver[NUM_FW_VER]; unsigned int min_fw_ver[NUM_FW_VER];
struct completion nvs_loading_complete;
}; };
int __devinit wlcore_probe(struct wl1271 *wl, struct platform_device *pdev); int __devinit wlcore_probe(struct wl1271 *wl, struct platform_device *pdev);