summaryrefslogtreecommitdiff
path: root/drivers/net/wireless/brcm80211/brcmfmac
diff options
context:
space:
mode:
authorDavid S. Miller <davem@davemloft.net>2014-07-28 17:36:25 -0700
committerDavid S. Miller <davem@davemloft.net>2014-07-28 17:36:25 -0700
commit3fd0202a0dfe07d255c5462d7d0e27673ca10430 (patch)
tree126483df9ff404e0d31cdcad18ad4280df06d89f /drivers/net/wireless/brcm80211/brcmfmac
parenta67eed571aa505f51a4d02cab765a9d4f6ef80c4 (diff)
parent9a244409d0b0cf3b1e46f1dc331f2c718597fae0 (diff)
downloadlinux-3fd0202a0dfe07d255c5462d7d0e27673ca10430.tar.gz
Merge tag 'master-2014-07-25' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-next
John W. Linville says: ==================== pull request: wireless-next 2014-07-25 Please pull this batch of updates intended for the 3.17 stream! For the mac80211 bits, Johannes says: "We have a lot of TDLS patches, among them a fix that should make hwsim tests happy again. The rest, this time, is mostly small fixes." For the Bluetooth bits, Gustavo says: "Some more patches for 3.17. The most important change here is the move of the 6lowpan code to net/6lowpan. It has been agreed with Davem that this change will go through the bluetooth tree. The rest are mostly clean up and fixes." and, "Here follows some more patches for 3.17. These are mostly fixes to what we've sent to you before for next merge window." For the iwlwifi bits, Emmanuel says: "I have the usual amount of BT Coex stuff. Arik continues to work on TDLS and Ariej contributes a few things for HS2.0. I added a few more things to the firmware debugging infrastructure. Eran fixes a small bug - pretty normal content." And for the Atheros bits, Kalle says: "For ath6kl me and Jessica added support for ar6004 hw3.0, our latest version of ar6004. For ath10k Janusz added a printout so that it's easier to check what ath10k kconfig options are enabled. He also added a debugfs file to configure maximum amsdu and ampdu values. Also we had few fixes as usual." On top of that is the usual large batch of various driver updates -- brcmfmac, mwifiex, the TI drivers, and wil6210 all get some action. RafaƂ has also been very busy with b43 and related updates. Also, I pulled the wireless tree into this in order to resolve a merge conflict... P.S. The change to fs/compat_ioctl.c reflects a name change in a Bluetooth header file... ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/wireless/brcm80211/brcmfmac')
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/Makefile1
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c23
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/chip.c28
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/dhd.h5
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.c196
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h74
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c13
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c274
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/feature.c136
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/feature.h86
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/firmware.c5
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/firmware.h5
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c100
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h4
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/usb.c49
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c958
16 files changed, 1082 insertions, 875 deletions
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/Makefile b/drivers/net/wireless/brcm80211/brcmfmac/Makefile
index 4cffb2ee3673..de0cff3df389 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/Makefile
+++ b/drivers/net/wireless/brcm80211/brcmfmac/Makefile
@@ -34,6 +34,7 @@ brcmfmac-objs += \
dhd_common.o \
dhd_linux.o \
firmware.o \
+ feature.o \
btcoex.o \
vendor.o
brcmfmac-$(CONFIG_BRCMFMAC_SDIO) += \
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c
index a16e644e7c08..f467cafe3e8f 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c
@@ -25,7 +25,6 @@
#include <linux/mmc/sdio.h>
#include <linux/mmc/core.h>
#include <linux/mmc/sdio_func.h>
-#include <linux/mmc/sdio_ids.h>
#include <linux/mmc/card.h>
#include <linux/mmc/host.h>
#include <linux/platform_device.h>
@@ -979,18 +978,20 @@ out:
return ret;
}
+#define BRCMF_SDIO_DEVICE(dev_id) \
+ {SDIO_DEVICE(BRCM_SDIO_VENDOR_ID_BROADCOM, dev_id)}
+
/* devices we support, null terminated */
static const struct sdio_device_id brcmf_sdmmc_ids[] = {
- {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_43143)},
- {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_43241)},
- {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4329)},
- {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4330)},
- {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4334)},
- {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_43362)},
- {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM,
- SDIO_DEVICE_ID_BROADCOM_4335_4339)},
- {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4354)},
- { /* end: all zeroes */ },
+ BRCMF_SDIO_DEVICE(BRCM_SDIO_43143_DEVICE_ID),
+ BRCMF_SDIO_DEVICE(BRCM_SDIO_43241_DEVICE_ID),
+ BRCMF_SDIO_DEVICE(BRCM_SDIO_4329_DEVICE_ID),
+ BRCMF_SDIO_DEVICE(BRCM_SDIO_4330_DEVICE_ID),
+ BRCMF_SDIO_DEVICE(BRCM_SDIO_4334_DEVICE_ID),
+ BRCMF_SDIO_DEVICE(BRCM_SDIO_43362_DEVICE_ID),
+ BRCMF_SDIO_DEVICE(BRCM_SDIO_4335_4339_DEVICE_ID),
+ BRCMF_SDIO_DEVICE(BRCM_SDIO_4354_DEVICE_ID),
+ { /* end: all zeroes */ }
};
MODULE_DEVICE_TABLE(sdio, brcmf_sdmmc_ids);
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/chip.c b/drivers/net/wireless/brcm80211/brcmfmac/chip.c
index c7c9f15c0fe0..96800db0536b 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/chip.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/chip.c
@@ -482,30 +482,30 @@ static inline int brcmf_chip_cores_check(struct brcmf_chip_priv *ci)
static void brcmf_chip_get_raminfo(struct brcmf_chip_priv *ci)
{
switch (ci->pub.chip) {
- case BCM4329_CHIP_ID:
+ case BRCM_CC_4329_CHIP_ID:
ci->pub.ramsize = BCM4329_RAMSIZE;
break;
- case BCM43143_CHIP_ID:
+ case BRCM_CC_43143_CHIP_ID:
ci->pub.ramsize = BCM43143_RAMSIZE;
break;
- case BCM43241_CHIP_ID:
+ case BRCM_CC_43241_CHIP_ID:
ci->pub.ramsize = 0x90000;
break;
- case BCM4330_CHIP_ID:
+ case BRCM_CC_4330_CHIP_ID:
ci->pub.ramsize = 0x48000;
break;
- case BCM4334_CHIP_ID:
+ case BRCM_CC_4334_CHIP_ID:
ci->pub.ramsize = 0x80000;
break;
- case BCM4335_CHIP_ID:
+ case BRCM_CC_4335_CHIP_ID:
ci->pub.ramsize = 0xc0000;
ci->pub.rambase = 0x180000;
break;
- case BCM43362_CHIP_ID:
+ case BRCM_CC_43362_CHIP_ID:
ci->pub.ramsize = 0x3c000;
break;
- case BCM4339_CHIP_ID:
- case BCM4354_CHIP_ID:
+ case BRCM_CC_4339_CHIP_ID:
+ case BRCM_CC_4354_CHIP_ID:
ci->pub.ramsize = 0xc0000;
ci->pub.rambase = 0x180000;
break;
@@ -682,7 +682,7 @@ static int brcmf_chip_recognition(struct brcmf_chip_priv *ci)
ci->pub.chiprev);
if (socitype == SOCI_SB) {
- if (ci->pub.chip != BCM4329_CHIP_ID) {
+ if (ci->pub.chip != BRCM_CC_4329_CHIP_ID) {
brcmf_err("SB chip is not supported\n");
return -ENODEV;
}
@@ -1008,13 +1008,13 @@ bool brcmf_chip_sr_capable(struct brcmf_chip *pub)
chip = container_of(pub, struct brcmf_chip_priv, pub);
switch (pub->chip) {
- case BCM4354_CHIP_ID:
+ case BRCM_CC_4354_CHIP_ID:
/* explicitly check SR engine enable bit */
pmu_cc3_mask = BIT(2);
/* fall-through */
- case BCM43241_CHIP_ID:
- case BCM4335_CHIP_ID:
- case BCM4339_CHIP_ID:
+ case BRCM_CC_43241_CHIP_ID:
+ case BRCM_CC_4335_CHIP_ID:
+ case BRCM_CC_4339_CHIP_ID:
/* read PMU chipcontrol register 3 */
addr = CORE_CC_REG(base, chipcontrol_addr);
chip->ops->write32(chip->ctx, addr, 3);
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd.h b/drivers/net/wireless/brcm80211/brcmfmac/dhd.h
index a8998eb60d22..7da6441bcfa8 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/dhd.h
+++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd.h
@@ -103,6 +103,10 @@ struct brcmf_pub {
struct brcmf_ampdu_rx_reorder
*reorder_flows[BRCMF_AMPDU_RX_REORDER_MAXFLOWS];
+
+ u32 feat_flags;
+ u32 chip_quirks;
+
#ifdef DEBUG
struct dentry *dbgfs_dir;
#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_txflowblock_if(struct brcmf_if *ifp,
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,
bool success);
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.c
index 03fe8aca4d32..be9f4f829192 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.c
@@ -41,37 +41,12 @@ void brcmf_debugfs_exit(void)
root_folder = NULL;
}
-static
-ssize_t brcmf_debugfs_chipinfo_read(struct file *f, char __user *data,
- size_t count, loff_t *ppos)
+static int brcmf_debugfs_chipinfo_read(struct seq_file *seq, void *data)
{
- struct brcmf_pub *drvr = f->private_data;
- struct brcmf_bus *bus = drvr->bus_if;
- char buf[40];
- int res;
-
- /* only allow read from start */
- if (*ppos > 0)
- return 0;
-
- res = scnprintf(buf, sizeof(buf), "chip: %x(%u) rev %u\n",
- bus->chip, bus->chip, bus->chiprev);
- return simple_read_from_buffer(data, count, ppos, buf, res);
-}
-
-static const struct file_operations brcmf_debugfs_chipinfo_ops = {
- .owner = THIS_MODULE,
- .open = simple_open,
- .read = brcmf_debugfs_chipinfo_read
-};
-
-static int brcmf_debugfs_create_chipinfo(struct brcmf_pub *drvr)
-{
- struct dentry *dentry = drvr->dbgfs_dir;
+ struct brcmf_bus *bus = dev_get_drvdata(seq->private);
- if (!IS_ERR_OR_NULL(dentry))
- debugfs_create_file("chipinfo", S_IRUGO, dentry, drvr,
- &brcmf_debugfs_chipinfo_ops);
+ seq_printf(seq, "chip: %x(%u) rev %u\n",
+ bus->chip, bus->chip, bus->chiprev);
return 0;
}
@@ -83,7 +58,8 @@ int brcmf_debugfs_attach(struct brcmf_pub *drvr)
return -ENODEV;
drvr->dbgfs_dir = debugfs_create_dir(dev_name(dev), root_folder);
- brcmf_debugfs_create_chipinfo(drvr);
+ brcmf_debugfs_add_entry(drvr, "chipinfo", brcmf_debugfs_chipinfo_read);
+
return PTR_ERR_OR_ZERO(drvr->dbgfs_dir);
}
@@ -98,148 +74,44 @@ struct dentry *brcmf_debugfs_get_devdir(struct brcmf_pub *drvr)
return drvr->dbgfs_dir;
}
-static
-ssize_t brcmf_debugfs_sdio_counter_read(struct file *f, char __user *data,
- size_t count, loff_t *ppos)
-{
- struct brcmf_sdio_count *sdcnt = f->private_data;
- char buf[750];
- int res;
-
- /* only allow read from start */
- if (*ppos > 0)
- return 0;
-
- res = scnprintf(buf, sizeof(buf),
- "intrcount: %u\nlastintrs: %u\n"
- "pollcnt: %u\nregfails: %u\n"
- "tx_sderrs: %u\nfcqueued: %u\n"
- "rxrtx: %u\nrx_toolong: %u\n"
- "rxc_errors: %u\nrx_hdrfail: %u\n"
- "rx_badhdr: %u\nrx_badseq: %u\n"
- "fc_rcvd: %u\nfc_xoff: %u\n"
- "fc_xon: %u\nrxglomfail: %u\n"
- "rxglomframes: %u\nrxglompkts: %u\n"
- "f2rxhdrs: %u\nf2rxdata: %u\n"
- "f2txdata: %u\nf1regdata: %u\n"
- "tickcnt: %u\ntx_ctlerrs: %lu\n"
- "tx_ctlpkts: %lu\nrx_ctlerrs: %lu\n"
- "rx_ctlpkts: %lu\nrx_readahead: %lu\n",
- sdcnt->intrcount, sdcnt->lastintrs,
- sdcnt->pollcnt, sdcnt->regfails,
- sdcnt->tx_sderrs, sdcnt->fcqueued,
- sdcnt->rxrtx, sdcnt->rx_toolong,
- sdcnt->rxc_errors, sdcnt->rx_hdrfail,
- sdcnt->rx_badhdr, sdcnt->rx_badseq,
- sdcnt->fc_rcvd, sdcnt->fc_xoff,
- sdcnt->fc_xon, sdcnt->rxglomfail,
- sdcnt->rxglomframes, sdcnt->rxglompkts,
- sdcnt->f2rxhdrs, sdcnt->f2rxdata,
- sdcnt->f2txdata, sdcnt->f1regdata,
- sdcnt->tickcnt, sdcnt->tx_ctlerrs,
- sdcnt->tx_ctlpkts, sdcnt->rx_ctlerrs,
- sdcnt->rx_ctlpkts, sdcnt->rx_readahead_cnt);
-
- return simple_read_from_buffer(data, count, ppos, buf, res);
-}
-
-static const struct file_operations brcmf_debugfs_sdio_counter_ops = {
- .owner = THIS_MODULE,
- .open = simple_open,
- .read = brcmf_debugfs_sdio_counter_read
+struct brcmf_debugfs_entry {
+ int (*read)(struct seq_file *seq, void *data);
+ struct brcmf_pub *drvr;
};
-void brcmf_debugfs_create_sdio_count(struct brcmf_pub *drvr,
- struct brcmf_sdio_count *sdcnt)
+static int brcmf_debugfs_entry_open(struct inode *inode, struct file *f)
{
- struct dentry *dentry = drvr->dbgfs_dir;
+ struct brcmf_debugfs_entry *entry = inode->i_private;
- if (!IS_ERR_OR_NULL(dentry))
- debugfs_create_file("counters", S_IRUGO, dentry,
- sdcnt, &brcmf_debugfs_sdio_counter_ops);
-}
-
-static
-ssize_t brcmf_debugfs_fws_stats_read(struct file *f, char __user *data,
- size_t count, loff_t *ppos)
-{
- struct brcmf_fws_stats *fwstats = f->private_data;
- char buf[650];
- int res;
-
- /* only allow read from start */
- if (*ppos > 0)
- return 0;
-
- res = scnprintf(buf, sizeof(buf),
- "header_pulls: %u\n"
- "header_only_pkt: %u\n"
- "tlv_parse_failed: %u\n"
- "tlv_invalid_type: %u\n"
- "mac_update_fails: %u\n"
- "ps_update_fails: %u\n"
- "if_update_fails: %u\n"
- "pkt2bus: %u\n"
- "generic_error: %u\n"
- "rollback_success: %u\n"
- "rollback_failed: %u\n"
- "delayq_full: %u\n"
- "supprq_full: %u\n"
- "txs_indicate: %u\n"
- "txs_discard: %u\n"
- "txs_suppr_core: %u\n"
- "txs_suppr_ps: %u\n"
- "txs_tossed: %u\n"
- "txs_host_tossed: %u\n"
- "bus_flow_block: %u\n"
- "fws_flow_block: %u\n"
- "send_pkts: BK:%u BE:%u VO:%u VI:%u BCMC:%u\n"
- "requested_sent: BK:%u BE:%u VO:%u VI:%u BCMC:%u\n",
- fwstats->header_pulls,
- fwstats->header_only_pkt,
- fwstats->tlv_parse_failed,
- fwstats->tlv_invalid_type,
- fwstats->mac_update_failed,
- fwstats->mac_ps_update_failed,
- fwstats->if_update_failed,
- fwstats->pkt2bus,
- fwstats->generic_error,
- fwstats->rollback_success,
- fwstats->rollback_failed,
- fwstats->delayq_full_error,
- fwstats->supprq_full_error,
- fwstats->txs_indicate,
- fwstats->txs_discard,
- fwstats->txs_supp_core,
- fwstats->txs_supp_ps,
- fwstats->txs_tossed,
- fwstats->txs_host_tossed,
- fwstats->bus_flow_block,
- fwstats->fws_flow_block,
- fwstats->send_pkts[0], fwstats->send_pkts[1],
- fwstats->send_pkts[2], fwstats->send_pkts[3],
- fwstats->send_pkts[4],
- fwstats->requested_sent[0],
- fwstats->requested_sent[1],
- fwstats->requested_sent[2],
- fwstats->requested_sent[3],
- fwstats->requested_sent[4]);
-
- return simple_read_from_buffer(data, count, ppos, buf, res);
+ return single_open(f, entry->read, entry->drvr->bus_if->dev);
}
-static const struct file_operations brcmf_debugfs_fws_stats_ops = {
+static const struct file_operations brcmf_debugfs_def_ops = {
.owner = THIS_MODULE,
- .open = simple_open,
- .read = brcmf_debugfs_fws_stats_read
+ .open = brcmf_debugfs_entry_open,
+ .release = single_release,
+ .read = seq_read,
+ .llseek = seq_lseek
};
-void brcmf_debugfs_create_fws_stats(struct brcmf_pub *drvr,
- struct brcmf_fws_stats *stats)
+int brcmf_debugfs_add_entry(struct brcmf_pub *drvr, const char *fn,
+ int (*read_fn)(struct seq_file *seq, void *data))
{
struct dentry *dentry = drvr->dbgfs_dir;
+ struct brcmf_debugfs_entry *entry;
+
+ if (IS_ERR_OR_NULL(dentry))
+ return -ENOENT;
+
+ entry = devm_kzalloc(drvr->bus_if->dev, sizeof(*entry), GFP_KERNEL);
+ if (!entry)
+ return -ENOMEM;
+
+ entry->read = read_fn;
+ entry->drvr = drvr;
+
+ dentry = debugfs_create_file(fn, S_IRUGO, dentry, entry,
+ &brcmf_debugfs_def_ops);
- if (!IS_ERR_OR_NULL(dentry))
- debugfs_create_file("fws_stats", S_IRUGO, dentry,
- stats, &brcmf_debugfs_fws_stats_ops);
+ return PTR_ERR_OR_ZERO(dentry);
}
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h b/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h
index ef52ed7abc69..6eade7c60c63 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h
+++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h
@@ -100,68 +100,6 @@ do { \
extern int brcmf_msg_level;
-/*
- * hold counter variables used in brcmfmac sdio driver.
- */
-struct brcmf_sdio_count {
- uint intrcount; /* Count of device interrupt callbacks */
- uint lastintrs; /* Count as of last watchdog timer */
- uint pollcnt; /* Count of active polls */
- uint regfails; /* Count of R_REG failures */
- uint tx_sderrs; /* Count of tx attempts with sd errors */
- uint fcqueued; /* Tx packets that got queued */
- uint rxrtx; /* Count of rtx requests (NAK to dongle) */
- uint rx_toolong; /* Receive frames too long to receive */
- uint rxc_errors; /* SDIO errors when reading control frames */
- uint rx_hdrfail; /* SDIO errors on header reads */
- uint rx_badhdr; /* Bad received headers (roosync?) */
- uint rx_badseq; /* Mismatched rx sequence number */
- uint fc_rcvd; /* Number of flow-control events received */
- uint fc_xoff; /* Number which turned on flow-control */
- uint fc_xon; /* Number which turned off flow-control */
- uint rxglomfail; /* Failed deglom attempts */
- uint rxglomframes; /* Number of glom frames (superframes) */
- uint rxglompkts; /* Number of packets from glom frames */
- uint f2rxhdrs; /* Number of header reads */
- uint f2rxdata; /* Number of frame data reads */
- uint f2txdata; /* Number of f2 frame writes */
- uint f1regdata; /* Number of f1 register accesses */
- uint tickcnt; /* Number of watchdog been schedule */
- ulong tx_ctlerrs; /* Err of sending ctrl frames */
- ulong tx_ctlpkts; /* Ctrl frames sent to dongle */
- ulong rx_ctlerrs; /* Err of processing rx ctrl frames */
- ulong rx_ctlpkts; /* Ctrl frames processed from dongle */
- ulong rx_readahead_cnt; /* packets where header read-ahead was used */
-};
-
-struct brcmf_fws_stats {
- u32 tlv_parse_failed;
- u32 tlv_invalid_type;
- u32 header_only_pkt;
- u32 header_pulls;
- u32 pkt2bus;
- u32 send_pkts[5];
- u32 requested_sent[5];
- u32 generic_error;
- u32 mac_update_failed;
- u32 mac_ps_update_failed;
- u32 if_update_failed;
- u32 packet_request_failed;
- u32 credit_request_failed;
- u32 rollback_success;
- u32 rollback_failed;
- u32 delayq_full_error;
- u32 supprq_full_error;
- u32 txs_indicate;
- u32 txs_discard;
- u32 txs_supp_core;
- u32 txs_supp_ps;
- u32 txs_tossed;
- u32 txs_host_tossed;
- u32 bus_flow_block;
- u32 fws_flow_block;
-};
-
struct brcmf_pub;
#ifdef DEBUG
void brcmf_debugfs_init(void);
@@ -169,10 +107,8 @@ void brcmf_debugfs_exit(void);
int brcmf_debugfs_attach(struct brcmf_pub *drvr);
void brcmf_debugfs_detach(struct brcmf_pub *drvr);
struct dentry *brcmf_debugfs_get_devdir(struct brcmf_pub *drvr);
-void brcmf_debugfs_create_sdio_count(struct brcmf_pub *drvr,
- struct brcmf_sdio_count *sdcnt);
-void brcmf_debugfs_create_fws_stats(struct brcmf_pub *drvr,
- struct brcmf_fws_stats *stats);
+int brcmf_debugfs_add_entry(struct brcmf_pub *drvr, const char *fn,
+ int (*read_fn)(struct seq_file *seq, void *data));
#else
static inline void brcmf_debugfs_init(void)
{
@@ -187,9 +123,11 @@ static inline int brcmf_debugfs_attach(struct brcmf_pub *drvr)
static inline void brcmf_debugfs_detach(struct brcmf_pub *drvr)
{
}
-static inline void brcmf_debugfs_create_fws_stats(struct brcmf_pub *drvr,
- struct brcmf_fws_stats *stats)
+static inline
+int brcmf_debugfs_add_entry(struct brcmf_pub *drvr, const char *fn,
+ int (*read_fn)(struct seq_file *seq, void *data))
{
+ return 0;
}
#endif
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c
index 2699441d4f41..347b4260f45b 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c
@@ -30,6 +30,7 @@
#include "wl_cfg80211.h"
#include "fwil.h"
#include "fwsignal.h"
+#include "feature.h"
#include "proto.h"
MODULE_AUTHOR("Broadcom Corporation");
@@ -937,6 +938,8 @@ int brcmf_bus_start(struct device *dev)
if (ret < 0)
goto fail;
+ brcmf_feat_attach(drvr);
+
ret = brcmf_fws_init(drvr);
if (ret < 0)
goto fail;
@@ -1074,16 +1077,6 @@ int brcmf_netdev_wait_pend8021x(struct net_device *ndev)
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)
{
#ifdef CONFIG_BRCMFMAC_SDIO
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c
index 8fa0dbbbda72..67d91d5cc13d 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c
@@ -391,6 +391,40 @@ struct brcmf_sdio_hdrinfo {
u16 tail_pad;
};
+/*
+ * hold counter variables
+ */
+struct brcmf_sdio_count {
+ uint intrcount; /* Count of device interrupt callbacks */
+ uint lastintrs; /* Count as of last watchdog timer */
+ uint pollcnt; /* Count of active polls */
+ uint regfails; /* Count of R_REG failures */
+ uint tx_sderrs; /* Count of tx attempts with sd errors */
+ uint fcqueued; /* Tx packets that got queued */
+ uint rxrtx; /* Count of rtx requests (NAK to dongle) */
+ uint rx_toolong; /* Receive frames too long to receive */
+ uint rxc_errors; /* SDIO errors when reading control frames */
+ uint rx_hdrfail; /* SDIO errors on header reads */
+ uint rx_badhdr; /* Bad received headers (roosync?) */
+ uint rx_badseq; /* Mismatched rx sequence number */
+ uint fc_rcvd; /* Number of flow-control events received */
+ uint fc_xoff; /* Number which turned on flow-control */
+ uint fc_xon; /* Number which turned off flow-control */
+ uint rxglomfail; /* Failed deglom attempts */
+ uint rxglomframes; /* Number of glom frames (superframes) */
+ uint rxglompkts; /* Number of packets from glom frames */
+ uint f2rxhdrs; /* Number of header reads */
+ uint f2rxdata; /* Number of frame data reads */
+ uint f2txdata; /* Number of f2 frame writes */
+ uint f1regdata; /* Number of f1 register accesses */
+ uint tickcnt; /* Number of watchdog been schedule */
+ ulong tx_ctlerrs; /* Err of sending ctrl frames */
+ ulong tx_ctlpkts; /* Ctrl frames sent to dongle */
+ ulong rx_ctlerrs; /* Err of processing rx ctrl frames */
+ ulong rx_ctlpkts; /* Ctrl frames processed from dongle */
+ ulong rx_readahead_cnt; /* packets where header read-ahead was used */
+};
+
/* misc chip info needed by some of the routines */
/* Private data for SDIO bus interaction */
struct brcmf_sdio {
@@ -620,40 +654,46 @@ enum brcmf_firmware_type {
name ## _FIRMWARE_NAME, name ## _NVRAM_NAME
static const struct brcmf_firmware_names brcmf_fwname_data[] = {
- { BCM43143_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM43143) },
- { BCM43241_CHIP_ID, 0x0000001F, BRCMF_FIRMWARE_NVRAM(BCM43241B0) },
- { BCM43241_CHIP_ID, 0xFFFFFFE0, BRCMF_FIRMWARE_NVRAM(BCM43241B4) },
- { BCM4329_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4329) },
- { BCM4330_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4330) },
- { BCM4334_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4334) },
- { BCM4335_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4335) },
- { BCM43362_CHIP_ID, 0xFFFFFFFE, BRCMF_FIRMWARE_NVRAM(BCM43362) },
- { BCM4339_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4339) },
- { BCM4354_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4354) }
+ { BRCM_CC_43143_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM43143) },
+ { BRCM_CC_43241_CHIP_ID, 0x0000001F, BRCMF_FIRMWARE_NVRAM(BCM43241B0) },
+ { BRCM_CC_43241_CHIP_ID, 0xFFFFFFE0, BRCMF_FIRMWARE_NVRAM(BCM43241B4) },
+ { BRCM_CC_4329_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4329) },
+ { BRCM_CC_4330_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4330) },
+ { BRCM_CC_4334_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4334) },
+ { BRCM_CC_4335_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4335) },
+ { BRCM_CC_43362_CHIP_ID, 0xFFFFFFFE, BRCMF_FIRMWARE_NVRAM(BCM43362) },
+ { BRCM_CC_4339_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4339) },
+ { BRCM_CC_4354_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4354) }
};
-static const char *brcmf_sdio_get_fwname(struct brcmf_chip *ci,
- enum brcmf_firmware_type type)
+static int brcmf_sdio_get_fwnames(struct brcmf_chip *ci,
+ struct brcmf_sdio_dev *sdiodev)
{
int i;
for (i = 0; i < ARRAY_SIZE(brcmf_fwname_data); i++) {
if (brcmf_fwname_data[i].chipid == ci->chip &&
- brcmf_fwname_data[i].revmsk & BIT(ci->chiprev)) {
- switch (type) {
- case BRCMF_FIRMWARE_BIN:
- return brcmf_fwname_data[i].bin;
- case BRCMF_FIRMWARE_NVRAM:
- return brcmf_fwname_data[i].nv;
- default:
- brcmf_err("invalid firmware type (%d)\n", type);
- return NULL;
- }
- }
+ brcmf_fwname_data[i].revmsk & BIT(ci->chiprev))
+ break;
}
- brcmf_err("Unknown chipid %d [%d]\n",
- ci->chip, ci->chiprev);
- return NULL;
+
+ if (i == ARRAY_SIZE(brcmf_fwname_data)) {
+ brcmf_err("Unknown chipid %d [%d]\n", ci->chip, ci->chiprev);
+ return -ENODEV;
+ }
+
+ /* check if firmware path is provided by module parameter */
+ if (brcmf_firmware_path[0] != '\0') {
+ if (brcmf_firmware_path[strlen(brcmf_firmware_path) - 1] != '/')
+ strcat(brcmf_firmware_path, "/");
+
+ strcpy(sdiodev->fw_name, brcmf_firmware_path);
+ strcpy(sdiodev->nvram_name, brcmf_firmware_path);
+ }
+ strcat(sdiodev->fw_name, brcmf_fwname_data[i].bin);
+ strcat(sdiodev->nvram_name, brcmf_fwname_data[i].nv);
+
+ return 0;
}
static void pkt_align(struct sk_buff *p, int len, int align)
@@ -2898,16 +2938,13 @@ brcmf_sdio_bus_txctl(struct device *dev, unsigned char *msg, uint msglen)
}
#ifdef DEBUG
-static int brcmf_sdio_dump_console(struct brcmf_sdio *bus,
- struct sdpcm_shared *sh, char __user *data,
- size_t count)
+static int brcmf_sdio_dump_console(struct seq_file *seq, struct brcmf_sdio *bus,
+ struct sdpcm_shared *sh)
{
u32 addr, console_ptr, console_size, console_index;
char *conbuf = NULL;
__le32 sh_val;
int rv;
- loff_t pos = 0;
- int nbytes = 0;
/* obtain console information from device memory */
addr = sh->console_addr + offsetof(struct rte_console, log_le);
@@ -2945,33 +2982,24 @@ static int brcmf_sdio_dump_console(struct brcmf_sdio *bus,
if (rv < 0)
goto done;
- rv = simple_read_from_buffer(data, count, &pos,
- conbuf + console_index,
- console_size - console_index);
+ rv = seq_write(seq, conbuf + console_index,
+ console_size - console_index);
if (rv < 0)
goto done;
- nbytes = rv;
- if (console_index > 0) {
- pos = 0;
- rv = simple_read_from_buffer(data+nbytes, count, &pos,
- conbuf, console_index - 1);
- if (rv < 0)
- goto done;
- rv += nbytes;
- }
+ if (console_index > 0)
+ rv = seq_write(seq, conbuf, console_index - 1);
+
done:
vfree(conbuf);
return rv;
}
-static int brcmf_sdio_trap_info(struct brcmf_sdio *bus, struct sdpcm_shared *sh,
- char __user *data, size_t count)
+static int brcmf_sdio_trap_info(struct seq_file *seq, struct brcmf_sdio *bus,
+ struct sdpcm_shared *sh)
{
- int error, res;
- char buf[350];
+ int error;
struct brcmf_trap_info tr;
- loff_t pos = 0;
if ((sh->flags & SDPCM_SHARED_TRAP) == 0) {
brcmf_dbg(INFO, "no trap in firmware\n");
@@ -2983,34 +3011,30 @@ static int brcmf_sdio_trap_info(struct brcmf_sdio *bus, struct sdpcm_shared *sh,
if (error < 0)
return error;
- res = scnprintf(buf, sizeof(buf),
- "dongle trap info: type 0x%x @ epc 0x%08x\n"
- " cpsr 0x%08x spsr 0x%08x sp 0x%08x\n"
- " lr 0x%08x pc 0x%08x offset 0x%x\n"
- " r0 0x%08x r1 0x%08x r2 0x%08x r3 0x%08x\n"
- " r4 0x%08x r5 0x%08x r6 0x%08x r7 0x%08x\n",
- le32_to_cpu(tr.type), le32_to_cpu(tr.epc),
- le32_to_cpu(tr.cpsr), le32_to_cpu(tr.spsr),
- le32_to_cpu(tr.r13), le32_to_cpu(tr.r14),
- le32_to_cpu(tr.pc), sh->trap_addr,
- le32_to_cpu(tr.r0), le32_to_cpu(tr.r1),
- le32_to_cpu(tr.r2), le32_to_cpu(tr.r3),
- le32_to_cpu(tr.r4), le32_to_cpu(tr.r5),
- le32_to_cpu(tr.r6), le32_to_cpu(tr.r7));
-
- return simple_read_from_buffer(data, count, &pos, buf, res);
+ seq_printf(seq,
+ "dongle trap info: type 0x%x @ epc 0x%08x\n"
+ " cpsr 0x%08x spsr 0x%08x sp 0x%08x\n"
+ " lr 0x%08x pc 0x%08x offset 0x%x\n"
+ " r0 0x%08x r1 0x%08x r2 0x%08x r3 0x%08x\n"
+ " r4 0x%08x r5 0x%08x r6 0x%08x r7 0x%08x\n",
+ le32_to_cpu(tr.type), le32_to_cpu(tr.epc),
+ le32_to_cpu(tr.cpsr), le32_to_cpu(tr.spsr),
+ le32_to_cpu(tr.r13), le32_to_cpu(tr.r14),
+ le32_to_cpu(tr.pc), sh->trap_addr,
+ le32_to_cpu(tr.r0), le32_to_cpu(tr.r1),
+ le32_to_cpu(tr.r2), le32_to_cpu(tr.r3),
+ le32_to_cpu(tr.r4), le32_to_cpu(tr.r5),
+ le32_to_cpu(tr.r6), le32_to_cpu(tr.r7));
+
+ return 0;
}
-static int brcmf_sdio_assert_info(struct brcmf_sdio *bus,
- struct sdpcm_shared *sh, char __user *data,
- size_t count)
+static int brcmf_sdio_assert_info(struct seq_file *seq, struct brcmf_sdio *bus,
+ struct sdpcm_shared *sh)
{
int error = 0;
- char buf[200];
char file[80] = "?";
char expr[80] = "<???>";
- int res;
- loff_t pos = 0;
if ((sh->flags & SDPCM_SHARED_ASSERT_BUILT) == 0) {
brcmf_dbg(INFO, "firmware not built with -assert\n");
@@ -3035,10 +3059,9 @@ static int brcmf_sdio_assert_info(struct brcmf_sdio *bus,
}
sdio_release_host(bus->sdiodev->func[1]);
- res = scnprintf(buf, sizeof(buf),
- "dongle assert: %s:%d: assert(%s)\n",
- file, sh->assert_line, expr);
- return simple_read_from_buffer(data, count, &pos, buf, res);
+ seq_printf(seq, "dongle assert: %s:%d: assert(%s)\n",
+ file, sh->assert_line, expr);
+ return 0;
}
static int brcmf_sdio_checkdied(struct brcmf_sdio *bus)
@@ -3062,59 +3085,75 @@ static int brcmf_sdio_checkdied(struct brcmf_sdio *bus)
return 0;
}
-static int brcmf_sdio_died_dump(struct brcmf_sdio *bus, char __user *data,
- size_t count, loff_t *ppos)
+static int brcmf_sdio_died_dump(struct seq_file *seq, struct brcmf_sdio *bus)
{
int error = 0;
struct sdpcm_shared sh;
- int nbytes = 0;
- loff_t pos = *ppos;
-
- if (pos != 0)
- return 0;
error = brcmf_sdio_readshared(bus, &sh);
if (error < 0)
goto done;
- error = brcmf_sdio_assert_info(bus, &sh, data, count);
+ error = brcmf_sdio_assert_info(seq, bus, &sh);
if (error < 0)
goto done;
- nbytes = error;
- error = brcmf_sdio_trap_info(bus, &sh, data+nbytes, count);
+ error = brcmf_sdio_trap_info(seq, bus, &sh);
if (error < 0)
goto done;
- nbytes += error;
- error = brcmf_sdio_dump_console(bus, &sh, data+nbytes, count);
- if (error < 0)
- goto done;
- nbytes += error;
+ error = brcmf_sdio_dump_console(seq, bus, &sh);
- error = nbytes;
- *ppos += nbytes;
done:
return error;
}
-static ssize_t brcmf_sdio_forensic_read(struct file *f, char __user *data,
- size_t count, loff_t *ppos)
+static int brcmf_sdio_forensic_read(struct seq_file *seq, void *data)
{
- struct brcmf_sdio *bus = f->private_data;
- int res;
+ struct brcmf_bus *bus_if = dev_get_drvdata(seq->private);
+ struct brcmf_sdio *bus = bus_if->bus_priv.sdio->bus;
- res = brcmf_sdio_died_dump(bus, data, count, ppos);
- if (res > 0)
- *ppos += res;
- return (ssize_t)res;
+ return brcmf_sdio_died_dump(seq, bus);
}
-static const struct file_operations brcmf_sdio_forensic_ops = {
- .owner = THIS_MODULE,
- .open = simple_open,
- .read = brcmf_sdio_forensic_read
-};
+static int brcmf_debugfs_sdio_count_read(struct seq_file *seq, void *data)
+{
+ struct brcmf_bus *bus_if = dev_get_drvdata(seq->private);
+ struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
+ struct brcmf_sdio_count *sdcnt = &sdiodev->bus->sdcnt;
+
+ seq_printf(seq,
+ "intrcount: %u\nlastintrs: %u\n"
+ "pollcnt: %u\nregfails: %u\n"
+ "tx_sderrs: %u\nfcqueued: %u\n"
+ "rxrtx: %u\nrx_toolong: %u\n"
+ "rxc_errors: %u\nrx_hdrfail: %u\n"
+ "rx_badhdr: %u\nrx_badseq: %u\n"
+ "fc_rcvd: %u\nfc_xoff: %u\n"
+ "fc_xon: %u\nrxglomfail: %u\n"
+ "rxglomframes: %u\nrxglompkts: %u\n"
+ "f2rxhdrs: %u\nf2rxdata: %u\n"
+ "f2txdata: %u\nf1regdata: %u\n"
+ "tickcnt: %u\ntx_ctlerrs: %lu\n"
+ "tx_ctlpkts: %lu\nrx_ctlerrs: %lu\n"
+ "rx_ctlpkts: %lu\nrx_readahead: %lu\n",
+ sdcnt->intrcount, sdcnt->lastintrs,
+ sdcnt->pollcnt, sdcnt->regfails,
+ sdcnt->tx_sderrs, sdcnt->fcqueued,
+ sdcnt->rxrtx, sdcnt->rx_toolong,
+ sdcnt->rxc_errors, sdcnt->rx_hdrfail,
+ sdcnt->rx_badhdr, sdcnt->rx_badseq,
+ sdcnt->fc_rcvd, sdcnt->fc_xoff,
+ sdcnt->fc_xon, sdcnt->rxglomfail,
+ sdcnt->rxglomframes, sdcnt->rxglompkts,
+ sdcnt->f2rxhdrs, sdcnt->f2rxdata,
+ sdcnt->f2txdata, sdcnt->f1regdata,
+ sdcnt->tickcnt, sdcnt->tx_ctlerrs,
+ sdcnt->tx_ctlpkts, sdcnt->rx_ctlerrs,
+ sdcnt->rx_ctlpkts, sdcnt->rx_readahead_cnt);
+
+ return 0;
+}
static void brcmf_sdio_debugfs_create(struct brcmf_sdio *bus)
{
@@ -3124,9 +3163,9 @@ static void brcmf_sdio_debugfs_create(struct brcmf_sdio *bus)
if (IS_ERR_OR_NULL(dentry))
return;
- debugfs_create_file("forensics", S_IRUGO, dentry, bus,
- &brcmf_sdio_forensic_ops);
- brcmf_debugfs_create_sdio_count(drvr, &bus->sdcnt);
+ brcmf_debugfs_add_entry(drvr, "forensics", brcmf_sdio_forensic_read);
+ brcmf_debugfs_add_entry(drvr, "counters",
+ brcmf_debugfs_sdio_count_read);
debugfs_create_u32("console_interval", 0644, dentry,
&bus->console_interval);
}
@@ -3598,17 +3637,17 @@ brcmf_sdio_drivestrengthinit(struct brcmf_sdio_dev *sdiodev,
return;
switch (SDIOD_DRVSTR_KEY(ci->chip, ci->pmurev)) {
- case SDIOD_DRVSTR_KEY(BCM4330_CHIP_ID, 12):
+ case SDIOD_DRVSTR_KEY(BRCM_CC_4330_CHIP_ID, 12):
str_tab = sdiod_drvstr_tab1_1v8;
str_mask = 0x00003800;
str_shift = 11;
break;
- case SDIOD_DRVSTR_KEY(BCM4334_CHIP_ID, 17):
+ case SDIOD_DRVSTR_KEY(BRCM_CC_4334_CHIP_ID, 17):
str_tab = sdiod_drvstr_tab6_1v8;
str_mask = 0x00001800;
str_shift = 11;
break;
- case SDIOD_DRVSTR_KEY(BCM43143_CHIP_ID, 17):
+ case SDIOD_DRVSTR_KEY(BRCM_CC_43143_CHIP_ID, 17):
/* note: 43143 does not support tristate */
i = ARRAY_SIZE(sdiod_drvstr_tab2_3v3) - 1;
if (drivestrength >= sdiod_drvstr_tab2_3v3[i].strength) {
@@ -3619,7 +3658,7 @@ brcmf_sdio_drivestrengthinit(struct brcmf_sdio_dev *sdiodev,
brcmf_err("Invalid SDIO Drive strength for chip %s, strength=%d\n",
ci->name, drivestrength);
break;
- case SDIOD_DRVSTR_KEY(BCM43362_CHIP_ID, 13):
+ case SDIOD_DRVSTR_KEY(BRCM_CC_43362_CHIP_ID, 13):
str_tab = sdiod_drive_strength_tab5_1v8;
str_mask = 0x00003800;
str_shift = 11;
@@ -3720,12 +3759,12 @@ static u32 brcmf_sdio_buscore_read32(void *ctx, u32 addr)
u32 val, rev;
val = brcmf_sdiod_regrl(sdiodev, addr, NULL);
- if (sdiodev->func[0]->device == SDIO_DEVICE_ID_BROADCOM_4335_4339 &&
+ if (sdiodev->func[0]->device == BRCM_SDIO_4335_4339_DEVICE_ID &&
addr == CORE_CC_REG(SI_ENUM_BASE, chipid)) {
rev = (val & CID_REV_MASK) >> CID_REV_SHIFT;
if (rev >= 2) {
val &= ~CID_ID_MASK;
- val |= BCM4339_CHIP_ID;
+ val |= BRCM_CC_4339_CHIP_ID;
}
}
return val;
@@ -4127,11 +4166,12 @@ struct brcmf_sdio *brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev)
brcmf_sdio_debugfs_create(bus);
brcmf_dbg(INFO, "completed!!\n");
+ ret = brcmf_sdio_get_fwnames(bus->ci, sdiodev);
+ if (ret)
+ goto fail;
+
ret = brcmf_fw_get_firmwares(sdiodev->dev, BRCMF_FW_REQUEST_NVRAM,
- brcmf_sdio_get_fwname(bus->ci,
- BRCMF_FIRMWARE_BIN),
- brcmf_sdio_get_fwname(bus->ci,
- BRCMF_FIRMWARE_NVRAM),
+ sdiodev->fw_name, sdiodev->nvram_name,
brcmf_sdio_firmware_callback);
if (ret != 0) {
brcmf_err("async firmware request failed: %d\n", ret);
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/feature.c b/drivers/net/wireless/brcm80211/brcmfmac/feature.c
new file mode 100644
index 000000000000..50877e3c5d2f
--- /dev/null
+++ b/drivers/net/wireless/brcm80211/brcmfmac/feature.c
@@ -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));
+}
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/feature.h b/drivers/net/wireless/brcm80211/brcmfmac/feature.h
new file mode 100644
index 000000000000..961d175f8afb
--- /dev/null
+++ b/drivers/net/wireless/brcm80211/brcmfmac/feature.h
@@ -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 */
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/firmware.c b/drivers/net/wireless/brcm80211/brcmfmac/firmware.c
index 7b7d237c1ddb..8ea9f283d2b8 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/firmware.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/firmware.c
@@ -18,10 +18,15 @@
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/firmware.h>
+#include <linux/module.h>
#include "dhd_dbg.h"
#include "firmware.h"
+char brcmf_firmware_path[BRCMF_FW_PATH_LEN];
+module_param_string(firmware_path, brcmf_firmware_path,
+ BRCMF_FW_PATH_LEN, 0440);
+
enum nvram_parser_state {
IDLE,
KEY,
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/firmware.h b/drivers/net/wireless/brcm80211/brcmfmac/firmware.h
index 6431bfd7afff..4d3482356b77 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/firmware.h
+++ b/drivers/net/wireless/brcm80211/brcmfmac/firmware.h
@@ -21,6 +21,11 @@
#define BRCMF_FW_REQ_FLAGS 0x00F0
#define BRCMF_FW_REQ_NV_OPTIONAL 0x0010
+#define BRCMF_FW_PATH_LEN 256
+#define BRCMF_FW_NAME_LEN 32
+
+extern char brcmf_firmware_path[];
+
void brcmf_fw_nvram_free(void *nvram);
/*
* Request firmware(s) asynchronously. When the asynchronous request
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c
index 699908de314a..d42f7d04b65f 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c
@@ -454,6 +454,34 @@ struct brcmf_fws_macdesc_table {
struct brcmf_fws_mac_descriptor other;
};
+struct brcmf_fws_stats {
+ u32 tlv_parse_failed;
+ u32 tlv_invalid_type;
+ u32 header_only_pkt;
+ u32 header_pulls;
+ u32 pkt2bus;
+ u32 send_pkts[5];
+ u32 requested_sent[5];
+ u32 generic_error;
+ u32 mac_update_failed;
+ u32 mac_ps_update_failed;
+ u32 if_update_failed;
+ u32 packet_request_failed;
+ u32 credit_request_failed;
+ u32 rollback_success;
+ u32 rollback_failed;
+ u32 delayq_full_error;
+ u32 supprq_full_error;
+ u32 txs_indicate;
+ u32 txs_discard;
+ u32 txs_supp_core;
+ u32 txs_supp_ps;
+ u32 txs_tossed;
+ u32 txs_host_tossed;
+ u32 bus_flow_block;
+ u32 fws_flow_block;
+};
+
struct brcmf_fws_info {
struct brcmf_pub *drvr;
spinlock_t spinlock;
@@ -2017,6 +2045,75 @@ static void brcmf_fws_dequeue_worker(struct work_struct *worker)
brcmf_fws_unlock(fws);
}
+#ifdef DEBUG
+static int brcmf_debugfs_fws_stats_read(struct seq_file *seq, void *data)
+{
+ struct brcmf_bus *bus_if = dev_get_drvdata(seq->private);
+ struct brcmf_fws_stats *fwstats = &bus_if->drvr->fws->stats;
+
+ seq_printf(seq,
+ "header_pulls: %u\n"
+ "header_only_pkt: %u\n"
+ "tlv_parse_failed: %u\n"
+ "tlv_invalid_type: %u\n"
+ "mac_update_fails: %u\n"
+ "ps_update_fails: %u\n"
+ "if_update_fails: %u\n"
+ "pkt2bus: %u\n"
+ "generic_error: %u\n"
+ "rollback_success: %u\n"
+ "rollback_failed: %u\n"
+ "delayq_full: %u\n"
+ "supprq_full: %u\n"
+ "txs_indicate: %u\n"
+ "txs_discard: %u\n"
+ "txs_suppr_core: %u\n"
+ "txs_suppr_ps: %u\n"
+ "txs_tossed: %u\n"
+ "txs_host_tossed: %u\n"
+ "bus_flow_block: %u\n"
+ "fws_flow_block: %u\n"
+ "send_pkts: BK:%u BE:%u VO:%u VI:%u BCMC:%u\n"
+ "requested_sent: BK:%u BE:%u VO:%u VI:%u BCMC:%u\n",
+ fwstats->header_pulls,
+ fwstats->header_only_pkt,
+ fwstats->tlv_parse_failed,
+ fwstats->tlv_invalid_type,
+ fwstats->mac_update_failed,
+ fwstats->mac_ps_update_failed,
+ fwstats->if_update_failed,
+ fwstats->pkt2bus,
+ fwstats->generic_error,
+ fwstats->rollback_success,
+ fwstats->rollback_failed,
+ fwstats->delayq_full_error,
+ fwstats->supprq_full_error,
+ fwstats->txs_indicate,
+ fwstats->txs_discard,
+ fwstats->txs_supp_core,
+ fwstats->txs_supp_ps,
+ fwstats->txs_tossed,
+ fwstats->txs_host_tossed,
+ fwstats->bus_flow_block,
+ fwstats->fws_flow_block,
+ fwstats->send_pkts[0], fwstats->send_pkts[1],
+ fwstats->send_pkts[2], fwstats->send_pkts[3],
+ fwstats->send_pkts[4],
+ fwstats->requested_sent[0],
+ fwstats->requested_sent[1],
+ fwstats->requested_sent[2],
+ fwstats->requested_sent[3],
+ fwstats->requested_sent[4]);
+
+ return 0;
+}
+#else
+static int brcmf_debugfs_fws_stats_read(struct seq_file *seq, void *data)
+{
+ return 0;
+}
+#endif
+
int brcmf_fws_init(struct brcmf_pub *drvr)
{
struct brcmf_fws_info *fws;
@@ -2107,7 +2204,8 @@ int brcmf_fws_init(struct brcmf_pub *drvr)
BRCMF_FWS_PSQ_LEN);
/* create debugfs file for statistics */
- brcmf_debugfs_create_fws_stats(drvr, &fws->stats);
+ brcmf_debugfs_add_entry(drvr, "fws_stats",
+ brcmf_debugfs_fws_stats_read);
brcmf_dbg(INFO, "%s bdcv2 tlv signaling [%x]\n",
fws->fw_signals ? "enabled" : "disabled", tlv);
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h b/drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h
index 3deab7959a0d..6c5e585ccda9 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h
+++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h
@@ -18,6 +18,8 @@
#define _BRCM_SDH_H_
#include <linux/skbuff.h>
+#include <linux/firmware.h>
+#include "firmware.h"
#define SDIO_FUNC_0 0
#define SDIO_FUNC_1 1
@@ -182,6 +184,8 @@ struct brcmf_sdio_dev {
uint max_segment_size;
uint txglomsz;
struct sg_table sgtable;
+ char fw_name[BRCMF_FW_PATH_LEN + BRCMF_FW_NAME_LEN];
+ char nvram_name[BRCMF_FW_PATH_LEN + BRCMF_FW_NAME_LEN];
};
/* sdio core registers */
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/usb.c b/drivers/net/wireless/brcm80211/brcmfmac/usb.c
index b732a99e402c..dc135915470d 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/usb.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/usb.c
@@ -21,6 +21,7 @@
#include <linux/vmalloc.h>
#include <brcmu_utils.h>
+#include <brcm_hw_ids.h>
#include <brcmu_wifi.h>
#include <dhd_bus.h>
#include <dhd_dbg.h>
@@ -913,16 +914,16 @@ static int brcmf_usb_dlrun(struct brcmf_usbdev_info *devinfo)
static bool brcmf_usb_chip_support(int chipid, int chiprev)
{
switch(chipid) {
- case 43143:
+ case BRCM_CC_43143_CHIP_ID:
return true;
- case 43235:
- case 43236:
- case 43238:
+ case BRCM_CC_43235_CHIP_ID:
+ case BRCM_CC_43236_CHIP_ID:
+ case BRCM_CC_43238_CHIP_ID:
return (chiprev == 3);
- case 43242:
+ case BRCM_CC_43242_CHIP_ID:
return true;
- case 43566:
- case 43569:
+ case BRCM_CC_43566_CHIP_ID:
+ case BRCM_CC_43569_CHIP_ID:
return true;
default:
break;
@@ -1016,16 +1017,16 @@ static int check_file(const u8 *headers)
static const char *brcmf_usb_get_fwname(struct brcmf_usbdev_info *devinfo)
{
switch (devinfo->bus_pub.devid) {
- case 43143:
+ case BRCM_CC_43143_CHIP_ID:
return BRCMF_USB_43143_FW_NAME;
- case 43235:
- case 43236:
- case 43238:
+ case BRCM_CC_43235_CHIP_ID:
+ case BRCM_CC_43236_CHIP_ID:
+ case BRCM_CC_43238_CHIP_ID:
return BRCMF_USB_43236_FW_NAME;
- case 43242:
+ case BRCM_CC_43242_CHIP_ID:
return BRCMF_USB_43242_FW_NAME;
- case 43566:
- case 43569:
+ case BRCM_CC_43566_CHIP_ID:
+ case BRCM_CC_43569_CHIP_ID:
return BRCMF_USB_43569_FW_NAME;
default:
return NULL;
@@ -1366,21 +1367,17 @@ static int brcmf_usb_reset_resume(struct usb_interface *intf)
brcmf_usb_probe_phase2);
}
-#define BRCMF_USB_VENDOR_ID_BROADCOM 0x0a5c
-#define BRCMF_USB_DEVICE_ID_43143 0xbd1e
-#define BRCMF_USB_DEVICE_ID_43236 0xbd17
-#define BRCMF_USB_DEVICE_ID_43242 0xbd1f
-#define BRCMF_USB_DEVICE_ID_43569 0xbd27
-#define BRCMF_USB_DEVICE_ID_BCMFW 0x0bdc
+#define BRCMF_USB_DEVICE(dev_id) \
+ { USB_DEVICE(BRCM_USB_VENDOR_ID_BROADCOM, dev_id) }
static struct usb_device_id brcmf_usb_devid_table[] = {
- { USB_DEVICE(BRCMF_USB_VENDOR_ID_BROADCOM, BRCMF_USB_DEVICE_ID_43143) },
- { USB_DEVICE(BRCMF_USB_VENDOR_ID_BROADCOM, BRCMF_USB_DEVICE_ID_43236) },
- { USB_DEVICE(BRCMF_USB_VENDOR_ID_BROADCOM, BRCMF_USB_DEVICE_ID_43242) },
- { USB_DEVICE(BRCMF_USB_VENDOR_ID_BROADCOM, BRCMF_USB_DEVICE_ID_43569) },
+ BRCMF_USB_DEVICE(BRCM_USB_43143_DEVICE_ID),
+ BRCMF_USB_DEVICE(BRCM_USB_43236_DEVICE_ID),
+ BRCMF_USB_DEVICE(BRCM_USB_43242_DEVICE_ID),
+ BRCMF_USB_DEVICE(BRCM_USB_43569_DEVICE_ID),
/* special entry for device with firmware loaded and running */
- { USB_DEVICE(BRCMF_USB_VENDOR_ID_BROADCOM, BRCMF_USB_DEVICE_ID_BCMFW) },
- { }
+ BRCMF_USB_DEVICE(BRCM_USB_BCMFW_DEVICE_ID),
+ { /* end: all zeroes */ }
};
MODULE_DEVICE_TABLE(usb, brcmf_usb_devid_table);
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c
index 9682cf213ec4..48078a321716 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c
@@ -33,6 +33,7 @@
#include "p2p.h"
#include "btcoex.h"
#include "wl_cfg80211.h"
+#include "feature.h"
#include "fwil.h"
#include "vendor.h"
@@ -102,24 +103,6 @@ static bool check_vif_up(struct brcmf_cfg80211_vif *vif)
return true;
}
-#define CHAN2G(_channel, _freq, _flags) { \
- .band = IEEE80211_BAND_2GHZ, \
- .center_freq = (_freq), \
- .hw_value = (_channel), \
- .flags = (_flags), \
- .max_antenna_gain = 0, \
- .max_power = 30, \
-}
-
-#define CHAN5G(_channel, _flags) { \
- .band = IEEE80211_BAND_5GHZ, \
- .center_freq = 5000 + (5 * (_channel)), \
- .hw_value = (_channel), \
- .flags = (_flags), \
- .max_antenna_gain = 0, \
- .max_power = 30, \
-}
-
#define RATE_TO_BASE100KBPS(rate) (((rate) * 10) / 2)
#define RATETAB_ENT(_rateid, _flags) \
{ \
@@ -148,58 +131,17 @@ static struct ieee80211_rate __wl_rates[] = {
#define wl_g_rates (__wl_rates + 0)
#define wl_g_rates_size 12
-static struct ieee80211_channel __wl_2ghz_channels[] = {
- CHAN2G(1, 2412, 0),
- CHAN2G(2, 2417, 0),
- CHAN2G(3, 2422, 0),
- CHAN2G(4, 2427, 0),
- CHAN2G(5, 2432, 0),
- CHAN2G(6, 2437, 0),
- CHAN2G(7, 2442, 0),
- CHAN2G(8, 2447, 0),
- CHAN2G(9, 2452, 0),
- CHAN2G(10, 2457, 0),
- CHAN2G(11, 2462, 0),
- CHAN2G(12, 2467, 0),
- CHAN2G(13, 2472, 0),
- CHAN2G(14, 2484, 0),
-};
-
-static struct ieee80211_channel __wl_5ghz_a_channels[] = {
- CHAN5G(34, 0), CHAN5G(36, 0),
- CHAN5G(38, 0), CHAN5G(40, 0),
- CHAN5G(42, 0), CHAN5G(44, 0),
- CHAN5G(46, 0), CHAN5G(48, 0),
- CHAN5G(52, 0), CHAN5G(56, 0),
- CHAN5G(60, 0), CHAN5G(64, 0),
- CHAN5G(100, 0), CHAN5G(104, 0),
- CHAN5G(108, 0), CHAN5G(112, 0),
- CHAN5G(116, 0), CHAN5G(120, 0),
- CHAN5G(124, 0), CHAN5G(128, 0),
- CHAN5G(132, 0), CHAN5G(136, 0),
- CHAN5G(140, 0), CHAN5G(149, 0),
- CHAN5G(153, 0), CHAN5G(157, 0),
- CHAN5G(161, 0), CHAN5G(165, 0),
- CHAN5G(184, 0), CHAN5G(188, 0),
- CHAN5G(192, 0), CHAN5G(196, 0),
- CHAN5G(200, 0), CHAN5G(204, 0),
- CHAN5G(208, 0), CHAN5G(212, 0),
- CHAN5G(216, 0),
-};
-
-static struct ieee80211_supported_band __wl_band_2ghz = {
+/* Band templates duplicated per wiphy. The channel info
+ * is filled in after querying the device.
+ */
+static const struct ieee80211_supported_band __wl_band_2ghz = {
.band = IEEE80211_BAND_2GHZ,
- .channels = __wl_2ghz_channels,
- .n_channels = ARRAY_SIZE(__wl_2ghz_channels),
.bitrates = wl_g_rates,
.n_bitrates = wl_g_rates_size,
- .ht_cap = {IEEE80211_HT_CAP_SUP_WIDTH_20_40, true},
};
-static struct ieee80211_supported_band __wl_band_5ghz_a = {
+static const struct ieee80211_supported_band __wl_band_5ghz_a = {
.band = IEEE80211_BAND_5GHZ,
- .channels = __wl_5ghz_a_channels,
- .n_channels = ARRAY_SIZE(__wl_5ghz_a_channels),
.bitrates = wl_a_rates,
.n_bitrates = wl_a_rates_size,
};
@@ -592,7 +534,7 @@ static struct wireless_dev *brcmf_cfg80211_add_iface(struct wiphy *wiphy,
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);
}
@@ -1619,17 +1561,10 @@ static
enum nl80211_auth_type brcmf_war_auth_type(struct brcmf_if *ifp,
enum nl80211_auth_type type)
{
- u32 ci;
- if (type == NL80211_AUTHTYPE_AUTOMATIC) {
- /* shift to ignore chip revision */
- ci = brcmf_get_chip_info(ifp) >> 4;
- switch (ci) {
- case 43236:
- brcmf_dbg(CONN, "43236 WAR: use OPEN instead of AUTO\n");
- return NL80211_AUTHTYPE_OPEN_SYSTEM;
- default:
- break;
- }
+ if (type == NL80211_AUTHTYPE_AUTOMATIC &&
+ brcmf_feat_is_quirk_enabled(ifp, BRCMF_FEAT_QUIRK_AUTO_AUTH)) {
+ brcmf_dbg(CONN, "WAR: use OPEN instead of AUTO\n");
+ type = NL80211_AUTHTYPE_OPEN_SYSTEM;
}
return type;
}
@@ -4284,122 +4219,6 @@ static struct cfg80211_ops wl_cfg80211_ops = {
.tdls_oper = brcmf_cfg80211_tdls_oper,
};
-static void brcmf_wiphy_pno_params(struct wiphy *wiphy)
-{
- /* scheduled scan settings */
- wiphy->max_sched_scan_ssids = BRCMF_PNO_MAX_PFN_COUNT;
- wiphy->max_match_sets = BRCMF_PNO_MAX_PFN_COUNT;
- wiphy->max_sched_scan_ie_len = BRCMF_SCAN_IE_LEN_MAX;
- wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN;
-}
-
-static const struct ieee80211_iface_limit brcmf_iface_limits[] = {
- {
- .max = 2,
- .types = BIT(NL80211_IFTYPE_STATION) |
- BIT(NL80211_IFTYPE_ADHOC) |
- BIT(NL80211_IFTYPE_AP)
- },
- {
- .max = 1,
- .types = BIT(NL80211_IFTYPE_P2P_CLIENT) |
- BIT(NL80211_IFTYPE_P2P_GO)
- },
- {
- .max = 1,
- .types = BIT(NL80211_IFTYPE_P2P_DEVICE)
- }
-};
-static const struct ieee80211_iface_combination brcmf_iface_combos[] = {
- {
- .max_interfaces = BRCMF_IFACE_MAX_CNT,
- .num_different_channels = 2,
- .n_limits = ARRAY_SIZE(brcmf_iface_limits),
- .limits = brcmf_iface_limits
- }
-};
-
-static const struct ieee80211_txrx_stypes
-brcmf_txrx_stypes[NUM_NL80211_IFTYPES] = {
- [NL80211_IFTYPE_STATION] = {
- .tx = 0xffff,
- .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
- BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
- },
- [NL80211_IFTYPE_P2P_CLIENT] = {
- .tx = 0xffff,
- .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
- BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
- },
- [NL80211_IFTYPE_P2P_GO] = {
- .tx = 0xffff,
- .rx = BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) |
- BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) |
- BIT(IEEE80211_STYPE_PROBE_REQ >> 4) |
- BIT(IEEE80211_STYPE_DISASSOC >> 4) |
- BIT(IEEE80211_STYPE_AUTH >> 4) |
- BIT(IEEE80211_STYPE_DEAUTH >> 4) |
- BIT(IEEE80211_STYPE_ACTION >> 4)
- },
- [NL80211_IFTYPE_P2P_DEVICE] = {
- .tx = 0xffff,
- .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
- BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
- }
-};
-
-static struct wiphy *brcmf_setup_wiphy(struct device *phydev)
-{
- struct wiphy *wiphy;
- s32 err = 0;
-
- wiphy = wiphy_new(&wl_cfg80211_ops, sizeof(struct brcmf_cfg80211_info));
- if (!wiphy) {
- brcmf_err("Could not allocate wiphy device\n");
- return ERR_PTR(-ENOMEM);
- }
- set_wiphy_dev(wiphy, phydev);
- wiphy->max_scan_ssids = WL_NUM_SCAN_MAX;
- wiphy->max_scan_ie_len = BRCMF_SCAN_IE_LEN_MAX;
- wiphy->max_num_pmkids = WL_NUM_PMKIDS_MAX;
- wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
- BIT(NL80211_IFTYPE_ADHOC) |
- BIT(NL80211_IFTYPE_AP) |
- BIT(NL80211_IFTYPE_P2P_CLIENT) |
- BIT(NL80211_IFTYPE_P2P_GO) |
- BIT(NL80211_IFTYPE_P2P_DEVICE);
- wiphy->iface_combinations = brcmf_iface_combos;
- wiphy->n_iface_combinations = ARRAY_SIZE(brcmf_iface_combos);
- wiphy->bands[IEEE80211_BAND_2GHZ] = &__wl_band_2ghz;
- wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
- wiphy->cipher_suites = __wl_cipher_suites;
- wiphy->n_cipher_suites = ARRAY_SIZE(__wl_cipher_suites);
- wiphy->flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT |
- WIPHY_FLAG_OFFCHAN_TX |
- WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL |
- WIPHY_FLAG_SUPPORTS_TDLS;
- if (!brcmf_roamoff)
- wiphy->flags |= WIPHY_FLAG_SUPPORTS_FW_ROAM;
- wiphy->mgmt_stypes = brcmf_txrx_stypes;
- wiphy->max_remain_on_channel_duration = 5000;
- brcmf_wiphy_pno_params(wiphy);
- brcmf_dbg(INFO, "Registering custom regulatory\n");
- wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG;
- wiphy_apply_custom_regulatory(wiphy, &brcmf_regdom);
-
- /* vendor commands/events support */
- wiphy->vendor_commands = brcmf_vendor_cmds;
- wiphy->n_vendor_commands = BRCMF_VNDR_CMDS_LAST - 1;
-
- err = wiphy_register(wiphy);
- if (err < 0) {
- brcmf_err("Could not register wiphy device (%d)\n", err);
- wiphy_free(wiphy);
- return ERR_PTR(err);
- }
- return wiphy;
-}
-
struct brcmf_cfg80211_vif *brcmf_alloc_vif(struct brcmf_cfg80211_info *cfg,
enum nl80211_iftype type,
bool pm_block)
@@ -4943,138 +4762,6 @@ static void init_vif_event(struct brcmf_cfg80211_vif_event *event)
mutex_init(&event->vif_event_lock);
}
-static int brcmf_enable_bw40_2g(struct brcmf_if *ifp)
-{
- struct brcmf_fil_bwcap_le band_bwcap;
- u32 val;
- int err;
-
- /* verify support for bw_cap command */
- val = WLC_BAND_5G;
- err = brcmf_fil_iovar_int_get(ifp, "bw_cap", &val);
-
- if (!err) {
- /* only set 2G bandwidth using bw_cap command */
- band_bwcap.band = cpu_to_le32(WLC_BAND_2G);
- band_bwcap.bw_cap = cpu_to_le32(WLC_BW_CAP_40MHZ);
- err = brcmf_fil_iovar_data_set(ifp, "bw_cap", &band_bwcap,
- sizeof(band_bwcap));
- } else {
- brcmf_dbg(INFO, "fallback to mimo_bw_cap\n");
- val = WLC_N_BW_40ALL;
- err = brcmf_fil_iovar_int_set(ifp, "mimo_bw_cap", val);
- }
- return err;
-}
-
-struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr,
- struct device *busdev)
-{
- struct net_device *ndev = drvr->iflist[0]->ndev;
- struct brcmf_cfg80211_info *cfg;
- struct wiphy *wiphy;
- struct brcmf_cfg80211_vif *vif;
- struct brcmf_if *ifp;
- s32 err = 0;
- s32 io_type;
-
- if (!ndev) {
- brcmf_err("ndev is invalid\n");
- return NULL;
- }
-
- ifp = netdev_priv(ndev);
- wiphy = brcmf_setup_wiphy(busdev);
- if (IS_ERR(wiphy))
- return NULL;
-
- cfg = wiphy_priv(wiphy);
- cfg->wiphy = wiphy;
- cfg->pub = drvr;
- init_vif_event(&cfg->vif_event);
- INIT_LIST_HEAD(&cfg->vif_list);
-
- vif = brcmf_alloc_vif(cfg, NL80211_IFTYPE_STATION, false);
- if (IS_ERR(vif)) {
- wiphy_free(wiphy);
- return NULL;
- }
-
- vif->ifp = ifp;
- vif->wdev.netdev = ndev;
- ndev->ieee80211_ptr = &vif->wdev;
- SET_NETDEV_DEV(ndev, wiphy_dev(cfg->wiphy));
-
- err = wl_init_priv(cfg);
- if (err) {
- brcmf_err("Failed to init iwm_priv (%d)\n", err);
- goto cfg80211_attach_out;
- }
- ifp->vif = vif;
-
- err = brcmf_p2p_attach(cfg);
- if (err) {
- brcmf_err("P2P initilisation failed (%d)\n", err);
- goto cfg80211_p2p_attach_out;
- }
- err = brcmf_btcoex_attach(cfg);
- if (err) {
- brcmf_err("BT-coex initialisation failed (%d)\n", err);
- brcmf_p2p_detach(&cfg->p2p);
- goto cfg80211_p2p_attach_out;
- }
-
- /* If cfg80211 didn't disable 40MHz HT CAP in wiphy_register(),
- * setup 40MHz in 2GHz band and enable OBSS scanning.
- */
- if (wiphy->bands[IEEE80211_BAND_2GHZ]->ht_cap.cap &
- IEEE80211_HT_CAP_SUP_WIDTH_20_40) {
- err = brcmf_enable_bw40_2g(ifp);
- if (!err)
- err = brcmf_fil_iovar_int_set(ifp, "obss_coex",
- BRCMF_OBSS_COEX_AUTO);
- }
- /* clear for now and rely on update later */
- wiphy->bands[IEEE80211_BAND_2GHZ]->ht_cap.ht_supported = false;
- wiphy->bands[IEEE80211_BAND_2GHZ]->ht_cap.cap = 0;
-
- err = brcmf_fil_iovar_int_set(ifp, "tdls_enable", 1);
- if (err) {
- brcmf_dbg(INFO, "TDLS not enabled (%d)\n", err);
- wiphy->flags &= ~WIPHY_FLAG_SUPPORTS_TDLS;
- }
-
- err = brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_VERSION,
- &io_type);
- if (err) {
- brcmf_err("Failed to get D11 version (%d)\n", err);
- goto cfg80211_p2p_attach_out;
- }
- cfg->d11inf.io_type = (u8)io_type;
- brcmu_d11_attach(&cfg->d11inf);
-
- return cfg;
-
-cfg80211_p2p_attach_out:
- wl_deinit_priv(cfg);
-
-cfg80211_attach_out:
- brcmf_free_vif(vif);
- return NULL;
-}
-
-void brcmf_cfg80211_detach(struct brcmf_cfg80211_info *cfg)
-{
- if (!cfg)
- return;
-
- WARN_ON(!list_empty(&cfg->vif_list));
- wiphy_unregister(cfg->wiphy);
- brcmf_btcoex_detach(cfg);
- wl_deinit_priv(cfg);
- wiphy_free(cfg->wiphy);
-}
-
static s32
brcmf_dongle_roam(struct brcmf_if *ifp, u32 bcn_timeout)
{
@@ -5167,25 +4854,77 @@ dongle_scantime_out:
return err;
}
+/* Filter the list of channels received from firmware counting only
+ * the 20MHz channels. The wiphy band data only needs those which get
+ * flagged to indicate if they can take part in higher bandwidth.
+ */
+static void brcmf_count_20mhz_channels(struct brcmf_cfg80211_info *cfg,
+ struct brcmf_chanspec_list *chlist,
+ u32 chcnt[])
+{
+ u32 total = le32_to_cpu(chlist->count);
+ struct brcmu_chan ch;
+ int i;
+
+ for (i = 0; i <= total; i++) {
+ ch.chspec = (u16)le32_to_cpu(chlist->element[i]);
+ cfg->d11inf.decchspec(&ch);
+
+ /* Firmware gives a ordered list. We skip non-20MHz
+ * channels is 2G. For 5G we can abort upon reaching
+ * a non-20MHz channel in the list.
+ */
+ if (ch.bw != BRCMU_CHAN_BW_20) {
+ if (ch.band == BRCMU_CHAN_BAND_5G)
+ break;
+ else
+ continue;
+ }
+
+ if (ch.band == BRCMU_CHAN_BAND_2G)
+ chcnt[0] += 1;
+ else if (ch.band == BRCMU_CHAN_BAND_5G)
+ chcnt[1] += 1;
+ }
+}
+
+static void brcmf_update_bw40_channel_flag(struct ieee80211_channel *channel,
+ struct brcmu_chan *ch)
+{
+ u32 ht40_flag;
-static s32 brcmf_construct_reginfo(struct brcmf_cfg80211_info *cfg,
- u32 bw_cap[])
+ ht40_flag = channel->flags & IEEE80211_CHAN_NO_HT40;
+ if (ch->sb == BRCMU_CHAN_SB_U) {
+ if (ht40_flag == IEEE80211_CHAN_NO_HT40)
+ channel->flags &= ~IEEE80211_CHAN_NO_HT40;
+ channel->flags |= IEEE80211_CHAN_NO_HT40PLUS;
+ } else {
+ /* It should be one of
+ * IEEE80211_CHAN_NO_HT40 or
+ * IEEE80211_CHAN_NO_HT40PLUS
+ */
+ channel->flags &= ~IEEE80211_CHAN_NO_HT40;
+ if (ht40_flag == IEEE80211_CHAN_NO_HT40)
+ channel->flags |= IEEE80211_CHAN_NO_HT40MINUS;
+ }
+}
+
+static int brcmf_construct_chaninfo(struct brcmf_cfg80211_info *cfg,
+ u32 bw_cap[])
{
struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
- struct ieee80211_channel *band_chan_arr;
+ struct ieee80211_supported_band *band;
+ struct ieee80211_channel *channel;
+ struct wiphy *wiphy;
struct brcmf_chanspec_list *list;
struct brcmu_chan ch;
- s32 err;
+ int err;
u8 *pbuf;
u32 i, j;
u32 total;
- enum ieee80211_band band;
- u32 channel;
- u32 *n_cnt;
+ u32 chaninfo;
+ u32 chcnt[2] = { 0, 0 };
u32 index;
- u32 ht40_flag;
- bool update;
- u32 array_size;
pbuf = kzalloc(BRCMF_DCMD_MEDLEN, GFP_KERNEL);
@@ -5198,11 +4937,45 @@ static s32 brcmf_construct_reginfo(struct brcmf_cfg80211_info *cfg,
BRCMF_DCMD_MEDLEN);
if (err) {
brcmf_err("get chanspecs error (%d)\n", err);
- goto exit;
+ goto fail_pbuf;
}
- __wl_band_2ghz.n_channels = 0;
- __wl_band_5ghz_a.n_channels = 0;
+ brcmf_count_20mhz_channels(cfg, list, chcnt);
+ wiphy = cfg_to_wiphy(cfg);
+ if (chcnt[0]) {
+ band = kmemdup(&__wl_band_2ghz, sizeof(__wl_band_2ghz),
+ GFP_KERNEL);
+ if (band == NULL) {
+ err = -ENOMEM;
+ goto fail_pbuf;
+ }
+ band->channels = kcalloc(chcnt[0], sizeof(*channel),
+ GFP_KERNEL);
+ if (band->channels == NULL) {
+ kfree(band);
+ err = -ENOMEM;
+ goto fail_pbuf;
+ }
+ band->n_channels = 0;
+ wiphy->bands[IEEE80211_BAND_2GHZ] = band;
+ }
+ if (chcnt[1]) {
+ band = kmemdup(&__wl_band_5ghz_a, sizeof(__wl_band_5ghz_a),
+ GFP_KERNEL);
+ if (band == NULL) {
+ err = -ENOMEM;
+ goto fail_band2g;
+ }
+ band->channels = kcalloc(chcnt[1], sizeof(*channel),
+ GFP_KERNEL);
+ if (band->channels == NULL) {
+ kfree(band);
+ err = -ENOMEM;
+ goto fail_band2g;
+ }
+ band->n_channels = 0;
+ wiphy->bands[IEEE80211_BAND_5GHZ] = band;
+ }
total = le32_to_cpu(list->count);
for (i = 0; i < total; i++) {
@@ -5210,100 +4983,151 @@ static s32 brcmf_construct_reginfo(struct brcmf_cfg80211_info *cfg,
cfg->d11inf.decchspec(&ch);
if (ch.band == BRCMU_CHAN_BAND_2G) {
- band_chan_arr = __wl_2ghz_channels;
- array_size = ARRAY_SIZE(__wl_2ghz_channels);
- n_cnt = &__wl_band_2ghz.n_channels;
- band = IEEE80211_BAND_2GHZ;
+ band = wiphy->bands[IEEE80211_BAND_2GHZ];
} else if (ch.band == BRCMU_CHAN_BAND_5G) {
- band_chan_arr = __wl_5ghz_a_channels;
- array_size = ARRAY_SIZE(__wl_5ghz_a_channels);
- n_cnt = &__wl_band_5ghz_a.n_channels;
- band = IEEE80211_BAND_5GHZ;
+ band = wiphy->bands[IEEE80211_BAND_5GHZ];
} else {
brcmf_err("Invalid channel Spec. 0x%x.\n", ch.chspec);
continue;
}
- if (!(bw_cap[band] & WLC_BW_40MHZ_BIT) &&
+ if (!(bw_cap[band->band] & WLC_BW_40MHZ_BIT) &&
ch.bw == BRCMU_CHAN_BW_40)
continue;
- if (!(bw_cap[band] & WLC_BW_80MHZ_BIT) &&
+ if (!(bw_cap[band->band] & WLC_BW_80MHZ_BIT) &&
ch.bw == BRCMU_CHAN_BW_80)
continue;
- update = false;
- for (j = 0; (j < *n_cnt && (*n_cnt < array_size)); j++) {
- if (band_chan_arr[j].hw_value == ch.chnum) {
- update = true;
+
+ channel = band->channels;
+ index = band->n_channels;
+ for (j = 0; j < band->n_channels; j++) {
+ if (channel[j].hw_value == ch.chnum) {
+ index = j;
break;
}
}
- if (update)
- index = j;
- else
- index = *n_cnt;
- if (index < array_size) {
- band_chan_arr[index].center_freq =
- ieee80211_channel_to_frequency(ch.chnum, band);
- band_chan_arr[index].hw_value = ch.chnum;
-
- /* assuming the chanspecs order is HT20,
- * HT40 upper, HT40 lower, and VHT80.
+ channel[index].center_freq =
+ ieee80211_channel_to_frequency(ch.chnum, band->band);
+ channel[index].hw_value = ch.chnum;
+
+ /* assuming the chanspecs order is HT20,
+ * HT40 upper, HT40 lower, and VHT80.
+ */
+ if (ch.bw == BRCMU_CHAN_BW_80) {
+ channel[index].flags &= ~IEEE80211_CHAN_NO_80MHZ;
+ } else if (ch.bw == BRCMU_CHAN_BW_40) {
+ brcmf_update_bw40_channel_flag(&channel[index], &ch);
+ } else {
+ /* disable other bandwidths for now as mentioned
+ * order assure they are enabled for subsequent
+ * chanspecs.
*/
- if (ch.bw == BRCMU_CHAN_BW_80) {
- band_chan_arr[index].flags &=
- ~IEEE80211_CHAN_NO_80MHZ;
- } else if (ch.bw == BRCMU_CHAN_BW_40) {
- ht40_flag = band_chan_arr[index].flags &
- IEEE80211_CHAN_NO_HT40;
- if (ch.sb == BRCMU_CHAN_SB_U) {
- if (ht40_flag == IEEE80211_CHAN_NO_HT40)
- band_chan_arr[index].flags &=
- ~IEEE80211_CHAN_NO_HT40;
- band_chan_arr[index].flags |=
- IEEE80211_CHAN_NO_HT40PLUS;
- } else {
- /* It should be one of
- * IEEE80211_CHAN_NO_HT40 or
- * IEEE80211_CHAN_NO_HT40PLUS
- */
- band_chan_arr[index].flags &=
- ~IEEE80211_CHAN_NO_HT40;
- if (ht40_flag == IEEE80211_CHAN_NO_HT40)
- band_chan_arr[index].flags |=
- IEEE80211_CHAN_NO_HT40MINUS;
- }
- } else {
- /* disable other bandwidths for now as mentioned
- * order assure they are enabled for subsequent
- * chanspecs.
- */
- band_chan_arr[index].flags =
- IEEE80211_CHAN_NO_HT40 |
- IEEE80211_CHAN_NO_80MHZ;
- ch.bw = BRCMU_CHAN_BW_20;
- cfg->d11inf.encchspec(&ch);
- channel = ch.chspec;
- err = brcmf_fil_bsscfg_int_get(ifp,
- "per_chan_info",
- &channel);
- if (!err) {
- if (channel & WL_CHAN_RADAR)
- band_chan_arr[index].flags |=
- (IEEE80211_CHAN_RADAR |
- IEEE80211_CHAN_NO_IR);
- if (channel & WL_CHAN_PASSIVE)
- band_chan_arr[index].flags |=
- IEEE80211_CHAN_NO_IR;
- }
+ channel[index].flags = IEEE80211_CHAN_NO_HT40 |
+ IEEE80211_CHAN_NO_80MHZ;
+ ch.bw = BRCMU_CHAN_BW_20;
+ cfg->d11inf.encchspec(&ch);
+ chaninfo = ch.chspec;
+ err = brcmf_fil_bsscfg_int_get(ifp, "per_chan_info",
+ &chaninfo);
+ if (!err) {
+ if (chaninfo & WL_CHAN_RADAR)
+ channel[index].flags |=
+ (IEEE80211_CHAN_RADAR |
+ IEEE80211_CHAN_NO_IR);
+ if (chaninfo & WL_CHAN_PASSIVE)
+ channel[index].flags |=
+ IEEE80211_CHAN_NO_IR;
}
- if (!update)
- (*n_cnt)++;
}
+ if (index == band->n_channels)
+ band->n_channels++;
}
-exit:
+ kfree(pbuf);
+ return 0;
+
+fail_band2g:
+ kfree(wiphy->bands[IEEE80211_BAND_2GHZ]->channels);
+ kfree(wiphy->bands[IEEE80211_BAND_2GHZ]);
+ wiphy->bands[IEEE80211_BAND_2GHZ] = NULL;
+fail_pbuf:
kfree(pbuf);
return err;
}
+static int brcmf_enable_bw40_2g(struct brcmf_cfg80211_info *cfg)
+{
+ struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
+ struct ieee80211_supported_band *band;
+ struct brcmf_fil_bwcap_le band_bwcap;
+ struct brcmf_chanspec_list *list;
+ u8 *pbuf;
+ u32 val;
+ int err;
+ struct brcmu_chan ch;
+ u32 num_chan;
+ int i, j;
+
+ /* verify support for bw_cap command */
+ val = WLC_BAND_5G;
+ err = brcmf_fil_iovar_int_get(ifp, "bw_cap", &val);
+
+ if (!err) {
+ /* only set 2G bandwidth using bw_cap command */
+ band_bwcap.band = cpu_to_le32(WLC_BAND_2G);
+ band_bwcap.bw_cap = cpu_to_le32(WLC_BW_CAP_40MHZ);
+ err = brcmf_fil_iovar_data_set(ifp, "bw_cap", &band_bwcap,
+ sizeof(band_bwcap));
+ } else {
+ brcmf_dbg(INFO, "fallback to mimo_bw_cap\n");
+ val = WLC_N_BW_40ALL;
+ err = brcmf_fil_iovar_int_set(ifp, "mimo_bw_cap", val);
+ }
+
+ if (!err) {
+ /* update channel info in 2G band */
+ pbuf = kzalloc(BRCMF_DCMD_MEDLEN, GFP_KERNEL);
+
+ if (pbuf == NULL)
+ return -ENOMEM;
+
+ ch.band = BRCMU_CHAN_BAND_2G;
+ ch.bw = BRCMU_CHAN_BW_40;
+ ch.chnum = 0;
+ cfg->d11inf.encchspec(&ch);
+
+ /* pass encoded chanspec in query */
+ *(__le16 *)pbuf = cpu_to_le16(ch.chspec);
+
+ err = brcmf_fil_iovar_data_get(ifp, "chanspecs", pbuf,
+ BRCMF_DCMD_MEDLEN);
+ if (err) {
+ brcmf_err("get chanspecs error (%d)\n", err);
+ kfree(pbuf);
+ return err;
+ }
+
+ band = cfg_to_wiphy(cfg)->bands[IEEE80211_BAND_2GHZ];
+ list = (struct brcmf_chanspec_list *)pbuf;
+ num_chan = le32_to_cpu(list->count);
+ for (i = 0; i < num_chan; i++) {
+ ch.chspec = (u16)le32_to_cpu(list->element[i]);
+ cfg->d11inf.decchspec(&ch);
+ if (WARN_ON(ch.band != BRCMU_CHAN_BAND_2G))
+ continue;
+ if (WARN_ON(ch.bw != BRCMU_CHAN_BW_40))
+ continue;
+ for (j = 0; j < band->n_channels; j++) {
+ if (band->channels[j].hw_value == ch.chnum)
+ break;
+ }
+ if (WARN_ON(j == band->n_channels))
+ continue;
+
+ brcmf_update_bw40_channel_flag(&band->channels[j], &ch);
+ }
+ }
+ return err;
+}
+
static void brcmf_get_bwcap(struct brcmf_if *ifp, u32 bw_cap[])
{
u32 band, mimo_bwcap;
@@ -5394,44 +5218,19 @@ static void brcmf_update_vht_cap(struct ieee80211_supported_band *band,
band->vht_cap.vht_mcs.tx_mcs_map = mcs_map;
}
-static s32 brcmf_update_wiphybands(struct brcmf_cfg80211_info *cfg)
+static int brcmf_setup_wiphybands(struct wiphy *wiphy)
{
+ struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy);
struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
- struct wiphy *wiphy;
- s32 phy_list;
- u32 band_list[3];
u32 nmode = 0;
u32 vhtmode = 0;
- u32 bw_cap[2] = { 0, 0 };
+ u32 bw_cap[2] = { WLC_BW_20MHZ_BIT, WLC_BW_20MHZ_BIT };
u32 rxchain;
u32 nchain;
- s8 phy;
- s32 err;
- u32 nband;
+ int err;
s32 i;
- struct ieee80211_supported_band *bands[2] = { NULL, NULL };
struct ieee80211_supported_band *band;
- err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_PHYLIST,
- &phy_list, sizeof(phy_list));
- if (err) {
- brcmf_err("BRCMF_C_GET_PHYLIST error (%d)\n", err);
- return err;
- }
-
- phy = ((char *)&phy_list)[0];
- brcmf_dbg(INFO, "BRCMF_C_GET_PHYLIST reported: %c phy\n", phy);
-
-
- err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BANDLIST,
- &band_list, sizeof(band_list));
- if (err) {
- brcmf_err("BRCMF_C_GET_BANDLIST error (%d)\n", err);
- return err;
- }
- brcmf_dbg(INFO, "BRCMF_C_GET_BANDLIST reported: 0x%08x 0x%08x 0x%08x phy\n",
- band_list[0], band_list[1], band_list[2]);
-
(void)brcmf_fil_iovar_int_get(ifp, "vhtmode", &vhtmode);
err = brcmf_fil_iovar_int_get(ifp, "nmode", &nmode);
if (err) {
@@ -5453,44 +5252,129 @@ static s32 brcmf_update_wiphybands(struct brcmf_cfg80211_info *cfg)
}
brcmf_dbg(INFO, "nchain=%d\n", nchain);
- err = brcmf_construct_reginfo(cfg, bw_cap);
+ err = brcmf_construct_chaninfo(cfg, bw_cap);
if (err) {
- brcmf_err("brcmf_construct_reginfo failed (%d)\n", err);
+ brcmf_err("brcmf_construct_chaninfo failed (%d)\n", err);
return err;
}
- nband = band_list[0];
-
- for (i = 1; i <= nband && i < ARRAY_SIZE(band_list); i++) {
- band = NULL;
- if ((band_list[i] == WLC_BAND_5G) &&
- (__wl_band_5ghz_a.n_channels > 0))
- band = &__wl_band_5ghz_a;
- else if ((band_list[i] == WLC_BAND_2G) &&
- (__wl_band_2ghz.n_channels > 0))
- band = &__wl_band_2ghz;
- else
+ wiphy = cfg_to_wiphy(cfg);
+ for (i = 0; i < ARRAY_SIZE(wiphy->bands); i++) {
+ band = wiphy->bands[i];
+ if (band == NULL)
continue;
if (nmode)
brcmf_update_ht_cap(band, bw_cap, nchain);
if (vhtmode)
brcmf_update_vht_cap(band, bw_cap, nchain);
- bands[band->band] = band;
}
- wiphy = cfg_to_wiphy(cfg);
- wiphy->bands[IEEE80211_BAND_2GHZ] = bands[IEEE80211_BAND_2GHZ];
- wiphy->bands[IEEE80211_BAND_5GHZ] = bands[IEEE80211_BAND_5GHZ];
- wiphy_apply_custom_regulatory(wiphy, &brcmf_regdom);
-
- return err;
+ return 0;
}
+static const struct ieee80211_iface_limit brcmf_iface_limits[] = {
+ {
+ .max = 2,
+ .types = BIT(NL80211_IFTYPE_STATION) |
+ BIT(NL80211_IFTYPE_ADHOC) |
+ BIT(NL80211_IFTYPE_AP)
+ },
+ {
+ .max = 1,
+ .types = BIT(NL80211_IFTYPE_P2P_CLIENT) |
+ BIT(NL80211_IFTYPE_P2P_GO)
+ },
+ {
+ .max = 1,
+ .types = BIT(NL80211_IFTYPE_P2P_DEVICE)
+ }
+};
+static struct ieee80211_iface_combination brcmf_iface_combos[] = {
+ {
+ .max_interfaces = BRCMF_IFACE_MAX_CNT,
+ .num_different_channels = 1,
+ .n_limits = ARRAY_SIZE(brcmf_iface_limits),
+ .limits = brcmf_iface_limits
+ }
+};
+
+static const struct ieee80211_txrx_stypes
+brcmf_txrx_stypes[NUM_NL80211_IFTYPES] = {
+ [NL80211_IFTYPE_STATION] = {
+ .tx = 0xffff,
+ .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
+ BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
+ },
+ [NL80211_IFTYPE_P2P_CLIENT] = {
+ .tx = 0xffff,
+ .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
+ BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
+ },
+ [NL80211_IFTYPE_P2P_GO] = {
+ .tx = 0xffff,
+ .rx = BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) |
+ BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) |
+ BIT(IEEE80211_STYPE_PROBE_REQ >> 4) |
+ BIT(IEEE80211_STYPE_DISASSOC >> 4) |
+ BIT(IEEE80211_STYPE_AUTH >> 4) |
+ BIT(IEEE80211_STYPE_DEAUTH >> 4) |
+ BIT(IEEE80211_STYPE_ACTION >> 4)
+ },
+ [NL80211_IFTYPE_P2P_DEVICE] = {
+ .tx = 0xffff,
+ .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
+ BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
+ }
+};
-static s32 brcmf_dongle_probecap(struct brcmf_cfg80211_info *cfg)
+static void brcmf_wiphy_pno_params(struct wiphy *wiphy)
{
- return brcmf_update_wiphybands(cfg);
+ /* scheduled scan settings */
+ wiphy->max_sched_scan_ssids = BRCMF_PNO_MAX_PFN_COUNT;
+ wiphy->max_match_sets = BRCMF_PNO_MAX_PFN_COUNT;
+ wiphy->max_sched_scan_ie_len = BRCMF_SCAN_IE_LEN_MAX;
+ wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN;
+}
+
+static int brcmf_setup_wiphy(struct wiphy *wiphy, struct brcmf_if *ifp)
+{
+ struct ieee80211_iface_combination ifc_combo;
+ wiphy->max_scan_ssids = WL_NUM_SCAN_MAX;
+ wiphy->max_scan_ie_len = BRCMF_SCAN_IE_LEN_MAX;
+ wiphy->max_num_pmkids = WL_NUM_PMKIDS_MAX;
+ wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
+ BIT(NL80211_IFTYPE_ADHOC) |
+ BIT(NL80211_IFTYPE_AP) |
+ BIT(NL80211_IFTYPE_P2P_CLIENT) |
+ BIT(NL80211_IFTYPE_P2P_GO) |
+ BIT(NL80211_IFTYPE_P2P_DEVICE);
+ /* need VSDB firmware feature for concurrent channels */
+ ifc_combo = brcmf_iface_combos[0];
+ if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MCHAN))
+ ifc_combo.num_different_channels = 2;
+ wiphy->iface_combinations = kmemdup(&ifc_combo,
+ sizeof(ifc_combo),
+ GFP_KERNEL);
+ wiphy->n_iface_combinations = ARRAY_SIZE(brcmf_iface_combos);
+ wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
+ wiphy->cipher_suites = __wl_cipher_suites;
+ wiphy->n_cipher_suites = ARRAY_SIZE(__wl_cipher_suites);
+ wiphy->flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT |
+ WIPHY_FLAG_OFFCHAN_TX |
+ WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL |
+ WIPHY_FLAG_SUPPORTS_TDLS;
+ if (!brcmf_roamoff)
+ wiphy->flags |= WIPHY_FLAG_SUPPORTS_FW_ROAM;
+ wiphy->mgmt_stypes = brcmf_txrx_stypes;
+ wiphy->max_remain_on_channel_duration = 5000;
+ brcmf_wiphy_pno_params(wiphy);
+
+ /* vendor commands/events support */
+ wiphy->vendor_commands = brcmf_vendor_cmds;
+ wiphy->n_vendor_commands = BRCMF_VNDR_CMDS_LAST - 1;
+
+ return brcmf_setup_wiphybands(wiphy);
}
static s32 brcmf_config_dongle(struct brcmf_cfg80211_info *cfg)
@@ -5528,9 +5412,6 @@ static s32 brcmf_config_dongle(struct brcmf_cfg80211_info *cfg)
NULL, NULL);
if (err)
goto default_conf_out;
- err = brcmf_dongle_probecap(cfg);
- if (err)
- goto default_conf_out;
brcmf_configure_arp_offload(ifp, true);
@@ -5658,3 +5539,150 @@ int brcmf_cfg80211_wait_vif_event_timeout(struct brcmf_cfg80211_info *cfg,
vif_event_equals(event, action), timeout);
}
+static void brcmf_free_wiphy(struct wiphy *wiphy)
+{
+ kfree(wiphy->iface_combinations);
+ if (wiphy->bands[IEEE80211_BAND_2GHZ]) {
+ kfree(wiphy->bands[IEEE80211_BAND_2GHZ]->channels);
+ kfree(wiphy->bands[IEEE80211_BAND_2GHZ]);
+ }
+ if (wiphy->bands[IEEE80211_BAND_5GHZ]) {
+ kfree(wiphy->bands[IEEE80211_BAND_5GHZ]->channels);
+ kfree(wiphy->bands[IEEE80211_BAND_5GHZ]);
+ }
+ wiphy_free(wiphy);
+}
+
+struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr,
+ struct device *busdev)
+{
+ struct net_device *ndev = drvr->iflist[0]->ndev;
+ struct brcmf_cfg80211_info *cfg;
+ struct wiphy *wiphy;
+ struct brcmf_cfg80211_vif *vif;
+ struct brcmf_if *ifp;
+ s32 err = 0;
+ s32 io_type;
+ u16 *cap = NULL;
+
+ if (!ndev) {
+ brcmf_err("ndev is invalid\n");
+ return NULL;
+ }
+
+ ifp = netdev_priv(ndev);
+ wiphy = wiphy_new(&wl_cfg80211_ops, sizeof(struct brcmf_cfg80211_info));
+ if (!wiphy) {
+ brcmf_err("Could not allocate wiphy device\n");
+ return NULL;
+ }
+ set_wiphy_dev(wiphy, busdev);
+
+ cfg = wiphy_priv(wiphy);
+ cfg->wiphy = wiphy;
+ cfg->pub = drvr;
+ init_vif_event(&cfg->vif_event);
+ INIT_LIST_HEAD(&cfg->vif_list);
+
+ vif = brcmf_alloc_vif(cfg, NL80211_IFTYPE_STATION, false);
+ if (IS_ERR(vif))
+ goto wiphy_out;
+
+ vif->ifp = ifp;
+ vif->wdev.netdev = ndev;
+ ndev->ieee80211_ptr = &vif->wdev;
+ SET_NETDEV_DEV(ndev, wiphy_dev(cfg->wiphy));
+
+ err = wl_init_priv(cfg);
+ if (err) {
+ brcmf_err("Failed to init iwm_priv (%d)\n", err);
+ brcmf_free_vif(vif);
+ goto wiphy_out;
+ }
+ ifp->vif = vif;
+
+ /* determine d11 io type before wiphy setup */
+ err = brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_VERSION, &io_type);
+ if (err) {
+ brcmf_err("Failed to get D11 version (%d)\n", err);
+ goto priv_out;
+ }
+ cfg->d11inf.io_type = (u8)io_type;
+ brcmu_d11_attach(&cfg->d11inf);
+
+ err = brcmf_setup_wiphy(wiphy, ifp);
+ if (err < 0)
+ goto priv_out;
+
+ brcmf_dbg(INFO, "Registering custom regulatory\n");
+ wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG;
+ wiphy_apply_custom_regulatory(wiphy, &brcmf_regdom);
+
+ /* firmware defaults to 40MHz disabled in 2G band. We signal
+ * cfg80211 here that we do and have it decide we can enable
+ * it. But first check if device does support 2G operation.
+ */
+ if (wiphy->bands[IEEE80211_BAND_2GHZ]) {
+ cap = &wiphy->bands[IEEE80211_BAND_2GHZ]->ht_cap.cap;
+ *cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40;
+ }
+ err = wiphy_register(wiphy);
+ if (err < 0) {
+ brcmf_err("Could not register wiphy device (%d)\n", err);
+ goto priv_out;
+ }
+
+ /* If cfg80211 didn't disable 40MHz HT CAP in wiphy_register(),
+ * setup 40MHz in 2GHz band and enable OBSS scanning.
+ */
+ if (cap && (*cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40)) {
+ err = brcmf_enable_bw40_2g(cfg);
+ if (!err)
+ err = brcmf_fil_iovar_int_set(ifp, "obss_coex",
+ BRCMF_OBSS_COEX_AUTO);
+ else
+ *cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
+ }
+
+ err = brcmf_p2p_attach(cfg);
+ if (err) {
+ brcmf_err("P2P initilisation failed (%d)\n", err);
+ goto wiphy_unreg_out;
+ }
+ err = brcmf_btcoex_attach(cfg);
+ if (err) {
+ brcmf_err("BT-coex initialisation failed (%d)\n", err);
+ brcmf_p2p_detach(&cfg->p2p);
+ goto wiphy_unreg_out;
+ }
+
+ err = brcmf_fil_iovar_int_set(ifp, "tdls_enable", 1);
+ if (err) {
+ brcmf_dbg(INFO, "TDLS not enabled (%d)\n", err);
+ wiphy->flags &= ~WIPHY_FLAG_SUPPORTS_TDLS;
+ }
+
+ return cfg;
+
+wiphy_unreg_out:
+ wiphy_unregister(cfg->wiphy);
+priv_out:
+ wl_deinit_priv(cfg);
+ brcmf_free_vif(vif);
+wiphy_out:
+ brcmf_free_wiphy(wiphy);
+ return NULL;
+}
+
+void brcmf_cfg80211_detach(struct brcmf_cfg80211_info *cfg)
+{
+ if (!cfg)
+ return;
+
+ WARN_ON(!list_empty(&cfg->vif_list));
+ wiphy_unregister(cfg->wiphy);
+ brcmf_btcoex_detach(cfg);
+ brcmf_p2p_detach(&cfg->p2p);
+ wl_deinit_priv(cfg);
+ brcmf_free_wiphy(cfg->wiphy);
+}