root/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. brcmf_feat_debugfs_read
  2. brcmf_feat_debugfs_read
  3. brcmf_feat_firmware_overrides
  4. brcmf_feat_iovar_int_get
  5. brcmf_feat_iovar_data_set
  6. brcmf_feat_firmware_capabilities
  7. brcmf_feat_fwcap_debugfs_read
  8. brcmf_feat_attach
  9. brcmf_feat_debugfs_create
  10. brcmf_feat_is_enabled
  11. brcmf_feat_is_quirk_enabled

   1 // SPDX-License-Identifier: ISC
   2 /*
   3  * Copyright (c) 2014 Broadcom Corporation
   4  */
   5 
   6 #include <linux/netdevice.h>
   7 #include <linux/module.h>
   8 
   9 #include <brcm_hw_ids.h>
  10 #include <brcmu_wifi.h>
  11 #include "core.h"
  12 #include "bus.h"
  13 #include "debug.h"
  14 #include "fwil.h"
  15 #include "fwil_types.h"
  16 #include "feature.h"
  17 #include "common.h"
  18 
  19 #define BRCMF_FW_UNSUPPORTED    23
  20 
  21 /*
  22  * expand feature list to array of feature strings.
  23  */
  24 #define BRCMF_FEAT_DEF(_f) \
  25         #_f,
  26 static const char *brcmf_feat_names[] = {
  27         BRCMF_FEAT_LIST
  28 };
  29 #undef BRCMF_FEAT_DEF
  30 
  31 struct brcmf_feat_fwcap {
  32         enum brcmf_feat_id feature;
  33         const char * const fwcap_id;
  34 };
  35 
  36 static const struct brcmf_feat_fwcap brcmf_fwcap_map[] = {
  37         { BRCMF_FEAT_MBSS, "mbss" },
  38         { BRCMF_FEAT_MCHAN, "mchan" },
  39         { BRCMF_FEAT_P2P, "p2p" },
  40         { BRCMF_FEAT_MONITOR, "monitor" },
  41         { BRCMF_FEAT_MONITOR_FMT_RADIOTAP, "rtap" },
  42         { BRCMF_FEAT_DOT11H, "802.11h" }
  43 };
  44 
  45 #ifdef DEBUG
  46 /*
  47  * expand quirk list to array of quirk strings.
  48  */
  49 #define BRCMF_QUIRK_DEF(_q) \
  50         #_q,
  51 static const char * const brcmf_quirk_names[] = {
  52         BRCMF_QUIRK_LIST
  53 };
  54 #undef BRCMF_QUIRK_DEF
  55 
  56 /**
  57  * brcmf_feat_debugfs_read() - expose feature info to debugfs.
  58  *
  59  * @seq: sequence for debugfs entry.
  60  * @data: raw data pointer.
  61  */
  62 static int brcmf_feat_debugfs_read(struct seq_file *seq, void *data)
  63 {
  64         struct brcmf_bus *bus_if = dev_get_drvdata(seq->private);
  65         u32 feats = bus_if->drvr->feat_flags;
  66         u32 quirks = bus_if->drvr->chip_quirks;
  67         int id;
  68 
  69         seq_printf(seq, "Features: %08x\n", feats);
  70         for (id = 0; id < BRCMF_FEAT_LAST; id++)
  71                 if (feats & BIT(id))
  72                         seq_printf(seq, "\t%s\n", brcmf_feat_names[id]);
  73         seq_printf(seq, "\nQuirks:   %08x\n", quirks);
  74         for (id = 0; id < BRCMF_FEAT_QUIRK_LAST; id++)
  75                 if (quirks & BIT(id))
  76                         seq_printf(seq, "\t%s\n", brcmf_quirk_names[id]);
  77         return 0;
  78 }
  79 #else
  80 static int brcmf_feat_debugfs_read(struct seq_file *seq, void *data)
  81 {
  82         return 0;
  83 }
  84 #endif /* DEBUG */
  85 
  86 struct brcmf_feat_fwfeat {
  87         const char * const fwid;
  88         u32 feat_flags;
  89 };
  90 
  91 static const struct brcmf_feat_fwfeat brcmf_feat_fwfeat_map[] = {
  92         /* brcmfmac43602-pcie.ap.bin from linux-firmware.git commit ea1178515b88 */
  93         { "01-6cb8e269", BIT(BRCMF_FEAT_MONITOR) },
  94         /* brcmfmac4366b-pcie.bin from linux-firmware.git commit 52442afee990 */
  95         { "01-c47a91a4", BIT(BRCMF_FEAT_MONITOR) },
  96         /* brcmfmac4366b-pcie.bin from linux-firmware.git commit 211de1679a68 */
  97         { "01-801fb449", BIT(BRCMF_FEAT_MONITOR_FMT_HW_RX_HDR) },
  98         /* brcmfmac4366c-pcie.bin from linux-firmware.git commit 211de1679a68 */
  99         { "01-d2cbb8fd", BIT(BRCMF_FEAT_MONITOR_FMT_HW_RX_HDR) },
 100 };
 101 
 102 static void brcmf_feat_firmware_overrides(struct brcmf_pub *drv)
 103 {
 104         const struct brcmf_feat_fwfeat *e;
 105         u32 feat_flags = 0;
 106         int i;
 107 
 108         for (i = 0; i < ARRAY_SIZE(brcmf_feat_fwfeat_map); i++) {
 109                 e = &brcmf_feat_fwfeat_map[i];
 110                 if (!strcmp(e->fwid, drv->fwver)) {
 111                         feat_flags = e->feat_flags;
 112                         break;
 113                 }
 114         }
 115 
 116         if (!feat_flags)
 117                 return;
 118 
 119         for (i = 0; i < BRCMF_FEAT_LAST; i++)
 120                 if (feat_flags & BIT(i))
 121                         brcmf_dbg(INFO, "enabling firmware feature: %s\n",
 122                                   brcmf_feat_names[i]);
 123         drv->feat_flags |= feat_flags;
 124 }
 125 
 126 /**
 127  * brcmf_feat_iovar_int_get() - determine feature through iovar query.
 128  *
 129  * @ifp: interface to query.
 130  * @id: feature id.
 131  * @name: iovar name.
 132  */
 133 static void brcmf_feat_iovar_int_get(struct brcmf_if *ifp,
 134                                      enum brcmf_feat_id id, char *name)
 135 {
 136         u32 data;
 137         int err;
 138 
 139         /* we need to know firmware error */
 140         ifp->fwil_fwerr = true;
 141 
 142         err = brcmf_fil_iovar_int_get(ifp, name, &data);
 143         if (err == 0) {
 144                 brcmf_dbg(INFO, "enabling feature: %s\n", brcmf_feat_names[id]);
 145                 ifp->drvr->feat_flags |= BIT(id);
 146         } else {
 147                 brcmf_dbg(TRACE, "%s feature check failed: %d\n",
 148                           brcmf_feat_names[id], err);
 149         }
 150 
 151         ifp->fwil_fwerr = false;
 152 }
 153 
 154 static void brcmf_feat_iovar_data_set(struct brcmf_if *ifp,
 155                                       enum brcmf_feat_id id, char *name,
 156                                       const void *data, size_t len)
 157 {
 158         int err;
 159 
 160         /* we need to know firmware error */
 161         ifp->fwil_fwerr = true;
 162 
 163         err = brcmf_fil_iovar_data_set(ifp, name, data, len);
 164         if (err != -BRCMF_FW_UNSUPPORTED) {
 165                 brcmf_dbg(INFO, "enabling feature: %s\n", brcmf_feat_names[id]);
 166                 ifp->drvr->feat_flags |= BIT(id);
 167         } else {
 168                 brcmf_dbg(TRACE, "%s feature check failed: %d\n",
 169                           brcmf_feat_names[id], err);
 170         }
 171 
 172         ifp->fwil_fwerr = false;
 173 }
 174 
 175 #define MAX_CAPS_BUFFER_SIZE    768
 176 static void brcmf_feat_firmware_capabilities(struct brcmf_if *ifp)
 177 {
 178         struct brcmf_pub *drvr = ifp->drvr;
 179         char caps[MAX_CAPS_BUFFER_SIZE];
 180         enum brcmf_feat_id id;
 181         int i, err;
 182 
 183         err = brcmf_fil_iovar_data_get(ifp, "cap", caps, sizeof(caps));
 184         if (err) {
 185                 bphy_err(drvr, "could not get firmware cap (%d)\n", err);
 186                 return;
 187         }
 188 
 189         brcmf_dbg(INFO, "[ %s]\n", caps);
 190 
 191         for (i = 0; i < ARRAY_SIZE(brcmf_fwcap_map); i++) {
 192                 if (strnstr(caps, brcmf_fwcap_map[i].fwcap_id, sizeof(caps))) {
 193                         id = brcmf_fwcap_map[i].feature;
 194                         brcmf_dbg(INFO, "enabling feature: %s\n",
 195                                   brcmf_feat_names[id]);
 196                         ifp->drvr->feat_flags |= BIT(id);
 197                 }
 198         }
 199 }
 200 
 201 /**
 202  * brcmf_feat_fwcap_debugfs_read() - expose firmware capabilities to debugfs.
 203  *
 204  * @seq: sequence for debugfs entry.
 205  * @data: raw data pointer.
 206  */
 207 static int brcmf_feat_fwcap_debugfs_read(struct seq_file *seq, void *data)
 208 {
 209         struct brcmf_bus *bus_if = dev_get_drvdata(seq->private);
 210         struct brcmf_pub *drvr = bus_if->drvr;
 211         struct brcmf_if *ifp = brcmf_get_ifp(drvr, 0);
 212         char caps[MAX_CAPS_BUFFER_SIZE + 1] = { };
 213         char *tmp;
 214         int err;
 215 
 216         err = brcmf_fil_iovar_data_get(ifp, "cap", caps, sizeof(caps));
 217         if (err) {
 218                 bphy_err(drvr, "could not get firmware cap (%d)\n", err);
 219                 return err;
 220         }
 221 
 222         /* Put every capability in a new line */
 223         for (tmp = caps; *tmp; tmp++) {
 224                 if (*tmp == ' ')
 225                         *tmp = '\n';
 226         }
 227 
 228         /* Usually there is a space at the end of capabilities string */
 229         seq_printf(seq, "%s", caps);
 230         /* So make sure we don't print two line breaks */
 231         if (tmp > caps && *(tmp - 1) != '\n')
 232                 seq_printf(seq, "\n");
 233 
 234         return 0;
 235 }
 236 
 237 void brcmf_feat_attach(struct brcmf_pub *drvr)
 238 {
 239         struct brcmf_if *ifp = brcmf_get_ifp(drvr, 0);
 240         struct brcmf_pno_macaddr_le pfn_mac;
 241         struct brcmf_gscan_config gscan_cfg;
 242         u32 wowl_cap;
 243         s32 err;
 244 
 245         brcmf_feat_firmware_capabilities(ifp);
 246         memset(&gscan_cfg, 0, sizeof(gscan_cfg));
 247         if (drvr->bus_if->chip != BRCM_CC_43430_CHIP_ID &&
 248             drvr->bus_if->chip != BRCM_CC_4345_CHIP_ID)
 249                 brcmf_feat_iovar_data_set(ifp, BRCMF_FEAT_GSCAN,
 250                                           "pfn_gscan_cfg",
 251                                           &gscan_cfg, sizeof(gscan_cfg));
 252         brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_PNO, "pfn");
 253         if (drvr->bus_if->wowl_supported)
 254                 brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_WOWL, "wowl");
 255         if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL)) {
 256                 err = brcmf_fil_iovar_int_get(ifp, "wowl_cap", &wowl_cap);
 257                 if (!err) {
 258                         ifp->drvr->feat_flags |= BIT(BRCMF_FEAT_WOWL_ARP_ND);
 259                         if (wowl_cap & BRCMF_WOWL_PFN_FOUND)
 260                                 ifp->drvr->feat_flags |=
 261                                         BIT(BRCMF_FEAT_WOWL_ND);
 262                         if (wowl_cap & BRCMF_WOWL_GTK_FAILURE)
 263                                 ifp->drvr->feat_flags |=
 264                                         BIT(BRCMF_FEAT_WOWL_GTK);
 265                 }
 266         }
 267         /* MBSS does not work for all chips */
 268         switch (drvr->bus_if->chip) {
 269         case BRCM_CC_4330_CHIP_ID:
 270         case BRCM_CC_43362_CHIP_ID:
 271                 ifp->drvr->feat_flags &= ~BIT(BRCMF_FEAT_MBSS);
 272                 break;
 273         default:
 274                 break;
 275         }
 276         brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_RSDB, "rsdb_mode");
 277         brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_TDLS, "tdls_enable");
 278         brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_MFP, "mfp");
 279 
 280         pfn_mac.version = BRCMF_PFN_MACADDR_CFG_VER;
 281         err = brcmf_fil_iovar_data_get(ifp, "pfn_macaddr", &pfn_mac,
 282                                        sizeof(pfn_mac));
 283         if (!err)
 284                 ifp->drvr->feat_flags |= BIT(BRCMF_FEAT_SCAN_RANDOM_MAC);
 285 
 286         if (drvr->settings->feature_disable) {
 287                 brcmf_dbg(INFO, "Features: 0x%02x, disable: 0x%02x\n",
 288                           ifp->drvr->feat_flags,
 289                           drvr->settings->feature_disable);
 290                 ifp->drvr->feat_flags &= ~drvr->settings->feature_disable;
 291         }
 292         brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_FWSUP, "sup_wpa");
 293 
 294         brcmf_feat_firmware_overrides(drvr);
 295 
 296         /* set chip related quirks */
 297         switch (drvr->bus_if->chip) {
 298         case BRCM_CC_43236_CHIP_ID:
 299                 drvr->chip_quirks |= BIT(BRCMF_FEAT_QUIRK_AUTO_AUTH);
 300                 break;
 301         case BRCM_CC_4329_CHIP_ID:
 302                 drvr->chip_quirks |= BIT(BRCMF_FEAT_QUIRK_NEED_MPC);
 303                 break;
 304         default:
 305                 /* no quirks */
 306                 break;
 307         }
 308 }
 309 
 310 void brcmf_feat_debugfs_create(struct brcmf_pub *drvr)
 311 {
 312         brcmf_debugfs_add_entry(drvr, "features", brcmf_feat_debugfs_read);
 313         brcmf_debugfs_add_entry(drvr, "fwcap", brcmf_feat_fwcap_debugfs_read);
 314 }
 315 
 316 bool brcmf_feat_is_enabled(struct brcmf_if *ifp, enum brcmf_feat_id id)
 317 {
 318         return (ifp->drvr->feat_flags & BIT(id));
 319 }
 320 
 321 bool brcmf_feat_is_quirk_enabled(struct brcmf_if *ifp,
 322                                  enum brcmf_feat_quirk quirk)
 323 {
 324         return (ifp->drvr->chip_quirks & BIT(quirk));
 325 }

/* [<][>][^][v][top][bottom][index][help] */