123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434 |
- From fdd0bd88ceaecf729db103ac8836af5805dd2dc1 Mon Sep 17 00:00:00 2001
- From: Chung-Hsien Hsu <[email protected]>
- Date: Fri, 10 Nov 2017 17:27:15 +0800
- Subject: [PATCH] brcmfmac: add CLM download support
- The firmware for brcmfmac devices includes information regarding
- regulatory constraints. For certain devices this information is kept
- separately in a binary form that needs to be downloaded to the device.
- This patch adds support to download this so-called CLM blob file. It
- uses the same naming scheme as the other firmware files with extension
- of .clm_blob.
- The CLM blob file is optional. If the file does not exist, the download
- process will be bypassed. It will not affect the driver loading.
- Reviewed-by: Arend van Spriel <[email protected]>
- Signed-off-by: Chung-Hsien Hsu <[email protected]>
- Signed-off-by: Kalle Valo <[email protected]>
- ---
- .../net/wireless/broadcom/brcm80211/brcmfmac/bus.h | 10 ++
- .../wireless/broadcom/brcm80211/brcmfmac/common.c | 157 +++++++++++++++++++++
- .../wireless/broadcom/brcm80211/brcmfmac/core.c | 2 +
- .../wireless/broadcom/brcm80211/brcmfmac/core.h | 2 +
- .../broadcom/brcm80211/brcmfmac/fwil_types.h | 31 ++++
- .../wireless/broadcom/brcm80211/brcmfmac/pcie.c | 19 +++
- .../wireless/broadcom/brcm80211/brcmfmac/sdio.c | 19 +++
- .../net/wireless/broadcom/brcm80211/brcmfmac/usb.c | 18 +++
- 8 files changed, 258 insertions(+)
- --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h
- +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h
- @@ -71,6 +71,7 @@ struct brcmf_bus_dcmd {
- * @wowl_config: specify if dongle is configured for wowl when going to suspend
- * @get_ramsize: obtain size of device memory.
- * @get_memdump: obtain device memory dump in provided buffer.
- + * @get_fwname: obtain firmware name.
- *
- * This structure provides an abstract interface towards the
- * bus specific driver. For control messages to common driver
- @@ -87,6 +88,8 @@ struct brcmf_bus_ops {
- void (*wowl_config)(struct device *dev, bool enabled);
- size_t (*get_ramsize)(struct device *dev);
- int (*get_memdump)(struct device *dev, void *data, size_t len);
- + int (*get_fwname)(struct device *dev, uint chip, uint chiprev,
- + unsigned char *fw_name);
- };
-
-
- @@ -224,6 +227,13 @@ int brcmf_bus_get_memdump(struct brcmf_b
- return bus->ops->get_memdump(bus->dev, data, len);
- }
-
- +static inline
- +int brcmf_bus_get_fwname(struct brcmf_bus *bus, uint chip, uint chiprev,
- + unsigned char *fw_name)
- +{
- + return bus->ops->get_fwname(bus->dev, chip, chiprev, fw_name);
- +}
- +
- /*
- * interface functions from common layer
- */
- --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c
- +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c
- @@ -18,6 +18,7 @@
- #include <linux/string.h>
- #include <linux/netdevice.h>
- #include <linux/module.h>
- +#include <linux/firmware.h>
- #include <brcmu_wifi.h>
- #include <brcmu_utils.h>
- #include "core.h"
- @@ -28,6 +29,7 @@
- #include "tracepoint.h"
- #include "common.h"
- #include "of.h"
- +#include "firmware.h"
-
- MODULE_AUTHOR("Broadcom Corporation");
- MODULE_DESCRIPTION("Broadcom 802.11 wireless LAN fullmac driver.");
- @@ -104,12 +106,140 @@ void brcmf_c_set_joinpref_default(struct
- brcmf_err("Set join_pref error (%d)\n", err);
- }
-
- +static int brcmf_c_download(struct brcmf_if *ifp, u16 flag,
- + struct brcmf_dload_data_le *dload_buf,
- + u32 len)
- +{
- + s32 err;
- +
- + flag |= (DLOAD_HANDLER_VER << DLOAD_FLAG_VER_SHIFT);
- + dload_buf->flag = cpu_to_le16(flag);
- + dload_buf->dload_type = cpu_to_le16(DL_TYPE_CLM);
- + dload_buf->len = cpu_to_le32(len);
- + dload_buf->crc = cpu_to_le32(0);
- + len = sizeof(*dload_buf) + len - 1;
- +
- + err = brcmf_fil_iovar_data_set(ifp, "clmload", dload_buf, len);
- +
- + return err;
- +}
- +
- +static int brcmf_c_get_clm_name(struct brcmf_if *ifp, u8 *clm_name)
- +{
- + struct brcmf_bus *bus = ifp->drvr->bus_if;
- + struct brcmf_rev_info *ri = &ifp->drvr->revinfo;
- + u8 fw_name[BRCMF_FW_NAME_LEN];
- + u8 *ptr;
- + size_t len;
- + s32 err;
- +
- + memset(fw_name, 0, BRCMF_FW_NAME_LEN);
- + err = brcmf_bus_get_fwname(bus, ri->chipnum, ri->chiprev, fw_name);
- + if (err) {
- + brcmf_err("get firmware name failed (%d)\n", err);
- + goto done;
- + }
- +
- + /* generate CLM blob file name */
- + ptr = strrchr(fw_name, '.');
- + if (!ptr) {
- + err = -ENOENT;
- + goto done;
- + }
- +
- + len = ptr - fw_name + 1;
- + if (len + strlen(".clm_blob") > BRCMF_FW_NAME_LEN) {
- + err = -E2BIG;
- + } else {
- + strlcpy(clm_name, fw_name, len);
- + strlcat(clm_name, ".clm_blob", BRCMF_FW_NAME_LEN);
- + }
- +done:
- + return err;
- +}
- +
- +static int brcmf_c_process_clm_blob(struct brcmf_if *ifp)
- +{
- + struct device *dev = ifp->drvr->bus_if->dev;
- + struct brcmf_dload_data_le *chunk_buf;
- + const struct firmware *clm = NULL;
- + u8 clm_name[BRCMF_FW_NAME_LEN];
- + u32 chunk_len;
- + u32 datalen;
- + u32 cumulative_len;
- + u16 dl_flag = DL_BEGIN;
- + u32 status;
- + s32 err;
- +
- + brcmf_dbg(TRACE, "Enter\n");
- +
- + memset(clm_name, 0, BRCMF_FW_NAME_LEN);
- + err = brcmf_c_get_clm_name(ifp, clm_name);
- + if (err) {
- + brcmf_err("get CLM blob file name failed (%d)\n", err);
- + return err;
- + }
- +
- + err = request_firmware(&clm, clm_name, dev);
- + if (err) {
- + if (err == -ENOENT) {
- + brcmf_dbg(INFO, "continue with CLM data currently present in firmware\n");
- + return 0;
- + }
- + brcmf_err("request CLM blob file failed (%d)\n", err);
- + return err;
- + }
- +
- + chunk_buf = kzalloc(sizeof(*chunk_buf) + MAX_CHUNK_LEN - 1, GFP_KERNEL);
- + if (!chunk_buf) {
- + err = -ENOMEM;
- + goto done;
- + }
- +
- + datalen = clm->size;
- + cumulative_len = 0;
- + do {
- + if (datalen > MAX_CHUNK_LEN) {
- + chunk_len = MAX_CHUNK_LEN;
- + } else {
- + chunk_len = datalen;
- + dl_flag |= DL_END;
- + }
- + memcpy(chunk_buf->data, clm->data + cumulative_len, chunk_len);
- +
- + err = brcmf_c_download(ifp, dl_flag, chunk_buf, chunk_len);
- +
- + dl_flag &= ~DL_BEGIN;
- +
- + cumulative_len += chunk_len;
- + datalen -= chunk_len;
- + } while ((datalen > 0) && (err == 0));
- +
- + if (err) {
- + brcmf_err("clmload (%zu byte file) failed (%d); ",
- + clm->size, err);
- + /* Retrieve clmload_status and print */
- + err = brcmf_fil_iovar_int_get(ifp, "clmload_status", &status);
- + if (err)
- + brcmf_err("get clmload_status failed (%d)\n", err);
- + else
- + brcmf_dbg(INFO, "clmload_status=%d\n", status);
- + err = -EIO;
- + }
- +
- + kfree(chunk_buf);
- +done:
- + release_firmware(clm);
- + return err;
- +}
- +
- int brcmf_c_preinit_dcmds(struct brcmf_if *ifp)
- {
- s8 eventmask[BRCMF_EVENTING_MASK_LEN];
- u8 buf[BRCMF_DCMD_SMLEN];
- struct brcmf_rev_info_le revinfo;
- struct brcmf_rev_info *ri;
- + char *clmver;
- char *ptr;
- s32 err;
-
- @@ -148,6 +278,13 @@ int brcmf_c_preinit_dcmds(struct brcmf_i
- }
- ri->result = err;
-
- + /* Do any CLM downloading */
- + err = brcmf_c_process_clm_blob(ifp);
- + if (err < 0) {
- + brcmf_err("download CLM blob file failed, %d\n", err);
- + goto done;
- + }
- +
- /* query for 'ver' to get version info from firmware */
- memset(buf, 0, sizeof(buf));
- strcpy(buf, "ver");
- @@ -167,6 +304,26 @@ int brcmf_c_preinit_dcmds(struct brcmf_i
- ptr = strrchr(buf, ' ') + 1;
- strlcpy(ifp->drvr->fwver, ptr, sizeof(ifp->drvr->fwver));
-
- + /* Query for 'clmver' to get CLM version info from firmware */
- + memset(buf, 0, sizeof(buf));
- + err = brcmf_fil_iovar_data_get(ifp, "clmver", buf, sizeof(buf));
- + if (err) {
- + brcmf_dbg(TRACE, "retrieving clmver failed, %d\n", err);
- + } else {
- + clmver = (char *)buf;
- + /* store CLM version for adding it to revinfo debugfs file */
- + memcpy(ifp->drvr->clmver, clmver, sizeof(ifp->drvr->clmver));
- +
- + /* Replace all newline/linefeed characters with space
- + * character
- + */
- + ptr = clmver;
- + while ((ptr = strnchr(ptr, '\n', sizeof(buf))) != NULL)
- + *ptr = ' ';
- +
- + brcmf_dbg(INFO, "CLM version = %s\n", clmver);
- + }
- +
- /* set mpc */
- err = brcmf_fil_iovar_int_set(ifp, "mpc", 1);
- if (err) {
- --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c
- +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c
- @@ -1009,6 +1009,8 @@ static int brcmf_revinfo_read(struct seq
- seq_printf(s, "anarev: %u\n", ri->anarev);
- seq_printf(s, "nvramrev: %08x\n", ri->nvramrev);
-
- + seq_printf(s, "clmver: %s\n", bus_if->drvr->clmver);
- +
- return 0;
- }
-
- --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h
- +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h
- @@ -141,6 +141,8 @@ struct brcmf_pub {
- struct notifier_block inetaddr_notifier;
- struct notifier_block inet6addr_notifier;
- struct brcmf_mp_device *settings;
- +
- + u8 clmver[BRCMF_DCMD_SMLEN];
- };
-
- /* forward declarations */
- --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h
- +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h
- @@ -155,6 +155,21 @@
- #define BRCMF_MFP_CAPABLE 1
- #define BRCMF_MFP_REQUIRED 2
-
- +/* MAX_CHUNK_LEN is the maximum length for data passing to firmware in each
- + * ioctl. It is relatively small because firmware has small maximum size input
- + * playload restriction for ioctls.
- + */
- +#define MAX_CHUNK_LEN 1400
- +
- +#define DLOAD_HANDLER_VER 1 /* Downloader version */
- +#define DLOAD_FLAG_VER_MASK 0xf000 /* Downloader version mask */
- +#define DLOAD_FLAG_VER_SHIFT 12 /* Downloader version shift */
- +
- +#define DL_BEGIN 0x0002
- +#define DL_END 0x0004
- +
- +#define DL_TYPE_CLM 2
- +
- /* join preference types for join_pref iovar */
- enum brcmf_join_pref_types {
- BRCMF_JOIN_PREF_RSSI = 1,
- @@ -827,6 +842,22 @@ struct brcmf_pno_macaddr_le {
- };
-
- /**
- + * struct brcmf_dload_data_le - data passing to firmware for downloading
- + * @flag: flags related to download data.
- + * @dload_type: type of download data.
- + * @len: length in bytes of download data.
- + * @crc: crc of download data.
- + * @data: download data.
- + */
- +struct brcmf_dload_data_le {
- + __le16 flag;
- + __le16 dload_type;
- + __le32 len;
- + __le32 crc;
- + u8 data[1];
- +};
- +
- +/**
- * struct brcmf_pno_bssid_le - bssid configuration for PNO scan.
- *
- * @bssid: BSS network identifier.
- --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c
- +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c
- @@ -1350,6 +1350,24 @@ static int brcmf_pcie_get_memdump(struct
- return 0;
- }
-
- +static int brcmf_pcie_get_fwname(struct device *dev, u32 chip, u32 chiprev,
- + u8 *fw_name)
- +{
- + struct brcmf_bus *bus_if = dev_get_drvdata(dev);
- + struct brcmf_pciedev *buspub = bus_if->bus_priv.pcie;
- + struct brcmf_pciedev_info *devinfo = buspub->devinfo;
- + int ret = 0;
- +
- + if (devinfo->fw_name[0] != '\0')
- + strlcpy(fw_name, devinfo->fw_name, BRCMF_FW_NAME_LEN);
- + else
- + ret = brcmf_fw_map_chip_to_name(chip, chiprev,
- + brcmf_pcie_fwnames,
- + ARRAY_SIZE(brcmf_pcie_fwnames),
- + fw_name, NULL);
- +
- + return ret;
- +}
-
- static const struct brcmf_bus_ops brcmf_pcie_bus_ops = {
- .txdata = brcmf_pcie_tx,
- @@ -1359,6 +1377,7 @@ static const struct brcmf_bus_ops brcmf_
- .wowl_config = brcmf_pcie_wowl_config,
- .get_ramsize = brcmf_pcie_get_ramsize,
- .get_memdump = brcmf_pcie_get_memdump,
- + .get_fwname = brcmf_pcie_get_fwname,
- };
-
-
- --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c
- +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c
- @@ -3985,6 +3985,24 @@ brcmf_sdio_watchdog(unsigned long data)
- }
- }
-
- +static int brcmf_sdio_get_fwname(struct device *dev, u32 chip, u32 chiprev,
- + u8 *fw_name)
- +{
- + struct brcmf_bus *bus_if = dev_get_drvdata(dev);
- + struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
- + int ret = 0;
- +
- + if (sdiodev->fw_name[0] != '\0')
- + strlcpy(fw_name, sdiodev->fw_name, BRCMF_FW_NAME_LEN);
- + else
- + ret = brcmf_fw_map_chip_to_name(chip, chiprev,
- + brcmf_sdio_fwnames,
- + ARRAY_SIZE(brcmf_sdio_fwnames),
- + fw_name, NULL);
- +
- + return ret;
- +}
- +
- static const struct brcmf_bus_ops brcmf_sdio_bus_ops = {
- .stop = brcmf_sdio_bus_stop,
- .preinit = brcmf_sdio_bus_preinit,
- @@ -3995,6 +4013,7 @@ static const struct brcmf_bus_ops brcmf_
- .wowl_config = brcmf_sdio_wowl_config,
- .get_ramsize = brcmf_sdio_bus_get_ramsize,
- .get_memdump = brcmf_sdio_bus_get_memdump,
- + .get_fwname = brcmf_sdio_get_fwname,
- };
-
- static void brcmf_sdio_firmware_callback(struct device *dev, int err,
- --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c
- +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c
- @@ -1128,12 +1128,30 @@ static void brcmf_usb_wowl_config(struct
- device_set_wakeup_enable(devinfo->dev, false);
- }
-
- +static int brcmf_usb_get_fwname(struct device *dev, u32 chip, u32 chiprev,
- + u8 *fw_name)
- +{
- + struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(dev);
- + int ret = 0;
- +
- + if (devinfo->fw_name[0] != '\0')
- + strlcpy(fw_name, devinfo->fw_name, BRCMF_FW_NAME_LEN);
- + else
- + ret = brcmf_fw_map_chip_to_name(chip, chiprev,
- + brcmf_usb_fwnames,
- + ARRAY_SIZE(brcmf_usb_fwnames),
- + fw_name, NULL);
- +
- + return ret;
- +}
- +
- static const struct brcmf_bus_ops brcmf_usb_bus_ops = {
- .txdata = brcmf_usb_tx,
- .stop = brcmf_usb_down,
- .txctl = brcmf_usb_tx_ctlpkt,
- .rxctl = brcmf_usb_rx_ctlpkt,
- .wowl_config = brcmf_usb_wowl_config,
- + .get_fwname = brcmf_usb_get_fwname,
- };
-
- static int brcmf_usb_bus_setup(struct brcmf_usbdev_info *devinfo)
|