]> git.ipfire.org Git - people/arne_f/kernel.git/commitdiff
brcmfmac: import rpi fixes
authorArne Fitzenreiter <arne_f@ipfire.org>
Sun, 27 May 2018 09:04:21 +0000 (11:04 +0200)
committerArne Fitzenreiter <arne_f@ipfire.org>
Fri, 14 May 2021 13:17:20 +0000 (15:17 +0200)
Signed-off-by: Arne Fitzenreiter <arne_f@ipfire.org>
drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h
drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c
drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c
drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h
drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.c
drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h
drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c
drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c
drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c
drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c

index 163ddc49f95117774f03184b9f0679bd82d086d2..fb60a7c66e2ce70f5b1da75851cdb74e06833760 100644 (file)
@@ -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,16 @@ int brcmf_bus_get_memdump(struct brcmf_bus *bus, void *data, size_t len)
        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)
+{
+       if (!bus->ops->get_fwname)
+               return -EOPNOTSUPP;
+
+       return bus->ops->get_fwname(bus->dev, chip, chiprev, fw_name);
+}
+
 /*
  * interface functions from common layer
  */
index b5fceba1080626b3cfcd427e84d084a749a50e0d..052c4bdccf5ed57ad3f1062a5775616e856974b5 100644 (file)
@@ -472,47 +472,6 @@ send_key_to_dongle(struct brcmf_if *ifp, struct brcmf_wsec_key *key)
        return err;
 }
 
-static s32
-brcmf_configure_arp_nd_offload(struct brcmf_if *ifp, bool enable)
-{
-       s32 err;
-       u32 mode;
-
-       if (enable)
-               mode = BRCMF_ARP_OL_AGENT | BRCMF_ARP_OL_PEER_AUTO_REPLY;
-       else
-               mode = 0;
-
-       /* Try to set and enable ARP offload feature, this may fail, then it  */
-       /* is simply not supported and err 0 will be returned                 */
-       err = brcmf_fil_iovar_int_set(ifp, "arp_ol", mode);
-       if (err) {
-               brcmf_dbg(TRACE, "failed to set ARP offload mode to 0x%x, err = %d\n",
-                         mode, err);
-               err = 0;
-       } else {
-               err = brcmf_fil_iovar_int_set(ifp, "arpoe", enable);
-               if (err) {
-                       brcmf_dbg(TRACE, "failed to configure (%d) ARP offload err = %d\n",
-                                 enable, err);
-                       err = 0;
-               } else
-                       brcmf_dbg(TRACE, "successfully configured (%d) ARP offload to 0x%x\n",
-                                 enable, mode);
-       }
-
-       err = brcmf_fil_iovar_int_set(ifp, "ndoe", enable);
-       if (err) {
-               brcmf_dbg(TRACE, "failed to configure (%d) ND offload err = %d\n",
-                         enable, err);
-               err = 0;
-       } else
-               brcmf_dbg(TRACE, "successfully configured (%d) ND offload to 0x%x\n",
-                         enable, mode);
-
-       return err;
-}
-
 static void
 brcmf_cfg80211_update_proto_addr_mode(struct wireless_dev *wdev)
 {
@@ -2810,6 +2769,8 @@ brcmf_cfg80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *ndev,
         * preference in cfg struct to apply this to
         * FW later while initializing the dongle
         */
+       pr_info("power management disabled\n");
+       enabled = false;
        cfg->pwr_save = enabled;
        if (!check_vif_up(ifp->vif)) {
 
@@ -6872,12 +6833,18 @@ static s32 brcmf_translate_country_code(struct brcmf_pub *drvr, char alpha2[2],
        struct brcmfmac_pd_cc *country_codes;
        struct brcmfmac_pd_cc_entry *cc;
        s32 found_index;
+       char ccode[BRCMF_COUNTRY_BUF_SZ];
+       int rev;
        int i;
 
+       memcpy(ccode, alpha2, sizeof(ccode));
+       rev = -1;
+
        country_codes = drvr->settings->country_codes;
        if (!country_codes) {
-               brcmf_dbg(TRACE, "No country codes configured for device\n");
-               return -EINVAL;
+               brcmf_dbg(TRACE, "No country codes configured for device"
+                                " - use requested value\n");
+               goto use_input_value;
        }
 
        if ((alpha2[0] == ccreq->country_abbrev[0]) &&
@@ -6901,10 +6868,14 @@ static s32 brcmf_translate_country_code(struct brcmf_pub *drvr, char alpha2[2],
                brcmf_dbg(TRACE, "No country code match found\n");
                return -EINVAL;
        }
-       memset(ccreq, 0, sizeof(*ccreq));
-       ccreq->rev = cpu_to_le32(country_codes->table[found_index].rev);
-       memcpy(ccreq->ccode, country_codes->table[found_index].cc,
+       rev = country_codes->table[found_index].rev;
+       memcpy(ccode, country_codes->table[found_index].cc,
               BRCMF_COUNTRY_BUF_SZ);
+
+use_input_value:
+       memset(ccreq, 0, sizeof(*ccreq));
+       ccreq->rev = cpu_to_le32(rev);
+       memcpy(ccreq->ccode, ccode, sizeof(ccode));
        ccreq->country_abbrev[0] = alpha2[0];
        ccreq->country_abbrev[1] = alpha2[1];
        ccreq->country_abbrev[2] = 0;
@@ -6928,6 +6899,8 @@ static void brcmf_cfg80211_reg_notifier(struct wiphy *wiphy,
        /* ignore non-ISO3166 country codes */
        for (i = 0; i < 2; i++)
                if (req->alpha2[i] < 'A' || req->alpha2[i] > 'Z') {
+                       if (req->alpha2[0] == '0' && req->alpha2[1] == '0')
+                               return;
                        brcmf_err("not an ISO3166 code (0x%02x 0x%02x)\n",
                                  req->alpha2[0], req->alpha2[1]);
                        return;
index 7a2b49587b4d32dde1af56a2979d4e1818500f84..b5889360da96096775db6425337a72156aab6d73 100644 (file)
@@ -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.");
@@ -69,7 +71,7 @@ static int brcmf_fcmode;
 module_param_named(fcmode, brcmf_fcmode, int, 0);
 MODULE_PARM_DESC(fcmode, "Mode of firmware signalled flow control");
 
-static int brcmf_roamoff;
+static int brcmf_roamoff = 1;
 module_param_named(roamoff, brcmf_roamoff, int, S_IRUSR);
 MODULE_PARM_DESC(roamoff, "Do not use internal roaming engine");
 
@@ -104,15 +106,170 @@ void brcmf_c_set_joinpref_default(struct brcmf_if *ifp)
                brcmf_err("Set join_pref error (%d)\n", err);
 }
 
+int brcmf_c_download_2_dongle(struct brcmf_if *ifp, char *dcmd, u16 flag,
+                             u16 dload_type, char *dload_buf, u32 len)
+{
+       struct brcmf_dload_data_le *dload_ptr;
+       u32 dload_data_offset;
+       u16 flags;
+       s32 err;
+
+       dload_ptr = (struct brcmf_dload_data_le *)dload_buf;
+       dload_data_offset = offsetof(struct brcmf_dload_data_le, data);
+       flags = flag | (DLOAD_HANDLER_VER << DLOAD_FLAG_VER_SHIFT);
+
+       dload_ptr->flag = cpu_to_le16(flags);
+       dload_ptr->dload_type = cpu_to_le16(dload_type);
+       dload_ptr->len = cpu_to_le32(len - dload_data_offset);
+       dload_ptr->crc = cpu_to_le32(0);
+       len = len + 8 - (len % 8);
+
+       err = brcmf_fil_iovar_data_set(ifp, dcmd, (void *)dload_buf, len);
+
+       return err;
+}
+
+int brcmf_c_get_clm_name(struct brcmf_if *ifp, u8 *clm_name)
+{
+       struct brcmf_rev_info_le revinfo;
+       struct brcmf_bus *bus = ifp->drvr->bus_if;
+       u8 fw_name[BRCMF_FW_NAME_LEN];
+       u8 *ptr;
+       size_t len;
+       u32 chipnum;
+       u32 chiprev;
+       s32 err;
+
+       err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_REVINFO, &revinfo,
+                                    sizeof(revinfo));
+       if (err < 0) {
+               brcmf_err("retrieving revision info failed (%d)\n", err);
+               goto done;
+       }
+
+       chipnum = le32_to_cpu(revinfo.chipnum);
+       chiprev = le32_to_cpu(revinfo.chiprev);
+
+       memset(fw_name, 0, BRCMF_FW_NAME_LEN);
+       err = brcmf_bus_get_fwname(bus, chipnum, 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, '.');
+       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;
+}
+
+int brcmf_c_process_clm_blob(struct brcmf_if *ifp)
+{
+       struct device *dev = ifp->drvr->bus_if->dev;
+       const struct firmware *clm = NULL;
+       u8 buf[BRCMF_DCMD_SMLEN];
+       u8 clm_name[BRCMF_FW_NAME_LEN];
+       u32 data_offset;
+       u32 size2alloc;
+       u8 *chunk_buf;
+       u32 chunk_len;
+       u32 datalen;
+       u32 cumulative_len = 0;
+       u16 dl_flag = DL_BEGIN;
+       s32 err;
+
+       brcmf_dbg(INFO, "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_direct(&clm, clm_name, dev);
+       if (err) {
+               if (err == -ENOENT)
+                       return 0;
+               brcmf_err("request CLM blob file failed (%d)\n", err);
+               return err;
+       }
+
+       datalen = clm->size;
+       data_offset = offsetof(struct brcmf_dload_data_le, data);
+       size2alloc = data_offset + MAX_CHUNK_LEN;
+
+       chunk_buf = kzalloc(size2alloc, GFP_KERNEL);
+       if (!chunk_buf) {
+               err = -ENOMEM;
+               goto done;
+       }
+
+       do {
+               if (datalen > MAX_CHUNK_LEN) {
+                       chunk_len = MAX_CHUNK_LEN;
+               } else {
+                       chunk_len = datalen;
+                       dl_flag |= DL_END;
+               }
+
+               memcpy(chunk_buf + data_offset, clm->data + cumulative_len,
+                      chunk_len);
+
+               err = brcmf_c_download_2_dongle(ifp, "clmload", dl_flag,
+                                               DL_TYPE_CLM, chunk_buf,
+                                               data_offset + chunk_len);
+
+               dl_flag &= ~DL_BEGIN;
+
+               cumulative_len += chunk_len;
+               datalen -= chunk_len;
+       } while ((datalen > 0) && (err == 0));
+
+       if (err) {
+               brcmf_err("clmload (%d byte file) failed (%d); ",
+                         (u32)clm->size, err);
+               /* Retrieve clmload_status and print */
+               memset(buf, 0, BRCMF_DCMD_SMLEN);
+               err = brcmf_fil_iovar_data_get(ifp, "clmload_status", buf,
+                                              BRCMF_DCMD_SMLEN);
+               if (err)
+                       brcmf_err("get clmload_status failed (%d)\n", err);
+               else
+                       brcmf_err("clmload_status=%d\n", *((int *)buf));
+               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;
 
+       /* 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;
+       }
+
        /* retreive mac address */
        err = brcmf_fil_iovar_data_get(ifp, "cur_etheraddr", ifp->mac_addr,
                                       sizeof(ifp->mac_addr));
@@ -167,6 +324,24 @@ int brcmf_c_preinit_dcmds(struct brcmf_if *ifp)
        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_err("retrieving clmver failed, %d\n", err);
+               goto done;
+       } else {
+               clmver = (char *)buf;
+               /* Replace all newline/linefeed characters with space
+                * character
+                */
+               ptr = clmver;
+               while ((ptr = strchr(ptr, '\n')) != NULL)
+                       *ptr = ' ';
+
+               brcmf_err("CLM version = %s\n", clmver);
+       }
+
        /* set mpc */
        err = brcmf_fil_iovar_int_set(ifp, "mpc", 1);
        if (err) {
index 590bef2defb94eb0ad68440a341b0a0495019c53..6bad533752325f629b6928badf9a4b183ffe5d6c 100644 (file)
@@ -71,6 +71,43 @@ struct brcmf_if *brcmf_get_ifp(struct brcmf_pub *drvr, int ifidx)
        return ifp;
 }
 
+void brcmf_configure_arp_nd_offload(struct brcmf_if *ifp, bool enable)
+{
+       s32 err;
+       u32 mode;
+
+       if (enable)
+               mode = BRCMF_ARP_OL_AGENT | BRCMF_ARP_OL_PEER_AUTO_REPLY;
+       else
+               mode = 0;
+
+       /* Try to set and enable ARP offload feature, this may fail, then it  */
+       /* is simply not supported and err 0 will be returned                 */
+       err = brcmf_fil_iovar_int_set(ifp, "arp_ol", mode);
+       if (err) {
+               brcmf_dbg(TRACE, "failed to set ARP offload mode to 0x%x, err = %d\n",
+                         mode, err);
+               err = 0;
+       } else {
+               err = brcmf_fil_iovar_int_set(ifp, "arpoe", enable);
+               if (err) {
+                       brcmf_dbg(TRACE, "failed to configure (%d) ARP offload err = %d\n",
+                                 enable, err);
+                       err = 0;
+               } else
+                       brcmf_dbg(TRACE, "successfully configured (%d) ARP offload to 0x%x\n",
+                                 enable, mode);
+       }
+
+       err = brcmf_fil_iovar_int_set(ifp, "ndoe", enable);
+       if (err)
+               brcmf_dbg(TRACE, "failed to configure (%d) ND offload err = %d\n",
+                         enable, err);
+       else
+               brcmf_dbg(TRACE, "successfully configured (%d) ND offload to 0x%x\n",
+                         enable, mode);
+}
+
 static void _brcmf_set_multicast_list(struct work_struct *work)
 {
        struct brcmf_if *ifp;
@@ -134,6 +171,8 @@ static void _brcmf_set_multicast_list(struct work_struct *work)
        if (err < 0)
                brcmf_err("Setting BRCMF_C_SET_PROMISC failed, %d\n",
                          err);
+
+       brcmf_configure_arp_nd_offload(ifp, !cmd_value);
 }
 
 #if IS_ENABLED(CONFIG_IPV6)
index a4dd313140f37dc1fbb6955e1148f48bccf22f44..b9a96cbc70f0865ee8c9cff3c61641443e41b7e7 100644 (file)
@@ -204,6 +204,7 @@ int brcmf_netdev_wait_pend8021x(struct brcmf_if *ifp);
 char *brcmf_ifname(struct brcmf_if *ifp);
 struct brcmf_if *brcmf_get_ifp(struct brcmf_pub *drvr, int ifidx);
 int brcmf_net_attach(struct brcmf_if *ifp, bool rtnl_locked);
+void brcmf_configure_arp_nd_offload(struct brcmf_if *ifp, bool enable);
 struct brcmf_if *brcmf_add_if(struct brcmf_pub *drvr, s32 bsscfgidx, s32 ifidx,
                              bool is_p2pdev, const char *name, u8 *mac_addr);
 void brcmf_remove_interface(struct brcmf_if *ifp, bool rtnl_locked);
index ef72baf6dd969c2478a2138ea57b9f691fdf12d8..e7eaa57d11d98f61280bd99013e649aacec7d0d6 100644 (file)
@@ -257,11 +257,6 @@ static void brcmf_fweh_event_worker(struct work_struct *work)
                brcmf_dbg_hex_dump(BRCMF_EVENT_ON(), event->data,
                                   min_t(u32, emsg.datalen, 64),
                                   "event payload, len=%d\n", emsg.datalen);
-               if (emsg.datalen > event->datalen) {
-                       brcmf_err("event invalid length header=%d, msg=%d\n",
-                                 event->datalen, emsg.datalen);
-                       goto event_free;
-               }
 
                /* special handling of interface event */
                if (event->code == BRCMF_E_IF) {
index 05bd636011ec96f050fe33af34a792db65736acd..d22a4db66e2ae25aa4897a20af3c8c28cf2b12d6 100644 (file)
 #define BRCMF_MFP_CAPABLE              1
 #define BRCMF_MFP_REQUIRED             2
 
+/* MAX_CHUNK_LEN is the amount of the clm file we send in each ioctl.
+ * It is relatively small because dongles (FW) have a small maximum size
+ * input payload 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_CRC_NOT_INUSE               0x0001
+#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,
@@ -932,4 +948,15 @@ struct brcmf_gscan_config {
        struct brcmf_gscan_bucket_config bucket[1];
 };
 
+/**
+ * struct brcmf_dload_data_le - data passing to firmware for downloading
+ */
+struct brcmf_dload_data_le {
+       __le16 flag;
+       __le16 dload_type;
+       __le32 len;
+       __le32 crc;
+       u8 data[1];
+};
+
 #endif /* FWIL_TYPES_H_ */
index 4a883f4bbf8850d654d2c737df28e368193217b2..bf5b02d47901540348e5b2e67cca98157b5f468b 100644 (file)
@@ -1863,7 +1863,6 @@ s32 brcmf_p2p_notify_rx_mgmt_p2p_probereq(struct brcmf_if *ifp,
        struct afx_hdl *afx_hdl = &p2p->afx_hdl;
        struct brcmf_cfg80211_vif *vif = ifp->vif;
        struct brcmf_rx_mgmt_data *rxframe = (struct brcmf_rx_mgmt_data *)data;
-       u16 chanspec = be16_to_cpu(rxframe->chanspec);
        struct brcmu_chan ch;
        u8 *mgmt_frame;
        u32 mgmt_frame_len;
@@ -1916,7 +1915,7 @@ s32 brcmf_p2p_notify_rx_mgmt_p2p_probereq(struct brcmf_if *ifp,
        cfg80211_rx_mgmt(&vif->wdev, freq, 0, mgmt_frame, mgmt_frame_len, 0);
 
        brcmf_dbg(INFO, "mgmt_frame_len (%d) , e->datalen (%d), chanspec (%04x), freq (%d)\n",
-                 mgmt_frame_len, e->datalen, chanspec, freq);
+                 mgmt_frame_len, e->datalen, ch.chspec, freq);
 
        return 0;
 }
index e6e9b00b79d71fddec6249be0109c088846bd35f..3c87157f5b8513d7bb00a4602c22859d4b0b1e0f 100644 (file)
@@ -1350,6 +1350,24 @@ static int brcmf_pcie_get_memdump(struct device *dev, void *data, size_t len)
        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_pcie_bus_ops = {
        .wowl_config = brcmf_pcie_wowl_config,
        .get_ramsize = brcmf_pcie_get_ramsize,
        .get_memdump = brcmf_pcie_get_memdump,
+       .get_fwname = brcmf_pcie_get_fwname,
 };
 
 
index d198a8780b96670635c6a74b206b347f4a502028..a4687885bd10bd35dd58c643c7b51d5f59171be8 100644 (file)
@@ -609,6 +609,7 @@ BRCMF_FW_NVRAM_DEF(4329, "brcmfmac4329-sdio.bin", "brcmfmac4329-sdio.txt");
 BRCMF_FW_NVRAM_DEF(4330, "brcmfmac4330-sdio.bin", "brcmfmac4330-sdio.txt");
 BRCMF_FW_NVRAM_DEF(4334, "brcmfmac4334-sdio.bin", "brcmfmac4334-sdio.txt");
 BRCMF_FW_NVRAM_DEF(43340, "brcmfmac43340-sdio.bin", "brcmfmac43340-sdio.txt");
+BRCMF_FW_NVRAM_DEF(43341, "brcmfmac43341-sdio.bin", "brcmfmac43341-sdio.txt");
 BRCMF_FW_NVRAM_DEF(4335, "brcmfmac4335-sdio.bin", "brcmfmac4335-sdio.txt");
 BRCMF_FW_NVRAM_DEF(43362, "brcmfmac43362-sdio.bin", "brcmfmac43362-sdio.txt");
 BRCMF_FW_NVRAM_DEF(4339, "brcmfmac4339-sdio.bin", "brcmfmac4339-sdio.txt");
@@ -629,7 +630,7 @@ static struct brcmf_firmware_mapping brcmf_sdio_fwnames[] = {
        BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4330_CHIP_ID, 0xFFFFFFFF, 4330),
        BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4334_CHIP_ID, 0xFFFFFFFF, 4334),
        BRCMF_FW_NVRAM_ENTRY(BRCM_CC_43340_CHIP_ID, 0xFFFFFFFF, 43340),
-       BRCMF_FW_NVRAM_ENTRY(BRCM_CC_43341_CHIP_ID, 0xFFFFFFFF, 43340),
+       BRCMF_FW_NVRAM_ENTRY(BRCM_CC_43341_CHIP_ID, 0xFFFFFFFF, 43341),
        BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4335_CHIP_ID, 0xFFFFFFFF, 4335),
        BRCMF_FW_NVRAM_ENTRY(BRCM_CC_43362_CHIP_ID, 0xFFFFFFFE, 43362),
        BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4339_CHIP_ID, 0xFFFFFFFF, 4339),
@@ -3980,6 +3981,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,
@@ -3990,6 +4009,7 @@ static const struct brcmf_bus_ops brcmf_sdio_bus_ops = {
        .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,
index 4ad830b7b1c9818b15bda5defc4f162d3e76ce53..3310a73561fc6b5f7a74e2a54ebb0f27a7b9e1af 100644 (file)
@@ -1135,12 +1135,30 @@ static void brcmf_usb_wowl_config(struct device *dev, bool enabled)
                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)