Commit c1416e77 authored by Arend van Spriel's avatar Arend van Spriel Committed by John W. Linville

brcmfmac: introduce asynchronous firmware loading

The driver needs firmware to be loaded to the device, which
is done through the firmware class API. The synchronous call
request_firmware() need root filesystem to be mounted and/or
user-mode helper. These may not be avaliable on the moment
it is called. Instead use request_firmware_nowait().
Reviewed-by: default avatarHante Meuleman <meuleman@broadcom.com>
Reviewed-by: default avatarFranky (Zhenhui) Lin <frankyl@broadcom.com>
Reviewed-by: default avatarDaniel (Deognyoun) Kim <dekim@broadcom.com>
Reviewed-by: default avatarPieter-Paul Giesberts <pieterpg@broadcom.com>
Signed-off-by: default avatarArend van Spriel <arend@broadcom.com>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent de389a53
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/device.h>
#include <linux/firmware.h> #include <linux/firmware.h>
#include "dhd_dbg.h" #include "dhd_dbg.h"
...@@ -224,3 +225,108 @@ void brcmf_fw_nvram_free(void *nvram) ...@@ -224,3 +225,108 @@ void brcmf_fw_nvram_free(void *nvram)
kfree(nvram); kfree(nvram);
} }
struct brcmf_fw {
struct device *dev;
u16 flags;
const struct firmware *code;
const char *nvram_name;
void (*done)(struct device *dev, const struct firmware *fw,
void *nvram_image, u32 nvram_len);
};
static void brcmf_fw_request_nvram_done(const struct firmware *fw, void *ctx)
{
struct brcmf_fw *fwctx = ctx;
u32 nvram_length = 0;
void *nvram = NULL;
brcmf_dbg(TRACE, "enter: dev=%s\n", dev_name(fwctx->dev));
if (!fw && !(fwctx->flags & BRCMF_FW_REQ_NV_OPTIONAL))
goto fail;
if (fw) {
nvram = brcmf_fw_nvram_strip(fw, &nvram_length);
release_firmware(fw);
if (!nvram && !(fwctx->flags & BRCMF_FW_REQ_NV_OPTIONAL))
goto fail;
}
fwctx->done(fwctx->dev, fwctx->code, nvram, nvram_length);
kfree(fwctx);
return;
fail:
brcmf_dbg(TRACE, "failed: dev=%s\n", dev_name(fwctx->dev));
if (fwctx->code)
release_firmware(fwctx->code);
device_release_driver(fwctx->dev);
kfree(fwctx);
}
static void brcmf_fw_request_code_done(const struct firmware *fw, void *ctx)
{
struct brcmf_fw *fwctx = ctx;
int ret;
brcmf_dbg(TRACE, "enter: dev=%s\n", dev_name(fwctx->dev));
if (!fw)
goto fail;
/* only requested code so done here */
if (!(fwctx->flags & BRCMF_FW_REQUEST_NVRAM)) {
fwctx->done(fwctx->dev, fw, NULL, 0);
kfree(fwctx);
return;
}
fwctx->code = fw;
ret = request_firmware_nowait(THIS_MODULE, true, fwctx->nvram_name,
fwctx->dev, GFP_KERNEL, fwctx,
brcmf_fw_request_nvram_done);
if (!ret)
return;
/* when nvram is optional call .done() callback here */
if (fwctx->flags & BRCMF_FW_REQ_NV_OPTIONAL) {
fwctx->done(fwctx->dev, fw, NULL, 0);
kfree(fwctx);
return;
}
/* failed nvram request */
release_firmware(fw);
fail:
brcmf_dbg(TRACE, "failed: dev=%s\n", dev_name(fwctx->dev));
device_release_driver(fwctx->dev);
kfree(fwctx);
}
int brcmf_fw_get_firmwares(struct device *dev, u16 flags,
const char *code, const char *nvram,
void (*fw_cb)(struct device *dev,
const struct firmware *fw,
void *nvram_image, u32 nvram_len))
{
struct brcmf_fw *fwctx;
brcmf_dbg(TRACE, "enter: dev=%s\n", dev_name(dev));
if (!fw_cb || !code)
return -EINVAL;
if ((flags & BRCMF_FW_REQUEST_NVRAM) && !nvram)
return -EINVAL;
fwctx = kzalloc(sizeof(*fwctx), GFP_KERNEL);
if (!fwctx)
return -ENOMEM;
fwctx->dev = dev;
fwctx->flags = flags;
fwctx->done = fw_cb;
if (flags & BRCMF_FW_REQUEST_NVRAM)
fwctx->nvram_name = nvram;
return request_firmware_nowait(THIS_MODULE, true, code, dev,
GFP_KERNEL, fwctx,
brcmf_fw_request_code_done);
}
...@@ -16,9 +16,22 @@ ...@@ -16,9 +16,22 @@
#ifndef BRCMFMAC_FIRMWARE_H #ifndef BRCMFMAC_FIRMWARE_H
#define BRCMFMAC_FIRMWARE_H #define BRCMFMAC_FIRMWARE_H
#define BRCMF_FW_REQUEST 0x000F
#define BRCMF_FW_REQUEST_NVRAM 0x0001
#define BRCMF_FW_REQ_FLAGS 0x00F0
#define BRCMF_FW_REQ_NV_OPTIONAL 0x0010
void *brcmf_fw_nvram_strip(const struct firmware *nv, u32 *new_length); void *brcmf_fw_nvram_strip(const struct firmware *nv, u32 *new_length);
void brcmf_fw_nvram_free(void *nvram); void brcmf_fw_nvram_free(void *nvram);
/*
* Request firmware(s) asynchronously. When the asynchronous request
* fails it will not use the callback, but call device_release_driver()
* instead which will call the driver .remove() callback.
*/
int brcmf_fw_get_firmwares(struct device *dev, u16 flags,
const char *code, const char *nvram,
void (*fw_cb)(struct device *dev,
const struct firmware *fw,
void *nvram_image, u32 nvram_len));
#endif /* BRCMFMAC_FIRMWARE_H */ #endif /* BRCMFMAC_FIRMWARE_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment