aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorArend van Spriel <arend@broadcom.com>2014-05-27 06:56:21 -0400
committerJohn W. Linville <linville@tuxdriver.com>2014-05-29 13:10:22 -0400
commitc1416e77a6166766c847f03f21cca18d8ac54dd3 (patch)
treea8c60e265b6f50d1c1b93db2d522bacdc73301ab
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>
-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 @@
16 16
17#include <linux/kernel.h> 17#include <linux/kernel.h>
18#include <linux/slab.h> 18#include <linux/slab.h>
19#include <linux/device.h>
19#include <linux/firmware.h> 20#include <linux/firmware.h>
20 21
21#include "dhd_dbg.h" 22#include "dhd_dbg.h"
@@ -224,3 +225,108 @@ void brcmf_fw_nvram_free(void *nvram)
224 kfree(nvram); 225 kfree(nvram);
225} 226}
226 227
228struct brcmf_fw {
229 struct device *dev;
230 u16 flags;
231 const struct firmware *code;
232 const char *nvram_name;
233 void (*done)(struct device *dev, const struct firmware *fw,
234 void *nvram_image, u32 nvram_len);
235};
236
237static void brcmf_fw_request_nvram_done(const struct firmware *fw, void *ctx)
238{
239 struct brcmf_fw *fwctx = ctx;
240 u32 nvram_length = 0;
241 void *nvram = NULL;
242
243 brcmf_dbg(TRACE, "enter: dev=%s\n", dev_name(fwctx->dev));
244 if (!fw && !(fwctx->flags & BRCMF_FW_REQ_NV_OPTIONAL))
245 goto fail;
246
247 if (fw) {
248 nvram = brcmf_fw_nvram_strip(fw, &nvram_length);
249 release_firmware(fw);
250 if (!nvram && !(fwctx->flags & BRCMF_FW_REQ_NV_OPTIONAL))
251 goto fail;
252 }
253
254 fwctx->done(fwctx->dev, fwctx->code, nvram, nvram_length);
255 kfree(fwctx);
256 return;
257
258fail:
259 brcmf_dbg(TRACE, "failed: dev=%s\n", dev_name(fwctx->dev));
260 if (fwctx->code)
261 release_firmware(fwctx->code);
262 device_release_driver(fwctx->dev);
263 kfree(fwctx);
264}
265
266static void brcmf_fw_request_code_done(const struct firmware *fw, void *ctx)
267{
268 struct brcmf_fw *fwctx = ctx;
269 int ret;
270
271 brcmf_dbg(TRACE, "enter: dev=%s\n", dev_name(fwctx->dev));
272 if (!fw)
273 goto fail;
274
275 /* only requested code so done here */
276 if (!(fwctx->flags & BRCMF_FW_REQUEST_NVRAM)) {
277 fwctx->done(fwctx->dev, fw, NULL, 0);
278 kfree(fwctx);
279 return;
280 }
281 fwctx->code = fw;
282 ret = request_firmware_nowait(THIS_MODULE, true, fwctx->nvram_name,
283 fwctx->dev, GFP_KERNEL, fwctx,
284 brcmf_fw_request_nvram_done);
285
286 if (!ret)
287 return;
288
289 /* when nvram is optional call .done() callback here */
290 if (fwctx->flags & BRCMF_FW_REQ_NV_OPTIONAL) {
291 fwctx->done(fwctx->dev, fw, NULL, 0);
292 kfree(fwctx);
293 return;
294 }
295
296 /* failed nvram request */
297 release_firmware(fw);
298fail:
299 brcmf_dbg(TRACE, "failed: dev=%s\n", dev_name(fwctx->dev));
300 device_release_driver(fwctx->dev);
301 kfree(fwctx);
302}
303
304int brcmf_fw_get_firmwares(struct device *dev, u16 flags,
305 const char *code, const char *nvram,
306 void (*fw_cb)(struct device *dev,
307 const struct firmware *fw,
308 void *nvram_image, u32 nvram_len))
309{
310 struct brcmf_fw *fwctx;
311
312 brcmf_dbg(TRACE, "enter: dev=%s\n", dev_name(dev));
313 if (!fw_cb || !code)
314 return -EINVAL;
315
316 if ((flags & BRCMF_FW_REQUEST_NVRAM) && !nvram)
317 return -EINVAL;
318
319 fwctx = kzalloc(sizeof(*fwctx), GFP_KERNEL);
320 if (!fwctx)
321 return -ENOMEM;
322
323 fwctx->dev = dev;
324 fwctx->flags = flags;
325 fwctx->done = fw_cb;
326 if (flags & BRCMF_FW_REQUEST_NVRAM)
327 fwctx->nvram_name = nvram;
328
329 return request_firmware_nowait(THIS_MODULE, true, code, dev,
330 GFP_KERNEL, fwctx,
331 brcmf_fw_request_code_done);
332}
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 @@
16#ifndef BRCMFMAC_FIRMWARE_H 16#ifndef BRCMFMAC_FIRMWARE_H
17#define BRCMFMAC_FIRMWARE_H 17#define BRCMFMAC_FIRMWARE_H
18 18
19#define BRCMF_FW_REQUEST 0x000F
20#define BRCMF_FW_REQUEST_NVRAM 0x0001
21#define BRCMF_FW_REQ_FLAGS 0x00F0
22#define BRCMF_FW_REQ_NV_OPTIONAL 0x0010
19 23
20void *brcmf_fw_nvram_strip(const struct firmware *nv, u32 *new_length); 24void *brcmf_fw_nvram_strip(const struct firmware *nv, u32 *new_length);
21void brcmf_fw_nvram_free(void *nvram); 25void brcmf_fw_nvram_free(void *nvram);
22 26/*
27 * Request firmware(s) asynchronously. When the asynchronous request
28 * fails it will not use the callback, but call device_release_driver()
29 * instead which will call the driver .remove() callback.
30 */
31int brcmf_fw_get_firmwares(struct device *dev, u16 flags,
32 const char *code, const char *nvram,
33 void (*fw_cb)(struct device *dev,
34 const struct firmware *fw,
35 void *nvram_image, u32 nvram_len));
23 36
24#endif /* BRCMFMAC_FIRMWARE_H */ 37#endif /* BRCMFMAC_FIRMWARE_H */