brcmfmac: introduce feature and quirk handling

Introducing a new source module that will be responsible for
identifying features and quirks related to the device being
handled.

Reviewed-by: Hante Meuleman <meuleman@broadcom.com>
Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com>
Reviewed-by: Daniel (Deognyoun) Kim <dekim@broadcom.com>
Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
Signed-off-by: Arend van Spriel <arend@broadcom.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
This commit is contained in:
Arend van Spriel 2014-07-12 08:49:39 +02:00 committed by John W. Linville
parent ccfd1e810b
commit c08437b4b5
6 changed files with 244 additions and 27 deletions

View File

@ -34,6 +34,7 @@ brcmfmac-objs += \
dhd_common.o \ dhd_common.o \
dhd_linux.o \ dhd_linux.o \
firmware.o \ firmware.o \
feature.o \
btcoex.o \ btcoex.o \
vendor.o vendor.o
brcmfmac-$(CONFIG_BRCMFMAC_SDIO) += \ brcmfmac-$(CONFIG_BRCMFMAC_SDIO) += \

View File

@ -103,6 +103,10 @@ struct brcmf_pub {
struct brcmf_ampdu_rx_reorder struct brcmf_ampdu_rx_reorder
*reorder_flows[BRCMF_AMPDU_RX_REORDER_MAXFLOWS]; *reorder_flows[BRCMF_AMPDU_RX_REORDER_MAXFLOWS];
u32 feat_flags;
u32 chip_quirks;
#ifdef DEBUG #ifdef DEBUG
struct dentry *dbgfs_dir; struct dentry *dbgfs_dir;
#endif #endif
@ -175,7 +179,6 @@ struct brcmf_if *brcmf_add_if(struct brcmf_pub *drvr, s32 bssidx, s32 ifidx,
void brcmf_del_if(struct brcmf_pub *drvr, s32 bssidx); void brcmf_del_if(struct brcmf_pub *drvr, s32 bssidx);
void brcmf_txflowblock_if(struct brcmf_if *ifp, void brcmf_txflowblock_if(struct brcmf_if *ifp,
enum brcmf_netif_stop_reason reason, bool state); enum brcmf_netif_stop_reason reason, bool state);
u32 brcmf_get_chip_info(struct brcmf_if *ifp);
void brcmf_txfinalize(struct brcmf_pub *drvr, struct sk_buff *txp, u8 ifidx, void brcmf_txfinalize(struct brcmf_pub *drvr, struct sk_buff *txp, u8 ifidx,
bool success); bool success);

View File

@ -30,6 +30,7 @@
#include "wl_cfg80211.h" #include "wl_cfg80211.h"
#include "fwil.h" #include "fwil.h"
#include "fwsignal.h" #include "fwsignal.h"
#include "feature.h"
#include "proto.h" #include "proto.h"
MODULE_AUTHOR("Broadcom Corporation"); MODULE_AUTHOR("Broadcom Corporation");
@ -936,6 +937,8 @@ int brcmf_bus_start(struct device *dev)
if (ret < 0) if (ret < 0)
goto fail; goto fail;
brcmf_feat_attach(drvr);
ret = brcmf_fws_init(drvr); ret = brcmf_fws_init(drvr);
if (ret < 0) if (ret < 0)
goto fail; goto fail;
@ -1073,16 +1076,6 @@ int brcmf_netdev_wait_pend8021x(struct net_device *ndev)
return !err; return !err;
} }
/*
* return chip id and rev of the device encoded in u32.
*/
u32 brcmf_get_chip_info(struct brcmf_if *ifp)
{
struct brcmf_bus *bus = ifp->drvr->bus_if;
return bus->chip << 4 | bus->chiprev;
}
static void brcmf_driver_register(struct work_struct *work) static void brcmf_driver_register(struct work_struct *work)
{ {
#ifdef CONFIG_BRCMFMAC_SDIO #ifdef CONFIG_BRCMFMAC_SDIO

View File

@ -0,0 +1,136 @@
/*
* Copyright (c) 2014 Broadcom Corporation
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <linux/netdevice.h>
#include <brcm_hw_ids.h>
#include "dhd.h"
#include "dhd_bus.h"
#include "dhd_dbg.h"
#include "fwil.h"
#include "feature.h"
/*
* firmware error code received if iovar is unsupported.
*/
#define EBRCMF_FEAT_UNSUPPORTED 23
/*
* expand feature list to array of feature strings.
*/
#define BRCMF_FEAT_DEF(_f) \
#_f,
static const char *brcmf_feat_names[] = {
BRCMF_FEAT_LIST
};
#undef BRCMF_FEAT_DEF
#ifdef DEBUG
/*
* expand quirk list to array of quirk strings.
*/
#define BRCMF_QUIRK_DEF(_q) \
#_q,
static const char * const brcmf_quirk_names[] = {
BRCMF_QUIRK_LIST
};
#undef BRCMF_QUIRK_DEF
/**
* brcmf_feat_debugfs_read() - expose feature info to debugfs.
*
* @seq: sequence for debugfs entry.
* @data: raw data pointer.
*/
static int brcmf_feat_debugfs_read(struct seq_file *seq, void *data)
{
struct brcmf_bus *bus_if = dev_get_drvdata(seq->private);
u32 feats = bus_if->drvr->feat_flags;
u32 quirks = bus_if->drvr->chip_quirks;
int id;
seq_printf(seq, "Features: %08x\n", feats);
for (id = 0; id < BRCMF_FEAT_LAST; id++)
if (feats & BIT(id))
seq_printf(seq, "\t%s\n", brcmf_feat_names[id]);
seq_printf(seq, "\nQuirks: %08x\n", quirks);
for (id = 0; id < BRCMF_FEAT_QUIRK_LAST; id++)
if (quirks & BIT(id))
seq_printf(seq, "\t%s\n", brcmf_quirk_names[id]);
return 0;
}
#else
static int brcmf_feat_debugfs_read(struct seq_file *seq, void *data)
{
return 0;
}
#endif /* DEBUG */
/**
* brcmf_feat_iovar_int_get() - determine feature through iovar query.
*
* @ifp: interface to query.
* @id: feature id.
* @name: iovar name.
*/
static void brcmf_feat_iovar_int_get(struct brcmf_if *ifp,
enum brcmf_feat_id id, char *name)
{
u32 data;
int err;
err = brcmf_fil_iovar_int_get(ifp, name, &data);
if (err == 0) {
brcmf_dbg(INFO, "enabling feature: %s\n", brcmf_feat_names[id]);
ifp->drvr->feat_flags |= BIT(id);
} else {
brcmf_dbg(TRACE, "%s feature check failed: %d\n",
brcmf_feat_names[id], err);
}
}
void brcmf_feat_attach(struct brcmf_pub *drvr)
{
struct brcmf_if *ifp = drvr->iflist[0];
brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_MCHAN, "mchan");
/* set chip related quirks */
switch (drvr->bus_if->chip) {
case BRCM_CC_43236_CHIP_ID:
drvr->chip_quirks |= BIT(BRCMF_FEAT_QUIRK_AUTO_AUTH);
break;
case BRCM_CC_4329_CHIP_ID:
drvr->chip_quirks |= BIT(BRCMF_FEAT_QUIRK_NEED_MPC);
break;
default:
/* no quirks */
break;
}
brcmf_debugfs_add_entry(drvr, "features", brcmf_feat_debugfs_read);
}
bool brcmf_feat_is_enabled(struct brcmf_if *ifp, enum brcmf_feat_id id)
{
return (ifp->drvr->feat_flags & BIT(id));
}
bool brcmf_feat_is_quirk_enabled(struct brcmf_if *ifp,
enum brcmf_feat_quirk quirk)
{
return (ifp->drvr->chip_quirks & BIT(quirk));
}

View File

@ -0,0 +1,86 @@
/*
* Copyright (c) 2014 Broadcom Corporation
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef _BRCMF_FEATURE_H
#define _BRCMF_FEATURE_H
/*
* Features:
*
* MCHAN: multi-channel for concurrent P2P.
*/
#define BRCMF_FEAT_LIST \
BRCMF_FEAT_DEF(MCHAN)
/*
* Quirks:
*
* AUTO_AUTH: workaround needed for automatic authentication type.
* NEED_MPC: driver needs to disable MPC during scanning operation.
*/
#define BRCMF_QUIRK_LIST \
BRCMF_QUIRK_DEF(AUTO_AUTH) \
BRCMF_QUIRK_DEF(NEED_MPC)
#define BRCMF_FEAT_DEF(_f) \
BRCMF_FEAT_ ## _f,
/*
* expand feature list to enumeration.
*/
enum brcmf_feat_id {
BRCMF_FEAT_LIST
BRCMF_FEAT_LAST
};
#undef BRCMF_FEAT_DEF
#define BRCMF_QUIRK_DEF(_q) \
BRCMF_FEAT_QUIRK_ ## _q,
/*
* expand quirk list to enumeration.
*/
enum brcmf_feat_quirk {
BRCMF_QUIRK_LIST
BRCMF_FEAT_QUIRK_LAST
};
#undef BRCMF_QUIRK_DEF
/**
* brcmf_feat_attach() - determine features and quirks.
*
* @drvr: driver instance.
*/
void brcmf_feat_attach(struct brcmf_pub *drvr);
/**
* brcmf_feat_is_enabled() - query feature.
*
* @ifp: interface instance.
* @id: feature id to check.
*
* Return: true is feature is enabled; otherwise false.
*/
bool brcmf_feat_is_enabled(struct brcmf_if *ifp, enum brcmf_feat_id id);
/**
* brcmf_feat_is_quirk_enabled() - query chip quirk.
*
* @ifp: interface instance.
* @quirk: quirk id to check.
*
* Return: true is quirk is enabled; otherwise false.
*/
bool brcmf_feat_is_quirk_enabled(struct brcmf_if *ifp,
enum brcmf_feat_quirk quirk);
#endif /* _BRCMF_FEATURE_H */

View File

@ -33,6 +33,7 @@
#include "p2p.h" #include "p2p.h"
#include "btcoex.h" #include "btcoex.h"
#include "wl_cfg80211.h" #include "wl_cfg80211.h"
#include "feature.h"
#include "fwil.h" #include "fwil.h"
#include "vendor.h" #include "vendor.h"
@ -592,7 +593,7 @@ static struct wireless_dev *brcmf_cfg80211_add_iface(struct wiphy *wiphy,
static void brcmf_scan_config_mpc(struct brcmf_if *ifp, int mpc) static void brcmf_scan_config_mpc(struct brcmf_if *ifp, int mpc)
{ {
if ((brcmf_get_chip_info(ifp) >> 4) == 0x4329) if (brcmf_feat_is_quirk_enabled(ifp, BRCMF_FEAT_QUIRK_NEED_MPC))
brcmf_set_mpc(ifp, mpc); brcmf_set_mpc(ifp, mpc);
} }
@ -1619,17 +1620,10 @@ static
enum nl80211_auth_type brcmf_war_auth_type(struct brcmf_if *ifp, enum nl80211_auth_type brcmf_war_auth_type(struct brcmf_if *ifp,
enum nl80211_auth_type type) enum nl80211_auth_type type)
{ {
u32 ci; if (type == NL80211_AUTHTYPE_AUTOMATIC &&
if (type == NL80211_AUTHTYPE_AUTOMATIC) { brcmf_feat_is_quirk_enabled(ifp, BRCMF_FEAT_QUIRK_AUTO_AUTH)) {
/* shift to ignore chip revision */ brcmf_dbg(CONN, "WAR: use OPEN instead of AUTO\n");
ci = brcmf_get_chip_info(ifp) >> 4; type = NL80211_AUTHTYPE_OPEN_SYSTEM;
switch (ci) {
case 43236:
brcmf_dbg(CONN, "43236 WAR: use OPEN instead of AUTO\n");
return NL80211_AUTHTYPE_OPEN_SYSTEM;
default:
break;
}
} }
return type; return type;
} }
@ -4310,10 +4304,10 @@ static const struct ieee80211_iface_limit brcmf_iface_limits[] = {
.types = BIT(NL80211_IFTYPE_P2P_DEVICE) .types = BIT(NL80211_IFTYPE_P2P_DEVICE)
} }
}; };
static const struct ieee80211_iface_combination brcmf_iface_combos[] = { static struct ieee80211_iface_combination brcmf_iface_combos[] = {
{ {
.max_interfaces = BRCMF_IFACE_MAX_CNT, .max_interfaces = BRCMF_IFACE_MAX_CNT,
.num_different_channels = 2, .num_different_channels = 1,
.n_limits = ARRAY_SIZE(brcmf_iface_limits), .n_limits = ARRAY_SIZE(brcmf_iface_limits),
.limits = brcmf_iface_limits .limits = brcmf_iface_limits
} }
@ -4348,7 +4342,8 @@ brcmf_txrx_stypes[NUM_NL80211_IFTYPES] = {
} }
}; };
static struct wiphy *brcmf_setup_wiphy(struct device *phydev) static
struct wiphy *brcmf_setup_wiphy(struct brcmf_if *ifp, struct device *phydev)
{ {
struct wiphy *wiphy; struct wiphy *wiphy;
s32 err = 0; s32 err = 0;
@ -4368,6 +4363,9 @@ static struct wiphy *brcmf_setup_wiphy(struct device *phydev)
BIT(NL80211_IFTYPE_P2P_CLIENT) | BIT(NL80211_IFTYPE_P2P_CLIENT) |
BIT(NL80211_IFTYPE_P2P_GO) | BIT(NL80211_IFTYPE_P2P_GO) |
BIT(NL80211_IFTYPE_P2P_DEVICE); BIT(NL80211_IFTYPE_P2P_DEVICE);
/* need VSDB firmware feature for concurrent channels */
if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MCHAN))
brcmf_iface_combos[0].num_different_channels = 2;
wiphy->iface_combinations = brcmf_iface_combos; wiphy->iface_combinations = brcmf_iface_combos;
wiphy->n_iface_combinations = ARRAY_SIZE(brcmf_iface_combos); wiphy->n_iface_combinations = ARRAY_SIZE(brcmf_iface_combos);
wiphy->bands[IEEE80211_BAND_2GHZ] = &__wl_band_2ghz; wiphy->bands[IEEE80211_BAND_2GHZ] = &__wl_band_2ghz;
@ -5567,7 +5565,7 @@ struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr,
} }
ifp = netdev_priv(ndev); ifp = netdev_priv(ndev);
wiphy = brcmf_setup_wiphy(busdev); wiphy = brcmf_setup_wiphy(ifp, busdev);
if (IS_ERR(wiphy)) if (IS_ERR(wiphy))
return NULL; return NULL;