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