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
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
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.
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>
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(+)
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.
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);
49 @@ -224,6 +227,13 @@ int brcmf_bus_get_memdump(struct brcmf_b
50 return bus->ops->get_memdump(bus->dev, data, len);
54 +int brcmf_bus_get_fwname(struct brcmf_bus *bus, uint chip, uint chiprev,
55 + unsigned char *fw_name)
57 + return bus->ops->get_fwname(bus->dev, chip, chiprev, fw_name);
61 * interface functions from common layer
63 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c
64 +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c
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>
74 #include "tracepoint.h"
77 +#include "firmware.h"
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);
85 +static int brcmf_c_download(struct brcmf_if *ifp, u16 flag,
86 + struct brcmf_dload_data_le *dload_buf,
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;
98 + err = brcmf_fil_iovar_data_set(ifp, "clmload", dload_buf, len);
103 +static int brcmf_c_get_clm_name(struct brcmf_if *ifp, u8 *clm_name)
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];
112 + memset(fw_name, 0, BRCMF_FW_NAME_LEN);
113 + err = brcmf_bus_get_fwname(bus, ri->chipnum, ri->chiprev, fw_name);
115 + brcmf_err("get firmware name failed (%d)\n", err);
119 + /* generate CLM blob file name */
120 + ptr = strrchr(fw_name, '.');
126 + len = ptr - fw_name + 1;
127 + if (len + strlen(".clm_blob") > BRCMF_FW_NAME_LEN) {
130 + strlcpy(clm_name, fw_name, len);
131 + strlcat(clm_name, ".clm_blob", BRCMF_FW_NAME_LEN);
137 +static int brcmf_c_process_clm_blob(struct brcmf_if *ifp)
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];
145 + u32 cumulative_len;
146 + u16 dl_flag = DL_BEGIN;
150 + brcmf_dbg(TRACE, "Enter\n");
152 + memset(clm_name, 0, BRCMF_FW_NAME_LEN);
153 + err = brcmf_c_get_clm_name(ifp, clm_name);
155 + brcmf_err("get CLM blob file name failed (%d)\n", err);
159 + err = request_firmware(&clm, clm_name, dev);
161 + if (err == -ENOENT) {
162 + brcmf_dbg(INFO, "continue with CLM data currently present in firmware\n");
165 + brcmf_err("request CLM blob file failed (%d)\n", err);
169 + chunk_buf = kzalloc(sizeof(*chunk_buf) + MAX_CHUNK_LEN - 1, GFP_KERNEL);
175 + datalen = clm->size;
176 + cumulative_len = 0;
178 + if (datalen > MAX_CHUNK_LEN) {
179 + chunk_len = MAX_CHUNK_LEN;
181 + chunk_len = datalen;
184 + memcpy(chunk_buf->data, clm->data + cumulative_len, chunk_len);
186 + err = brcmf_c_download(ifp, dl_flag, chunk_buf, chunk_len);
188 + dl_flag &= ~DL_BEGIN;
190 + cumulative_len += chunk_len;
191 + datalen -= chunk_len;
192 + } while ((datalen > 0) && (err == 0));
195 + brcmf_err("clmload (%zu byte file) failed (%d); ",
197 + /* Retrieve clmload_status and print */
198 + err = brcmf_fil_iovar_int_get(ifp, "clmload_status", &status);
200 + brcmf_err("get clmload_status failed (%d)\n", err);
202 + brcmf_dbg(INFO, "clmload_status=%d\n", status);
208 + release_firmware(clm);
212 int brcmf_c_preinit_dcmds(struct brcmf_if *ifp)
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;
222 @@ -148,6 +278,13 @@ int brcmf_c_preinit_dcmds(struct brcmf_i
226 + /* Do any CLM downloading */
227 + err = brcmf_c_process_clm_blob(ifp);
229 + brcmf_err("download CLM blob file failed, %d\n", err);
233 /* query for 'ver' to get version info from firmware */
234 memset(buf, 0, sizeof(buf));
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));
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));
244 + brcmf_dbg(TRACE, "retrieving clmver failed, %d\n", err);
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));
250 + /* Replace all newline/linefeed characters with space
254 + while ((ptr = strnchr(ptr, '\n', sizeof(buf))) != NULL)
257 + brcmf_dbg(INFO, "CLM version = %s\n", clmver);
261 err = brcmf_fil_iovar_int_set(ifp, "mpc", 1);
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);
269 + seq_printf(s, "clmver: %s\n", bus_if->drvr->clmver);
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;
281 + u8 clmver[BRCMF_DCMD_SMLEN];
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
288 #define BRCMF_MFP_CAPABLE 1
289 #define BRCMF_MFP_REQUIRED 2
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.
295 +#define MAX_CHUNK_LEN 1400
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 */
301 +#define DL_BEGIN 0x0002
302 +#define DL_END 0x0004
304 +#define DL_TYPE_CLM 2
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 {
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.
320 +struct brcmf_dload_data_le {
329 * struct brcmf_pno_bssid_le - bssid configuration for PNO scan.
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
338 +static int brcmf_pcie_get_fwname(struct device *dev, u32 chip, u32 chiprev,
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;
346 + if (devinfo->fw_name[0] != '\0')
347 + strlcpy(fw_name, devinfo->fw_name, BRCMF_FW_NAME_LEN);
349 + ret = brcmf_fw_map_chip_to_name(chip, chiprev,
350 + brcmf_pcie_fwnames,
351 + ARRAY_SIZE(brcmf_pcie_fwnames),
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,
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)
373 +static int brcmf_sdio_get_fwname(struct device *dev, u32 chip, u32 chiprev,
376 + struct brcmf_bus *bus_if = dev_get_drvdata(dev);
377 + struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
380 + if (sdiodev->fw_name[0] != '\0')
381 + strlcpy(fw_name, sdiodev->fw_name, BRCMF_FW_NAME_LEN);
383 + ret = brcmf_fw_map_chip_to_name(chip, chiprev,
384 + brcmf_sdio_fwnames,
385 + ARRAY_SIZE(brcmf_sdio_fwnames),
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,
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);
408 +static int brcmf_usb_get_fwname(struct device *dev, u32 chip, u32 chiprev,
411 + struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(dev);
414 + if (devinfo->fw_name[0] != '\0')
415 + strlcpy(fw_name, devinfo->fw_name, BRCMF_FW_NAME_LEN);
417 + ret = brcmf_fw_map_chip_to_name(chip, chiprev,
419 + ARRAY_SIZE(brcmf_usb_fwnames),
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,
434 static int brcmf_usb_bus_setup(struct brcmf_usbdev_info *devinfo)