firmware.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607
  1. /*
  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. #include <linux/kernel.h>
  17. #include <linux/slab.h>
  18. #include <linux/device.h>
  19. #include <linux/firmware.h>
  20. #include <linux/module.h>
  21. #include <linux/bcm47xx_nvram.h>
  22. #include "debug.h"
  23. #include "firmware.h"
  24. #include "core.h"
  25. #include "common.h"
  26. #define BRCMF_FW_MAX_NVRAM_SIZE 64000
  27. #define BRCMF_FW_NVRAM_DEVPATH_LEN 19 /* devpath0=pcie/1/4/ */
  28. #define BRCMF_FW_NVRAM_PCIEDEV_LEN 10 /* pcie/1/4/ + \0 */
  29. #define BRCMF_FW_DEFAULT_BOARDREV "boardrev=0xff"
  30. enum nvram_parser_state {
  31. IDLE,
  32. KEY,
  33. VALUE,
  34. COMMENT,
  35. END
  36. };
  37. /**
  38. * struct nvram_parser - internal info for parser.
  39. *
  40. * @state: current parser state.
  41. * @data: input buffer being parsed.
  42. * @nvram: output buffer with parse result.
  43. * @nvram_len: lenght of parse result.
  44. * @line: current line.
  45. * @column: current column in line.
  46. * @pos: byte offset in input buffer.
  47. * @entry: start position of key,value entry.
  48. * @multi_dev_v1: detect pcie multi device v1 (compressed).
  49. * @multi_dev_v2: detect pcie multi device v2.
  50. * @boardrev_found: nvram contains boardrev information.
  51. */
  52. struct nvram_parser {
  53. enum nvram_parser_state state;
  54. const u8 *data;
  55. u8 *nvram;
  56. u32 nvram_len;
  57. u32 line;
  58. u32 column;
  59. u32 pos;
  60. u32 entry;
  61. bool multi_dev_v1;
  62. bool multi_dev_v2;
  63. bool boardrev_found;
  64. };
  65. /**
  66. * is_nvram_char() - check if char is a valid one for NVRAM entry
  67. *
  68. * It accepts all printable ASCII chars except for '#' which opens a comment.
  69. * Please note that ' ' (space) while accepted is not a valid key name char.
  70. */
  71. static bool is_nvram_char(char c)
  72. {
  73. /* comment marker excluded */
  74. if (c == '#')
  75. return false;
  76. /* key and value may have any other readable character */
  77. return (c >= 0x20 && c < 0x7f);
  78. }
  79. static bool is_whitespace(char c)
  80. {
  81. return (c == ' ' || c == '\r' || c == '\n' || c == '\t');
  82. }
  83. static enum nvram_parser_state brcmf_nvram_handle_idle(struct nvram_parser *nvp)
  84. {
  85. char c;
  86. c = nvp->data[nvp->pos];
  87. if (c == '\n')
  88. return COMMENT;
  89. if (is_whitespace(c) || c == '\0')
  90. goto proceed;
  91. if (c == '#')
  92. return COMMENT;
  93. if (is_nvram_char(c)) {
  94. nvp->entry = nvp->pos;
  95. return KEY;
  96. }
  97. brcmf_dbg(INFO, "warning: ln=%d:col=%d: ignoring invalid character\n",
  98. nvp->line, nvp->column);
  99. proceed:
  100. nvp->column++;
  101. nvp->pos++;
  102. return IDLE;
  103. }
  104. static enum nvram_parser_state brcmf_nvram_handle_key(struct nvram_parser *nvp)
  105. {
  106. enum nvram_parser_state st = nvp->state;
  107. char c;
  108. c = nvp->data[nvp->pos];
  109. if (c == '=') {
  110. /* ignore RAW1 by treating as comment */
  111. if (strncmp(&nvp->data[nvp->entry], "RAW1", 4) == 0)
  112. st = COMMENT;
  113. else
  114. st = VALUE;
  115. if (strncmp(&nvp->data[nvp->entry], "devpath", 7) == 0)
  116. nvp->multi_dev_v1 = true;
  117. if (strncmp(&nvp->data[nvp->entry], "pcie/", 5) == 0)
  118. nvp->multi_dev_v2 = true;
  119. if (strncmp(&nvp->data[nvp->entry], "boardrev", 8) == 0)
  120. nvp->boardrev_found = true;
  121. } else if (!is_nvram_char(c) || c == ' ') {
  122. brcmf_dbg(INFO, "warning: ln=%d:col=%d: '=' expected, skip invalid key entry\n",
  123. nvp->line, nvp->column);
  124. return COMMENT;
  125. }
  126. nvp->column++;
  127. nvp->pos++;
  128. return st;
  129. }
  130. static enum nvram_parser_state
  131. brcmf_nvram_handle_value(struct nvram_parser *nvp)
  132. {
  133. char c;
  134. char *skv;
  135. char *ekv;
  136. u32 cplen;
  137. c = nvp->data[nvp->pos];
  138. if (!is_nvram_char(c)) {
  139. /* key,value pair complete */
  140. ekv = (u8 *)&nvp->data[nvp->pos];
  141. skv = (u8 *)&nvp->data[nvp->entry];
  142. cplen = ekv - skv;
  143. if (nvp->nvram_len + cplen + 1 >= BRCMF_FW_MAX_NVRAM_SIZE)
  144. return END;
  145. /* copy to output buffer */
  146. memcpy(&nvp->nvram[nvp->nvram_len], skv, cplen);
  147. nvp->nvram_len += cplen;
  148. nvp->nvram[nvp->nvram_len] = '\0';
  149. nvp->nvram_len++;
  150. return IDLE;
  151. }
  152. nvp->pos++;
  153. nvp->column++;
  154. return VALUE;
  155. }
  156. static enum nvram_parser_state
  157. brcmf_nvram_handle_comment(struct nvram_parser *nvp)
  158. {
  159. char *eoc, *sol;
  160. sol = (char *)&nvp->data[nvp->pos];
  161. eoc = strchr(sol, '\n');
  162. if (!eoc) {
  163. eoc = strchr(sol, '\0');
  164. if (!eoc)
  165. return END;
  166. }
  167. /* eat all moving to next line */
  168. nvp->line++;
  169. nvp->column = 1;
  170. nvp->pos += (eoc - sol) + 1;
  171. return IDLE;
  172. }
  173. static enum nvram_parser_state brcmf_nvram_handle_end(struct nvram_parser *nvp)
  174. {
  175. /* final state */
  176. return END;
  177. }
  178. static enum nvram_parser_state
  179. (*nv_parser_states[])(struct nvram_parser *nvp) = {
  180. brcmf_nvram_handle_idle,
  181. brcmf_nvram_handle_key,
  182. brcmf_nvram_handle_value,
  183. brcmf_nvram_handle_comment,
  184. brcmf_nvram_handle_end
  185. };
  186. static int brcmf_init_nvram_parser(struct nvram_parser *nvp,
  187. const u8 *data, size_t data_len)
  188. {
  189. size_t size;
  190. memset(nvp, 0, sizeof(*nvp));
  191. nvp->data = data;
  192. /* Limit size to MAX_NVRAM_SIZE, some files contain lot of comment */
  193. if (data_len > BRCMF_FW_MAX_NVRAM_SIZE)
  194. size = BRCMF_FW_MAX_NVRAM_SIZE;
  195. else
  196. size = data_len;
  197. /* Alloc for extra 0 byte + roundup by 4 + length field */
  198. size += 1 + 3 + sizeof(u32);
  199. nvp->nvram = kzalloc(size, GFP_KERNEL);
  200. if (!nvp->nvram)
  201. return -ENOMEM;
  202. nvp->line = 1;
  203. nvp->column = 1;
  204. return 0;
  205. }
  206. /* brcmf_fw_strip_multi_v1 :Some nvram files contain settings for multiple
  207. * devices. Strip it down for one device, use domain_nr/bus_nr to determine
  208. * which data is to be returned. v1 is the version where nvram is stored
  209. * compressed and "devpath" maps to index for valid entries.
  210. */
  211. static void brcmf_fw_strip_multi_v1(struct nvram_parser *nvp, u16 domain_nr,
  212. u16 bus_nr)
  213. {
  214. /* Device path with a leading '=' key-value separator */
  215. char pci_path[] = "=pci/?/?";
  216. size_t pci_len;
  217. char pcie_path[] = "=pcie/?/?";
  218. size_t pcie_len;
  219. u32 i, j;
  220. bool found;
  221. u8 *nvram;
  222. u8 id;
  223. nvram = kzalloc(nvp->nvram_len + 1 + 3 + sizeof(u32), GFP_KERNEL);
  224. if (!nvram)
  225. goto fail;
  226. /* min length: devpath0=pcie/1/4/ + 0:x=y */
  227. if (nvp->nvram_len < BRCMF_FW_NVRAM_DEVPATH_LEN + 6)
  228. goto fail;
  229. /* First search for the devpathX and see if it is the configuration
  230. * for domain_nr/bus_nr. Search complete nvp
  231. */
  232. snprintf(pci_path, sizeof(pci_path), "=pci/%d/%d", domain_nr,
  233. bus_nr);
  234. pci_len = strlen(pci_path);
  235. snprintf(pcie_path, sizeof(pcie_path), "=pcie/%d/%d", domain_nr,
  236. bus_nr);
  237. pcie_len = strlen(pcie_path);
  238. found = false;
  239. i = 0;
  240. while (i < nvp->nvram_len - BRCMF_FW_NVRAM_DEVPATH_LEN) {
  241. /* Format: devpathX=pcie/Y/Z/
  242. * Y = domain_nr, Z = bus_nr, X = virtual ID
  243. */
  244. if (strncmp(&nvp->nvram[i], "devpath", 7) == 0 &&
  245. (!strncmp(&nvp->nvram[i + 8], pci_path, pci_len) ||
  246. !strncmp(&nvp->nvram[i + 8], pcie_path, pcie_len))) {
  247. id = nvp->nvram[i + 7] - '0';
  248. found = true;
  249. break;
  250. }
  251. while (nvp->nvram[i] != 0)
  252. i++;
  253. i++;
  254. }
  255. if (!found)
  256. goto fail;
  257. /* Now copy all valid entries, release old nvram and assign new one */
  258. i = 0;
  259. j = 0;
  260. while (i < nvp->nvram_len) {
  261. if ((nvp->nvram[i] - '0' == id) && (nvp->nvram[i + 1] == ':')) {
  262. i += 2;
  263. if (strncmp(&nvp->nvram[i], "boardrev", 8) == 0)
  264. nvp->boardrev_found = true;
  265. while (nvp->nvram[i] != 0) {
  266. nvram[j] = nvp->nvram[i];
  267. i++;
  268. j++;
  269. }
  270. nvram[j] = 0;
  271. j++;
  272. }
  273. while (nvp->nvram[i] != 0)
  274. i++;
  275. i++;
  276. }
  277. kfree(nvp->nvram);
  278. nvp->nvram = nvram;
  279. nvp->nvram_len = j;
  280. return;
  281. fail:
  282. kfree(nvram);
  283. nvp->nvram_len = 0;
  284. }
  285. /* brcmf_fw_strip_multi_v2 :Some nvram files contain settings for multiple
  286. * devices. Strip it down for one device, use domain_nr/bus_nr to determine
  287. * which data is to be returned. v2 is the version where nvram is stored
  288. * uncompressed, all relevant valid entries are identified by
  289. * pcie/domain_nr/bus_nr:
  290. */
  291. static void brcmf_fw_strip_multi_v2(struct nvram_parser *nvp, u16 domain_nr,
  292. u16 bus_nr)
  293. {
  294. char prefix[BRCMF_FW_NVRAM_PCIEDEV_LEN];
  295. size_t len;
  296. u32 i, j;
  297. u8 *nvram;
  298. nvram = kzalloc(nvp->nvram_len + 1 + 3 + sizeof(u32), GFP_KERNEL);
  299. if (!nvram)
  300. goto fail;
  301. /* Copy all valid entries, release old nvram and assign new one.
  302. * Valid entries are of type pcie/X/Y/ where X = domain_nr and
  303. * Y = bus_nr.
  304. */
  305. snprintf(prefix, sizeof(prefix), "pcie/%d/%d/", domain_nr, bus_nr);
  306. len = strlen(prefix);
  307. i = 0;
  308. j = 0;
  309. while (i < nvp->nvram_len - len) {
  310. if (strncmp(&nvp->nvram[i], prefix, len) == 0) {
  311. i += len;
  312. if (strncmp(&nvp->nvram[i], "boardrev", 8) == 0)
  313. nvp->boardrev_found = true;
  314. while (nvp->nvram[i] != 0) {
  315. nvram[j] = nvp->nvram[i];
  316. i++;
  317. j++;
  318. }
  319. nvram[j] = 0;
  320. j++;
  321. }
  322. while (nvp->nvram[i] != 0)
  323. i++;
  324. i++;
  325. }
  326. kfree(nvp->nvram);
  327. nvp->nvram = nvram;
  328. nvp->nvram_len = j;
  329. return;
  330. fail:
  331. kfree(nvram);
  332. nvp->nvram_len = 0;
  333. }
  334. static void brcmf_fw_add_defaults(struct nvram_parser *nvp)
  335. {
  336. if (nvp->boardrev_found)
  337. return;
  338. memcpy(&nvp->nvram[nvp->nvram_len], &BRCMF_FW_DEFAULT_BOARDREV,
  339. strlen(BRCMF_FW_DEFAULT_BOARDREV));
  340. nvp->nvram_len += strlen(BRCMF_FW_DEFAULT_BOARDREV);
  341. nvp->nvram[nvp->nvram_len] = '\0';
  342. nvp->nvram_len++;
  343. }
  344. /* brcmf_nvram_strip :Takes a buffer of "<var>=<value>\n" lines read from a fil
  345. * and ending in a NUL. Removes carriage returns, empty lines, comment lines,
  346. * and converts newlines to NULs. Shortens buffer as needed and pads with NULs.
  347. * End of buffer is completed with token identifying length of buffer.
  348. */
  349. static void *brcmf_fw_nvram_strip(const u8 *data, size_t data_len,
  350. u32 *new_length, u16 domain_nr, u16 bus_nr)
  351. {
  352. struct nvram_parser nvp;
  353. u32 pad;
  354. u32 token;
  355. __le32 token_le;
  356. if (brcmf_init_nvram_parser(&nvp, data, data_len) < 0)
  357. return NULL;
  358. while (nvp.pos < data_len) {
  359. nvp.state = nv_parser_states[nvp.state](&nvp);
  360. if (nvp.state == END)
  361. break;
  362. }
  363. if (nvp.multi_dev_v1) {
  364. nvp.boardrev_found = false;
  365. brcmf_fw_strip_multi_v1(&nvp, domain_nr, bus_nr);
  366. } else if (nvp.multi_dev_v2) {
  367. nvp.boardrev_found = false;
  368. brcmf_fw_strip_multi_v2(&nvp, domain_nr, bus_nr);
  369. }
  370. if (nvp.nvram_len == 0) {
  371. kfree(nvp.nvram);
  372. return NULL;
  373. }
  374. brcmf_fw_add_defaults(&nvp);
  375. pad = nvp.nvram_len;
  376. *new_length = roundup(nvp.nvram_len + 1, 4);
  377. while (pad != *new_length) {
  378. nvp.nvram[pad] = 0;
  379. pad++;
  380. }
  381. token = *new_length / 4;
  382. token = (~token << 16) | (token & 0x0000FFFF);
  383. token_le = cpu_to_le32(token);
  384. memcpy(&nvp.nvram[*new_length], &token_le, sizeof(token_le));
  385. *new_length += sizeof(token_le);
  386. return nvp.nvram;
  387. }
  388. void brcmf_fw_nvram_free(void *nvram)
  389. {
  390. kfree(nvram);
  391. }
  392. struct brcmf_fw {
  393. struct device *dev;
  394. u16 flags;
  395. const struct firmware *code;
  396. const char *nvram_name;
  397. u16 domain_nr;
  398. u16 bus_nr;
  399. void (*done)(struct device *dev, int err, const struct firmware *fw,
  400. void *nvram_image, u32 nvram_len);
  401. };
  402. static void brcmf_fw_request_nvram_done(const struct firmware *fw, void *ctx)
  403. {
  404. struct brcmf_fw *fwctx = ctx;
  405. u32 nvram_length = 0;
  406. void *nvram = NULL;
  407. u8 *data = NULL;
  408. size_t data_len;
  409. bool raw_nvram;
  410. brcmf_dbg(TRACE, "enter: dev=%s\n", dev_name(fwctx->dev));
  411. if (fw && fw->data) {
  412. data = (u8 *)fw->data;
  413. data_len = fw->size;
  414. raw_nvram = false;
  415. } else {
  416. data = bcm47xx_nvram_get_contents(&data_len);
  417. if (!data && !(fwctx->flags & BRCMF_FW_REQ_NV_OPTIONAL))
  418. goto fail;
  419. raw_nvram = true;
  420. }
  421. if (data)
  422. nvram = brcmf_fw_nvram_strip(data, data_len, &nvram_length,
  423. fwctx->domain_nr, fwctx->bus_nr);
  424. if (raw_nvram)
  425. bcm47xx_nvram_release_contents(data);
  426. release_firmware(fw);
  427. if (!nvram && !(fwctx->flags & BRCMF_FW_REQ_NV_OPTIONAL))
  428. goto fail;
  429. fwctx->done(fwctx->dev, 0, fwctx->code, nvram, nvram_length);
  430. kfree(fwctx);
  431. return;
  432. fail:
  433. brcmf_dbg(TRACE, "failed: dev=%s\n", dev_name(fwctx->dev));
  434. release_firmware(fwctx->code);
  435. fwctx->done(fwctx->dev, -ENOENT, NULL, NULL, 0);
  436. kfree(fwctx);
  437. }
  438. static void brcmf_fw_request_code_done(const struct firmware *fw, void *ctx)
  439. {
  440. struct brcmf_fw *fwctx = ctx;
  441. int ret = 0;
  442. brcmf_dbg(TRACE, "enter: dev=%s\n", dev_name(fwctx->dev));
  443. if (!fw) {
  444. ret = -ENOENT;
  445. goto fail;
  446. }
  447. /* only requested code so done here */
  448. if (!(fwctx->flags & BRCMF_FW_REQUEST_NVRAM))
  449. goto done;
  450. fwctx->code = fw;
  451. ret = reject_firmware_nowait(THIS_MODULE, true, fwctx->nvram_name,
  452. fwctx->dev, GFP_KERNEL, fwctx,
  453. brcmf_fw_request_nvram_done);
  454. /* pass NULL to nvram callback for bcm47xx fallback */
  455. if (ret)
  456. brcmf_fw_request_nvram_done(NULL, fwctx);
  457. return;
  458. fail:
  459. brcmf_dbg(TRACE, "failed: dev=%s\n", dev_name(fwctx->dev));
  460. done:
  461. fwctx->done(fwctx->dev, ret, fw, NULL, 0);
  462. kfree(fwctx);
  463. }
  464. int brcmf_fw_get_firmwares_pcie(struct device *dev, u16 flags,
  465. const char *code, const char *nvram,
  466. void (*fw_cb)(struct device *dev, int err,
  467. const struct firmware *fw,
  468. void *nvram_image, u32 nvram_len),
  469. u16 domain_nr, u16 bus_nr)
  470. {
  471. struct brcmf_fw *fwctx;
  472. brcmf_dbg(TRACE, "enter: dev=%s\n", dev_name(dev));
  473. if (!fw_cb || !code)
  474. return -EINVAL;
  475. if ((flags & BRCMF_FW_REQUEST_NVRAM) && !nvram)
  476. return -EINVAL;
  477. fwctx = kzalloc(sizeof(*fwctx), GFP_KERNEL);
  478. if (!fwctx)
  479. return -ENOMEM;
  480. fwctx->dev = dev;
  481. fwctx->flags = flags;
  482. fwctx->done = fw_cb;
  483. if (flags & BRCMF_FW_REQUEST_NVRAM)
  484. fwctx->nvram_name = nvram;
  485. fwctx->domain_nr = domain_nr;
  486. fwctx->bus_nr = bus_nr;
  487. return reject_firmware_nowait(THIS_MODULE, true, code, dev,
  488. GFP_KERNEL, fwctx,
  489. brcmf_fw_request_code_done);
  490. }
  491. int brcmf_fw_get_firmwares(struct device *dev, u16 flags,
  492. const char *code, const char *nvram,
  493. void (*fw_cb)(struct device *dev, int err,
  494. const struct firmware *fw,
  495. void *nvram_image, u32 nvram_len))
  496. {
  497. return brcmf_fw_get_firmwares_pcie(dev, flags, code, nvram, fw_cb, 0,
  498. 0);
  499. }
  500. int brcmf_fw_map_chip_to_name(u32 chip, u32 chiprev,
  501. struct brcmf_firmware_mapping mapping_table[],
  502. u32 table_size, char fw_name[BRCMF_FW_NAME_LEN],
  503. char nvram_name[BRCMF_FW_NAME_LEN])
  504. {
  505. u32 i;
  506. char end;
  507. for (i = 0; i < table_size; i++) {
  508. if (mapping_table[i].chipid == chip &&
  509. mapping_table[i].revmask & BIT(chiprev))
  510. break;
  511. }
  512. if (i == table_size) {
  513. brcmf_err("Unknown chipid %d [%d]\n", chip, chiprev);
  514. return -ENODEV;
  515. }
  516. /* check if firmware path is provided by module parameter */
  517. if (brcmf_mp_global.firmware_path[0] != '\0') {
  518. strlcpy(fw_name, brcmf_mp_global.firmware_path,
  519. BRCMF_FW_NAME_LEN);
  520. if ((nvram_name) && (mapping_table[i].nvram))
  521. strlcpy(nvram_name, brcmf_mp_global.firmware_path,
  522. BRCMF_FW_NAME_LEN);
  523. end = brcmf_mp_global.firmware_path[
  524. strlen(brcmf_mp_global.firmware_path) - 1];
  525. if (end != '/') {
  526. strlcat(fw_name, "/", BRCMF_FW_NAME_LEN);
  527. if ((nvram_name) && (mapping_table[i].nvram))
  528. strlcat(nvram_name, "/", BRCMF_FW_NAME_LEN);
  529. }
  530. }
  531. strlcat(fw_name, mapping_table[i].fw, BRCMF_FW_NAME_LEN);
  532. if ((nvram_name) && (mapping_table[i].nvram))
  533. strlcat(nvram_name, mapping_table[i].nvram, BRCMF_FW_NAME_LEN);
  534. return 0;
  535. }