OpenWrt – Blame information for rev 4
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
4 | office | 1 | From fdd0bd88ceaecf729db103ac8836af5805dd2dc1 Mon Sep 17 00:00:00 2001 |
2 | From: Chung-Hsien Hsu <stanley.hsu@cypress.com> |
||
3 | Date: Fri, 10 Nov 2017 17:27:15 +0800 |
||
4 | Subject: [PATCH] brcmfmac: add CLM download support |
||
5 | |||
6 | The firmware for brcmfmac devices includes information regarding |
||
7 | regulatory constraints. For certain devices this information is kept |
||
8 | separately in a binary form that needs to be downloaded to the device. |
||
9 | This patch adds support to download this so-called CLM blob file. It |
||
10 | uses the same naming scheme as the other firmware files with extension |
||
11 | of .clm_blob. |
||
12 | |||
13 | The CLM blob file is optional. If the file does not exist, the download |
||
14 | process will be bypassed. It will not affect the driver loading. |
||
15 | |||
16 | Reviewed-by: Arend van Spriel <arend.vanspriel@broadcom.com> |
||
17 | Signed-off-by: Chung-Hsien Hsu <stanley.hsu@cypress.com> |
||
18 | Signed-off-by: Kalle Valo <kvalo@codeaurora.org> |
||
19 | --- |
||
20 | .../net/wireless/broadcom/brcm80211/brcmfmac/bus.h | 10 ++ |
||
21 | .../wireless/broadcom/brcm80211/brcmfmac/common.c | 157 +++++++++++++++++++++ |
||
22 | .../wireless/broadcom/brcm80211/brcmfmac/core.c | 2 + |
||
23 | .../wireless/broadcom/brcm80211/brcmfmac/core.h | 2 + |
||
24 | .../broadcom/brcm80211/brcmfmac/fwil_types.h | 31 ++++ |
||
25 | .../wireless/broadcom/brcm80211/brcmfmac/pcie.c | 19 +++ |
||
26 | .../wireless/broadcom/brcm80211/brcmfmac/sdio.c | 19 +++ |
||
27 | .../net/wireless/broadcom/brcm80211/brcmfmac/usb.c | 18 +++ |
||
28 | 8 files changed, 258 insertions(+) |
||
29 | |||
30 | --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h |
||
31 | +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h |
||
32 | @@ -71,6 +71,7 @@ struct brcmf_bus_dcmd { |
||
33 | * @wowl_config: specify if dongle is configured for wowl when going to suspend |
||
34 | * @get_ramsize: obtain size of device memory. |
||
35 | * @get_memdump: obtain device memory dump in provided buffer. |
||
36 | + * @get_fwname: obtain firmware name. |
||
37 | * |
||
38 | * This structure provides an abstract interface towards the |
||
39 | * bus specific driver. For control messages to common driver |
||
40 | @@ -87,6 +88,8 @@ struct brcmf_bus_ops { |
||
41 | void (*wowl_config)(struct device *dev, bool enabled); |
||
42 | size_t (*get_ramsize)(struct device *dev); |
||
43 | int (*get_memdump)(struct device *dev, void *data, size_t len); |
||
44 | + int (*get_fwname)(struct device *dev, uint chip, uint chiprev, |
||
45 | + unsigned char *fw_name); |
||
46 | }; |
||
47 | |||
48 | |||
49 | @@ -224,6 +227,13 @@ int brcmf_bus_get_memdump(struct brcmf_b |
||
50 | return bus->ops->get_memdump(bus->dev, data, len); |
||
51 | } |
||
52 | |||
53 | +static inline |
||
54 | +int brcmf_bus_get_fwname(struct brcmf_bus *bus, uint chip, uint chiprev, |
||
55 | + unsigned char *fw_name) |
||
56 | +{ |
||
57 | + return bus->ops->get_fwname(bus->dev, chip, chiprev, fw_name); |
||
58 | +} |
||
59 | + |
||
60 | /* |
||
61 | * interface functions from common layer |
||
62 | */ |
||
63 | --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c |
||
64 | +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c |
||
65 | @@ -18,6 +18,7 @@ |
||
66 | #include <linux/string.h> |
||
67 | #include <linux/netdevice.h> |
||
68 | #include <linux/module.h> |
||
69 | +#include <linux/firmware.h> |
||
70 | #include <brcmu_wifi.h> |
||
71 | #include <brcmu_utils.h> |
||
72 | #include "core.h" |
||
73 | @@ -28,6 +29,7 @@ |
||
74 | #include "tracepoint.h" |
||
75 | #include "common.h" |
||
76 | #include "of.h" |
||
77 | +#include "firmware.h" |
||
78 | |||
79 | MODULE_AUTHOR("Broadcom Corporation"); |
||
80 | MODULE_DESCRIPTION("Broadcom 802.11 wireless LAN fullmac driver."); |
||
81 | @@ -104,12 +106,140 @@ void brcmf_c_set_joinpref_default(struct |
||
82 | brcmf_err("Set join_pref error (%d)\n", err); |
||
83 | } |
||
84 | |||
85 | +static int brcmf_c_download(struct brcmf_if *ifp, u16 flag, |
||
86 | + struct brcmf_dload_data_le *dload_buf, |
||
87 | + u32 len) |
||
88 | +{ |
||
89 | + s32 err; |
||
90 | + |
||
91 | + flag |= (DLOAD_HANDLER_VER << DLOAD_FLAG_VER_SHIFT); |
||
92 | + dload_buf->flag = cpu_to_le16(flag); |
||
93 | + dload_buf->dload_type = cpu_to_le16(DL_TYPE_CLM); |
||
94 | + dload_buf->len = cpu_to_le32(len); |
||
95 | + dload_buf->crc = cpu_to_le32(0); |
||
96 | + len = sizeof(*dload_buf) + len - 1; |
||
97 | + |
||
98 | + err = brcmf_fil_iovar_data_set(ifp, "clmload", dload_buf, len); |
||
99 | + |
||
100 | + return err; |
||
101 | +} |
||
102 | + |
||
103 | +static int brcmf_c_get_clm_name(struct brcmf_if *ifp, u8 *clm_name) |
||
104 | +{ |
||
105 | + struct brcmf_bus *bus = ifp->drvr->bus_if; |
||
106 | + struct brcmf_rev_info *ri = &ifp->drvr->revinfo; |
||
107 | + u8 fw_name[BRCMF_FW_NAME_LEN]; |
||
108 | + u8 *ptr; |
||
109 | + size_t len; |
||
110 | + s32 err; |
||
111 | + |
||
112 | + memset(fw_name, 0, BRCMF_FW_NAME_LEN); |
||
113 | + err = brcmf_bus_get_fwname(bus, ri->chipnum, ri->chiprev, fw_name); |
||
114 | + if (err) { |
||
115 | + brcmf_err("get firmware name failed (%d)\n", err); |
||
116 | + goto done; |
||
117 | + } |
||
118 | + |
||
119 | + /* generate CLM blob file name */ |
||
120 | + ptr = strrchr(fw_name, '.'); |
||
121 | + if (!ptr) { |
||
122 | + err = -ENOENT; |
||
123 | + goto done; |
||
124 | + } |
||
125 | + |
||
126 | + len = ptr - fw_name + 1; |
||
127 | + if (len + strlen(".clm_blob") > BRCMF_FW_NAME_LEN) { |
||
128 | + err = -E2BIG; |
||
129 | + } else { |
||
130 | + strlcpy(clm_name, fw_name, len); |
||
131 | + strlcat(clm_name, ".clm_blob", BRCMF_FW_NAME_LEN); |
||
132 | + } |
||
133 | +done: |
||
134 | + return err; |
||
135 | +} |
||
136 | + |
||
137 | +static int brcmf_c_process_clm_blob(struct brcmf_if *ifp) |
||
138 | +{ |
||
139 | + struct device *dev = ifp->drvr->bus_if->dev; |
||
140 | + struct brcmf_dload_data_le *chunk_buf; |
||
141 | + const struct firmware *clm = NULL; |
||
142 | + u8 clm_name[BRCMF_FW_NAME_LEN]; |
||
143 | + u32 chunk_len; |
||
144 | + u32 datalen; |
||
145 | + u32 cumulative_len; |
||
146 | + u16 dl_flag = DL_BEGIN; |
||
147 | + u32 status; |
||
148 | + s32 err; |
||
149 | + |
||
150 | + brcmf_dbg(TRACE, "Enter\n"); |
||
151 | + |
||
152 | + memset(clm_name, 0, BRCMF_FW_NAME_LEN); |
||
153 | + err = brcmf_c_get_clm_name(ifp, clm_name); |
||
154 | + if (err) { |
||
155 | + brcmf_err("get CLM blob file name failed (%d)\n", err); |
||
156 | + return err; |
||
157 | + } |
||
158 | + |
||
159 | + err = request_firmware(&clm, clm_name, dev); |
||
160 | + if (err) { |
||
161 | + if (err == -ENOENT) { |
||
162 | + brcmf_dbg(INFO, "continue with CLM data currently present in firmware\n"); |
||
163 | + return 0; |
||
164 | + } |
||
165 | + brcmf_err("request CLM blob file failed (%d)\n", err); |
||
166 | + return err; |
||
167 | + } |
||
168 | + |
||
169 | + chunk_buf = kzalloc(sizeof(*chunk_buf) + MAX_CHUNK_LEN - 1, GFP_KERNEL); |
||
170 | + if (!chunk_buf) { |
||
171 | + err = -ENOMEM; |
||
172 | + goto done; |
||
173 | + } |
||
174 | + |
||
175 | + datalen = clm->size; |
||
176 | + cumulative_len = 0; |
||
177 | + do { |
||
178 | + if (datalen > MAX_CHUNK_LEN) { |
||
179 | + chunk_len = MAX_CHUNK_LEN; |
||
180 | + } else { |
||
181 | + chunk_len = datalen; |
||
182 | + dl_flag |= DL_END; |
||
183 | + } |
||
184 | + memcpy(chunk_buf->data, clm->data + cumulative_len, chunk_len); |
||
185 | + |
||
186 | + err = brcmf_c_download(ifp, dl_flag, chunk_buf, chunk_len); |
||
187 | + |
||
188 | + dl_flag &= ~DL_BEGIN; |
||
189 | + |
||
190 | + cumulative_len += chunk_len; |
||
191 | + datalen -= chunk_len; |
||
192 | + } while ((datalen > 0) && (err == 0)); |
||
193 | + |
||
194 | + if (err) { |
||
195 | + brcmf_err("clmload (%zu byte file) failed (%d); ", |
||
196 | + clm->size, err); |
||
197 | + /* Retrieve clmload_status and print */ |
||
198 | + err = brcmf_fil_iovar_int_get(ifp, "clmload_status", &status); |
||
199 | + if (err) |
||
200 | + brcmf_err("get clmload_status failed (%d)\n", err); |
||
201 | + else |
||
202 | + brcmf_dbg(INFO, "clmload_status=%d\n", status); |
||
203 | + err = -EIO; |
||
204 | + } |
||
205 | + |
||
206 | + kfree(chunk_buf); |
||
207 | +done: |
||
208 | + release_firmware(clm); |
||
209 | + return err; |
||
210 | +} |
||
211 | + |
||
212 | int brcmf_c_preinit_dcmds(struct brcmf_if *ifp) |
||
213 | { |
||
214 | s8 eventmask[BRCMF_EVENTING_MASK_LEN]; |
||
215 | u8 buf[BRCMF_DCMD_SMLEN]; |
||
216 | struct brcmf_rev_info_le revinfo; |
||
217 | struct brcmf_rev_info *ri; |
||
218 | + char *clmver; |
||
219 | char *ptr; |
||
220 | s32 err; |
||
221 | |||
222 | @@ -148,6 +278,13 @@ int brcmf_c_preinit_dcmds(struct brcmf_i |
||
223 | } |
||
224 | ri->result = err; |
||
225 | |||
226 | + /* Do any CLM downloading */ |
||
227 | + err = brcmf_c_process_clm_blob(ifp); |
||
228 | + if (err < 0) { |
||
229 | + brcmf_err("download CLM blob file failed, %d\n", err); |
||
230 | + goto done; |
||
231 | + } |
||
232 | + |
||
233 | /* query for 'ver' to get version info from firmware */ |
||
234 | memset(buf, 0, sizeof(buf)); |
||
235 | strcpy(buf, "ver"); |
||
236 | @@ -167,6 +304,26 @@ int brcmf_c_preinit_dcmds(struct brcmf_i |
||
237 | ptr = strrchr(buf, ' ') + 1; |
||
238 | strlcpy(ifp->drvr->fwver, ptr, sizeof(ifp->drvr->fwver)); |
||
239 | |||
240 | + /* Query for 'clmver' to get CLM version info from firmware */ |
||
241 | + memset(buf, 0, sizeof(buf)); |
||
242 | + err = brcmf_fil_iovar_data_get(ifp, "clmver", buf, sizeof(buf)); |
||
243 | + if (err) { |
||
244 | + brcmf_dbg(TRACE, "retrieving clmver failed, %d\n", err); |
||
245 | + } else { |
||
246 | + clmver = (char *)buf; |
||
247 | + /* store CLM version for adding it to revinfo debugfs file */ |
||
248 | + memcpy(ifp->drvr->clmver, clmver, sizeof(ifp->drvr->clmver)); |
||
249 | + |
||
250 | + /* Replace all newline/linefeed characters with space |
||
251 | + * character |
||
252 | + */ |
||
253 | + ptr = clmver; |
||
254 | + while ((ptr = strnchr(ptr, '\n', sizeof(buf))) != NULL) |
||
255 | + *ptr = ' '; |
||
256 | + |
||
257 | + brcmf_dbg(INFO, "CLM version = %s\n", clmver); |
||
258 | + } |
||
259 | + |
||
260 | /* set mpc */ |
||
261 | err = brcmf_fil_iovar_int_set(ifp, "mpc", 1); |
||
262 | if (err) { |
||
263 | --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c |
||
264 | +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c |
||
265 | @@ -1009,6 +1009,8 @@ static int brcmf_revinfo_read(struct seq |
||
266 | seq_printf(s, "anarev: %u\n", ri->anarev); |
||
267 | seq_printf(s, "nvramrev: %08x\n", ri->nvramrev); |
||
268 | |||
269 | + seq_printf(s, "clmver: %s\n", bus_if->drvr->clmver); |
||
270 | + |
||
271 | return 0; |
||
272 | } |
||
273 | |||
274 | --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h |
||
275 | +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h |
||
276 | @@ -141,6 +141,8 @@ struct brcmf_pub { |
||
277 | struct notifier_block inetaddr_notifier; |
||
278 | struct notifier_block inet6addr_notifier; |
||
279 | struct brcmf_mp_device *settings; |
||
280 | + |
||
281 | + u8 clmver[BRCMF_DCMD_SMLEN]; |
||
282 | }; |
||
283 | |||
284 | /* forward declarations */ |
||
285 | --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h |
||
286 | +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h |
||
287 | @@ -155,6 +155,21 @@ |
||
288 | #define BRCMF_MFP_CAPABLE 1 |
||
289 | #define BRCMF_MFP_REQUIRED 2 |
||
290 | |||
291 | +/* MAX_CHUNK_LEN is the maximum length for data passing to firmware in each |
||
292 | + * ioctl. It is relatively small because firmware has small maximum size input |
||
293 | + * playload restriction for ioctls. |
||
294 | + */ |
||
295 | +#define MAX_CHUNK_LEN 1400 |
||
296 | + |
||
297 | +#define DLOAD_HANDLER_VER 1 /* Downloader version */ |
||
298 | +#define DLOAD_FLAG_VER_MASK 0xf000 /* Downloader version mask */ |
||
299 | +#define DLOAD_FLAG_VER_SHIFT 12 /* Downloader version shift */ |
||
300 | + |
||
301 | +#define DL_BEGIN 0x0002 |
||
302 | +#define DL_END 0x0004 |
||
303 | + |
||
304 | +#define DL_TYPE_CLM 2 |
||
305 | + |
||
306 | /* join preference types for join_pref iovar */ |
||
307 | enum brcmf_join_pref_types { |
||
308 | BRCMF_JOIN_PREF_RSSI = 1, |
||
309 | @@ -827,6 +842,22 @@ struct brcmf_pno_macaddr_le { |
||
310 | }; |
||
311 | |||
312 | /** |
||
313 | + * struct brcmf_dload_data_le - data passing to firmware for downloading |
||
314 | + * @flag: flags related to download data. |
||
315 | + * @dload_type: type of download data. |
||
316 | + * @len: length in bytes of download data. |
||
317 | + * @crc: crc of download data. |
||
318 | + * @data: download data. |
||
319 | + */ |
||
320 | +struct brcmf_dload_data_le { |
||
321 | + __le16 flag; |
||
322 | + __le16 dload_type; |
||
323 | + __le32 len; |
||
324 | + __le32 crc; |
||
325 | + u8 data[1]; |
||
326 | +}; |
||
327 | + |
||
328 | +/** |
||
329 | * struct brcmf_pno_bssid_le - bssid configuration for PNO scan. |
||
330 | * |
||
331 | * @bssid: BSS network identifier. |
||
332 | --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c |
||
333 | +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c |
||
334 | @@ -1350,6 +1350,24 @@ static int brcmf_pcie_get_memdump(struct |
||
335 | return 0; |
||
336 | } |
||
337 | |||
338 | +static int brcmf_pcie_get_fwname(struct device *dev, u32 chip, u32 chiprev, |
||
339 | + u8 *fw_name) |
||
340 | +{ |
||
341 | + struct brcmf_bus *bus_if = dev_get_drvdata(dev); |
||
342 | + struct brcmf_pciedev *buspub = bus_if->bus_priv.pcie; |
||
343 | + struct brcmf_pciedev_info *devinfo = buspub->devinfo; |
||
344 | + int ret = 0; |
||
345 | + |
||
346 | + if (devinfo->fw_name[0] != '\0') |
||
347 | + strlcpy(fw_name, devinfo->fw_name, BRCMF_FW_NAME_LEN); |
||
348 | + else |
||
349 | + ret = brcmf_fw_map_chip_to_name(chip, chiprev, |
||
350 | + brcmf_pcie_fwnames, |
||
351 | + ARRAY_SIZE(brcmf_pcie_fwnames), |
||
352 | + fw_name, NULL); |
||
353 | + |
||
354 | + return ret; |
||
355 | +} |
||
356 | |||
357 | static const struct brcmf_bus_ops brcmf_pcie_bus_ops = { |
||
358 | .txdata = brcmf_pcie_tx, |
||
359 | @@ -1359,6 +1377,7 @@ static const struct brcmf_bus_ops brcmf_ |
||
360 | .wowl_config = brcmf_pcie_wowl_config, |
||
361 | .get_ramsize = brcmf_pcie_get_ramsize, |
||
362 | .get_memdump = brcmf_pcie_get_memdump, |
||
363 | + .get_fwname = brcmf_pcie_get_fwname, |
||
364 | }; |
||
365 | |||
366 | |||
367 | --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c |
||
368 | +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c |
||
369 | @@ -3985,6 +3985,24 @@ brcmf_sdio_watchdog(unsigned long data) |
||
370 | } |
||
371 | } |
||
372 | |||
373 | +static int brcmf_sdio_get_fwname(struct device *dev, u32 chip, u32 chiprev, |
||
374 | + u8 *fw_name) |
||
375 | +{ |
||
376 | + struct brcmf_bus *bus_if = dev_get_drvdata(dev); |
||
377 | + struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; |
||
378 | + int ret = 0; |
||
379 | + |
||
380 | + if (sdiodev->fw_name[0] != '\0') |
||
381 | + strlcpy(fw_name, sdiodev->fw_name, BRCMF_FW_NAME_LEN); |
||
382 | + else |
||
383 | + ret = brcmf_fw_map_chip_to_name(chip, chiprev, |
||
384 | + brcmf_sdio_fwnames, |
||
385 | + ARRAY_SIZE(brcmf_sdio_fwnames), |
||
386 | + fw_name, NULL); |
||
387 | + |
||
388 | + return ret; |
||
389 | +} |
||
390 | + |
||
391 | static const struct brcmf_bus_ops brcmf_sdio_bus_ops = { |
||
392 | .stop = brcmf_sdio_bus_stop, |
||
393 | .preinit = brcmf_sdio_bus_preinit, |
||
394 | @@ -3995,6 +4013,7 @@ static const struct brcmf_bus_ops brcmf_ |
||
395 | .wowl_config = brcmf_sdio_wowl_config, |
||
396 | .get_ramsize = brcmf_sdio_bus_get_ramsize, |
||
397 | .get_memdump = brcmf_sdio_bus_get_memdump, |
||
398 | + .get_fwname = brcmf_sdio_get_fwname, |
||
399 | }; |
||
400 | |||
401 | static void brcmf_sdio_firmware_callback(struct device *dev, int err, |
||
402 | --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c |
||
403 | +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c |
||
404 | @@ -1128,12 +1128,30 @@ static void brcmf_usb_wowl_config(struct |
||
405 | device_set_wakeup_enable(devinfo->dev, false); |
||
406 | } |
||
407 | |||
408 | +static int brcmf_usb_get_fwname(struct device *dev, u32 chip, u32 chiprev, |
||
409 | + u8 *fw_name) |
||
410 | +{ |
||
411 | + struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(dev); |
||
412 | + int ret = 0; |
||
413 | + |
||
414 | + if (devinfo->fw_name[0] != '\0') |
||
415 | + strlcpy(fw_name, devinfo->fw_name, BRCMF_FW_NAME_LEN); |
||
416 | + else |
||
417 | + ret = brcmf_fw_map_chip_to_name(chip, chiprev, |
||
418 | + brcmf_usb_fwnames, |
||
419 | + ARRAY_SIZE(brcmf_usb_fwnames), |
||
420 | + fw_name, NULL); |
||
421 | + |
||
422 | + return ret; |
||
423 | +} |
||
424 | + |
||
425 | static const struct brcmf_bus_ops brcmf_usb_bus_ops = { |
||
426 | .txdata = brcmf_usb_tx, |
||
427 | .stop = brcmf_usb_down, |
||
428 | .txctl = brcmf_usb_tx_ctlpkt, |
||
429 | .rxctl = brcmf_usb_rx_ctlpkt, |
||
430 | .wowl_config = brcmf_usb_wowl_config, |
||
431 | + .get_fwname = brcmf_usb_get_fwname, |
||
432 | }; |
||
433 | |||
434 | static int brcmf_usb_bus_setup(struct brcmf_usbdev_info *devinfo) |