diff options
-rw-r--r-- | drivers/net/wireless/brcm80211/brcmfmac/firmware.c | 106 | ||||
-rw-r--r-- | drivers/net/wireless/brcm80211/brcmfmac/firmware.h | 15 |
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 | ||
228 | struct 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 | |||
237 | static 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 | |||
258 | fail: | ||
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 | |||
266 | static 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); | ||
298 | fail: | ||
299 | brcmf_dbg(TRACE, "failed: dev=%s\n", dev_name(fwctx->dev)); | ||
300 | device_release_driver(fwctx->dev); | ||
301 | kfree(fwctx); | ||
302 | } | ||
303 | |||
304 | int 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 | ||
20 | void *brcmf_fw_nvram_strip(const struct firmware *nv, u32 *new_length); | 24 | void *brcmf_fw_nvram_strip(const struct firmware *nv, u32 *new_length); |
21 | void brcmf_fw_nvram_free(void *nvram); | 25 | void 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 | */ | ||
31 | int 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 */ |