summaryrefslogtreecommitdiff
path: root/drivers/net/wireless/brcm80211/brcmfmac
diff options
context:
space:
mode:
authorArend van Spriel <arend@broadcom.com>2014-05-27 12:56:21 +0200
committerJohn W. Linville <linville@tuxdriver.com>2014-05-29 13:10:22 -0400
commitc1416e77a6166766c847f03f21cca18d8ac54dd3 (patch)
treea8c60e265b6f50d1c1b93db2d522bacdc73301ab /drivers/net/wireless/brcm80211/brcmfmac
parentde389a533b88de18b98d5f007996aff6f2885180 (diff)
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>
Diffstat (limited to 'drivers/net/wireless/brcm80211/brcmfmac')
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/firmware.c106
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/firmware.h15
2 files changed, 120 insertions, 1 deletions
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/firmware.c b/drivers/net/wireless/brcm80211/brcmfmac/firmware.c
index ff5b86733d5a..b1525591f2e7 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/firmware.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/firmware.c
@@ -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);
+}
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/firmware.h b/drivers/net/wireless/brcm80211/brcmfmac/firmware.h
index 127633bc242d..25b64bd97609 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/firmware.h
+++ b/drivers/net/wireless/brcm80211/brcmfmac/firmware.h
@@ -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 */