blob: 743f16b6a07296e8e5b695598002d78669222870 [file] [log] [blame]
Hante Meulemana74d0362014-01-13 22:20:22 +01001/*
2 * Copyright (c) 2013 Broadcom Corporation
3 *
4 * Permission to use, copy, modify, and/or distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
11 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
13 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
14 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17#include <linux/kernel.h>
18#include <linux/slab.h>
Arend van Sprielc1416e72014-05-27 12:56:21 +020019#include <linux/device.h>
Hante Meulemana74d0362014-01-13 22:20:22 +010020#include <linux/firmware.h>
Daniel Kimc1b20532014-07-12 08:49:37 +020021#include <linux/module.h>
Hante Meulemana74d0362014-01-13 22:20:22 +010022
Hante Meulemana8e8ed32014-10-28 14:56:13 +010023#include "debug.h"
Arend van Sprieldabedab2014-05-27 12:56:17 +020024#include "firmware.h"
Hante Meulemana74d0362014-01-13 22:20:22 +010025
Hante Meulemanc4365532015-04-14 20:10:33 +020026#define BRCMF_FW_MAX_NVRAM_SIZE 64000
27#define BRCMF_FW_NVRAM_DEVPATH_LEN 19 /* devpath0=pcie/1/4/ */
Rafał Miłeckiae8c2362015-05-20 09:34:21 +020028#define BRCMF_FW_NVRAM_PCIEDEV_LEN 10 /* pcie/1/4/ + \0 */
Hante Meulemanc4365532015-04-14 20:10:33 +020029
Daniel Kimc1b20532014-07-12 08:49:37 +020030char brcmf_firmware_path[BRCMF_FW_PATH_LEN];
31module_param_string(firmware_path, brcmf_firmware_path,
32 BRCMF_FW_PATH_LEN, 0440);
33
Arend van Spriel3e99b082014-05-12 10:47:31 +020034enum nvram_parser_state {
35 IDLE,
36 KEY,
37 VALUE,
38 COMMENT,
39 END
40};
41
42/**
43 * struct nvram_parser - internal info for parser.
44 *
45 * @state: current parser state.
Rafał Miłecki11f09d42015-06-04 22:11:07 +020046 * @data: input buffer being parsed.
Arend van Spriel3e99b082014-05-12 10:47:31 +020047 * @nvram: output buffer with parse result.
48 * @nvram_len: lenght of parse result.
49 * @line: current line.
50 * @column: current column in line.
51 * @pos: byte offset in input buffer.
52 * @entry: start position of key,value entry.
Hante Meulemanc4365532015-04-14 20:10:33 +020053 * @multi_dev_v1: detect pcie multi device v1 (compressed).
54 * @multi_dev_v2: detect pcie multi device v2.
Arend van Spriel3e99b082014-05-12 10:47:31 +020055 */
56struct nvram_parser {
57 enum nvram_parser_state state;
Rafał Miłecki11f09d42015-06-04 22:11:07 +020058 const u8 *data;
Arend van Spriel3e99b082014-05-12 10:47:31 +020059 u8 *nvram;
60 u32 nvram_len;
61 u32 line;
62 u32 column;
63 u32 pos;
64 u32 entry;
Hante Meulemanc4365532015-04-14 20:10:33 +020065 bool multi_dev_v1;
66 bool multi_dev_v2;
Arend van Spriel3e99b082014-05-12 10:47:31 +020067};
68
Rafał Miłeckifc23e812015-05-23 09:15:33 +020069/**
70 * is_nvram_char() - check if char is a valid one for NVRAM entry
71 *
72 * It accepts all printable ASCII chars except for '#' which opens a comment.
73 * Please note that ' ' (space) while accepted is not a valid key name char.
74 */
Arend van Spriel3e99b082014-05-12 10:47:31 +020075static bool is_nvram_char(char c)
76{
77 /* comment marker excluded */
78 if (c == '#')
79 return false;
80
81 /* key and value may have any other readable character */
Rafał Miłeckifc23e812015-05-23 09:15:33 +020082 return (c >= 0x20 && c < 0x7f);
Arend van Spriel3e99b082014-05-12 10:47:31 +020083}
84
85static bool is_whitespace(char c)
86{
87 return (c == ' ' || c == '\r' || c == '\n' || c == '\t');
88}
89
90static enum nvram_parser_state brcmf_nvram_handle_idle(struct nvram_parser *nvp)
91{
92 char c;
93
Rafał Miłecki11f09d42015-06-04 22:11:07 +020094 c = nvp->data[nvp->pos];
Arend van Spriel3e99b082014-05-12 10:47:31 +020095 if (c == '\n')
96 return COMMENT;
97 if (is_whitespace(c))
98 goto proceed;
99 if (c == '#')
100 return COMMENT;
101 if (is_nvram_char(c)) {
102 nvp->entry = nvp->pos;
103 return KEY;
104 }
105 brcmf_dbg(INFO, "warning: ln=%d:col=%d: ignoring invalid character\n",
106 nvp->line, nvp->column);
107proceed:
108 nvp->column++;
109 nvp->pos++;
110 return IDLE;
111}
112
113static enum nvram_parser_state brcmf_nvram_handle_key(struct nvram_parser *nvp)
114{
115 enum nvram_parser_state st = nvp->state;
116 char c;
117
Rafał Miłecki11f09d42015-06-04 22:11:07 +0200118 c = nvp->data[nvp->pos];
Arend van Spriel3e99b082014-05-12 10:47:31 +0200119 if (c == '=') {
Arend van Spriel4165fe92015-01-25 20:31:43 +0100120 /* ignore RAW1 by treating as comment */
Rafał Miłecki11f09d42015-06-04 22:11:07 +0200121 if (strncmp(&nvp->data[nvp->entry], "RAW1", 4) == 0)
Arend van Spriel4165fe92015-01-25 20:31:43 +0100122 st = COMMENT;
123 else
124 st = VALUE;
Rafał Miłecki11f09d42015-06-04 22:11:07 +0200125 if (strncmp(&nvp->data[nvp->entry], "devpath", 7) == 0)
Hante Meulemanc4365532015-04-14 20:10:33 +0200126 nvp->multi_dev_v1 = true;
Rafał Miłecki11f09d42015-06-04 22:11:07 +0200127 if (strncmp(&nvp->data[nvp->entry], "pcie/", 5) == 0)
Hante Meulemanc4365532015-04-14 20:10:33 +0200128 nvp->multi_dev_v2 = true;
Rafał Miłeckifc23e812015-05-23 09:15:33 +0200129 } else if (!is_nvram_char(c) || c == ' ') {
Arend van Spriel3e99b082014-05-12 10:47:31 +0200130 brcmf_dbg(INFO, "warning: ln=%d:col=%d: '=' expected, skip invalid key entry\n",
131 nvp->line, nvp->column);
132 return COMMENT;
133 }
134
135 nvp->column++;
136 nvp->pos++;
137 return st;
138}
139
140static enum nvram_parser_state
141brcmf_nvram_handle_value(struct nvram_parser *nvp)
142{
143 char c;
144 char *skv;
145 char *ekv;
146 u32 cplen;
147
Rafał Miłecki11f09d42015-06-04 22:11:07 +0200148 c = nvp->data[nvp->pos];
Arend van Spriel3e99b082014-05-12 10:47:31 +0200149 if (!is_nvram_char(c)) {
150 /* key,value pair complete */
Rafał Miłecki11f09d42015-06-04 22:11:07 +0200151 ekv = (u8 *)&nvp->data[nvp->pos];
152 skv = (u8 *)&nvp->data[nvp->entry];
Arend van Spriel3e99b082014-05-12 10:47:31 +0200153 cplen = ekv - skv;
Hante Meulemanc4365532015-04-14 20:10:33 +0200154 if (nvp->nvram_len + cplen + 1 >= BRCMF_FW_MAX_NVRAM_SIZE)
155 return END;
Arend van Spriel3e99b082014-05-12 10:47:31 +0200156 /* copy to output buffer */
157 memcpy(&nvp->nvram[nvp->nvram_len], skv, cplen);
158 nvp->nvram_len += cplen;
159 nvp->nvram[nvp->nvram_len] = '\0';
160 nvp->nvram_len++;
161 return IDLE;
162 }
163 nvp->pos++;
164 nvp->column++;
165 return VALUE;
166}
167
168static enum nvram_parser_state
169brcmf_nvram_handle_comment(struct nvram_parser *nvp)
170{
Rafał Miłecki279b4cb2015-05-20 13:59:54 +0200171 char *eoc, *sol;
Arend van Spriel3e99b082014-05-12 10:47:31 +0200172
Rafał Miłecki11f09d42015-06-04 22:11:07 +0200173 sol = (char *)&nvp->data[nvp->pos];
Rafał Miłecki279b4cb2015-05-20 13:59:54 +0200174 eoc = strchr(sol, '\n');
175 if (!eoc) {
176 eoc = strchr(sol, '\0');
177 if (!eoc)
178 return END;
179 }
Arend van Spriel3e99b082014-05-12 10:47:31 +0200180
181 /* eat all moving to next line */
182 nvp->line++;
183 nvp->column = 1;
Rafał Miłecki279b4cb2015-05-20 13:59:54 +0200184 nvp->pos += (eoc - sol) + 1;
Arend van Spriel3e99b082014-05-12 10:47:31 +0200185 return IDLE;
186}
187
188static enum nvram_parser_state brcmf_nvram_handle_end(struct nvram_parser *nvp)
189{
190 /* final state */
191 return END;
192}
193
194static enum nvram_parser_state
195(*nv_parser_states[])(struct nvram_parser *nvp) = {
196 brcmf_nvram_handle_idle,
197 brcmf_nvram_handle_key,
198 brcmf_nvram_handle_value,
199 brcmf_nvram_handle_comment,
200 brcmf_nvram_handle_end
201};
202
203static int brcmf_init_nvram_parser(struct nvram_parser *nvp,
Rafał Miłecki11f09d42015-06-04 22:11:07 +0200204 const u8 *data, size_t data_len)
Arend van Spriel3e99b082014-05-12 10:47:31 +0200205{
Hante Meulemanc4365532015-04-14 20:10:33 +0200206 size_t size;
207
Arend van Spriel3e99b082014-05-12 10:47:31 +0200208 memset(nvp, 0, sizeof(*nvp));
Rafał Miłecki11f09d42015-06-04 22:11:07 +0200209 nvp->data = data;
Hante Meulemanc4365532015-04-14 20:10:33 +0200210 /* Limit size to MAX_NVRAM_SIZE, some files contain lot of comment */
Rafał Miłecki11f09d42015-06-04 22:11:07 +0200211 if (data_len > BRCMF_FW_MAX_NVRAM_SIZE)
Hante Meulemanc4365532015-04-14 20:10:33 +0200212 size = BRCMF_FW_MAX_NVRAM_SIZE;
213 else
Rafał Miłecki11f09d42015-06-04 22:11:07 +0200214 size = data_len;
Arend van Spriel3e99b082014-05-12 10:47:31 +0200215 /* Alloc for extra 0 byte + roundup by 4 + length field */
Hante Meulemanc4365532015-04-14 20:10:33 +0200216 size += 1 + 3 + sizeof(u32);
217 nvp->nvram = kzalloc(size, GFP_KERNEL);
Arend van Spriel3e99b082014-05-12 10:47:31 +0200218 if (!nvp->nvram)
219 return -ENOMEM;
220
221 nvp->line = 1;
222 nvp->column = 1;
223 return 0;
224}
225
Hante Meulemanc4365532015-04-14 20:10:33 +0200226/* brcmf_fw_strip_multi_v1 :Some nvram files contain settings for multiple
227 * devices. Strip it down for one device, use domain_nr/bus_nr to determine
228 * which data is to be returned. v1 is the version where nvram is stored
229 * compressed and "devpath" maps to index for valid entries.
230 */
231static void brcmf_fw_strip_multi_v1(struct nvram_parser *nvp, u16 domain_nr,
232 u16 bus_nr)
233{
Rafał Miłecki5d084082015-05-20 11:01:08 +0200234 /* Device path with a leading '=' key-value separator */
Rafał Miłeckif33d5912015-05-28 14:19:21 +0200235 char pci_path[] = "=pci/?/?";
236 size_t pci_len;
Rafał Miłecki5d084082015-05-20 11:01:08 +0200237 char pcie_path[] = "=pcie/?/?";
238 size_t pcie_len;
239
Hante Meulemanc4365532015-04-14 20:10:33 +0200240 u32 i, j;
241 bool found;
242 u8 *nvram;
243 u8 id;
244
245 nvram = kzalloc(nvp->nvram_len + 1 + 3 + sizeof(u32), GFP_KERNEL);
246 if (!nvram)
247 goto fail;
248
249 /* min length: devpath0=pcie/1/4/ + 0:x=y */
250 if (nvp->nvram_len < BRCMF_FW_NVRAM_DEVPATH_LEN + 6)
251 goto fail;
252
253 /* First search for the devpathX and see if it is the configuration
254 * for domain_nr/bus_nr. Search complete nvp
255 */
Rafał Miłeckif33d5912015-05-28 14:19:21 +0200256 snprintf(pci_path, sizeof(pci_path), "=pci/%d/%d", domain_nr,
257 bus_nr);
258 pci_len = strlen(pci_path);
Rafał Miłecki5d084082015-05-20 11:01:08 +0200259 snprintf(pcie_path, sizeof(pcie_path), "=pcie/%d/%d", domain_nr,
260 bus_nr);
261 pcie_len = strlen(pcie_path);
Hante Meulemanc4365532015-04-14 20:10:33 +0200262 found = false;
263 i = 0;
264 while (i < nvp->nvram_len - BRCMF_FW_NVRAM_DEVPATH_LEN) {
265 /* Format: devpathX=pcie/Y/Z/
266 * Y = domain_nr, Z = bus_nr, X = virtual ID
267 */
Rafał Miłeckif33d5912015-05-28 14:19:21 +0200268 if (strncmp(&nvp->nvram[i], "devpath", 7) == 0 &&
269 (!strncmp(&nvp->nvram[i + 8], pci_path, pci_len) ||
270 !strncmp(&nvp->nvram[i + 8], pcie_path, pcie_len))) {
Rafał Miłecki5d084082015-05-20 11:01:08 +0200271 id = nvp->nvram[i + 7] - '0';
272 found = true;
273 break;
Hante Meulemanc4365532015-04-14 20:10:33 +0200274 }
275 while (nvp->nvram[i] != 0)
276 i++;
277 i++;
278 }
279 if (!found)
280 goto fail;
281
282 /* Now copy all valid entries, release old nvram and assign new one */
283 i = 0;
284 j = 0;
285 while (i < nvp->nvram_len) {
286 if ((nvp->nvram[i] - '0' == id) && (nvp->nvram[i + 1] == ':')) {
287 i += 2;
288 while (nvp->nvram[i] != 0) {
289 nvram[j] = nvp->nvram[i];
290 i++;
291 j++;
292 }
293 nvram[j] = 0;
294 j++;
295 }
296 while (nvp->nvram[i] != 0)
297 i++;
298 i++;
299 }
300 kfree(nvp->nvram);
301 nvp->nvram = nvram;
302 nvp->nvram_len = j;
303 return;
304
305fail:
306 kfree(nvram);
307 nvp->nvram_len = 0;
308}
309
310/* brcmf_fw_strip_multi_v2 :Some nvram files contain settings for multiple
311 * devices. Strip it down for one device, use domain_nr/bus_nr to determine
312 * which data is to be returned. v2 is the version where nvram is stored
313 * uncompressed, all relevant valid entries are identified by
314 * pcie/domain_nr/bus_nr:
315 */
316static void brcmf_fw_strip_multi_v2(struct nvram_parser *nvp, u16 domain_nr,
317 u16 bus_nr)
318{
Rafał Miłeckiae8c2362015-05-20 09:34:21 +0200319 char prefix[BRCMF_FW_NVRAM_PCIEDEV_LEN];
320 size_t len;
Hante Meulemanc4365532015-04-14 20:10:33 +0200321 u32 i, j;
322 u8 *nvram;
323
324 nvram = kzalloc(nvp->nvram_len + 1 + 3 + sizeof(u32), GFP_KERNEL);
325 if (!nvram)
326 goto fail;
327
328 /* Copy all valid entries, release old nvram and assign new one.
329 * Valid entries are of type pcie/X/Y/ where X = domain_nr and
330 * Y = bus_nr.
331 */
Rafał Miłeckiae8c2362015-05-20 09:34:21 +0200332 snprintf(prefix, sizeof(prefix), "pcie/%d/%d/", domain_nr, bus_nr);
333 len = strlen(prefix);
Hante Meulemanc4365532015-04-14 20:10:33 +0200334 i = 0;
335 j = 0;
Rafał Miłeckiae8c2362015-05-20 09:34:21 +0200336 while (i < nvp->nvram_len - len) {
337 if (strncmp(&nvp->nvram[i], prefix, len) == 0) {
338 i += len;
Hante Meulemanc4365532015-04-14 20:10:33 +0200339 while (nvp->nvram[i] != 0) {
340 nvram[j] = nvp->nvram[i];
341 i++;
342 j++;
343 }
344 nvram[j] = 0;
345 j++;
346 }
347 while (nvp->nvram[i] != 0)
348 i++;
349 i++;
350 }
351 kfree(nvp->nvram);
352 nvp->nvram = nvram;
353 nvp->nvram_len = j;
354 return;
355fail:
356 kfree(nvram);
357 nvp->nvram_len = 0;
358}
359
Arend van Spriel3e99b082014-05-12 10:47:31 +0200360/* brcmf_nvram_strip :Takes a buffer of "<var>=<value>\n" lines read from a fil
Hante Meulemana74d0362014-01-13 22:20:22 +0100361 * and ending in a NUL. Removes carriage returns, empty lines, comment lines,
362 * and converts newlines to NULs. Shortens buffer as needed and pads with NULs.
363 * End of buffer is completed with token identifying length of buffer.
364 */
Rafał Miłecki11f09d42015-06-04 22:11:07 +0200365static void *brcmf_fw_nvram_strip(const u8 *data, size_t data_len,
366 u32 *new_length, u16 domain_nr, u16 bus_nr)
Hante Meulemana74d0362014-01-13 22:20:22 +0100367{
Arend van Spriel3e99b082014-05-12 10:47:31 +0200368 struct nvram_parser nvp;
369 u32 pad;
Hante Meulemana74d0362014-01-13 22:20:22 +0100370 u32 token;
371 __le32 token_le;
372
Rafał Miłecki11f09d42015-06-04 22:11:07 +0200373 if (brcmf_init_nvram_parser(&nvp, data, data_len) < 0)
Hante Meulemana74d0362014-01-13 22:20:22 +0100374 return NULL;
375
Rafał Miłecki11f09d42015-06-04 22:11:07 +0200376 while (nvp.pos < data_len) {
Arend van Spriel3e99b082014-05-12 10:47:31 +0200377 nvp.state = nv_parser_states[nvp.state](&nvp);
378 if (nvp.state == END)
Hante Meulemana74d0362014-01-13 22:20:22 +0100379 break;
Hante Meulemana74d0362014-01-13 22:20:22 +0100380 }
Hante Meulemanc4365532015-04-14 20:10:33 +0200381 if (nvp.multi_dev_v1)
382 brcmf_fw_strip_multi_v1(&nvp, domain_nr, bus_nr);
383 else if (nvp.multi_dev_v2)
384 brcmf_fw_strip_multi_v2(&nvp, domain_nr, bus_nr);
385
386 if (nvp.nvram_len == 0) {
387 kfree(nvp.nvram);
388 return NULL;
389 }
390
Arend van Spriel3e99b082014-05-12 10:47:31 +0200391 pad = nvp.nvram_len;
392 *new_length = roundup(nvp.nvram_len + 1, 4);
393 while (pad != *new_length) {
394 nvp.nvram[pad] = 0;
395 pad++;
Hante Meulemana74d0362014-01-13 22:20:22 +0100396 }
397
398 token = *new_length / 4;
399 token = (~token << 16) | (token & 0x0000FFFF);
400 token_le = cpu_to_le32(token);
401
Arend van Spriel3e99b082014-05-12 10:47:31 +0200402 memcpy(&nvp.nvram[*new_length], &token_le, sizeof(token_le));
Hante Meulemana74d0362014-01-13 22:20:22 +0100403 *new_length += sizeof(token_le);
404
Arend van Spriel3e99b082014-05-12 10:47:31 +0200405 return nvp.nvram;
Hante Meulemana74d0362014-01-13 22:20:22 +0100406}
407
Arend van Sprieldabedab2014-05-27 12:56:17 +0200408void brcmf_fw_nvram_free(void *nvram)
Hante Meulemana74d0362014-01-13 22:20:22 +0100409{
410 kfree(nvram);
411}
412
Arend van Sprielc1416e72014-05-27 12:56:21 +0200413struct brcmf_fw {
414 struct device *dev;
415 u16 flags;
416 const struct firmware *code;
417 const char *nvram_name;
Hante Meulemanc4365532015-04-14 20:10:33 +0200418 u16 domain_nr;
419 u16 bus_nr;
Arend van Sprielc1416e72014-05-27 12:56:21 +0200420 void (*done)(struct device *dev, const struct firmware *fw,
421 void *nvram_image, u32 nvram_len);
422};
423
424static void brcmf_fw_request_nvram_done(const struct firmware *fw, void *ctx)
425{
426 struct brcmf_fw *fwctx = ctx;
427 u32 nvram_length = 0;
428 void *nvram = NULL;
429
430 brcmf_dbg(TRACE, "enter: dev=%s\n", dev_name(fwctx->dev));
431 if (!fw && !(fwctx->flags & BRCMF_FW_REQ_NV_OPTIONAL))
432 goto fail;
433
434 if (fw) {
Rafał Miłecki11f09d42015-06-04 22:11:07 +0200435 nvram = brcmf_fw_nvram_strip(fw->data, fw->size, &nvram_length,
Hante Meulemanc4365532015-04-14 20:10:33 +0200436 fwctx->domain_nr, fwctx->bus_nr);
Arend van Sprielc1416e72014-05-27 12:56:21 +0200437 release_firmware(fw);
438 if (!nvram && !(fwctx->flags & BRCMF_FW_REQ_NV_OPTIONAL))
439 goto fail;
440 }
441
442 fwctx->done(fwctx->dev, fwctx->code, nvram, nvram_length);
443 kfree(fwctx);
444 return;
445
446fail:
447 brcmf_dbg(TRACE, "failed: dev=%s\n", dev_name(fwctx->dev));
Markus Elfringac96ce82014-11-20 16:42:51 +0100448 release_firmware(fwctx->code);
Arend van Sprielc1416e72014-05-27 12:56:21 +0200449 device_release_driver(fwctx->dev);
450 kfree(fwctx);
451}
452
453static void brcmf_fw_request_code_done(const struct firmware *fw, void *ctx)
454{
455 struct brcmf_fw *fwctx = ctx;
456 int ret;
457
458 brcmf_dbg(TRACE, "enter: dev=%s\n", dev_name(fwctx->dev));
459 if (!fw)
460 goto fail;
461
462 /* only requested code so done here */
463 if (!(fwctx->flags & BRCMF_FW_REQUEST_NVRAM)) {
464 fwctx->done(fwctx->dev, fw, NULL, 0);
465 kfree(fwctx);
466 return;
467 }
468 fwctx->code = fw;
469 ret = request_firmware_nowait(THIS_MODULE, true, fwctx->nvram_name,
470 fwctx->dev, GFP_KERNEL, fwctx,
471 brcmf_fw_request_nvram_done);
472
473 if (!ret)
474 return;
475
476 /* when nvram is optional call .done() callback here */
477 if (fwctx->flags & BRCMF_FW_REQ_NV_OPTIONAL) {
478 fwctx->done(fwctx->dev, fw, NULL, 0);
479 kfree(fwctx);
480 return;
481 }
482
483 /* failed nvram request */
484 release_firmware(fw);
485fail:
486 brcmf_dbg(TRACE, "failed: dev=%s\n", dev_name(fwctx->dev));
487 device_release_driver(fwctx->dev);
488 kfree(fwctx);
489}
490
Hante Meulemanc4365532015-04-14 20:10:33 +0200491int brcmf_fw_get_firmwares_pcie(struct device *dev, u16 flags,
492 const char *code, const char *nvram,
493 void (*fw_cb)(struct device *dev,
494 const struct firmware *fw,
495 void *nvram_image, u32 nvram_len),
496 u16 domain_nr, u16 bus_nr)
Arend van Sprielc1416e72014-05-27 12:56:21 +0200497{
498 struct brcmf_fw *fwctx;
499
500 brcmf_dbg(TRACE, "enter: dev=%s\n", dev_name(dev));
501 if (!fw_cb || !code)
502 return -EINVAL;
503
504 if ((flags & BRCMF_FW_REQUEST_NVRAM) && !nvram)
505 return -EINVAL;
506
507 fwctx = kzalloc(sizeof(*fwctx), GFP_KERNEL);
508 if (!fwctx)
509 return -ENOMEM;
510
511 fwctx->dev = dev;
512 fwctx->flags = flags;
513 fwctx->done = fw_cb;
514 if (flags & BRCMF_FW_REQUEST_NVRAM)
515 fwctx->nvram_name = nvram;
Hante Meulemanc4365532015-04-14 20:10:33 +0200516 fwctx->domain_nr = domain_nr;
517 fwctx->bus_nr = bus_nr;
Arend van Sprielc1416e72014-05-27 12:56:21 +0200518
519 return request_firmware_nowait(THIS_MODULE, true, code, dev,
520 GFP_KERNEL, fwctx,
521 brcmf_fw_request_code_done);
522}
Hante Meulemanc4365532015-04-14 20:10:33 +0200523
524int brcmf_fw_get_firmwares(struct device *dev, u16 flags,
525 const char *code, const char *nvram,
526 void (*fw_cb)(struct device *dev,
527 const struct firmware *fw,
528 void *nvram_image, u32 nvram_len))
529{
530 return brcmf_fw_get_firmwares_pcie(dev, flags, code, nvram, fw_cb, 0,
531 0);
532}
533