diff options
Diffstat (limited to 'drivers/net/wireless/brcm80211/brcmfmac')
-rw-r--r-- | drivers/net/wireless/brcm80211/brcmfmac/Makefile | 1 | ||||
-rw-r--r-- | drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c | 23 | ||||
-rw-r--r-- | drivers/net/wireless/brcm80211/brcmfmac/chip.c | 28 | ||||
-rw-r--r-- | drivers/net/wireless/brcm80211/brcmfmac/dhd.h | 5 | ||||
-rw-r--r-- | drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.c | 196 | ||||
-rw-r--r-- | drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h | 74 | ||||
-rw-r--r-- | drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c | 13 | ||||
-rw-r--r-- | drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c | 274 | ||||
-rw-r--r-- | drivers/net/wireless/brcm80211/brcmfmac/feature.c | 136 | ||||
-rw-r--r-- | drivers/net/wireless/brcm80211/brcmfmac/feature.h | 86 | ||||
-rw-r--r-- | drivers/net/wireless/brcm80211/brcmfmac/firmware.c | 5 | ||||
-rw-r--r-- | drivers/net/wireless/brcm80211/brcmfmac/firmware.h | 5 | ||||
-rw-r--r-- | drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c | 100 | ||||
-rw-r--r-- | drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h | 4 | ||||
-rw-r--r-- | drivers/net/wireless/brcm80211/brcmfmac/usb.c | 49 | ||||
-rw-r--r-- | drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c | 958 |
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); +} |