xref: /OK3568_Linux_fs/kernel/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: ISC
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * Copyright (c) 2013 Broadcom Corporation
4*4882a593Smuzhiyun  */
5*4882a593Smuzhiyun 
6*4882a593Smuzhiyun #include <linux/efi.h>
7*4882a593Smuzhiyun #include <linux/kernel.h>
8*4882a593Smuzhiyun #include <linux/slab.h>
9*4882a593Smuzhiyun #include <linux/device.h>
10*4882a593Smuzhiyun #include <linux/firmware.h>
11*4882a593Smuzhiyun #include <linux/module.h>
12*4882a593Smuzhiyun #include <linux/bcm47xx_nvram.h>
13*4882a593Smuzhiyun 
14*4882a593Smuzhiyun #include "debug.h"
15*4882a593Smuzhiyun #include "firmware.h"
16*4882a593Smuzhiyun #include "core.h"
17*4882a593Smuzhiyun #include "common.h"
18*4882a593Smuzhiyun #include "chip.h"
19*4882a593Smuzhiyun 
20*4882a593Smuzhiyun #define BRCMF_FW_MAX_NVRAM_SIZE			64000
21*4882a593Smuzhiyun #define BRCMF_FW_NVRAM_DEVPATH_LEN		19	/* devpath0=pcie/1/4/ */
22*4882a593Smuzhiyun #define BRCMF_FW_NVRAM_PCIEDEV_LEN		10	/* pcie/1/4/ + \0 */
23*4882a593Smuzhiyun #define BRCMF_FW_DEFAULT_BOARDREV		"boardrev=0xff"
24*4882a593Smuzhiyun 
25*4882a593Smuzhiyun enum nvram_parser_state {
26*4882a593Smuzhiyun 	IDLE,
27*4882a593Smuzhiyun 	KEY,
28*4882a593Smuzhiyun 	VALUE,
29*4882a593Smuzhiyun 	COMMENT,
30*4882a593Smuzhiyun 	END
31*4882a593Smuzhiyun };
32*4882a593Smuzhiyun 
33*4882a593Smuzhiyun /**
34*4882a593Smuzhiyun  * struct nvram_parser - internal info for parser.
35*4882a593Smuzhiyun  *
36*4882a593Smuzhiyun  * @state: current parser state.
37*4882a593Smuzhiyun  * @data: input buffer being parsed.
38*4882a593Smuzhiyun  * @nvram: output buffer with parse result.
39*4882a593Smuzhiyun  * @nvram_len: length of parse result.
40*4882a593Smuzhiyun  * @line: current line.
41*4882a593Smuzhiyun  * @column: current column in line.
42*4882a593Smuzhiyun  * @pos: byte offset in input buffer.
43*4882a593Smuzhiyun  * @entry: start position of key,value entry.
44*4882a593Smuzhiyun  * @multi_dev_v1: detect pcie multi device v1 (compressed).
45*4882a593Smuzhiyun  * @multi_dev_v2: detect pcie multi device v2.
46*4882a593Smuzhiyun  * @boardrev_found: nvram contains boardrev information.
47*4882a593Smuzhiyun  */
48*4882a593Smuzhiyun struct nvram_parser {
49*4882a593Smuzhiyun 	enum nvram_parser_state state;
50*4882a593Smuzhiyun 	const u8 *data;
51*4882a593Smuzhiyun 	u8 *nvram;
52*4882a593Smuzhiyun 	u32 nvram_len;
53*4882a593Smuzhiyun 	u32 line;
54*4882a593Smuzhiyun 	u32 column;
55*4882a593Smuzhiyun 	u32 pos;
56*4882a593Smuzhiyun 	u32 entry;
57*4882a593Smuzhiyun 	bool multi_dev_v1;
58*4882a593Smuzhiyun 	bool multi_dev_v2;
59*4882a593Smuzhiyun 	bool boardrev_found;
60*4882a593Smuzhiyun };
61*4882a593Smuzhiyun 
62*4882a593Smuzhiyun /*
63*4882a593Smuzhiyun  * is_nvram_char() - check if char is a valid one for NVRAM entry
64*4882a593Smuzhiyun  *
65*4882a593Smuzhiyun  * It accepts all printable ASCII chars except for '#' which opens a comment.
66*4882a593Smuzhiyun  * Please note that ' ' (space) while accepted is not a valid key name char.
67*4882a593Smuzhiyun  */
is_nvram_char(char c)68*4882a593Smuzhiyun static bool is_nvram_char(char c)
69*4882a593Smuzhiyun {
70*4882a593Smuzhiyun 	/* comment marker excluded */
71*4882a593Smuzhiyun 	if (c == '#')
72*4882a593Smuzhiyun 		return false;
73*4882a593Smuzhiyun 
74*4882a593Smuzhiyun 	/* key and value may have any other readable character */
75*4882a593Smuzhiyun 	return (c >= 0x20 && c < 0x7f);
76*4882a593Smuzhiyun }
77*4882a593Smuzhiyun 
is_whitespace(char c)78*4882a593Smuzhiyun static bool is_whitespace(char c)
79*4882a593Smuzhiyun {
80*4882a593Smuzhiyun 	return (c == ' ' || c == '\r' || c == '\n' || c == '\t');
81*4882a593Smuzhiyun }
82*4882a593Smuzhiyun 
brcmf_nvram_handle_idle(struct nvram_parser * nvp)83*4882a593Smuzhiyun static enum nvram_parser_state brcmf_nvram_handle_idle(struct nvram_parser *nvp)
84*4882a593Smuzhiyun {
85*4882a593Smuzhiyun 	char c;
86*4882a593Smuzhiyun 
87*4882a593Smuzhiyun 	c = nvp->data[nvp->pos];
88*4882a593Smuzhiyun 	if (c == '\n')
89*4882a593Smuzhiyun 		return COMMENT;
90*4882a593Smuzhiyun 	if (is_whitespace(c) || c == '\0')
91*4882a593Smuzhiyun 		goto proceed;
92*4882a593Smuzhiyun 	if (c == '#')
93*4882a593Smuzhiyun 		return COMMENT;
94*4882a593Smuzhiyun 	if (is_nvram_char(c)) {
95*4882a593Smuzhiyun 		nvp->entry = nvp->pos;
96*4882a593Smuzhiyun 		return KEY;
97*4882a593Smuzhiyun 	}
98*4882a593Smuzhiyun 	brcmf_dbg(INFO, "warning: ln=%d:col=%d: ignoring invalid character\n",
99*4882a593Smuzhiyun 		  nvp->line, nvp->column);
100*4882a593Smuzhiyun proceed:
101*4882a593Smuzhiyun 	nvp->column++;
102*4882a593Smuzhiyun 	nvp->pos++;
103*4882a593Smuzhiyun 	return IDLE;
104*4882a593Smuzhiyun }
105*4882a593Smuzhiyun 
brcmf_nvram_handle_key(struct nvram_parser * nvp)106*4882a593Smuzhiyun static enum nvram_parser_state brcmf_nvram_handle_key(struct nvram_parser *nvp)
107*4882a593Smuzhiyun {
108*4882a593Smuzhiyun 	enum nvram_parser_state st = nvp->state;
109*4882a593Smuzhiyun 	char c;
110*4882a593Smuzhiyun 
111*4882a593Smuzhiyun 	c = nvp->data[nvp->pos];
112*4882a593Smuzhiyun 	if (c == '=') {
113*4882a593Smuzhiyun 		/* ignore RAW1 by treating as comment */
114*4882a593Smuzhiyun 		if (strncmp(&nvp->data[nvp->entry], "RAW1", 4) == 0)
115*4882a593Smuzhiyun 			st = COMMENT;
116*4882a593Smuzhiyun 		else
117*4882a593Smuzhiyun 			st = VALUE;
118*4882a593Smuzhiyun 		if (strncmp(&nvp->data[nvp->entry], "devpath", 7) == 0)
119*4882a593Smuzhiyun 			nvp->multi_dev_v1 = true;
120*4882a593Smuzhiyun 		if (strncmp(&nvp->data[nvp->entry], "pcie/", 5) == 0)
121*4882a593Smuzhiyun 			nvp->multi_dev_v2 = true;
122*4882a593Smuzhiyun 		if (strncmp(&nvp->data[nvp->entry], "boardrev", 8) == 0)
123*4882a593Smuzhiyun 			nvp->boardrev_found = true;
124*4882a593Smuzhiyun 	} else if (!is_nvram_char(c) || c == ' ') {
125*4882a593Smuzhiyun 		brcmf_dbg(INFO, "warning: ln=%d:col=%d: '=' expected, skip invalid key entry\n",
126*4882a593Smuzhiyun 			  nvp->line, nvp->column);
127*4882a593Smuzhiyun 		return COMMENT;
128*4882a593Smuzhiyun 	}
129*4882a593Smuzhiyun 
130*4882a593Smuzhiyun 	nvp->column++;
131*4882a593Smuzhiyun 	nvp->pos++;
132*4882a593Smuzhiyun 	return st;
133*4882a593Smuzhiyun }
134*4882a593Smuzhiyun 
135*4882a593Smuzhiyun static enum nvram_parser_state
brcmf_nvram_handle_value(struct nvram_parser * nvp)136*4882a593Smuzhiyun brcmf_nvram_handle_value(struct nvram_parser *nvp)
137*4882a593Smuzhiyun {
138*4882a593Smuzhiyun 	char c;
139*4882a593Smuzhiyun 	char *skv;
140*4882a593Smuzhiyun 	char *ekv;
141*4882a593Smuzhiyun 	u32 cplen;
142*4882a593Smuzhiyun 
143*4882a593Smuzhiyun 	c = nvp->data[nvp->pos];
144*4882a593Smuzhiyun 	if (!is_nvram_char(c)) {
145*4882a593Smuzhiyun 		/* key,value pair complete */
146*4882a593Smuzhiyun 		ekv = (u8 *)&nvp->data[nvp->pos];
147*4882a593Smuzhiyun 		skv = (u8 *)&nvp->data[nvp->entry];
148*4882a593Smuzhiyun 		cplen = ekv - skv;
149*4882a593Smuzhiyun 		if (nvp->nvram_len + cplen + 1 >= BRCMF_FW_MAX_NVRAM_SIZE)
150*4882a593Smuzhiyun 			return END;
151*4882a593Smuzhiyun 		/* copy to output buffer */
152*4882a593Smuzhiyun 		memcpy(&nvp->nvram[nvp->nvram_len], skv, cplen);
153*4882a593Smuzhiyun 		nvp->nvram_len += cplen;
154*4882a593Smuzhiyun 		nvp->nvram[nvp->nvram_len] = '\0';
155*4882a593Smuzhiyun 		nvp->nvram_len++;
156*4882a593Smuzhiyun 		return IDLE;
157*4882a593Smuzhiyun 	}
158*4882a593Smuzhiyun 	nvp->pos++;
159*4882a593Smuzhiyun 	nvp->column++;
160*4882a593Smuzhiyun 	return VALUE;
161*4882a593Smuzhiyun }
162*4882a593Smuzhiyun 
163*4882a593Smuzhiyun static enum nvram_parser_state
brcmf_nvram_handle_comment(struct nvram_parser * nvp)164*4882a593Smuzhiyun brcmf_nvram_handle_comment(struct nvram_parser *nvp)
165*4882a593Smuzhiyun {
166*4882a593Smuzhiyun 	char *eoc, *sol;
167*4882a593Smuzhiyun 
168*4882a593Smuzhiyun 	sol = (char *)&nvp->data[nvp->pos];
169*4882a593Smuzhiyun 	eoc = strchr(sol, '\n');
170*4882a593Smuzhiyun 	if (!eoc) {
171*4882a593Smuzhiyun 		eoc = strchr(sol, '\0');
172*4882a593Smuzhiyun 		if (!eoc)
173*4882a593Smuzhiyun 			return END;
174*4882a593Smuzhiyun 	}
175*4882a593Smuzhiyun 
176*4882a593Smuzhiyun 	/* eat all moving to next line */
177*4882a593Smuzhiyun 	nvp->line++;
178*4882a593Smuzhiyun 	nvp->column = 1;
179*4882a593Smuzhiyun 	nvp->pos += (eoc - sol) + 1;
180*4882a593Smuzhiyun 	return IDLE;
181*4882a593Smuzhiyun }
182*4882a593Smuzhiyun 
brcmf_nvram_handle_end(struct nvram_parser * nvp)183*4882a593Smuzhiyun static enum nvram_parser_state brcmf_nvram_handle_end(struct nvram_parser *nvp)
184*4882a593Smuzhiyun {
185*4882a593Smuzhiyun 	/* final state */
186*4882a593Smuzhiyun 	return END;
187*4882a593Smuzhiyun }
188*4882a593Smuzhiyun 
189*4882a593Smuzhiyun static enum nvram_parser_state
190*4882a593Smuzhiyun (*nv_parser_states[])(struct nvram_parser *nvp) = {
191*4882a593Smuzhiyun 	brcmf_nvram_handle_idle,
192*4882a593Smuzhiyun 	brcmf_nvram_handle_key,
193*4882a593Smuzhiyun 	brcmf_nvram_handle_value,
194*4882a593Smuzhiyun 	brcmf_nvram_handle_comment,
195*4882a593Smuzhiyun 	brcmf_nvram_handle_end
196*4882a593Smuzhiyun };
197*4882a593Smuzhiyun 
brcmf_init_nvram_parser(struct nvram_parser * nvp,const u8 * data,size_t data_len)198*4882a593Smuzhiyun static int brcmf_init_nvram_parser(struct nvram_parser *nvp,
199*4882a593Smuzhiyun 				   const u8 *data, size_t data_len)
200*4882a593Smuzhiyun {
201*4882a593Smuzhiyun 	size_t size;
202*4882a593Smuzhiyun 
203*4882a593Smuzhiyun 	memset(nvp, 0, sizeof(*nvp));
204*4882a593Smuzhiyun 	nvp->data = data;
205*4882a593Smuzhiyun 	/* Limit size to MAX_NVRAM_SIZE, some files contain lot of comment */
206*4882a593Smuzhiyun 	if (data_len > BRCMF_FW_MAX_NVRAM_SIZE)
207*4882a593Smuzhiyun 		size = BRCMF_FW_MAX_NVRAM_SIZE;
208*4882a593Smuzhiyun 	else
209*4882a593Smuzhiyun 		size = data_len;
210*4882a593Smuzhiyun 	/* Add space for properties we may add */
211*4882a593Smuzhiyun 	size += strlen(BRCMF_FW_DEFAULT_BOARDREV) + 1;
212*4882a593Smuzhiyun 	/* Alloc for extra 0 byte + roundup by 4 + length field */
213*4882a593Smuzhiyun 	size += 1 + 3 + sizeof(u32);
214*4882a593Smuzhiyun 	nvp->nvram = kzalloc(size, GFP_KERNEL);
215*4882a593Smuzhiyun 	if (!nvp->nvram)
216*4882a593Smuzhiyun 		return -ENOMEM;
217*4882a593Smuzhiyun 
218*4882a593Smuzhiyun 	nvp->line = 1;
219*4882a593Smuzhiyun 	nvp->column = 1;
220*4882a593Smuzhiyun 	return 0;
221*4882a593Smuzhiyun }
222*4882a593Smuzhiyun 
223*4882a593Smuzhiyun /* brcmf_fw_strip_multi_v1 :Some nvram files contain settings for multiple
224*4882a593Smuzhiyun  * devices. Strip it down for one device, use domain_nr/bus_nr to determine
225*4882a593Smuzhiyun  * which data is to be returned. v1 is the version where nvram is stored
226*4882a593Smuzhiyun  * compressed and "devpath" maps to index for valid entries.
227*4882a593Smuzhiyun  */
brcmf_fw_strip_multi_v1(struct nvram_parser * nvp,u16 domain_nr,u16 bus_nr)228*4882a593Smuzhiyun static void brcmf_fw_strip_multi_v1(struct nvram_parser *nvp, u16 domain_nr,
229*4882a593Smuzhiyun 				    u16 bus_nr)
230*4882a593Smuzhiyun {
231*4882a593Smuzhiyun 	/* Device path with a leading '=' key-value separator */
232*4882a593Smuzhiyun 	char pci_path[] = "=pci/?/?";
233*4882a593Smuzhiyun 	size_t pci_len;
234*4882a593Smuzhiyun 	char pcie_path[] = "=pcie/?/?";
235*4882a593Smuzhiyun 	size_t pcie_len;
236*4882a593Smuzhiyun 
237*4882a593Smuzhiyun 	u32 i, j;
238*4882a593Smuzhiyun 	bool found;
239*4882a593Smuzhiyun 	u8 *nvram;
240*4882a593Smuzhiyun 	u8 id;
241*4882a593Smuzhiyun 
242*4882a593Smuzhiyun 	nvram = kzalloc(nvp->nvram_len + 1 + 3 + sizeof(u32), GFP_KERNEL);
243*4882a593Smuzhiyun 	if (!nvram)
244*4882a593Smuzhiyun 		goto fail;
245*4882a593Smuzhiyun 
246*4882a593Smuzhiyun 	/* min length: devpath0=pcie/1/4/ + 0:x=y */
247*4882a593Smuzhiyun 	if (nvp->nvram_len < BRCMF_FW_NVRAM_DEVPATH_LEN + 6)
248*4882a593Smuzhiyun 		goto fail;
249*4882a593Smuzhiyun 
250*4882a593Smuzhiyun 	/* First search for the devpathX and see if it is the configuration
251*4882a593Smuzhiyun 	 * for domain_nr/bus_nr. Search complete nvp
252*4882a593Smuzhiyun 	 */
253*4882a593Smuzhiyun 	snprintf(pci_path, sizeof(pci_path), "=pci/%d/%d", domain_nr,
254*4882a593Smuzhiyun 		 bus_nr);
255*4882a593Smuzhiyun 	pci_len = strlen(pci_path);
256*4882a593Smuzhiyun 	snprintf(pcie_path, sizeof(pcie_path), "=pcie/%d/%d", domain_nr,
257*4882a593Smuzhiyun 		 bus_nr);
258*4882a593Smuzhiyun 	pcie_len = strlen(pcie_path);
259*4882a593Smuzhiyun 	found = false;
260*4882a593Smuzhiyun 	i = 0;
261*4882a593Smuzhiyun 	while (i < nvp->nvram_len - BRCMF_FW_NVRAM_DEVPATH_LEN) {
262*4882a593Smuzhiyun 		/* Format: devpathX=pcie/Y/Z/
263*4882a593Smuzhiyun 		 * Y = domain_nr, Z = bus_nr, X = virtual ID
264*4882a593Smuzhiyun 		 */
265*4882a593Smuzhiyun 		if (strncmp(&nvp->nvram[i], "devpath", 7) == 0 &&
266*4882a593Smuzhiyun 		    (!strncmp(&nvp->nvram[i + 8], pci_path, pci_len) ||
267*4882a593Smuzhiyun 		     !strncmp(&nvp->nvram[i + 8], pcie_path, pcie_len))) {
268*4882a593Smuzhiyun 			id = nvp->nvram[i + 7] - '0';
269*4882a593Smuzhiyun 			found = true;
270*4882a593Smuzhiyun 			break;
271*4882a593Smuzhiyun 		}
272*4882a593Smuzhiyun 		while (nvp->nvram[i] != 0)
273*4882a593Smuzhiyun 			i++;
274*4882a593Smuzhiyun 		i++;
275*4882a593Smuzhiyun 	}
276*4882a593Smuzhiyun 	if (!found)
277*4882a593Smuzhiyun 		goto fail;
278*4882a593Smuzhiyun 
279*4882a593Smuzhiyun 	/* Now copy all valid entries, release old nvram and assign new one */
280*4882a593Smuzhiyun 	i = 0;
281*4882a593Smuzhiyun 	j = 0;
282*4882a593Smuzhiyun 	while (i < nvp->nvram_len) {
283*4882a593Smuzhiyun 		if ((nvp->nvram[i] - '0' == id) && (nvp->nvram[i + 1] == ':')) {
284*4882a593Smuzhiyun 			i += 2;
285*4882a593Smuzhiyun 			if (strncmp(&nvp->nvram[i], "boardrev", 8) == 0)
286*4882a593Smuzhiyun 				nvp->boardrev_found = true;
287*4882a593Smuzhiyun 			while (nvp->nvram[i] != 0) {
288*4882a593Smuzhiyun 				nvram[j] = nvp->nvram[i];
289*4882a593Smuzhiyun 				i++;
290*4882a593Smuzhiyun 				j++;
291*4882a593Smuzhiyun 			}
292*4882a593Smuzhiyun 			nvram[j] = 0;
293*4882a593Smuzhiyun 			j++;
294*4882a593Smuzhiyun 		}
295*4882a593Smuzhiyun 		while (nvp->nvram[i] != 0)
296*4882a593Smuzhiyun 			i++;
297*4882a593Smuzhiyun 		i++;
298*4882a593Smuzhiyun 	}
299*4882a593Smuzhiyun 	kfree(nvp->nvram);
300*4882a593Smuzhiyun 	nvp->nvram = nvram;
301*4882a593Smuzhiyun 	nvp->nvram_len = j;
302*4882a593Smuzhiyun 	return;
303*4882a593Smuzhiyun 
304*4882a593Smuzhiyun fail:
305*4882a593Smuzhiyun 	kfree(nvram);
306*4882a593Smuzhiyun 	nvp->nvram_len = 0;
307*4882a593Smuzhiyun }
308*4882a593Smuzhiyun 
309*4882a593Smuzhiyun /* brcmf_fw_strip_multi_v2 :Some nvram files contain settings for multiple
310*4882a593Smuzhiyun  * devices. Strip it down for one device, use domain_nr/bus_nr to determine
311*4882a593Smuzhiyun  * which data is to be returned. v2 is the version where nvram is stored
312*4882a593Smuzhiyun  * uncompressed, all relevant valid entries are identified by
313*4882a593Smuzhiyun  * pcie/domain_nr/bus_nr:
314*4882a593Smuzhiyun  */
brcmf_fw_strip_multi_v2(struct nvram_parser * nvp,u16 domain_nr,u16 bus_nr)315*4882a593Smuzhiyun static void brcmf_fw_strip_multi_v2(struct nvram_parser *nvp, u16 domain_nr,
316*4882a593Smuzhiyun 				    u16 bus_nr)
317*4882a593Smuzhiyun {
318*4882a593Smuzhiyun 	char prefix[BRCMF_FW_NVRAM_PCIEDEV_LEN];
319*4882a593Smuzhiyun 	size_t len;
320*4882a593Smuzhiyun 	u32 i, j;
321*4882a593Smuzhiyun 	u8 *nvram;
322*4882a593Smuzhiyun 
323*4882a593Smuzhiyun 	nvram = kzalloc(nvp->nvram_len + 1 + 3 + sizeof(u32), GFP_KERNEL);
324*4882a593Smuzhiyun 	if (!nvram)
325*4882a593Smuzhiyun 		goto fail;
326*4882a593Smuzhiyun 
327*4882a593Smuzhiyun 	/* Copy all valid entries, release old nvram and assign new one.
328*4882a593Smuzhiyun 	 * Valid entries are of type pcie/X/Y/ where X = domain_nr and
329*4882a593Smuzhiyun 	 * Y = bus_nr.
330*4882a593Smuzhiyun 	 */
331*4882a593Smuzhiyun 	snprintf(prefix, sizeof(prefix), "pcie/%d/%d/", domain_nr, bus_nr);
332*4882a593Smuzhiyun 	len = strlen(prefix);
333*4882a593Smuzhiyun 	i = 0;
334*4882a593Smuzhiyun 	j = 0;
335*4882a593Smuzhiyun 	while (i < nvp->nvram_len - len) {
336*4882a593Smuzhiyun 		if (strncmp(&nvp->nvram[i], prefix, len) == 0) {
337*4882a593Smuzhiyun 			i += len;
338*4882a593Smuzhiyun 			if (strncmp(&nvp->nvram[i], "boardrev", 8) == 0)
339*4882a593Smuzhiyun 				nvp->boardrev_found = true;
340*4882a593Smuzhiyun 			while (nvp->nvram[i] != 0) {
341*4882a593Smuzhiyun 				nvram[j] = nvp->nvram[i];
342*4882a593Smuzhiyun 				i++;
343*4882a593Smuzhiyun 				j++;
344*4882a593Smuzhiyun 			}
345*4882a593Smuzhiyun 			nvram[j] = 0;
346*4882a593Smuzhiyun 			j++;
347*4882a593Smuzhiyun 		}
348*4882a593Smuzhiyun 		while (nvp->nvram[i] != 0)
349*4882a593Smuzhiyun 			i++;
350*4882a593Smuzhiyun 		i++;
351*4882a593Smuzhiyun 	}
352*4882a593Smuzhiyun 	kfree(nvp->nvram);
353*4882a593Smuzhiyun 	nvp->nvram = nvram;
354*4882a593Smuzhiyun 	nvp->nvram_len = j;
355*4882a593Smuzhiyun 	return;
356*4882a593Smuzhiyun fail:
357*4882a593Smuzhiyun 	kfree(nvram);
358*4882a593Smuzhiyun 	nvp->nvram_len = 0;
359*4882a593Smuzhiyun }
360*4882a593Smuzhiyun 
brcmf_fw_add_defaults(struct nvram_parser * nvp)361*4882a593Smuzhiyun static void brcmf_fw_add_defaults(struct nvram_parser *nvp)
362*4882a593Smuzhiyun {
363*4882a593Smuzhiyun 	if (nvp->boardrev_found)
364*4882a593Smuzhiyun 		return;
365*4882a593Smuzhiyun 
366*4882a593Smuzhiyun 	memcpy(&nvp->nvram[nvp->nvram_len], &BRCMF_FW_DEFAULT_BOARDREV,
367*4882a593Smuzhiyun 	       strlen(BRCMF_FW_DEFAULT_BOARDREV));
368*4882a593Smuzhiyun 	nvp->nvram_len += strlen(BRCMF_FW_DEFAULT_BOARDREV);
369*4882a593Smuzhiyun 	nvp->nvram[nvp->nvram_len] = '\0';
370*4882a593Smuzhiyun 	nvp->nvram_len++;
371*4882a593Smuzhiyun }
372*4882a593Smuzhiyun 
373*4882a593Smuzhiyun /* brcmf_nvram_strip :Takes a buffer of "<var>=<value>\n" lines read from a fil
374*4882a593Smuzhiyun  * and ending in a NUL. Removes carriage returns, empty lines, comment lines,
375*4882a593Smuzhiyun  * and converts newlines to NULs. Shortens buffer as needed and pads with NULs.
376*4882a593Smuzhiyun  * End of buffer is completed with token identifying length of buffer.
377*4882a593Smuzhiyun  */
brcmf_fw_nvram_strip(const u8 * data,size_t data_len,u32 * new_length,u16 domain_nr,u16 bus_nr)378*4882a593Smuzhiyun static void *brcmf_fw_nvram_strip(const u8 *data, size_t data_len,
379*4882a593Smuzhiyun 				  u32 *new_length, u16 domain_nr, u16 bus_nr)
380*4882a593Smuzhiyun {
381*4882a593Smuzhiyun 	struct nvram_parser nvp;
382*4882a593Smuzhiyun 	u32 pad;
383*4882a593Smuzhiyun 	u32 token;
384*4882a593Smuzhiyun 	__le32 token_le;
385*4882a593Smuzhiyun 
386*4882a593Smuzhiyun 	if (brcmf_init_nvram_parser(&nvp, data, data_len) < 0)
387*4882a593Smuzhiyun 		return NULL;
388*4882a593Smuzhiyun 
389*4882a593Smuzhiyun 	while (nvp.pos < data_len) {
390*4882a593Smuzhiyun 		nvp.state = nv_parser_states[nvp.state](&nvp);
391*4882a593Smuzhiyun 		if (nvp.state == END)
392*4882a593Smuzhiyun 			break;
393*4882a593Smuzhiyun 	}
394*4882a593Smuzhiyun 	if (nvp.multi_dev_v1) {
395*4882a593Smuzhiyun 		nvp.boardrev_found = false;
396*4882a593Smuzhiyun 		brcmf_fw_strip_multi_v1(&nvp, domain_nr, bus_nr);
397*4882a593Smuzhiyun 	} else if (nvp.multi_dev_v2) {
398*4882a593Smuzhiyun 		nvp.boardrev_found = false;
399*4882a593Smuzhiyun 		brcmf_fw_strip_multi_v2(&nvp, domain_nr, bus_nr);
400*4882a593Smuzhiyun 	}
401*4882a593Smuzhiyun 
402*4882a593Smuzhiyun 	if (nvp.nvram_len == 0) {
403*4882a593Smuzhiyun 		kfree(nvp.nvram);
404*4882a593Smuzhiyun 		return NULL;
405*4882a593Smuzhiyun 	}
406*4882a593Smuzhiyun 
407*4882a593Smuzhiyun 	brcmf_fw_add_defaults(&nvp);
408*4882a593Smuzhiyun 
409*4882a593Smuzhiyun 	pad = nvp.nvram_len;
410*4882a593Smuzhiyun 	*new_length = roundup(nvp.nvram_len + 1, 4);
411*4882a593Smuzhiyun 	while (pad != *new_length) {
412*4882a593Smuzhiyun 		nvp.nvram[pad] = 0;
413*4882a593Smuzhiyun 		pad++;
414*4882a593Smuzhiyun 	}
415*4882a593Smuzhiyun 
416*4882a593Smuzhiyun 	token = *new_length / 4;
417*4882a593Smuzhiyun 	token = (~token << 16) | (token & 0x0000FFFF);
418*4882a593Smuzhiyun 	token_le = cpu_to_le32(token);
419*4882a593Smuzhiyun 
420*4882a593Smuzhiyun 	memcpy(&nvp.nvram[*new_length], &token_le, sizeof(token_le));
421*4882a593Smuzhiyun 	*new_length += sizeof(token_le);
422*4882a593Smuzhiyun 
423*4882a593Smuzhiyun 	return nvp.nvram;
424*4882a593Smuzhiyun }
425*4882a593Smuzhiyun 
brcmf_fw_nvram_free(void * nvram)426*4882a593Smuzhiyun void brcmf_fw_nvram_free(void *nvram)
427*4882a593Smuzhiyun {
428*4882a593Smuzhiyun 	kfree(nvram);
429*4882a593Smuzhiyun }
430*4882a593Smuzhiyun 
431*4882a593Smuzhiyun struct brcmf_fw {
432*4882a593Smuzhiyun 	struct device *dev;
433*4882a593Smuzhiyun 	struct brcmf_fw_request *req;
434*4882a593Smuzhiyun 	u32 curpos;
435*4882a593Smuzhiyun 	void (*done)(struct device *dev, int err, struct brcmf_fw_request *req);
436*4882a593Smuzhiyun };
437*4882a593Smuzhiyun 
438*4882a593Smuzhiyun static void brcmf_fw_request_done(const struct firmware *fw, void *ctx);
439*4882a593Smuzhiyun 
440*4882a593Smuzhiyun #ifdef CONFIG_EFI
441*4882a593Smuzhiyun /* In some cases the EFI-var stored nvram contains "ccode=ALL" or "ccode=XV"
442*4882a593Smuzhiyun  * to specify "worldwide" compatible settings, but these 2 ccode-s do not work
443*4882a593Smuzhiyun  * properly. "ccode=ALL" causes channels 12 and 13 to not be available,
444*4882a593Smuzhiyun  * "ccode=XV" causes all 5GHz channels to not be available. So we replace both
445*4882a593Smuzhiyun  * with "ccode=X2" which allows channels 12+13 and 5Ghz channels in
446*4882a593Smuzhiyun  * no-Initiate-Radiation mode. This means that we will never send on these
447*4882a593Smuzhiyun  * channels without first having received valid wifi traffic on the channel.
448*4882a593Smuzhiyun  */
brcmf_fw_fix_efi_nvram_ccode(char * data,unsigned long data_len)449*4882a593Smuzhiyun static void brcmf_fw_fix_efi_nvram_ccode(char *data, unsigned long data_len)
450*4882a593Smuzhiyun {
451*4882a593Smuzhiyun 	char *ccode;
452*4882a593Smuzhiyun 
453*4882a593Smuzhiyun 	ccode = strnstr((char *)data, "ccode=ALL", data_len);
454*4882a593Smuzhiyun 	if (!ccode)
455*4882a593Smuzhiyun 		ccode = strnstr((char *)data, "ccode=XV\r", data_len);
456*4882a593Smuzhiyun 	if (!ccode)
457*4882a593Smuzhiyun 		return;
458*4882a593Smuzhiyun 
459*4882a593Smuzhiyun 	ccode[6] = 'X';
460*4882a593Smuzhiyun 	ccode[7] = '2';
461*4882a593Smuzhiyun 	ccode[8] = '\r';
462*4882a593Smuzhiyun }
463*4882a593Smuzhiyun 
brcmf_fw_nvram_from_efi(size_t * data_len_ret)464*4882a593Smuzhiyun static u8 *brcmf_fw_nvram_from_efi(size_t *data_len_ret)
465*4882a593Smuzhiyun {
466*4882a593Smuzhiyun 	const u16 name[] = { 'n', 'v', 'r', 'a', 'm', 0 };
467*4882a593Smuzhiyun 	struct efivar_entry *nvram_efivar;
468*4882a593Smuzhiyun 	unsigned long data_len = 0;
469*4882a593Smuzhiyun 	u8 *data = NULL;
470*4882a593Smuzhiyun 	int err;
471*4882a593Smuzhiyun 
472*4882a593Smuzhiyun 	nvram_efivar = kzalloc(sizeof(*nvram_efivar), GFP_KERNEL);
473*4882a593Smuzhiyun 	if (!nvram_efivar)
474*4882a593Smuzhiyun 		return NULL;
475*4882a593Smuzhiyun 
476*4882a593Smuzhiyun 	memcpy(&nvram_efivar->var.VariableName, name, sizeof(name));
477*4882a593Smuzhiyun 	nvram_efivar->var.VendorGuid = EFI_GUID(0x74b00bd9, 0x805a, 0x4d61,
478*4882a593Smuzhiyun 						0xb5, 0x1f, 0x43, 0x26,
479*4882a593Smuzhiyun 						0x81, 0x23, 0xd1, 0x13);
480*4882a593Smuzhiyun 
481*4882a593Smuzhiyun 	err = efivar_entry_size(nvram_efivar, &data_len);
482*4882a593Smuzhiyun 	if (err)
483*4882a593Smuzhiyun 		goto fail;
484*4882a593Smuzhiyun 
485*4882a593Smuzhiyun 	data = kmalloc(data_len, GFP_KERNEL);
486*4882a593Smuzhiyun 	if (!data)
487*4882a593Smuzhiyun 		goto fail;
488*4882a593Smuzhiyun 
489*4882a593Smuzhiyun 	err = efivar_entry_get(nvram_efivar, NULL, &data_len, data);
490*4882a593Smuzhiyun 	if (err)
491*4882a593Smuzhiyun 		goto fail;
492*4882a593Smuzhiyun 
493*4882a593Smuzhiyun 	brcmf_fw_fix_efi_nvram_ccode(data, data_len);
494*4882a593Smuzhiyun 	brcmf_info("Using nvram EFI variable\n");
495*4882a593Smuzhiyun 
496*4882a593Smuzhiyun 	kfree(nvram_efivar);
497*4882a593Smuzhiyun 	*data_len_ret = data_len;
498*4882a593Smuzhiyun 	return data;
499*4882a593Smuzhiyun 
500*4882a593Smuzhiyun fail:
501*4882a593Smuzhiyun 	kfree(data);
502*4882a593Smuzhiyun 	kfree(nvram_efivar);
503*4882a593Smuzhiyun 	return NULL;
504*4882a593Smuzhiyun }
505*4882a593Smuzhiyun #else
brcmf_fw_nvram_from_efi(size_t * data_len)506*4882a593Smuzhiyun static inline u8 *brcmf_fw_nvram_from_efi(size_t *data_len) { return NULL; }
507*4882a593Smuzhiyun #endif
508*4882a593Smuzhiyun 
brcmf_fw_free_request(struct brcmf_fw_request * req)509*4882a593Smuzhiyun static void brcmf_fw_free_request(struct brcmf_fw_request *req)
510*4882a593Smuzhiyun {
511*4882a593Smuzhiyun 	struct brcmf_fw_item *item;
512*4882a593Smuzhiyun 	int i;
513*4882a593Smuzhiyun 
514*4882a593Smuzhiyun 	for (i = 0, item = &req->items[0]; i < req->n_items; i++, item++) {
515*4882a593Smuzhiyun 		if (item->type == BRCMF_FW_TYPE_BINARY)
516*4882a593Smuzhiyun 			release_firmware(item->binary);
517*4882a593Smuzhiyun 		else if (item->type == BRCMF_FW_TYPE_NVRAM)
518*4882a593Smuzhiyun 			brcmf_fw_nvram_free(item->nv_data.data);
519*4882a593Smuzhiyun 	}
520*4882a593Smuzhiyun 	kfree(req);
521*4882a593Smuzhiyun }
522*4882a593Smuzhiyun 
brcmf_fw_request_nvram_done(const struct firmware * fw,void * ctx)523*4882a593Smuzhiyun static int brcmf_fw_request_nvram_done(const struct firmware *fw, void *ctx)
524*4882a593Smuzhiyun {
525*4882a593Smuzhiyun 	struct brcmf_fw *fwctx = ctx;
526*4882a593Smuzhiyun 	struct brcmf_fw_item *cur;
527*4882a593Smuzhiyun 	bool free_bcm47xx_nvram = false;
528*4882a593Smuzhiyun 	bool kfree_nvram = false;
529*4882a593Smuzhiyun 	u32 nvram_length = 0;
530*4882a593Smuzhiyun 	void *nvram = NULL;
531*4882a593Smuzhiyun 	u8 *data = NULL;
532*4882a593Smuzhiyun 	size_t data_len;
533*4882a593Smuzhiyun 
534*4882a593Smuzhiyun 	brcmf_dbg(TRACE, "enter: dev=%s\n", dev_name(fwctx->dev));
535*4882a593Smuzhiyun 
536*4882a593Smuzhiyun 	cur = &fwctx->req->items[fwctx->curpos];
537*4882a593Smuzhiyun 
538*4882a593Smuzhiyun 	if (fw && fw->data) {
539*4882a593Smuzhiyun 		data = (u8 *)fw->data;
540*4882a593Smuzhiyun 		data_len = fw->size;
541*4882a593Smuzhiyun 	} else {
542*4882a593Smuzhiyun 		if ((data = bcm47xx_nvram_get_contents(&data_len)))
543*4882a593Smuzhiyun 			free_bcm47xx_nvram = true;
544*4882a593Smuzhiyun 		else if ((data = brcmf_fw_nvram_from_efi(&data_len)))
545*4882a593Smuzhiyun 			kfree_nvram = true;
546*4882a593Smuzhiyun 		else if (!(cur->flags & BRCMF_FW_REQF_OPTIONAL))
547*4882a593Smuzhiyun 			goto fail;
548*4882a593Smuzhiyun 	}
549*4882a593Smuzhiyun 
550*4882a593Smuzhiyun 	if (data)
551*4882a593Smuzhiyun 		nvram = brcmf_fw_nvram_strip(data, data_len, &nvram_length,
552*4882a593Smuzhiyun 					     fwctx->req->domain_nr,
553*4882a593Smuzhiyun 					     fwctx->req->bus_nr);
554*4882a593Smuzhiyun 
555*4882a593Smuzhiyun 	if (free_bcm47xx_nvram)
556*4882a593Smuzhiyun 		bcm47xx_nvram_release_contents(data);
557*4882a593Smuzhiyun 	if (kfree_nvram)
558*4882a593Smuzhiyun 		kfree(data);
559*4882a593Smuzhiyun 
560*4882a593Smuzhiyun 	release_firmware(fw);
561*4882a593Smuzhiyun 	if (!nvram && !(cur->flags & BRCMF_FW_REQF_OPTIONAL))
562*4882a593Smuzhiyun 		goto fail;
563*4882a593Smuzhiyun 
564*4882a593Smuzhiyun 	brcmf_dbg(TRACE, "nvram %p len %d\n", nvram, nvram_length);
565*4882a593Smuzhiyun 	cur->nv_data.data = nvram;
566*4882a593Smuzhiyun 	cur->nv_data.len = nvram_length;
567*4882a593Smuzhiyun 	return 0;
568*4882a593Smuzhiyun 
569*4882a593Smuzhiyun fail:
570*4882a593Smuzhiyun 	return -ENOENT;
571*4882a593Smuzhiyun }
572*4882a593Smuzhiyun 
brcmf_fw_complete_request(const struct firmware * fw,struct brcmf_fw * fwctx)573*4882a593Smuzhiyun static int brcmf_fw_complete_request(const struct firmware *fw,
574*4882a593Smuzhiyun 				     struct brcmf_fw *fwctx)
575*4882a593Smuzhiyun {
576*4882a593Smuzhiyun 	struct brcmf_fw_item *cur = &fwctx->req->items[fwctx->curpos];
577*4882a593Smuzhiyun 	int ret = 0;
578*4882a593Smuzhiyun 
579*4882a593Smuzhiyun 	brcmf_dbg(TRACE, "firmware %s %sfound\n", cur->path, fw ? "" : "not ");
580*4882a593Smuzhiyun 
581*4882a593Smuzhiyun 	switch (cur->type) {
582*4882a593Smuzhiyun 	case BRCMF_FW_TYPE_NVRAM:
583*4882a593Smuzhiyun 		ret = brcmf_fw_request_nvram_done(fw, fwctx);
584*4882a593Smuzhiyun 		break;
585*4882a593Smuzhiyun 	case BRCMF_FW_TYPE_BINARY:
586*4882a593Smuzhiyun 		if (fw)
587*4882a593Smuzhiyun 			cur->binary = fw;
588*4882a593Smuzhiyun 		else
589*4882a593Smuzhiyun 			ret = -ENOENT;
590*4882a593Smuzhiyun 		break;
591*4882a593Smuzhiyun 	default:
592*4882a593Smuzhiyun 		/* something fishy here so bail out early */
593*4882a593Smuzhiyun 		brcmf_err("unknown fw type: %d\n", cur->type);
594*4882a593Smuzhiyun 		release_firmware(fw);
595*4882a593Smuzhiyun 		ret = -EINVAL;
596*4882a593Smuzhiyun 	}
597*4882a593Smuzhiyun 
598*4882a593Smuzhiyun 	return (cur->flags & BRCMF_FW_REQF_OPTIONAL) ? 0 : ret;
599*4882a593Smuzhiyun }
600*4882a593Smuzhiyun 
brcmf_fw_request_firmware(const struct firmware ** fw,struct brcmf_fw * fwctx)601*4882a593Smuzhiyun static int brcmf_fw_request_firmware(const struct firmware **fw,
602*4882a593Smuzhiyun 				     struct brcmf_fw *fwctx)
603*4882a593Smuzhiyun {
604*4882a593Smuzhiyun 	struct brcmf_fw_item *cur = &fwctx->req->items[fwctx->curpos];
605*4882a593Smuzhiyun 	int ret;
606*4882a593Smuzhiyun 
607*4882a593Smuzhiyun 	/* nvram files are board-specific, first try a board-specific path */
608*4882a593Smuzhiyun 	if (cur->type == BRCMF_FW_TYPE_NVRAM && fwctx->req->board_type) {
609*4882a593Smuzhiyun 		char alt_path[BRCMF_FW_NAME_LEN];
610*4882a593Smuzhiyun 
611*4882a593Smuzhiyun 		strlcpy(alt_path, cur->path, BRCMF_FW_NAME_LEN);
612*4882a593Smuzhiyun 		/* strip .txt at the end */
613*4882a593Smuzhiyun 		alt_path[strlen(alt_path) - 4] = 0;
614*4882a593Smuzhiyun 		strlcat(alt_path, ".", BRCMF_FW_NAME_LEN);
615*4882a593Smuzhiyun 		strlcat(alt_path, fwctx->req->board_type, BRCMF_FW_NAME_LEN);
616*4882a593Smuzhiyun 		strlcat(alt_path, ".txt", BRCMF_FW_NAME_LEN);
617*4882a593Smuzhiyun 
618*4882a593Smuzhiyun 		ret = request_firmware(fw, alt_path, fwctx->dev);
619*4882a593Smuzhiyun 		if (ret == 0)
620*4882a593Smuzhiyun 			return ret;
621*4882a593Smuzhiyun 	}
622*4882a593Smuzhiyun 
623*4882a593Smuzhiyun 	return request_firmware(fw, cur->path, fwctx->dev);
624*4882a593Smuzhiyun }
625*4882a593Smuzhiyun 
brcmf_fw_request_done(const struct firmware * fw,void * ctx)626*4882a593Smuzhiyun static void brcmf_fw_request_done(const struct firmware *fw, void *ctx)
627*4882a593Smuzhiyun {
628*4882a593Smuzhiyun 	struct brcmf_fw *fwctx = ctx;
629*4882a593Smuzhiyun 	int ret;
630*4882a593Smuzhiyun 
631*4882a593Smuzhiyun 	ret = brcmf_fw_complete_request(fw, fwctx);
632*4882a593Smuzhiyun 
633*4882a593Smuzhiyun 	while (ret == 0 && ++fwctx->curpos < fwctx->req->n_items) {
634*4882a593Smuzhiyun 		brcmf_fw_request_firmware(&fw, fwctx);
635*4882a593Smuzhiyun 		ret = brcmf_fw_complete_request(fw, ctx);
636*4882a593Smuzhiyun 	}
637*4882a593Smuzhiyun 
638*4882a593Smuzhiyun 	if (ret) {
639*4882a593Smuzhiyun 		brcmf_fw_free_request(fwctx->req);
640*4882a593Smuzhiyun 		fwctx->req = NULL;
641*4882a593Smuzhiyun 	}
642*4882a593Smuzhiyun 	fwctx->done(fwctx->dev, ret, fwctx->req);
643*4882a593Smuzhiyun 	kfree(fwctx);
644*4882a593Smuzhiyun }
645*4882a593Smuzhiyun 
brcmf_fw_request_is_valid(struct brcmf_fw_request * req)646*4882a593Smuzhiyun static bool brcmf_fw_request_is_valid(struct brcmf_fw_request *req)
647*4882a593Smuzhiyun {
648*4882a593Smuzhiyun 	struct brcmf_fw_item *item;
649*4882a593Smuzhiyun 	int i;
650*4882a593Smuzhiyun 
651*4882a593Smuzhiyun 	if (!req->n_items)
652*4882a593Smuzhiyun 		return false;
653*4882a593Smuzhiyun 
654*4882a593Smuzhiyun 	for (i = 0, item = &req->items[0]; i < req->n_items; i++, item++) {
655*4882a593Smuzhiyun 		if (!item->path)
656*4882a593Smuzhiyun 			return false;
657*4882a593Smuzhiyun 	}
658*4882a593Smuzhiyun 	return true;
659*4882a593Smuzhiyun }
660*4882a593Smuzhiyun 
brcmf_fw_get_firmwares(struct device * dev,struct brcmf_fw_request * req,void (* fw_cb)(struct device * dev,int err,struct brcmf_fw_request * req))661*4882a593Smuzhiyun int brcmf_fw_get_firmwares(struct device *dev, struct brcmf_fw_request *req,
662*4882a593Smuzhiyun 			   void (*fw_cb)(struct device *dev, int err,
663*4882a593Smuzhiyun 					 struct brcmf_fw_request *req))
664*4882a593Smuzhiyun {
665*4882a593Smuzhiyun 	struct brcmf_fw_item *first = &req->items[0];
666*4882a593Smuzhiyun 	struct brcmf_fw *fwctx;
667*4882a593Smuzhiyun 	int ret;
668*4882a593Smuzhiyun 
669*4882a593Smuzhiyun 	brcmf_dbg(TRACE, "enter: dev=%s\n", dev_name(dev));
670*4882a593Smuzhiyun 	if (!fw_cb)
671*4882a593Smuzhiyun 		return -EINVAL;
672*4882a593Smuzhiyun 
673*4882a593Smuzhiyun 	if (!brcmf_fw_request_is_valid(req))
674*4882a593Smuzhiyun 		return -EINVAL;
675*4882a593Smuzhiyun 
676*4882a593Smuzhiyun 	fwctx = kzalloc(sizeof(*fwctx), GFP_KERNEL);
677*4882a593Smuzhiyun 	if (!fwctx)
678*4882a593Smuzhiyun 		return -ENOMEM;
679*4882a593Smuzhiyun 
680*4882a593Smuzhiyun 	fwctx->dev = dev;
681*4882a593Smuzhiyun 	fwctx->req = req;
682*4882a593Smuzhiyun 	fwctx->done = fw_cb;
683*4882a593Smuzhiyun 
684*4882a593Smuzhiyun 	ret = request_firmware_nowait(THIS_MODULE, true, first->path,
685*4882a593Smuzhiyun 				      fwctx->dev, GFP_KERNEL, fwctx,
686*4882a593Smuzhiyun 				      brcmf_fw_request_done);
687*4882a593Smuzhiyun 	if (ret < 0)
688*4882a593Smuzhiyun 		brcmf_fw_request_done(NULL, fwctx);
689*4882a593Smuzhiyun 
690*4882a593Smuzhiyun 	return 0;
691*4882a593Smuzhiyun }
692*4882a593Smuzhiyun 
693*4882a593Smuzhiyun struct brcmf_fw_request *
brcmf_fw_alloc_request(u32 chip,u32 chiprev,const struct brcmf_firmware_mapping mapping_table[],u32 table_size,struct brcmf_fw_name * fwnames,u32 n_fwnames)694*4882a593Smuzhiyun brcmf_fw_alloc_request(u32 chip, u32 chiprev,
695*4882a593Smuzhiyun 		       const struct brcmf_firmware_mapping mapping_table[],
696*4882a593Smuzhiyun 		       u32 table_size, struct brcmf_fw_name *fwnames,
697*4882a593Smuzhiyun 		       u32 n_fwnames)
698*4882a593Smuzhiyun {
699*4882a593Smuzhiyun 	struct brcmf_fw_request *fwreq;
700*4882a593Smuzhiyun 	char chipname[12];
701*4882a593Smuzhiyun 	const char *mp_path;
702*4882a593Smuzhiyun 	size_t mp_path_len;
703*4882a593Smuzhiyun 	u32 i, j;
704*4882a593Smuzhiyun 	char end = '\0';
705*4882a593Smuzhiyun 
706*4882a593Smuzhiyun 	for (i = 0; i < table_size; i++) {
707*4882a593Smuzhiyun 		if (mapping_table[i].chipid == chip &&
708*4882a593Smuzhiyun 		    mapping_table[i].revmask & BIT(chiprev))
709*4882a593Smuzhiyun 			break;
710*4882a593Smuzhiyun 	}
711*4882a593Smuzhiyun 
712*4882a593Smuzhiyun 	brcmf_chip_name(chip, chiprev, chipname, sizeof(chipname));
713*4882a593Smuzhiyun 
714*4882a593Smuzhiyun 	if (i == table_size) {
715*4882a593Smuzhiyun 		brcmf_err("Unknown chip %s\n", chipname);
716*4882a593Smuzhiyun 		return NULL;
717*4882a593Smuzhiyun 	}
718*4882a593Smuzhiyun 
719*4882a593Smuzhiyun 	fwreq = kzalloc(struct_size(fwreq, items, n_fwnames), GFP_KERNEL);
720*4882a593Smuzhiyun 	if (!fwreq)
721*4882a593Smuzhiyun 		return NULL;
722*4882a593Smuzhiyun 
723*4882a593Smuzhiyun 	brcmf_info("using %s for chip %s\n",
724*4882a593Smuzhiyun 		   mapping_table[i].fw_base, chipname);
725*4882a593Smuzhiyun 
726*4882a593Smuzhiyun 	mp_path = brcmf_mp_global.firmware_path;
727*4882a593Smuzhiyun 	mp_path_len = strnlen(mp_path, BRCMF_FW_ALTPATH_LEN);
728*4882a593Smuzhiyun 	if (mp_path_len)
729*4882a593Smuzhiyun 		end = mp_path[mp_path_len - 1];
730*4882a593Smuzhiyun 
731*4882a593Smuzhiyun 	fwreq->n_items = n_fwnames;
732*4882a593Smuzhiyun 
733*4882a593Smuzhiyun 	for (j = 0; j < n_fwnames; j++) {
734*4882a593Smuzhiyun 		fwreq->items[j].path = fwnames[j].path;
735*4882a593Smuzhiyun 		fwnames[j].path[0] = '\0';
736*4882a593Smuzhiyun 		/* check if firmware path is provided by module parameter */
737*4882a593Smuzhiyun 		if (brcmf_mp_global.firmware_path[0] != '\0') {
738*4882a593Smuzhiyun 			strlcpy(fwnames[j].path, mp_path,
739*4882a593Smuzhiyun 				BRCMF_FW_NAME_LEN);
740*4882a593Smuzhiyun 
741*4882a593Smuzhiyun 			if (end != '/') {
742*4882a593Smuzhiyun 				strlcat(fwnames[j].path, "/",
743*4882a593Smuzhiyun 					BRCMF_FW_NAME_LEN);
744*4882a593Smuzhiyun 			}
745*4882a593Smuzhiyun 		}
746*4882a593Smuzhiyun 		strlcat(fwnames[j].path, mapping_table[i].fw_base,
747*4882a593Smuzhiyun 			BRCMF_FW_NAME_LEN);
748*4882a593Smuzhiyun 		strlcat(fwnames[j].path, fwnames[j].extension,
749*4882a593Smuzhiyun 			BRCMF_FW_NAME_LEN);
750*4882a593Smuzhiyun 		fwreq->items[j].path = fwnames[j].path;
751*4882a593Smuzhiyun 	}
752*4882a593Smuzhiyun 
753*4882a593Smuzhiyun 	return fwreq;
754*4882a593Smuzhiyun }
755