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: Hante Meuleman <meuleman@broadcom.com> Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com> Reviewed-by: Daniel (Deognyoun) Kim <dekim@broadcom.com> Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com> Signed-off-by: Arend van Spriel <arend@broadcom.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
This commit is contained in:
parent
de389a533b
commit
c1416e77a6
|
@ -16,6 +16,7 @@
|
|||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/firmware.h>
|
||||
|
||||
#include "dhd_dbg.h"
|
||||
|
@ -224,3 +225,108 @@ void brcmf_fw_nvram_free(void *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 @@
|
|||
#ifndef 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_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 */
|
||||
|
|
Loading…
Reference in New Issue