Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+106 −0
Original line number Diff line number Diff line
@@ -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);
}
+14 −1
Original line number Diff line number Diff line
@@ -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 */