--- /dev/null
+From: Felix Fietkau <nbd@openwrt.org>
+Date: Tue, 2 Jun 2015 10:35:46 +0200
+Subject: [PATCH] ath9k: fix DMA stop sequence for AR9003+
+
+AR93xx and newer needs to stop rx before tx to avoid getting the DMA
+engine or MAC into a stuck state.
+This should reduce/fix the occurence of "Failed to stop Tx DMA" logspam.
+
+Cc: stable@vger.kernel.org
+Signed-off-by: Felix Fietkau <nbd@openwrt.org>
+---
+
+--- a/drivers/net/wireless/ath/ath9k/main.c
++++ b/drivers/net/wireless/ath/ath9k/main.c
+@@ -216,11 +216,13 @@ static bool ath_prepare_reset(struct ath
+ ath_stop_ani(sc);
+ ath9k_hw_disable_interrupts(ah);
+
+- if (!ath_drain_all_txq(sc))
+- ret = false;
+-
+- if (!ath_stoprecv(sc))
+- ret = false;
++ if (AR_SREV_9300_20_OR_LATER(ah)) {
++ ret &= ath_stoprecv(sc);
++ ret &= ath_drain_all_txq(sc);
++ } else {
++ ret &= ath_drain_all_txq(sc);
++ ret &= ath_stoprecv(sc);
++ }
+
+ return ret;
+ }
--- /dev/null
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <zajec5@gmail.com>
+Date: Thu, 28 May 2015 14:19:21 +0200
+Subject: [PATCH] brcmfmac: support NVRAMs containing pci devpaths (instead of
+ pcie)
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Recently Broadcom added support for NVRAMs with entries for multiple
+PCIe devices. One of the supported formats is based on prefixes defined
+like: devpath0=pcie/1/4/ and entries like 0:foo=bar 0:baz=qux etc.
+
+Unfortunately there are also a bit older devices using different way of
+defining prefixes, e.g. SmartRG SR400ac (2 x BCM43602) with entries:
+devpath0=pci/1/1/
+devpath1=pci/2/1
+Broadcom stated this old format will never be used/supported by brcmfmac
+but given the simplicity of this patch I'll insist on supporting it.
+
+Signed-off-by: Rafał Miłecki <zajec5@gmail.com>
+Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
+---
+
+--- a/drivers/net/wireless/brcm80211/brcmfmac/firmware.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/firmware.c
+@@ -232,6 +232,8 @@ static void brcmf_fw_strip_multi_v1(stru
+ u16 bus_nr)
+ {
+ /* Device path with a leading '=' key-value separator */
++ char pci_path[] = "=pci/?/?";
++ size_t pci_len;
+ char pcie_path[] = "=pcie/?/?";
+ size_t pcie_len;
+
+@@ -251,6 +253,9 @@ static void brcmf_fw_strip_multi_v1(stru
+ /* First search for the devpathX and see if it is the configuration
+ * for domain_nr/bus_nr. Search complete nvp
+ */
++ snprintf(pci_path, sizeof(pci_path), "=pci/%d/%d", domain_nr,
++ bus_nr);
++ pci_len = strlen(pci_path);
+ snprintf(pcie_path, sizeof(pcie_path), "=pcie/%d/%d", domain_nr,
+ bus_nr);
+ pcie_len = strlen(pcie_path);
+@@ -260,8 +265,9 @@ static void brcmf_fw_strip_multi_v1(stru
+ /* Format: devpathX=pcie/Y/Z/
+ * Y = domain_nr, Z = bus_nr, X = virtual ID
+ */
+- if ((strncmp(&nvp->nvram[i], "devpath", 7) == 0) &&
+- (strncmp(&nvp->nvram[i + 8], pcie_path, pcie_len) == 0)) {
++ if (strncmp(&nvp->nvram[i], "devpath", 7) == 0 &&
++ (!strncmp(&nvp->nvram[i + 8], pci_path, pci_len) ||
++ !strncmp(&nvp->nvram[i + 8], pcie_path, pcie_len))) {
+ id = nvp->nvram[i + 7] - '0';
+ found = true;
+ break;
--- /dev/null
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <zajec5@gmail.com>
+Date: Sun, 31 May 2015 02:52:26 +0200
+Subject: [PATCH] brcmfmac: set wiphy perm_addr to hardware MAC address
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+This allows e.g. user space to use /sys/class/ieee80211/*/macaddress
+
+Signed-off-by: Rafał Miłecki <zajec5@gmail.com>
+Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
+---
+
+--- a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c
+@@ -6070,6 +6070,7 @@ struct brcmf_cfg80211_info *brcmf_cfg802
+ brcmf_err("Could not allocate wiphy device\n");
+ return NULL;
+ }
++ memcpy(wiphy->perm_addr, drvr->mac, ETH_ALEN);
+ set_wiphy_dev(wiphy, busdev);
+
+ cfg = wiphy_priv(wiphy);
--- /dev/null
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <zajec5@gmail.com>
+Date: Thu, 4 Jun 2015 22:11:07 +0200
+Subject: [PATCH] brcmfmac: use direct data pointer in NVRAM parser struct
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+As we plan to add support for platform NVRAM we should store direct
+data pointer without the extra struct firmware layer. This will allow
+us to support other sources with the only requirement being u8 buffer.
+
+Signed-off-by: Rafał Miłecki <zajec5@gmail.com>
+Acked-by: Arend van Spriel <arend@broadcom.com>
+Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
+---
+
+--- a/drivers/net/wireless/brcm80211/brcmfmac/firmware.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/firmware.c
+@@ -43,7 +43,7 @@ enum nvram_parser_state {
+ * struct nvram_parser - internal info for parser.
+ *
+ * @state: current parser state.
+- * @fwnv: input buffer being parsed.
++ * @data: input buffer being parsed.
+ * @nvram: output buffer with parse result.
+ * @nvram_len: lenght of parse result.
+ * @line: current line.
+@@ -55,7 +55,7 @@ enum nvram_parser_state {
+ */
+ struct nvram_parser {
+ enum nvram_parser_state state;
+- const struct firmware *fwnv;
++ const u8 *data;
+ u8 *nvram;
+ u32 nvram_len;
+ u32 line;
+@@ -91,7 +91,7 @@ static enum nvram_parser_state brcmf_nvr
+ {
+ char c;
+
+- c = nvp->fwnv->data[nvp->pos];
++ c = nvp->data[nvp->pos];
+ if (c == '\n')
+ return COMMENT;
+ if (is_whitespace(c))
+@@ -115,16 +115,16 @@ static enum nvram_parser_state brcmf_nvr
+ enum nvram_parser_state st = nvp->state;
+ char c;
+
+- c = nvp->fwnv->data[nvp->pos];
++ c = nvp->data[nvp->pos];
+ if (c == '=') {
+ /* ignore RAW1 by treating as comment */
+- if (strncmp(&nvp->fwnv->data[nvp->entry], "RAW1", 4) == 0)
++ if (strncmp(&nvp->data[nvp->entry], "RAW1", 4) == 0)
+ st = COMMENT;
+ else
+ st = VALUE;
+- if (strncmp(&nvp->fwnv->data[nvp->entry], "devpath", 7) == 0)
++ if (strncmp(&nvp->data[nvp->entry], "devpath", 7) == 0)
+ nvp->multi_dev_v1 = true;
+- if (strncmp(&nvp->fwnv->data[nvp->entry], "pcie/", 5) == 0)
++ if (strncmp(&nvp->data[nvp->entry], "pcie/", 5) == 0)
+ nvp->multi_dev_v2 = true;
+ } else if (!is_nvram_char(c) || c == ' ') {
+ brcmf_dbg(INFO, "warning: ln=%d:col=%d: '=' expected, skip invalid key entry\n",
+@@ -145,11 +145,11 @@ brcmf_nvram_handle_value(struct nvram_pa
+ char *ekv;
+ u32 cplen;
+
+- c = nvp->fwnv->data[nvp->pos];
++ c = nvp->data[nvp->pos];
+ if (!is_nvram_char(c)) {
+ /* key,value pair complete */
+- ekv = (u8 *)&nvp->fwnv->data[nvp->pos];
+- skv = (u8 *)&nvp->fwnv->data[nvp->entry];
++ ekv = (u8 *)&nvp->data[nvp->pos];
++ skv = (u8 *)&nvp->data[nvp->entry];
+ cplen = ekv - skv;
+ if (nvp->nvram_len + cplen + 1 >= BRCMF_FW_MAX_NVRAM_SIZE)
+ return END;
+@@ -170,7 +170,7 @@ brcmf_nvram_handle_comment(struct nvram_
+ {
+ char *eoc, *sol;
+
+- sol = (char *)&nvp->fwnv->data[nvp->pos];
++ sol = (char *)&nvp->data[nvp->pos];
+ eoc = strchr(sol, '\n');
+ if (!eoc) {
+ eoc = strchr(sol, '\0');
+@@ -201,17 +201,17 @@ static enum nvram_parser_state
+ };
+
+ static int brcmf_init_nvram_parser(struct nvram_parser *nvp,
+- const struct firmware *nv)
++ const u8 *data, size_t data_len)
+ {
+ size_t size;
+
+ memset(nvp, 0, sizeof(*nvp));
+- nvp->fwnv = nv;
++ nvp->data = data;
+ /* Limit size to MAX_NVRAM_SIZE, some files contain lot of comment */
+- if (nv->size > BRCMF_FW_MAX_NVRAM_SIZE)
++ if (data_len > BRCMF_FW_MAX_NVRAM_SIZE)
+ size = BRCMF_FW_MAX_NVRAM_SIZE;
+ else
+- size = nv->size;
++ size = data_len;
+ /* Alloc for extra 0 byte + roundup by 4 + length field */
+ size += 1 + 3 + sizeof(u32);
+ nvp->nvram = kzalloc(size, GFP_KERNEL);
+@@ -362,18 +362,18 @@ fail:
+ * and converts newlines to NULs. Shortens buffer as needed and pads with NULs.
+ * End of buffer is completed with token identifying length of buffer.
+ */
+-static void *brcmf_fw_nvram_strip(const struct firmware *nv, u32 *new_length,
+- u16 domain_nr, u16 bus_nr)
++static void *brcmf_fw_nvram_strip(const u8 *data, size_t data_len,
++ u32 *new_length, u16 domain_nr, u16 bus_nr)
+ {
+ struct nvram_parser nvp;
+ u32 pad;
+ u32 token;
+ __le32 token_le;
+
+- if (brcmf_init_nvram_parser(&nvp, nv) < 0)
++ if (brcmf_init_nvram_parser(&nvp, data, data_len) < 0)
+ return NULL;
+
+- while (nvp.pos < nv->size) {
++ while (nvp.pos < data_len) {
+ nvp.state = nv_parser_states[nvp.state](&nvp);
+ if (nvp.state == END)
+ break;
+@@ -432,7 +432,7 @@ static void brcmf_fw_request_nvram_done(
+ goto fail;
+
+ if (fw) {
+- nvram = brcmf_fw_nvram_strip(fw, &nvram_length,
++ nvram = brcmf_fw_nvram_strip(fw->data, fw->size, &nvram_length,
+ fwctx->domain_nr, fwctx->bus_nr);
+ release_firmware(fw);
+ if (!nvram && !(fwctx->flags & BRCMF_FW_REQ_NV_OPTIONAL))
--- /dev/null
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <zajec5@gmail.com>
+Date: Sat, 6 Jun 2015 22:45:59 +0200
+Subject: [PATCH] b43: fix support for 14e4:4321 PCI dev with BCM4321 chipset
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+It seems Broadcom released two devices with conflicting device id. There
+are for sure 14e4:4321 PCI devices with BCM4321 (N-PHY) chipset, they
+can be found in routers, e.g. Netgear WNR834Bv2. However, according to
+Broadcom public sources 0x4321 is also used for 5 GHz BCM4306 (G-PHY).
+It's unsure if they meant PCI device id, or "virtual" id (from SPROM).
+To distinguish these devices lets check PHY type (G vs. N).
+
+Signed-off-by: Rafał Miłecki <zajec5@gmail.com>
+Cc: <stable@vger.kernel.org> # 3.16+
+Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
+---
+
+--- a/drivers/net/wireless/b43/main.c
++++ b/drivers/net/wireless/b43/main.c
+@@ -5365,6 +5365,10 @@ static void b43_supported_bands(struct b
+ *have_5ghz_phy = true;
+ return;
+ case 0x4321: /* BCM4306 */
++ /* There are 14e4:4321 PCI devs with 2.4 GHz BCM4321 (N-PHY) */
++ if (dev->phy.type != B43_PHYTYPE_G)
++ break;
++ /* fall through */
+ case 0x4313: /* BCM4311 */
+ case 0x431a: /* BCM4318 */
+ case 0x432a: /* BCM4321 */
--- /dev/null
+From: Felix Fietkau <nbd@openwrt.org>
+Date: Sun, 7 Jun 2015 13:53:35 +0200
+Subject: [PATCH] ath9k: force rx_clear when disabling rx
+
+This makes stopping Rx more reliable and should reduce the frequency of
+Rx related DMA stop warnings
+
+Cc: stable@vger.kernel.org
+Signed-off-by: Felix Fietkau <nbd@openwrt.org>
+---
+
+--- a/drivers/net/wireless/ath/ath9k/mac.c
++++ b/drivers/net/wireless/ath/ath9k/mac.c
+@@ -677,13 +677,15 @@ void ath9k_hw_startpcureceive(struct ath
+
+ ath9k_ani_reset(ah, is_scanning);
+
+- REG_CLR_BIT(ah, AR_DIAG_SW, (AR_DIAG_RX_DIS | AR_DIAG_RX_ABORT));
++ REG_CLR_BIT(ah, AR_DIAG_SW,
++ AR_DIAG_RX_DIS | AR_DIAG_RX_ABORT | AR_DIAG_FORCE_RX_CLEAR);
+ }
+ EXPORT_SYMBOL(ath9k_hw_startpcureceive);
+
+ void ath9k_hw_abortpcurecv(struct ath_hw *ah)
+ {
+- REG_SET_BIT(ah, AR_DIAG_SW, AR_DIAG_RX_ABORT | AR_DIAG_RX_DIS);
++ REG_SET_BIT(ah, AR_DIAG_SW,
++ AR_DIAG_RX_DIS | AR_DIAG_RX_ABORT | AR_DIAG_FORCE_RX_CLEAR);
+
+ ath9k_hw_disable_mib_counters(ah);
+ }
--- /dev/null
+From: Hante Meuleman <meuleman@broadcom.com>
+Date: Mon, 8 Jun 2015 14:38:32 +0200
+Subject: [PATCH] brcmfmac: Update msgbuf read pointer quicker.
+
+On device to host data using msgbuf the read pointer gets updated
+once all data is processed. Updating this pointer more frequently
+allows the firmware to add more data quicker. This will result in
+slightly higher and more stable throughput on CPU bounded host
+processors.
+
+Reviewed-by: Arend Van Spriel <arend@broadcom.com>
+Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com>
+Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
+Signed-off-by: Hante Meuleman <meuleman@broadcom.com>
+Signed-off-by: Arend van Spriel <arend@broadcom.com>
+Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
+---
+
+--- a/drivers/net/wireless/brcm80211/brcmfmac/commonring.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/commonring.c
+@@ -223,8 +223,6 @@ void brcmf_commonring_write_cancel(struc
+ void *brcmf_commonring_get_read_ptr(struct brcmf_commonring *commonring,
+ u16 *n_items)
+ {
+- void *ret_addr;
+-
+ if (commonring->cr_update_wptr)
+ commonring->cr_update_wptr(commonring->cr_ctx);
+
+@@ -235,19 +233,18 @@ void *brcmf_commonring_get_read_ptr(stru
+ if (*n_items == 0)
+ return NULL;
+
+- ret_addr = commonring->buf_addr +
+- (commonring->r_ptr * commonring->item_len);
+-
+- commonring->r_ptr += *n_items;
+- if (commonring->r_ptr == commonring->depth)
+- commonring->r_ptr = 0;
+-
+- return ret_addr;
++ return commonring->buf_addr +
++ (commonring->r_ptr * commonring->item_len);
+ }
+
+
+-int brcmf_commonring_read_complete(struct brcmf_commonring *commonring)
++int brcmf_commonring_read_complete(struct brcmf_commonring *commonring,
++ u16 n_items)
+ {
++ commonring->r_ptr += n_items;
++ if (commonring->r_ptr == commonring->depth)
++ commonring->r_ptr = 0;
++
+ if (commonring->cr_write_rptr)
+ return commonring->cr_write_rptr(commonring->cr_ctx);
+
+--- a/drivers/net/wireless/brcm80211/brcmfmac/commonring.h
++++ b/drivers/net/wireless/brcm80211/brcmfmac/commonring.h
+@@ -62,7 +62,8 @@ void brcmf_commonring_write_cancel(struc
+ u16 n_items);
+ void *brcmf_commonring_get_read_ptr(struct brcmf_commonring *commonring,
+ u16 *n_items);
+-int brcmf_commonring_read_complete(struct brcmf_commonring *commonring);
++int brcmf_commonring_read_complete(struct brcmf_commonring *commonring,
++ u16 n_items);
+
+ #define brcmf_commonring_n_items(commonring) (commonring->depth)
+ #define brcmf_commonring_len_item(commonring) (commonring->item_len)
+--- a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c
+@@ -75,6 +75,8 @@
+
+ #define BRCMF_MSGBUF_DELAY_TXWORKER_THRS 96
+ #define BRCMF_MSGBUF_TRICKLE_TXWORKER_THRS 32
++#define BRCMF_MSGBUF_UPDATE_RX_PTR_THRS 48
++
+
+ struct msgbuf_common_hdr {
+ u8 msgtype;
+@@ -1257,19 +1259,27 @@ static void brcmf_msgbuf_process_rx(stru
+ {
+ void *buf;
+ u16 count;
++ u16 processed;
+
+ again:
+ buf = brcmf_commonring_get_read_ptr(commonring, &count);
+ if (buf == NULL)
+ return;
+
++ processed = 0;
+ while (count) {
+ brcmf_msgbuf_process_msgtype(msgbuf,
+ buf + msgbuf->rx_dataoffset);
+ buf += brcmf_commonring_len_item(commonring);
++ processed++;
++ if (processed == BRCMF_MSGBUF_UPDATE_RX_PTR_THRS) {
++ brcmf_commonring_read_complete(commonring, processed);
++ processed = 0;
++ }
+ count--;
+ }
+- brcmf_commonring_read_complete(commonring);
++ if (processed)
++ brcmf_commonring_read_complete(commonring, processed);
+
+ if (commonring->r_ptr == 0)
+ goto again;
--- /dev/null
+From: Arend van Spriel <arend@broadcom.com>
+Date: Mon, 8 Jun 2015 14:38:33 +0200
+Subject: [PATCH] brcmfmac: remove chipinfo debugfs entry
+
+The information provided by chipinfo is also provided by the
+revinfo debugfs entry. Removing it from debugfs.
+
+Reviewed-by: Hante Meuleman <meuleman@broadcom.com>
+Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
+Signed-off-by: Arend van Spriel <arend@broadcom.com>
+Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
+---
+
+--- a/drivers/net/wireless/brcm80211/brcmfmac/debug.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/debug.c
+@@ -41,15 +41,6 @@ void brcmf_debugfs_exit(void)
+ root_folder = NULL;
+ }
+
+-static int brcmf_debugfs_chipinfo_read(struct seq_file *seq, void *data)
+-{
+- struct brcmf_bus *bus = dev_get_drvdata(seq->private);
+-
+- seq_printf(seq, "chip: %x(%u) rev %u\n",
+- bus->chip, bus->chip, bus->chiprev);
+- return 0;
+-}
+-
+ int brcmf_debugfs_attach(struct brcmf_pub *drvr)
+ {
+ struct device *dev = drvr->bus_if->dev;
+@@ -58,7 +49,6 @@ int brcmf_debugfs_attach(struct brcmf_pu
+ return -ENODEV;
+
+ drvr->dbgfs_dir = debugfs_create_dir(dev_name(dev), root_folder);
+- brcmf_debugfs_add_entry(drvr, "chipinfo", brcmf_debugfs_chipinfo_read);
+
+ return PTR_ERR_OR_ZERO(drvr->dbgfs_dir);
+ }
--- /dev/null
+From: Arend van Spriel <arend@broadcom.com>
+Date: Mon, 8 Jun 2015 14:38:34 +0200
+Subject: [PATCH] brcmfmac: remove watchdog reset from
+ brcmf_pcie_buscoreprep()
+
+The watchdog reset as done in brcmf_pcie_buscoreprep() is not
+sufficient. It needs to modify PCIe core registers as well
+which is properly done by brcmf_pcie_reset_device() after the
+chip recognition is done. So the faulty watchdog reset can be
+removed as it was causing driver reload to fail and hang the
+system requiring a power-cycle. Instead the call to to the
+brcmf_pcie_reset_device() function is done twice in the unload.
+
+Reviewed-by: Hante Meuleman <meuleman@broadcom.com>
+Reviewed-by: Daniel (Deognyoun) Kim <dekim@broadcom.com>
+Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com>
+Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
+Signed-off-by: Arend van Spriel <arend@broadcom.com>
+Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
+---
+
+--- a/drivers/net/wireless/brcm80211/brcmfmac/pcie.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/pcie.c
+@@ -1629,20 +1629,7 @@ static void brcmf_pcie_buscore_write32(v
+
+ static int brcmf_pcie_buscoreprep(void *ctx)
+ {
+- struct brcmf_pciedev_info *devinfo = (struct brcmf_pciedev_info *)ctx;
+- int err;
+-
+- err = brcmf_pcie_get_resource(devinfo);
+- if (err == 0) {
+- /* Set CC watchdog to reset all the cores on the chip to bring
+- * back dongle to a sane state.
+- */
+- brcmf_pcie_buscore_write32(ctx, CORE_CC_REG(SI_ENUM_BASE,
+- watchdog), 4);
+- msleep(100);
+- }
+-
+- return err;
++ return brcmf_pcie_get_resource(ctx);
+ }
+
+
+@@ -1824,6 +1811,7 @@ brcmf_pcie_remove(struct pci_dev *pdev)
+ brcmf_pcie_intr_disable(devinfo);
+
+ brcmf_detach(&pdev->dev);
++ brcmf_pcie_reset_device(devinfo);
+
+ kfree(bus->bus_priv.pcie);
+ kfree(bus->msgbuf->flowrings);
--- /dev/null
+From: Arend van Spriel <arend@broadcom.com>
+Date: Mon, 8 Jun 2015 14:38:35 +0200
+Subject: [PATCH] brcmfmac: use debugfs_create_devm_seqfile() helper
+ function
+
+Some time ago the function debugfs_create_devm_seqfile() was
+introduced in debugfs. The caller simply needs to provide a
+device pointer and read function. The function brcmf_debugfs_add_entry()
+is now simply a wrapper only doing the work for CONFIG_BRCMDBG.
+
+Reviewed-by: Hante Meuleman <meuleman@broadcom.com>
+Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com>
+Reviewed-by: Daniel (Deognyoun) Kim <dekim@broadcom.com>
+Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
+Signed-off-by: Arend van Spriel <arend@broadcom.com>
+Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
+---
+
+--- a/drivers/net/wireless/brcm80211/brcmfmac/debug.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/debug.c
+@@ -64,44 +64,12 @@ struct dentry *brcmf_debugfs_get_devdir(
+ return drvr->dbgfs_dir;
+ }
+
+-struct brcmf_debugfs_entry {
+- int (*read)(struct seq_file *seq, void *data);
+- struct brcmf_pub *drvr;
+-};
+-
+-static int brcmf_debugfs_entry_open(struct inode *inode, struct file *f)
+-{
+- struct brcmf_debugfs_entry *entry = inode->i_private;
+-
+- return single_open(f, entry->read, entry->drvr->bus_if->dev);
+-}
+-
+-static const struct file_operations brcmf_debugfs_def_ops = {
+- .owner = THIS_MODULE,
+- .open = brcmf_debugfs_entry_open,
+- .release = single_release,
+- .read = seq_read,
+- .llseek = seq_lseek
+-};
+-
+ 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);
++ struct dentry *e;
+
+- return PTR_ERR_OR_ZERO(dentry);
++ e = debugfs_create_devm_seqfile(drvr->bus_if->dev, fn,
++ drvr->dbgfs_dir, read_fn);
++ return PTR_ERR_OR_ZERO(e);
+ }
--- /dev/null
+From: Felix Fietkau <nbd@openwrt.org>
+Date: Sun, 21 Jun 2015 19:45:59 +0200
+Subject: [PATCH] ath9k_hw: fix device ID check for AR956x
+
+Because of the missing return, the macVersion value was being
+overwritten with an invalid register read
+
+Signed-off-by: Felix Fietkau <nbd@openwrt.org>
+---
+
+--- a/drivers/net/wireless/ath/ath9k/hw.c
++++ b/drivers/net/wireless/ath/ath9k/hw.c
+@@ -278,6 +278,7 @@ static void ath9k_hw_read_revisions(stru
+ return;
+ case AR9300_DEVID_QCA956X:
+ ah->hw_version.macVersion = AR_SREV_VERSION_9561;
++ return;
+ }
+
+ val = REG_READ(ah, AR_SREV) & AR_SREV_ID;
--- /dev/null
+From: Pontus Fuchs <pontusf@broadcom.com>
+Date: Thu, 11 Jun 2015 00:12:17 +0200
+Subject: [PATCH] brcmfmac: Check if firmware supports p2p
+
+Add a feature flag to reflect the firmware's p2p capability.
+
+Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
+Reviewed-by: Hante Meuleman <meuleman@broadcom.com>
+Reviewed-by: Arend Van Spriel <arend@broadcom.com>
+Signed-off-by: Pontus Fuchs <pontusf@broadcom.com>
+Signed-off-by: Arend van Spriel <arend@broadcom.com>
+Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
+---
+
+--- a/drivers/net/wireless/brcm80211/brcmfmac/feature.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/feature.c
+@@ -129,6 +129,7 @@ void brcmf_feat_attach(struct brcmf_pub
+ brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_WOWL, "wowl");
+ if (drvr->bus_if->chip != BRCM_CC_43362_CHIP_ID)
+ brcmf_feat_iovar_int_set(ifp, BRCMF_FEAT_MBSS, "mbss", 0);
++ brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_P2P, "p2p");
+
+ /* set chip related quirks */
+ switch (drvr->bus_if->chip) {
+--- a/drivers/net/wireless/brcm80211/brcmfmac/feature.h
++++ b/drivers/net/wireless/brcm80211/brcmfmac/feature.h
+@@ -23,12 +23,14 @@
+ * MCHAN: multi-channel for concurrent P2P.
+ * PNO: preferred network offload.
+ * WOWL: Wake-On-WLAN.
++ * P2P: peer-to-peer
+ */
+ #define BRCMF_FEAT_LIST \
+ BRCMF_FEAT_DEF(MBSS) \
+ BRCMF_FEAT_DEF(MCHAN) \
+ BRCMF_FEAT_DEF(PNO) \
+- BRCMF_FEAT_DEF(WOWL)
++ BRCMF_FEAT_DEF(WOWL) \
++ BRCMF_FEAT_DEF(P2P)
+ /*
+ * Quirks:
+ *
--- /dev/null
+From: Pontus Fuchs <pontusf@broadcom.com>
+Date: Thu, 11 Jun 2015 00:12:18 +0200
+Subject: [PATCH] brcmfmac: Build wiphy mode and interface combinations
+ dynamically
+
+Switch from using semi hard coded interface combinations. This makes
+it easier to announce what the firmware actually supports. This fixes
+the case where brcmfmac announces p2p but the firmware doesn't
+support it.
+
+Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
+Reviewed-by: Hante Meuleman <meuleman@broadcom.com>
+Reviewed-by: Arend Van Spriel <arend@broadcom.com>
+Signed-off-by: Pontus Fuchs <pontusf@broadcom.com>
+Signed-off-by: Arend van Spriel <arend@broadcom.com>
+Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
+---
+
+--- a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c
+@@ -52,8 +52,6 @@
+ #define BRCMF_PNO_SCAN_COMPLETE 1
+ #define BRCMF_PNO_SCAN_INCOMPLETE 0
+
+-#define BRCMF_IFACE_MAX_CNT 3
+-
+ #define WPA_OUI "\x00\x50\xF2" /* WPA OUI */
+ #define WPA_OUI_TYPE 1
+ #define RSN_OUI "\x00\x0F\xAC" /* RSN OUI */
+@@ -5639,53 +5637,6 @@ static int brcmf_setup_wiphybands(struct
+ return 0;
+ }
+
+-static const struct ieee80211_iface_limit brcmf_iface_limits_mbss[] = {
+- {
+- .max = 1,
+- .types = BIT(NL80211_IFTYPE_STATION) |
+- BIT(NL80211_IFTYPE_ADHOC)
+- },
+- {
+- .max = 4,
+- .types = 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_limit brcmf_iface_limits_sbss[] = {
+- {
+- .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_sbss),
+- .limits = brcmf_iface_limits_sbss,
+- }
+-};
+-
+ static const struct ieee80211_txrx_stypes
+ brcmf_txrx_stypes[NUM_NL80211_IFTYPES] = {
+ [NL80211_IFTYPE_STATION] = {
+@@ -5715,6 +5666,67 @@ brcmf_txrx_stypes[NUM_NL80211_IFTYPES] =
+ }
+ };
+
++static int brcmf_setup_ifmodes(struct wiphy *wiphy, struct brcmf_if *ifp)
++{
++ struct ieee80211_iface_combination *combo = NULL;
++ struct ieee80211_iface_limit *limits = NULL;
++ int i = 0, max_iface_cnt;
++
++ combo = kzalloc(sizeof(*combo), GFP_KERNEL);
++ if (!combo)
++ goto err;
++
++ limits = kzalloc(sizeof(*limits) * 4, GFP_KERNEL);
++ if (!limits)
++ goto err;
++
++ wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
++ BIT(NL80211_IFTYPE_ADHOC) |
++ BIT(NL80211_IFTYPE_AP);
++
++ if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MCHAN))
++ combo->num_different_channels = 2;
++ else
++ combo->num_different_channels = 1;
++
++ if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS)) {
++ limits[i].max = 1;
++ limits[i++].types = BIT(NL80211_IFTYPE_STATION);
++ limits[i].max = 4;
++ limits[i++].types = BIT(NL80211_IFTYPE_AP);
++ max_iface_cnt = 5;
++ } else {
++ limits[i].max = 2;
++ limits[i++].types = BIT(NL80211_IFTYPE_STATION) |
++ BIT(NL80211_IFTYPE_AP);
++ max_iface_cnt = 2;
++ }
++
++ if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_P2P)) {
++ wiphy->interface_modes |= BIT(NL80211_IFTYPE_P2P_CLIENT) |
++ BIT(NL80211_IFTYPE_P2P_GO) |
++ BIT(NL80211_IFTYPE_P2P_DEVICE);
++ limits[i].max = 1;
++ limits[i++].types = BIT(NL80211_IFTYPE_P2P_CLIENT) |
++ BIT(NL80211_IFTYPE_P2P_GO);
++ limits[i].max = 1;
++ limits[i++].types = BIT(NL80211_IFTYPE_P2P_DEVICE);
++ max_iface_cnt += 2;
++ }
++ combo->max_interfaces = max_iface_cnt;
++ combo->limits = limits;
++ combo->n_limits = i;
++
++ wiphy->iface_combinations = combo;
++ wiphy->n_iface_combinations = 1;
++ return 0;
++
++err:
++ kfree(limits);
++ kfree(combo);
++ return -ENOMEM;
++}
++
+ static void brcmf_wiphy_pno_params(struct wiphy *wiphy)
+ {
+ /* scheduled scan settings */
+@@ -5745,7 +5757,6 @@ static void brcmf_wiphy_wowl_params(stru
+ static int brcmf_setup_wiphy(struct wiphy *wiphy, struct brcmf_if *ifp)
+ {
+ struct ieee80211_supported_band *band;
+- struct ieee80211_iface_combination ifc_combo;
+ __le32 bandlist[3];
+ u32 n_bands;
+ int err, i;
+@@ -5753,24 +5764,11 @@ static int brcmf_setup_wiphy(struct wiph
+ 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;
+- if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS)) {
+- ifc_combo.n_limits = ARRAY_SIZE(brcmf_iface_limits_mbss),
+- ifc_combo.limits = brcmf_iface_limits_mbss;
+- }
+- wiphy->iface_combinations = kmemdup(&ifc_combo,
+- sizeof(ifc_combo),
+- GFP_KERNEL);
+- wiphy->n_iface_combinations = ARRAY_SIZE(brcmf_iface_combos);
++
++ err = brcmf_setup_ifmodes(wiphy, ifp);
++ if (err)
++ return err;
++
+ wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
+ wiphy->cipher_suites = __wl_cipher_suites;
+ wiphy->n_cipher_suites = ARRAY_SIZE(__wl_cipher_suites);
+@@ -6035,6 +6033,8 @@ static void brcmf_free_wiphy(struct wiph
+ if (!wiphy)
+ return;
+
++ if (wiphy->iface_combinations)
++ kfree(wiphy->iface_combinations->limits);
+ kfree(wiphy->iface_combinations);
+ if (wiphy->bands[IEEE80211_BAND_2GHZ]) {
+ kfree(wiphy->bands[IEEE80211_BAND_2GHZ]->channels);
--- /dev/null
+From: Arend van Spriel <arend@broadcom.com>
+Date: Thu, 11 Jun 2015 00:12:19 +0200
+Subject: [PATCH] brcmfmac: rework .get_station() callback
+
+The .get_station() cfg80211 callback is used in several scenarios. In
+managed mode it can obtain information about the access-point and its
+BSS parameters. In managed mode it can also obtain information about
+TDLS peers. In AP mode it can obtain information about connected
+clients.
+
+Reviewed-by: Hante Meuleman <meuleman@broadcom.com>
+Reviewed-by: Daniel (Deognyoun) Kim <dekim@broadcom.com>
+Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com>
+Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
+Signed-off-by: Arend van Spriel <arend@broadcom.com>
+Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
+---
+
+--- a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c
+@@ -2395,27 +2395,80 @@ brcmf_cfg80211_reconfigure_wep(struct br
+ brcmf_err("set wsec error (%d)\n", err);
+ }
+
++static void brcmf_convert_sta_flags(u32 fw_sta_flags, struct station_info *si)
++{
++ struct nl80211_sta_flag_update *sfu;
++
++ brcmf_dbg(TRACE, "flags %08x\n", fw_sta_flags);
++ si->filled |= BIT(NL80211_STA_INFO_STA_FLAGS);
++ sfu = &si->sta_flags;
++ sfu->mask = BIT(NL80211_STA_FLAG_WME) |
++ BIT(NL80211_STA_FLAG_AUTHENTICATED) |
++ BIT(NL80211_STA_FLAG_ASSOCIATED) |
++ BIT(NL80211_STA_FLAG_AUTHORIZED);
++ if (fw_sta_flags & BRCMF_STA_WME)
++ sfu->set |= BIT(NL80211_STA_FLAG_WME);
++ if (fw_sta_flags & BRCMF_STA_AUTHE)
++ sfu->set |= BIT(NL80211_STA_FLAG_AUTHENTICATED);
++ if (fw_sta_flags & BRCMF_STA_ASSOC)
++ sfu->set |= BIT(NL80211_STA_FLAG_ASSOCIATED);
++ if (fw_sta_flags & BRCMF_STA_AUTHO)
++ sfu->set |= BIT(NL80211_STA_FLAG_AUTHORIZED);
++}
++
++static void brcmf_fill_bss_param(struct brcmf_if *ifp, struct station_info *si)
++{
++ struct {
++ __le32 len;
++ struct brcmf_bss_info_le bss_le;
++ } *buf;
++ u16 capability;
++ int err;
++
++ buf = kzalloc(WL_BSS_INFO_MAX, GFP_KERNEL);
++ if (!buf)
++ return;
++
++ buf->len = cpu_to_le32(WL_BSS_INFO_MAX);
++ err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BSS_INFO, buf,
++ WL_BSS_INFO_MAX);
++ if (err) {
++ brcmf_err("Failed to get bss info (%d)\n", err);
++ return;
++ }
++ si->filled |= BIT(NL80211_STA_INFO_BSS_PARAM);
++ si->bss_param.beacon_interval = le16_to_cpu(buf->bss_le.beacon_period);
++ si->bss_param.dtim_period = buf->bss_le.dtim_period;
++ capability = le16_to_cpu(buf->bss_le.capability);
++ if (capability & IEEE80211_HT_STBC_PARAM_DUAL_CTS_PROT)
++ si->bss_param.flags |= BSS_PARAM_FLAGS_CTS_PROT;
++ if (capability & WLAN_CAPABILITY_SHORT_PREAMBLE)
++ si->bss_param.flags |= BSS_PARAM_FLAGS_SHORT_PREAMBLE;
++ if (capability & WLAN_CAPABILITY_SHORT_SLOT_TIME)
++ si->bss_param.flags |= BSS_PARAM_FLAGS_SHORT_SLOT_TIME;
++}
++
+ static s32
+ brcmf_cfg80211_get_station(struct wiphy *wiphy, struct net_device *ndev,
+ const u8 *mac, struct station_info *sinfo)
+ {
+ struct brcmf_if *ifp = netdev_priv(ndev);
+- struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
+- struct brcmf_scb_val_le scb_val;
+- int rssi;
+- s32 rate;
+ s32 err = 0;
+- u8 *bssid = profile->bssid;
+ struct brcmf_sta_info_le sta_info_le;
+- u32 beacon_period;
+- u32 dtim_period;
++ u32 sta_flags;
++ u32 is_tdls_peer;
+
+ brcmf_dbg(TRACE, "Enter, MAC %pM\n", mac);
+ if (!check_vif_up(ifp->vif))
+ return -EIO;
+
+- if (brcmf_is_apmode(ifp->vif)) {
+- memcpy(&sta_info_le, mac, ETH_ALEN);
++ memset(&sta_info_le, 0, sizeof(sta_info_le));
++ memcpy(&sta_info_le, mac, ETH_ALEN);
++ err = brcmf_fil_iovar_data_get(ifp, "tdls_sta_info",
++ &sta_info_le,
++ sizeof(sta_info_le));
++ is_tdls_peer = !err;
++ if (err) {
+ err = brcmf_fil_iovar_data_get(ifp, "sta_info",
+ &sta_info_le,
+ sizeof(sta_info_le));
+@@ -2423,73 +2476,48 @@ brcmf_cfg80211_get_station(struct wiphy
+ brcmf_err("GET STA INFO failed, %d\n", err);
+ goto done;
+ }
+- sinfo->filled = BIT(NL80211_STA_INFO_INACTIVE_TIME);
+- sinfo->inactive_time = le32_to_cpu(sta_info_le.idle) * 1000;
+- if (le32_to_cpu(sta_info_le.flags) & BRCMF_STA_ASSOC) {
+- sinfo->filled |= BIT(NL80211_STA_INFO_CONNECTED_TIME);
+- sinfo->connected_time = le32_to_cpu(sta_info_le.in);
+- }
+- brcmf_dbg(TRACE, "STA idle time : %d ms, connected time :%d sec\n",
+- sinfo->inactive_time, sinfo->connected_time);
+- } else if (ifp->vif->wdev.iftype == NL80211_IFTYPE_STATION) {
+- if (memcmp(mac, bssid, ETH_ALEN)) {
+- brcmf_err("Wrong Mac address cfg_mac-%pM wl_bssid-%pM\n",
+- mac, bssid);
+- err = -ENOENT;
+- goto done;
+- }
+- /* Report the current tx rate */
+- err = brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_RATE, &rate);
+- if (err) {
+- brcmf_err("Could not get rate (%d)\n", err);
+- goto done;
+- } else {
++ }
++ brcmf_dbg(TRACE, "version %d\n", le16_to_cpu(sta_info_le.ver));
++ sinfo->filled = BIT(NL80211_STA_INFO_INACTIVE_TIME);
++ sinfo->inactive_time = le32_to_cpu(sta_info_le.idle) * 1000;
++ sta_flags = le32_to_cpu(sta_info_le.flags);
++ brcmf_convert_sta_flags(sta_flags, sinfo);
++ sinfo->sta_flags.mask |= BIT(NL80211_STA_FLAG_TDLS_PEER);
++ if (is_tdls_peer)
++ sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_TDLS_PEER);
++ else
++ sinfo->sta_flags.set &= ~BIT(NL80211_STA_FLAG_TDLS_PEER);
++ if (sta_flags & BRCMF_STA_ASSOC) {
++ sinfo->filled |= BIT(NL80211_STA_INFO_CONNECTED_TIME);
++ sinfo->connected_time = le32_to_cpu(sta_info_le.in);
++ brcmf_fill_bss_param(ifp, sinfo);
++ }
++ if (sta_flags & BRCMF_STA_SCBSTATS) {
++ sinfo->filled |= BIT(NL80211_STA_INFO_TX_FAILED);
++ sinfo->tx_failed = le32_to_cpu(sta_info_le.tx_failures);
++ sinfo->filled |= BIT(NL80211_STA_INFO_TX_PACKETS);
++ sinfo->tx_packets = le32_to_cpu(sta_info_le.tx_pkts);
++ sinfo->tx_packets += le32_to_cpu(sta_info_le.tx_mcast_pkts);
++ sinfo->filled |= BIT(NL80211_STA_INFO_RX_PACKETS);
++ sinfo->rx_packets = le32_to_cpu(sta_info_le.rx_ucast_pkts);
++ sinfo->rx_packets += le32_to_cpu(sta_info_le.rx_mcast_pkts);
++ if (sinfo->tx_packets) {
+ sinfo->filled |= BIT(NL80211_STA_INFO_TX_BITRATE);
+- sinfo->txrate.legacy = rate * 5;
+- brcmf_dbg(CONN, "Rate %d Mbps\n", rate / 2);
++ sinfo->txrate.legacy = le32_to_cpu(sta_info_le.tx_rate);
++ sinfo->txrate.legacy /= 100;
+ }
+-
+- if (test_bit(BRCMF_VIF_STATUS_CONNECTED,
+- &ifp->vif->sme_state)) {
+- memset(&scb_val, 0, sizeof(scb_val));
+- err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_RSSI,
+- &scb_val, sizeof(scb_val));
+- if (err) {
+- brcmf_err("Could not get rssi (%d)\n", err);
+- goto done;
+- } else {
+- rssi = le32_to_cpu(scb_val.val);
+- sinfo->filled |= BIT(NL80211_STA_INFO_SIGNAL);
+- sinfo->signal = rssi;
+- brcmf_dbg(CONN, "RSSI %d dBm\n", rssi);
+- }
+- err = brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_BCNPRD,
+- &beacon_period);
+- if (err) {
+- brcmf_err("Could not get beacon period (%d)\n",
+- err);
+- goto done;
+- } else {
+- sinfo->bss_param.beacon_interval =
+- beacon_period;
+- brcmf_dbg(CONN, "Beacon peroid %d\n",
+- beacon_period);
+- }
+- err = brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_DTIMPRD,
+- &dtim_period);
+- if (err) {
+- brcmf_err("Could not get DTIM period (%d)\n",
+- err);
+- goto done;
+- } else {
+- sinfo->bss_param.dtim_period = dtim_period;
+- brcmf_dbg(CONN, "DTIM peroid %d\n",
+- dtim_period);
+- }
+- sinfo->filled |= BIT(NL80211_STA_INFO_BSS_PARAM);
++ if (sinfo->rx_packets) {
++ sinfo->filled |= BIT(NL80211_STA_INFO_RX_BITRATE);
++ sinfo->rxrate.legacy = le32_to_cpu(sta_info_le.rx_rate);
++ sinfo->rxrate.legacy /= 100;
++ }
++ if (le16_to_cpu(sta_info_le.ver) >= 4) {
++ sinfo->filled |= BIT(NL80211_STA_INFO_TX_BYTES);
++ sinfo->tx_bytes = le64_to_cpu(sta_info_le.tx_tot_bytes);
++ sinfo->filled |= BIT(NL80211_STA_INFO_RX_BYTES);
++ sinfo->rx_bytes = le64_to_cpu(sta_info_le.rx_tot_bytes);
+ }
+- } else
+- err = -EPERM;
++ }
+ done:
+ brcmf_dbg(TRACE, "Exit\n");
+ return err;
+--- a/drivers/net/wireless/brcm80211/brcmfmac/fwil_types.h
++++ b/drivers/net/wireless/brcm80211/brcmfmac/fwil_types.h
+@@ -32,7 +32,11 @@
+ #define BRCMF_BSS_INFO_VERSION 109 /* curr ver of brcmf_bss_info_le struct */
+ #define BRCMF_BSS_RSSI_ON_CHANNEL 0x0002
+
+-#define BRCMF_STA_ASSOC 0x10 /* Associated */
++#define BRCMF_STA_WME 0x00000002 /* WMM association */
++#define BRCMF_STA_AUTHE 0x00000008 /* Authenticated */
++#define BRCMF_STA_ASSOC 0x00000010 /* Associated */
++#define BRCMF_STA_AUTHO 0x00000020 /* Authorized */
++#define BRCMF_STA_SCBSTATS 0x00004000 /* Per STA debug stats */
+
+ /* size of brcmf_scan_params not including variable length array */
+ #define BRCMF_SCAN_PARAMS_FIXED_SIZE 64
+@@ -113,6 +117,7 @@
+ #define BRCMF_WOWL_MAXPATTERNSIZE 128
+
+ #define BRCMF_COUNTRY_BUF_SZ 4
++#define BRCMF_ANT_MAX 4
+
+ /* join preference types for join_pref iovar */
+ enum brcmf_join_pref_types {
+@@ -456,25 +461,61 @@ struct brcmf_channel_info_le {
+ };
+
+ struct brcmf_sta_info_le {
+- __le16 ver; /* version of this struct */
+- __le16 len; /* length in bytes of this structure */
+- __le16 cap; /* sta's advertised capabilities */
+- __le32 flags; /* flags defined below */
+- __le32 idle; /* time since data pkt rx'd from sta */
+- u8 ea[ETH_ALEN]; /* Station address */
+- __le32 count; /* # rates in this set */
+- u8 rates[BRCMF_MAXRATES_IN_SET]; /* rates in 500kbps units */
++ __le16 ver; /* version of this struct */
++ __le16 len; /* length in bytes of this structure */
++ __le16 cap; /* sta's advertised capabilities */
++ __le32 flags; /* flags defined below */
++ __le32 idle; /* time since data pkt rx'd from sta */
++ u8 ea[ETH_ALEN]; /* Station address */
++ __le32 count; /* # rates in this set */
++ u8 rates[BRCMF_MAXRATES_IN_SET]; /* rates in 500kbps units */
+ /* w/hi bit set if basic */
+- __le32 in; /* seconds elapsed since associated */
+- __le32 listen_interval_inms; /* Min Listen interval in ms for STA */
+- __le32 tx_pkts; /* # of packets transmitted */
+- __le32 tx_failures; /* # of packets failed */
+- __le32 rx_ucast_pkts; /* # of unicast packets received */
+- __le32 rx_mcast_pkts; /* # of multicast packets received */
+- __le32 tx_rate; /* Rate of last successful tx frame */
+- __le32 rx_rate; /* Rate of last successful rx frame */
+- __le32 rx_decrypt_succeeds; /* # of packet decrypted successfully */
+- __le32 rx_decrypt_failures; /* # of packet decrypted failed */
++ __le32 in; /* seconds elapsed since associated */
++ __le32 listen_interval_inms; /* Min Listen interval in ms for STA */
++ __le32 tx_pkts; /* # of packets transmitted */
++ __le32 tx_failures; /* # of packets failed */
++ __le32 rx_ucast_pkts; /* # of unicast packets received */
++ __le32 rx_mcast_pkts; /* # of multicast packets received */
++ __le32 tx_rate; /* Rate of last successful tx frame */
++ __le32 rx_rate; /* Rate of last successful rx frame */
++ __le32 rx_decrypt_succeeds; /* # of packet decrypted successfully */
++ __le32 rx_decrypt_failures; /* # of packet decrypted failed */
++ __le32 tx_tot_pkts; /* # of tx pkts (ucast + mcast) */
++ __le32 rx_tot_pkts; /* # of data packets recvd (uni + mcast) */
++ __le32 tx_mcast_pkts; /* # of mcast pkts txed */
++ __le64 tx_tot_bytes; /* data bytes txed (ucast + mcast) */
++ __le64 rx_tot_bytes; /* data bytes recvd (ucast + mcast) */
++ __le64 tx_ucast_bytes; /* data bytes txed (ucast) */
++ __le64 tx_mcast_bytes; /* # data bytes txed (mcast) */
++ __le64 rx_ucast_bytes; /* data bytes recvd (ucast) */
++ __le64 rx_mcast_bytes; /* data bytes recvd (mcast) */
++ s8 rssi[BRCMF_ANT_MAX]; /* per antenna rssi */
++ s8 nf[BRCMF_ANT_MAX]; /* per antenna noise floor */
++ __le16 aid; /* association ID */
++ __le16 ht_capabilities; /* advertised ht caps */
++ __le16 vht_flags; /* converted vht flags */
++ __le32 tx_pkts_retry_cnt; /* # of frames where a retry was
++ * exhausted.
++ */
++ __le32 tx_pkts_retry_exhausted; /* # of user frames where a retry
++ * was exhausted
++ */
++ s8 rx_lastpkt_rssi[BRCMF_ANT_MAX]; /* Per antenna RSSI of last
++ * received data frame.
++ */
++ /* TX WLAN retry/failure statistics:
++ * Separated for host requested frames and locally generated frames.
++ * Include unicast frame only where the retries/failures can be counted.
++ */
++ __le32 tx_pkts_total; /* # user frames sent successfully */
++ __le32 tx_pkts_retries; /* # user frames retries */
++ __le32 tx_pkts_fw_total; /* # FW generated sent successfully */
++ __le32 tx_pkts_fw_retries; /* # retries for FW generated frames */
++ __le32 tx_pkts_fw_retry_exhausted; /* # FW generated where a retry
++ * was exhausted
++ */
++ __le32 rx_pkts_retried; /* # rx with retry bit set */
++ __le32 tx_rate_fallback; /* lowest fallback TX rate */
+ };
+
+ struct brcmf_chanspec_list {
--- /dev/null
+From: Arend van Spriel <arend@broadcom.com>
+Date: Thu, 11 Jun 2015 00:12:20 +0200
+Subject: [PATCH] brcmfmac: have sdio return -EIO when device communication
+ is not possible
+
+The bus interface functions txctl and rxctl may be used while the device
+can not be accessed, eg. upon driver .remove() callback. This patch will
+immediately return -EIO when this is the case which speeds up the module
+unload.
+
+Reviewed-by: Hante Meuleman <meuleman@broadcom.com>
+Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com>
+Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
+Signed-off-by: Arend van Spriel <arend@broadcom.com>
+Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
+---
+
+--- a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c
+@@ -988,6 +988,7 @@ static void brcmf_sdiod_freezer_detach(s
+
+ static int brcmf_sdiod_remove(struct brcmf_sdio_dev *sdiodev)
+ {
++ sdiodev->state = BRCMF_SDIOD_DOWN;
+ if (sdiodev->bus) {
+ brcmf_sdio_remove(sdiodev->bus);
+ sdiodev->bus = NULL;
+--- a/drivers/net/wireless/brcm80211/brcmfmac/sdio.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c
+@@ -2820,6 +2820,8 @@ static int brcmf_sdio_bus_txdata(struct
+ struct brcmf_sdio *bus = sdiodev->bus;
+
+ brcmf_dbg(TRACE, "Enter: pkt: data %p len %d\n", pkt->data, pkt->len);
++ if (sdiodev->state != BRCMF_SDIOD_DATA)
++ return -EIO;
+
+ /* Add space for the header */
+ skb_push(pkt, bus->tx_hdrlen);
+@@ -2948,6 +2950,8 @@ brcmf_sdio_bus_txctl(struct device *dev,
+ int ret;
+
+ brcmf_dbg(TRACE, "Enter\n");
++ if (sdiodev->state != BRCMF_SDIOD_DATA)
++ return -EIO;
+
+ /* Send from dpc */
+ bus->ctrl_frame_buf = msg;
+@@ -3238,6 +3242,8 @@ brcmf_sdio_bus_rxctl(struct device *dev,
+ struct brcmf_sdio *bus = sdiodev->bus;
+
+ brcmf_dbg(TRACE, "Enter\n");
++ if (sdiodev->state != BRCMF_SDIOD_DATA)
++ return -EIO;
+
+ /* Wait until control frame is available */
+ timeleft = brcmf_sdio_dcmd_resp_wait(bus, &bus->rxlen, &pending);
--- /dev/null
+From: Felix Fietkau <nbd@openwrt.org>
+Date: Thu, 2 Jul 2015 13:35:05 +0200
+Subject: [PATCH] ath9k: make DMA stop related messages debug-only
+
+A long time ago, ath9k had issues during reset where the DMA engine
+would stay active and could potentially corrupt memory.
+To debug those issues, the driver would print warnings whenever they
+occur.
+
+Nowadays, these issues are gone and the primary cause of these messages
+is if the MAC is stuck during reset or busy processing a long
+transmission. This is fairly harmless, yet these messages continue to
+worry users.
+
+To reduce the number of bogus bug reports, turn these messages into
+debug messages and count their occurence in the "reset" debugfs file.
+
+Signed-off-by: Felix Fietkau <nbd@openwrt.org>
+---
+
+--- a/drivers/net/wireless/ath/ath9k/debug.c
++++ b/drivers/net/wireless/ath/ath9k/debug.c
+@@ -765,6 +765,8 @@ static int read_file_reset(struct seq_fi
+ [RESET_TYPE_BEACON_STUCK] = "Stuck Beacon",
+ [RESET_TYPE_MCI] = "MCI Reset",
+ [RESET_TYPE_CALIBRATION] = "Calibration error",
++ [RESET_TX_DMA_ERROR] = "Tx DMA stop error",
++ [RESET_RX_DMA_ERROR] = "Rx DMA stop error",
+ };
+ int i;
+
+--- a/drivers/net/wireless/ath/ath9k/debug.h
++++ b/drivers/net/wireless/ath/ath9k/debug.h
+@@ -50,6 +50,8 @@ enum ath_reset_type {
+ RESET_TYPE_BEACON_STUCK,
+ RESET_TYPE_MCI,
+ RESET_TYPE_CALIBRATION,
++ RESET_TX_DMA_ERROR,
++ RESET_RX_DMA_ERROR,
+ __RESET_TYPE_MAX
+ };
+
+--- a/drivers/net/wireless/ath/ath9k/recv.c
++++ b/drivers/net/wireless/ath/ath9k/recv.c
+@@ -496,10 +496,9 @@ bool ath_stoprecv(struct ath_softc *sc)
+
+ if (!(ah->ah_flags & AH_UNPLUGGED) &&
+ unlikely(!stopped)) {
+- ath_err(ath9k_hw_common(sc->sc_ah),
+- "Could not stop RX, we could be "
+- "confusing the DMA engine when we start RX up\n");
+- ATH_DBG_WARN_ON_ONCE(!stopped);
++ ath_dbg(ath9k_hw_common(sc->sc_ah), RESET,
++ "Failed to stop Rx DMA\n");
++ RESET_STAT_INC(sc, RESET_RX_DMA_ERROR);
+ }
+ return stopped && !reset;
+ }
+--- a/drivers/net/wireless/ath/ath9k/xmit.c
++++ b/drivers/net/wireless/ath/ath9k/xmit.c
+@@ -1896,8 +1896,11 @@ bool ath_drain_all_txq(struct ath_softc
+ npend |= BIT(i);
+ }
+
+- if (npend)
+- ath_err(common, "Failed to stop TX DMA, queues=0x%03x!\n", npend);
++ if (npend) {
++ RESET_STAT_INC(sc, RESET_TX_DMA_ERROR);
++ ath_dbg(common, RESET,
++ "Failed to stop TX DMA, queues=0x%03x!\n", npend);
++ }
+
+ for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) {
+ if (!ATH_TXQ_SETUP(sc, i))
--- /dev/null
+From: Arend van Spriel <arend@broadcom.com>
+Date: Thu, 11 Jun 2015 00:12:21 +0200
+Subject: [PATCH] brcmfmac: free ifp for non-netdev interface in p2p module
+
+Making it more clear by freeing the ifp in same place where the
+vif object is freed.
+
+Reviewed-by: Hante Meuleman <meuleman@broadcom.com>
+Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com>
+Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
+Signed-off-by: Arend van Spriel <arend@broadcom.com>
+Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
+---
+
+--- a/drivers/net/wireless/brcm80211/brcmfmac/core.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/core.c
+@@ -867,8 +867,6 @@ static void brcmf_del_if(struct brcmf_pu
+ }
+ /* unregister will take care of freeing it */
+ unregister_netdev(ifp->ndev);
+- } else {
+- kfree(ifp);
+ }
+ }
+
+--- a/drivers/net/wireless/brcm80211/brcmfmac/p2p.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/p2p.c
+@@ -2238,6 +2238,7 @@ static void brcmf_p2p_delete_p2pdev(stru
+ {
+ cfg80211_unregister_wdev(&vif->wdev);
+ p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif = NULL;
++ kfree(vif->ifp);
+ brcmf_free_vif(vif);
+ }
+
+@@ -2361,6 +2362,8 @@ int brcmf_p2p_del_vif(struct wiphy *wiph
+ break;
+
+ case NL80211_IFTYPE_P2P_DEVICE:
++ brcmf_p2p_cancel_remain_on_channel(vif->ifp);
++ brcmf_p2p_deinit_discovery(p2p);
+ brcmf_p2p_delete_p2pdev(p2p, vif);
+ return 0;
+ default:
--- /dev/null
+From: Arend van Spriel <arend@broadcom.com>
+Date: Thu, 11 Jun 2015 00:12:22 +0200
+Subject: [PATCH] brcmfmac: move p2p attach/detach functions
+
+Moving two functions in p2p.c as is so next change will be
+easier to review.
+
+Reviewed-by: Hante Meuleman <meuleman@broadcom.com>
+Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com>
+Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
+Signed-off-by: Arend van Spriel <arend@broadcom.com>
+Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
+---
+
+--- a/drivers/net/wireless/brcm80211/brcmfmac/p2p.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/p2p.c
+@@ -1908,105 +1908,6 @@ s32 brcmf_p2p_notify_rx_mgmt_p2p_probere
+
+
+ /**
+- * brcmf_p2p_attach() - attach for P2P.
+- *
+- * @cfg: driver private data for cfg80211 interface.
+- */
+-s32 brcmf_p2p_attach(struct brcmf_cfg80211_info *cfg)
+-{
+- struct brcmf_if *pri_ifp;
+- struct brcmf_if *p2p_ifp;
+- struct brcmf_cfg80211_vif *p2p_vif;
+- struct brcmf_p2p_info *p2p;
+- struct brcmf_pub *drvr;
+- s32 bssidx;
+- s32 err = 0;
+-
+- p2p = &cfg->p2p;
+- p2p->cfg = cfg;
+-
+- drvr = cfg->pub;
+-
+- pri_ifp = drvr->iflist[0];
+- p2p_ifp = drvr->iflist[1];
+-
+- p2p->bss_idx[P2PAPI_BSSCFG_PRIMARY].vif = pri_ifp->vif;
+-
+- if (p2p_ifp) {
+- p2p_vif = brcmf_alloc_vif(cfg, NL80211_IFTYPE_P2P_DEVICE,
+- false);
+- if (IS_ERR(p2p_vif)) {
+- brcmf_err("could not create discovery vif\n");
+- err = -ENOMEM;
+- goto exit;
+- }
+-
+- p2p_vif->ifp = p2p_ifp;
+- p2p_ifp->vif = p2p_vif;
+- p2p_vif->wdev.netdev = p2p_ifp->ndev;
+- p2p_ifp->ndev->ieee80211_ptr = &p2p_vif->wdev;
+- SET_NETDEV_DEV(p2p_ifp->ndev, wiphy_dev(cfg->wiphy));
+-
+- p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif = p2p_vif;
+-
+- brcmf_p2p_generate_bss_mac(p2p, NULL);
+- memcpy(p2p_ifp->mac_addr, p2p->dev_addr, ETH_ALEN);
+- brcmf_p2p_set_firmware(pri_ifp, p2p->dev_addr);
+-
+- /* Initialize P2P Discovery in the firmware */
+- err = brcmf_fil_iovar_int_set(pri_ifp, "p2p_disc", 1);
+- if (err < 0) {
+- brcmf_err("set p2p_disc error\n");
+- brcmf_free_vif(p2p_vif);
+- goto exit;
+- }
+- /* obtain bsscfg index for P2P discovery */
+- err = brcmf_fil_iovar_int_get(pri_ifp, "p2p_dev", &bssidx);
+- if (err < 0) {
+- brcmf_err("retrieving discover bsscfg index failed\n");
+- brcmf_free_vif(p2p_vif);
+- goto exit;
+- }
+- /* Verify that firmware uses same bssidx as driver !! */
+- if (p2p_ifp->bssidx != bssidx) {
+- brcmf_err("Incorrect bssidx=%d, compared to p2p_ifp->bssidx=%d\n",
+- bssidx, p2p_ifp->bssidx);
+- brcmf_free_vif(p2p_vif);
+- goto exit;
+- }
+-
+- init_completion(&p2p->send_af_done);
+- INIT_WORK(&p2p->afx_hdl.afx_work, brcmf_p2p_afx_handler);
+- init_completion(&p2p->afx_hdl.act_frm_scan);
+- init_completion(&p2p->wait_next_af);
+- }
+-exit:
+- return err;
+-}
+-
+-
+-/**
+- * brcmf_p2p_detach() - detach P2P.
+- *
+- * @p2p: P2P specific data.
+- */
+-void brcmf_p2p_detach(struct brcmf_p2p_info *p2p)
+-{
+- struct brcmf_cfg80211_vif *vif;
+-
+- vif = p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif;
+- if (vif != NULL) {
+- brcmf_p2p_cancel_remain_on_channel(vif->ifp);
+- brcmf_p2p_deinit_discovery(p2p);
+- /* remove discovery interface */
+- brcmf_free_vif(vif);
+- p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif = NULL;
+- }
+- /* just set it all to zero */
+- memset(p2p, 0, sizeof(*p2p));
+-}
+-
+-/**
+ * brcmf_p2p_get_current_chanspec() - Get current operation channel.
+ *
+ * @p2p: P2P specific data.
+@@ -2425,3 +2326,102 @@ void brcmf_p2p_stop_device(struct wiphy
+ clear_bit(BRCMF_VIF_STATUS_READY, &vif->sme_state);
+ mutex_unlock(&cfg->usr_sync);
+ }
++
++/**
++ * brcmf_p2p_attach() - attach for P2P.
++ *
++ * @cfg: driver private data for cfg80211 interface.
++ */
++s32 brcmf_p2p_attach(struct brcmf_cfg80211_info *cfg)
++{
++ struct brcmf_if *pri_ifp;
++ struct brcmf_if *p2p_ifp;
++ struct brcmf_cfg80211_vif *p2p_vif;
++ struct brcmf_p2p_info *p2p;
++ struct brcmf_pub *drvr;
++ s32 bssidx;
++ s32 err = 0;
++
++ p2p = &cfg->p2p;
++ p2p->cfg = cfg;
++
++ drvr = cfg->pub;
++
++ pri_ifp = drvr->iflist[0];
++ p2p_ifp = drvr->iflist[1];
++
++ p2p->bss_idx[P2PAPI_BSSCFG_PRIMARY].vif = pri_ifp->vif;
++
++ if (p2p_ifp) {
++ p2p_vif = brcmf_alloc_vif(cfg, NL80211_IFTYPE_P2P_DEVICE,
++ false);
++ if (IS_ERR(p2p_vif)) {
++ brcmf_err("could not create discovery vif\n");
++ err = -ENOMEM;
++ goto exit;
++ }
++
++ p2p_vif->ifp = p2p_ifp;
++ p2p_ifp->vif = p2p_vif;
++ p2p_vif->wdev.netdev = p2p_ifp->ndev;
++ p2p_ifp->ndev->ieee80211_ptr = &p2p_vif->wdev;
++ SET_NETDEV_DEV(p2p_ifp->ndev, wiphy_dev(cfg->wiphy));
++
++ p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif = p2p_vif;
++
++ brcmf_p2p_generate_bss_mac(p2p, NULL);
++ memcpy(p2p_ifp->mac_addr, p2p->dev_addr, ETH_ALEN);
++ brcmf_p2p_set_firmware(pri_ifp, p2p->dev_addr);
++
++ /* Initialize P2P Discovery in the firmware */
++ err = brcmf_fil_iovar_int_set(pri_ifp, "p2p_disc", 1);
++ if (err < 0) {
++ brcmf_err("set p2p_disc error\n");
++ brcmf_free_vif(p2p_vif);
++ goto exit;
++ }
++ /* obtain bsscfg index for P2P discovery */
++ err = brcmf_fil_iovar_int_get(pri_ifp, "p2p_dev", &bssidx);
++ if (err < 0) {
++ brcmf_err("retrieving discover bsscfg index failed\n");
++ brcmf_free_vif(p2p_vif);
++ goto exit;
++ }
++ /* Verify that firmware uses same bssidx as driver !! */
++ if (p2p_ifp->bssidx != bssidx) {
++ brcmf_err("Incorrect bssidx=%d, compared to p2p_ifp->bssidx=%d\n",
++ bssidx, p2p_ifp->bssidx);
++ brcmf_free_vif(p2p_vif);
++ goto exit;
++ }
++
++ init_completion(&p2p->send_af_done);
++ INIT_WORK(&p2p->afx_hdl.afx_work, brcmf_p2p_afx_handler);
++ init_completion(&p2p->afx_hdl.act_frm_scan);
++ init_completion(&p2p->wait_next_af);
++ }
++exit:
++ return err;
++}
++
++/**
++ * brcmf_p2p_detach() - detach P2P.
++ *
++ * @p2p: P2P specific data.
++ */
++void brcmf_p2p_detach(struct brcmf_p2p_info *p2p)
++{
++ struct brcmf_cfg80211_vif *vif;
++
++ vif = p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif;
++ if (vif != NULL) {
++ brcmf_p2p_cancel_remain_on_channel(vif->ifp);
++ brcmf_p2p_deinit_discovery(p2p);
++ /* remove discovery interface */
++ brcmf_free_vif(vif);
++ p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif = NULL;
++ }
++ /* just set it all to zero */
++ memset(p2p, 0, sizeof(*p2p));
++}
++
--- /dev/null
+From: Arend van Spriel <arend@broadcom.com>
+Date: Thu, 11 Jun 2015 00:12:23 +0200
+Subject: [PATCH] brcmfmac: assure p2pdev is unregistered upon driver
+ unload
+
+When unloading the driver with a p2pdev interface it resulted in
+a warning upon calling wiphy_unregister() and subsequently a crash
+in the driver. This patch assures the p2pdev is unregistered calling
+unregister_wdev() before doing the wiphy_unregister().
+
+Reviewed-by: Hante Meuleman <meuleman@broadcom.com>
+Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com>
+Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
+Signed-off-by: Arend van Spriel <arend@broadcom.com>
+Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
+---
+
+--- a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c
+@@ -6206,10 +6206,8 @@ void brcmf_cfg80211_detach(struct brcmf_
+ if (!cfg)
+ return;
+
+- WARN_ON(!list_empty(&cfg->vif_list));
+- wiphy_unregister(cfg->wiphy);
+ brcmf_btcoex_detach(cfg);
+- brcmf_p2p_detach(&cfg->p2p);
++ wiphy_unregister(cfg->wiphy);
+ wl_deinit_priv(cfg);
+ brcmf_free_wiphy(cfg->wiphy);
+ }
+--- a/drivers/net/wireless/brcm80211/brcmfmac/core.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/core.c
+@@ -1098,6 +1098,7 @@ void brcmf_detach(struct device *dev)
+
+ /* stop firmware event handling */
+ brcmf_fweh_detach(drvr);
++ brcmf_p2p_detach(&drvr->config->p2p);
+
+ brcmf_bus_change_state(bus_if, BRCMF_BUS_DOWN);
+
+--- a/drivers/net/wireless/brcm80211/brcmfmac/p2p.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/p2p.c
+@@ -16,6 +16,7 @@
+ #include <linux/slab.h>
+ #include <linux/netdevice.h>
+ #include <linux/etherdevice.h>
++#include <linux/rtnetlink.h>
+ #include <net/cfg80211.h>
+
+ #include <brcmu_wifi.h>
+@@ -2418,8 +2419,9 @@ void brcmf_p2p_detach(struct brcmf_p2p_i
+ brcmf_p2p_cancel_remain_on_channel(vif->ifp);
+ brcmf_p2p_deinit_discovery(p2p);
+ /* remove discovery interface */
+- brcmf_free_vif(vif);
+- p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif = NULL;
++ rtnl_lock();
++ brcmf_p2p_delete_p2pdev(p2p, vif);
++ rtnl_unlock();
+ }
+ /* just set it all to zero */
+ memset(p2p, 0, sizeof(*p2p));
--- /dev/null
+From: Arend van Spriel <arend@broadcom.com>
+Date: Mon, 15 Jun 2015 22:48:38 +0200
+Subject: [PATCH] brcmfmac: fix double free of p2pdev interface
+
+When freeing the driver ifp pointer it should also be removed from
+the driver interface list, which is what brcmf_remove_interface()
+does. Otherwise, the ifp pointer will be freed twice triggering
+a kernel oops.
+
+Fixes: f37d69a4babc ("brcmfmac: free ifp for non-netdev interface in p2p module")
+Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
+Reviewed-by: Hante Meuleman <meuleman@broadcom.com>
+Signed-off-by: Arend van Spriel <arend@broadcom.com>
+Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
+---
+
+--- a/drivers/net/wireless/brcm80211/brcmfmac/p2p.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/p2p.c
+@@ -2140,7 +2140,7 @@ static void brcmf_p2p_delete_p2pdev(stru
+ {
+ cfg80211_unregister_wdev(&vif->wdev);
+ p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif = NULL;
+- kfree(vif->ifp);
++ brcmf_remove_interface(vif->ifp->drvr, vif->ifp->bssidx);
+ brcmf_free_vif(vif);
+ }
+
--- /dev/null
+From: Arend van Spriel <arend@broadcom.com>
+Date: Mon, 15 Jun 2015 22:48:39 +0200
+Subject: [PATCH] brcmfmac: make brcmf_p2p_detach() call conditional
+
+During verification of error handling in brcmf_cfg80211_attach() a
+null pointer dereference occurred upon calling brcmf_p2p_detach()
+from brcmf_detach(). This should only be called when the
+brcmf_cfg80211_attach() has succeeded.
+
+Fixes: f7a40873d2fa ("brcmfmac: assure p2pdev is unregistered upon driver unload")
+Reviewed-by: Hante Meuleman <meuleman@broadcom.com>
+Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com>
+Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
+Signed-off-by: Arend van Spriel <arend@broadcom.com>
+Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
+---
+
+--- a/drivers/net/wireless/brcm80211/brcmfmac/core.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/core.c
+@@ -1098,7 +1098,8 @@ void brcmf_detach(struct device *dev)
+
+ /* stop firmware event handling */
+ brcmf_fweh_detach(drvr);
+- brcmf_p2p_detach(&drvr->config->p2p);
++ if (drvr->config)
++ brcmf_p2p_detach(&drvr->config->p2p);
+
+ brcmf_bus_change_state(bus_if, BRCMF_BUS_DOWN);
+
--- /dev/null
+From: Rafa? Mi?ecki <zajec5@gmail.com>
+Date: Thu, 9 Jul 2015 17:07:08 +0200
+Subject: [PATCH] brcmfmac: set wiphy's addresses to provide valid MACs
+
+Broadcom's firmware requires every BSS to use MAC address with unique
+last few bits. The amount of bits may depend on a particular firmware,
+it was verified to be 2 for BCM43602 one.
+If this condition won't be fulfilled firmware will reject such MAC:
+brcmfmac: _brcmf_set_mac_address: Setting cur_etheraddr failed, -52
+
+We don't want to simply set addr_mask as it would also disallow using
+locally administrated bit. Instead let's build a list of addresses
+manually enabling 0x2 bit for extra interfaces.
+
+Signed-off-by: Rafa? Mi?ecki <zajec5@gmail.com>
+Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
+---
+
+--- a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c
+@@ -5784,6 +5784,7 @@ static void brcmf_wiphy_wowl_params(stru
+
+ static int brcmf_setup_wiphy(struct wiphy *wiphy, struct brcmf_if *ifp)
+ {
++ struct brcmf_pub *drvr = ifp->drvr;
+ struct ieee80211_supported_band *band;
+ __le32 bandlist[3];
+ u32 n_bands;
+@@ -5797,6 +5798,19 @@ static int brcmf_setup_wiphy(struct wiph
+ if (err)
+ return err;
+
++ for (i = 0; i < wiphy->iface_combinations->max_interfaces &&
++ i < ARRAY_SIZE(drvr->addresses); i++) {
++ u8 *addr = drvr->addresses[i].addr;
++
++ memcpy(addr, drvr->mac, ETH_ALEN);
++ if (i) {
++ addr[0] |= BIT(1);
++ addr[ETH_ALEN - 1] ^= i;
++ }
++ }
++ wiphy->addresses = drvr->addresses;
++ wiphy->n_addresses = i;
++
+ wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
+ wiphy->cipher_suites = __wl_cipher_suites;
+ wiphy->n_cipher_suites = ARRAY_SIZE(__wl_cipher_suites);
+--- a/drivers/net/wireless/brcm80211/brcmfmac/core.h
++++ b/drivers/net/wireless/brcm80211/brcmfmac/core.h
+@@ -21,6 +21,7 @@
+ #ifndef BRCMFMAC_CORE_H
+ #define BRCMFMAC_CORE_H
+
++#include <net/cfg80211.h>
+ #include "fweh.h"
+
+ #define TOE_TX_CSUM_OL 0x00000001
+@@ -118,6 +119,8 @@ struct brcmf_pub {
+ /* Multicast data packets sent to dongle */
+ unsigned long tx_multicast;
+
++ struct mac_address addresses[BRCMF_MAX_IFS];
++
+ struct brcmf_if *iflist[BRCMF_MAX_IFS];
+
+ struct mutex proto_block;
--- /dev/null
+From: Vineet Gupta <Vineet.Gupta1@synopsys.com>
+Date: Thu, 9 Jul 2015 13:43:18 +0530
+Subject: [PATCH] brcmfmac: dhd_sdio.c: use existing atomic_or primitive
+
+There's already a generic implementation so use that instead.
+
+Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
+---
+
+--- a/drivers/net/wireless/brcm80211/brcmfmac/sdio.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c
+@@ -2564,15 +2564,6 @@ static inline void brcmf_sdio_clrintr(st
+ }
+ }
+
+-static void atomic_orr(int val, atomic_t *v)
+-{
+- int old_val;
+-
+- old_val = atomic_read(v);
+- while (atomic_cmpxchg(v, old_val, val | old_val) != old_val)
+- old_val = atomic_read(v);
+-}
+-
+ static int brcmf_sdio_intr_rstatus(struct brcmf_sdio *bus)
+ {
+ struct brcmf_core *buscore;
+@@ -2595,7 +2586,7 @@ static int brcmf_sdio_intr_rstatus(struc
+ if (val) {
+ brcmf_sdiod_regwl(bus->sdiodev, addr, val, &ret);
+ bus->sdcnt.f1regdata++;
+- atomic_orr(val, &bus->intstatus);
++ atomic_or(val, &bus->intstatus);
+ }
+
+ return ret;
+@@ -2712,7 +2703,7 @@ static void brcmf_sdio_dpc(struct brcmf_
+
+ /* Keep still-pending events for next scheduling */
+ if (intstatus)
+- atomic_orr(intstatus, &bus->intstatus);
++ atomic_or(intstatus, &bus->intstatus);
+
+ brcmf_sdio_clrintr(bus);
+
--- /dev/null
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <zajec5@gmail.com>
+Date: Thu, 20 Aug 2015 00:16:42 +0200
+Subject: [PATCH] brcmfmac: check all combinations when setting wiphy's
+ addresses
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Broadcom is working on better reflection of interface combinations. With
+upcoming patches we may have 1st combination supporting less interfaces
+than others.
+To don't run out of addresses check all combinations to find the one
+with the greatest max_interfaces value.
+
+Signed-off-by: Rafał Miłecki <zajec5@gmail.com>
+Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
+---
+
+--- a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c
+@@ -5785,7 +5785,9 @@ static void brcmf_wiphy_wowl_params(stru
+ static int brcmf_setup_wiphy(struct wiphy *wiphy, struct brcmf_if *ifp)
+ {
+ struct brcmf_pub *drvr = ifp->drvr;
++ const struct ieee80211_iface_combination *combo;
+ struct ieee80211_supported_band *band;
++ u16 max_interfaces = 0;
+ __le32 bandlist[3];
+ u32 n_bands;
+ int err, i;
+@@ -5798,8 +5800,13 @@ static int brcmf_setup_wiphy(struct wiph
+ if (err)
+ return err;
+
+- for (i = 0; i < wiphy->iface_combinations->max_interfaces &&
+- i < ARRAY_SIZE(drvr->addresses); i++) {
++ for (i = 0, combo = wiphy->iface_combinations;
++ i < wiphy->n_iface_combinations; i++, combo++) {
++ max_interfaces = max(max_interfaces, combo->max_interfaces);
++ }
++
++ for (i = 0; i < max_interfaces && i < ARRAY_SIZE(drvr->addresses);
++ i++) {
+ u8 *addr = drvr->addresses[i].addr;
+
+ memcpy(addr, drvr->mac, ETH_ALEN);
--- /dev/null
+From: Arend van Spriel <arend@broadcom.com>
+Date: Thu, 20 Aug 2015 22:06:03 +0200
+Subject: [PATCH] brcmfmac: correct interface combination info
+
+The interface combination provided by brcmfmac did not truly reflect
+the combinations supported by driver and/or firmware.
+
+Reviewed-by: Hante Meuleman <meuleman@broadcom.com>
+Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com>
+Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
+Reviewed-by: Pontus Fuchs <pontusf@broadcom.com>
+Signed-off-by: Arend van Spriel <arend@broadcom.com>
+Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
+---
+
+--- a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c
+@@ -5694,63 +5694,132 @@ brcmf_txrx_stypes[NUM_NL80211_IFTYPES] =
+ }
+ };
+
++/**
++ * brcmf_setup_ifmodes() - determine interface modes and combinations.
++ *
++ * @wiphy: wiphy object.
++ * @ifp: interface object needed for feat module api.
++ *
++ * The interface modes and combinations are determined dynamically here
++ * based on firmware functionality.
++ *
++ * no p2p and no mbss:
++ *
++ * #STA <= 1, #AP <= 1, channels = 1, 2 total
++ *
++ * no p2p and mbss:
++ *
++ * #STA <= 1, #AP <= 1, channels = 1, 2 total
++ * #AP <= 4, matching BI, channels = 1, 4 total
++ *
++ * p2p, no mchan, and mbss:
++ *
++ * #STA <= 1, #P2P-DEV <= 1, #{P2P-CL, P2P-GO} <= 1, channels = 1, 3 total
++ * #STA <= 1, #P2P-DEV <= 1, #AP <= 1, #P2P-CL <= 1, channels = 1, 4 total
++ * #AP <= 4, matching BI, channels = 1, 4 total
++ *
++ * p2p, mchan, and mbss:
++ *
++ * #STA <= 1, #P2P-DEV <= 1, #{P2P-CL, P2P-GO} <= 1, channels = 2, 3 total
++ * #STA <= 1, #P2P-DEV <= 1, #AP <= 1, #P2P-CL <= 1, channels = 1, 4 total
++ * #AP <= 4, matching BI, channels = 1, 4 total
++ */
+ static int brcmf_setup_ifmodes(struct wiphy *wiphy, struct brcmf_if *ifp)
+ {
+ struct ieee80211_iface_combination *combo = NULL;
+- struct ieee80211_iface_limit *limits = NULL;
+- int i = 0, max_iface_cnt;
++ struct ieee80211_iface_limit *c0_limits = NULL;
++ struct ieee80211_iface_limit *p2p_limits = NULL;
++ struct ieee80211_iface_limit *mbss_limits = NULL;
++ bool mbss, p2p;
++ int i, c, n_combos;
+
+- combo = kzalloc(sizeof(*combo), GFP_KERNEL);
++ mbss = brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS);
++ p2p = brcmf_feat_is_enabled(ifp, BRCMF_FEAT_P2P);
++
++ n_combos = 1 + !!p2p + !!mbss;
++ combo = kcalloc(n_combos, sizeof(*combo), GFP_KERNEL);
+ if (!combo)
+ goto err;
+
+- limits = kzalloc(sizeof(*limits) * 4, GFP_KERNEL);
+- if (!limits)
++ c0_limits = kcalloc(p2p ? 3 : 2, sizeof(*c0_limits), GFP_KERNEL);
++ if (!c0_limits)
+ goto err;
+
++ if (p2p) {
++ p2p_limits = kcalloc(4, sizeof(*p2p_limits), GFP_KERNEL);
++ if (!p2p_limits)
++ goto err;
++ }
++
++ if (mbss) {
++ mbss_limits = kcalloc(1, sizeof(*mbss_limits), GFP_KERNEL);
++ if (!mbss_limits)
++ goto err;
++ }
++
+ wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
+ BIT(NL80211_IFTYPE_ADHOC) |
+ BIT(NL80211_IFTYPE_AP);
+
+- if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MCHAN))
+- combo->num_different_channels = 2;
+- else
+- combo->num_different_channels = 1;
+-
+- if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS)) {
+- limits[i].max = 1;
+- limits[i++].types = BIT(NL80211_IFTYPE_STATION);
+- limits[i].max = 4;
+- limits[i++].types = BIT(NL80211_IFTYPE_AP);
+- max_iface_cnt = 5;
+- } else {
+- limits[i].max = 2;
+- limits[i++].types = BIT(NL80211_IFTYPE_STATION) |
+- BIT(NL80211_IFTYPE_AP);
+- max_iface_cnt = 2;
+- }
+-
+- if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_P2P)) {
++ c = 0;
++ i = 0;
++ combo[c].num_different_channels = 1;
++ c0_limits[i].max = 1;
++ c0_limits[i++].types = BIT(NL80211_IFTYPE_STATION);
++ if (p2p) {
++ if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MCHAN))
++ combo[c].num_different_channels = 2;
+ wiphy->interface_modes |= BIT(NL80211_IFTYPE_P2P_CLIENT) |
+ BIT(NL80211_IFTYPE_P2P_GO) |
+ BIT(NL80211_IFTYPE_P2P_DEVICE);
+- limits[i].max = 1;
+- limits[i++].types = BIT(NL80211_IFTYPE_P2P_CLIENT) |
+- BIT(NL80211_IFTYPE_P2P_GO);
+- limits[i].max = 1;
+- limits[i++].types = BIT(NL80211_IFTYPE_P2P_DEVICE);
+- max_iface_cnt += 2;
+- }
+- combo->max_interfaces = max_iface_cnt;
+- combo->limits = limits;
+- combo->n_limits = i;
++ c0_limits[i].max = 1;
++ c0_limits[i++].types = BIT(NL80211_IFTYPE_P2P_DEVICE);
++ c0_limits[i].max = 1;
++ c0_limits[i++].types = BIT(NL80211_IFTYPE_P2P_CLIENT) |
++ BIT(NL80211_IFTYPE_P2P_GO);
++ } else {
++ c0_limits[i].max = 1;
++ c0_limits[i++].types = BIT(NL80211_IFTYPE_AP);
++ }
++ combo[c].max_interfaces = i;
++ combo[c].n_limits = i;
++ combo[c].limits = c0_limits;
++
++ if (p2p) {
++ c++;
++ i = 0;
++ combo[c].num_different_channels = 1;
++ p2p_limits[i].max = 1;
++ p2p_limits[i++].types = BIT(NL80211_IFTYPE_STATION);
++ p2p_limits[i].max = 1;
++ p2p_limits[i++].types = BIT(NL80211_IFTYPE_AP);
++ p2p_limits[i].max = 1;
++ p2p_limits[i++].types = BIT(NL80211_IFTYPE_P2P_CLIENT);
++ p2p_limits[i].max = 1;
++ p2p_limits[i++].types = BIT(NL80211_IFTYPE_P2P_DEVICE);
++ combo[c].max_interfaces = i;
++ combo[c].n_limits = i;
++ combo[c].limits = p2p_limits;
++ }
+
++ if (mbss) {
++ c++;
++ combo[c].beacon_int_infra_match = true;
++ combo[c].num_different_channels = 1;
++ mbss_limits[0].max = 4;
++ mbss_limits[0].types = BIT(NL80211_IFTYPE_AP);
++ combo[c].max_interfaces = 4;
++ combo[c].n_limits = 1;
++ combo[c].limits = mbss_limits;
++ }
++ wiphy->n_iface_combinations = n_combos;
+ wiphy->iface_combinations = combo;
+- wiphy->n_iface_combinations = 1;
+ return 0;
+
+ err:
+- kfree(limits);
++ kfree(c0_limits);
++ kfree(p2p_limits);
++ kfree(mbss_limits);
+ kfree(combo);
+ return -ENOMEM;
+ }
+@@ -6079,11 +6148,15 @@ static void brcmf_cfg80211_reg_notifier(
+
+ static void brcmf_free_wiphy(struct wiphy *wiphy)
+ {
++ int i;
++
+ if (!wiphy)
+ return;
+
+- if (wiphy->iface_combinations)
+- kfree(wiphy->iface_combinations->limits);
++ if (wiphy->iface_combinations) {
++ for (i = 0; i < wiphy->n_iface_combinations; i++)
++ kfree(wiphy->iface_combinations[i].limits);
++ }
+ kfree(wiphy->iface_combinations);
+ if (wiphy->bands[IEEE80211_BAND_2GHZ]) {
+ kfree(wiphy->bands[IEEE80211_BAND_2GHZ]->channels);
--- /dev/null
+From: Franky Lin <frankyl@broadcom.com>
+Date: Thu, 20 Aug 2015 22:06:04 +0200
+Subject: [PATCH] brcmfmac: add debugfs entry for msgbuf statistics
+
+Expose ring buffer read/write pointers and other useful statistics
+through debugfs.
+
+Reviewed-by: Arend Van Spriel <arend@broadcom.com>
+Reviewed-by: Hante Meuleman <meuleman@broadcom.com>
+Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
+Signed-off-by: Franky Lin <frankyl@broadcom.com>
+Signed-off-by: Arend van Spriel <arend@broadcom.com>
+Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
+---
+
+--- a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c
+@@ -1360,6 +1360,60 @@ void brcmf_msgbuf_delete_flowring(struct
+ }
+ }
+
++#ifdef DEBUG
++static int brcmf_msgbuf_stats_read(struct seq_file *seq, void *data)
++{
++ struct brcmf_bus *bus_if = dev_get_drvdata(seq->private);
++ struct brcmf_pub *drvr = bus_if->drvr;
++ struct brcmf_msgbuf *msgbuf = (struct brcmf_msgbuf *)drvr->proto->pd;
++ struct brcmf_commonring *commonring;
++ u16 i;
++ struct brcmf_flowring_ring *ring;
++ struct brcmf_flowring_hash *hash;
++
++ commonring = msgbuf->commonrings[BRCMF_H2D_MSGRING_CONTROL_SUBMIT];
++ seq_printf(seq, "h2d_ctl_submit: rp %4u, wp %4u, depth %4u\n",
++ commonring->r_ptr, commonring->w_ptr, commonring->depth);
++ commonring = msgbuf->commonrings[BRCMF_H2D_MSGRING_RXPOST_SUBMIT];
++ seq_printf(seq, "h2d_rx_submit: rp %4u, wp %4u, depth %4u\n",
++ commonring->r_ptr, commonring->w_ptr, commonring->depth);
++ commonring = msgbuf->commonrings[BRCMF_D2H_MSGRING_CONTROL_COMPLETE];
++ seq_printf(seq, "d2h_ctl_cmplt: rp %4u, wp %4u, depth %4u\n",
++ commonring->r_ptr, commonring->w_ptr, commonring->depth);
++ commonring = msgbuf->commonrings[BRCMF_D2H_MSGRING_TX_COMPLETE];
++ seq_printf(seq, "d2h_tx_cmplt: rp %4u, wp %4u, depth %4u\n",
++ commonring->r_ptr, commonring->w_ptr, commonring->depth);
++ commonring = msgbuf->commonrings[BRCMF_D2H_MSGRING_RX_COMPLETE];
++ seq_printf(seq, "d2h_rx_cmplt: rp %4u, wp %4u, depth %4u\n",
++ commonring->r_ptr, commonring->w_ptr, commonring->depth);
++
++ seq_printf(seq, "\nh2d_flowrings: depth %u\n",
++ BRCMF_H2D_TXFLOWRING_MAX_ITEM);
++ seq_puts(seq, "Active flowrings:\n");
++ hash = msgbuf->flow->hash;
++ for (i = 0; i < msgbuf->flow->nrofrings; i++) {
++ if (!msgbuf->flow->rings[i])
++ continue;
++ ring = msgbuf->flow->rings[i];
++ if (ring->status != RING_OPEN)
++ continue;
++ commonring = msgbuf->flowrings[i];
++ hash = &msgbuf->flow->hash[ring->hash_id];
++ seq_printf(seq, "id %3u: rp %4u, wp %4u, qlen %4u, blocked %u\n"
++ " ifidx %u, fifo %u, da %pM\n",
++ i, commonring->r_ptr, commonring->w_ptr,
++ skb_queue_len(&ring->skblist), ring->blocked,
++ hash->ifidx, hash->fifo, hash->mac);
++ }
++
++ return 0;
++}
++#else
++static int brcmf_msgbuf_stats_read(struct seq_file *seq, void *data)
++{
++ return 0;
++}
++#endif
+
+ int brcmf_proto_msgbuf_attach(struct brcmf_pub *drvr)
+ {
+@@ -1460,6 +1514,8 @@ int brcmf_proto_msgbuf_attach(struct brc
+ spin_lock_init(&msgbuf->flowring_work_lock);
+ INIT_LIST_HEAD(&msgbuf->work_queue);
+
++ brcmf_debugfs_add_entry(drvr, "msgbuf_stats", brcmf_msgbuf_stats_read);
++
+ return 0;
+
+ fail:
--- /dev/null
+From: Arend van Spriel <arend@broadcom.com>
+Date: Thu, 20 Aug 2015 22:06:05 +0200
+Subject: [PATCH] brcmfmac: make use of cfg80211_check_combinations()
+
+Use cfg80211_check_combinations() so we can bail out early when an
+interface add or change results in an invalid combination.
+
+Reviewed-by: Hante Meuleman <meuleman@broadcom.com>
+Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com>
+Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
+Signed-off-by: Arend van Spriel <arend@broadcom.com>
+Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
+---
+
+--- a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c
+@@ -469,6 +469,36 @@ brcmf_find_wpsie(const u8 *parse, u32 le
+ return NULL;
+ }
+
++static int brcmf_vif_change_validate(struct brcmf_cfg80211_info *cfg,
++ struct brcmf_cfg80211_vif *vif,
++ enum nl80211_iftype new_type)
++{
++ int iftype_num[NUM_NL80211_IFTYPES];
++ struct brcmf_cfg80211_vif *pos;
++
++ memset(&iftype_num[0], 0, sizeof(iftype_num));
++ list_for_each_entry(pos, &cfg->vif_list, list)
++ if (pos == vif)
++ iftype_num[new_type]++;
++ else
++ iftype_num[pos->wdev.iftype]++;
++
++ return cfg80211_check_combinations(cfg->wiphy, 1, 0, iftype_num);
++}
++
++static int brcmf_vif_add_validate(struct brcmf_cfg80211_info *cfg,
++ enum nl80211_iftype new_type)
++{
++ int iftype_num[NUM_NL80211_IFTYPES];
++ struct brcmf_cfg80211_vif *pos;
++
++ memset(&iftype_num[0], 0, sizeof(iftype_num));
++ list_for_each_entry(pos, &cfg->vif_list, list)
++ iftype_num[pos->wdev.iftype]++;
++
++ iftype_num[new_type]++;
++ return cfg80211_check_combinations(cfg->wiphy, 1, 0, iftype_num);
++}
+
+ static void convert_key_from_CPU(struct brcmf_wsec_key *key,
+ struct brcmf_wsec_key_le *key_le)
+@@ -662,8 +692,14 @@ static struct wireless_dev *brcmf_cfg802
+ struct vif_params *params)
+ {
+ struct wireless_dev *wdev;
++ int err;
+
+ brcmf_dbg(TRACE, "enter: %s type %d\n", name, type);
++ err = brcmf_vif_add_validate(wiphy_to_cfg(wiphy), type);
++ if (err) {
++ brcmf_err("iface validation failed: err=%d\n", err);
++ return ERR_PTR(err);
++ }
+ switch (type) {
+ case NL80211_IFTYPE_ADHOC:
+ case NL80211_IFTYPE_STATION:
+@@ -822,8 +858,12 @@ brcmf_cfg80211_change_iface(struct wiphy
+ s32 ap = 0;
+ s32 err = 0;
+
+- brcmf_dbg(TRACE, "Enter, ndev=%p, type=%d\n", ndev, type);
+-
++ brcmf_dbg(TRACE, "Enter, idx=%d, type=%d\n", ifp->bssidx, type);
++ err = brcmf_vif_change_validate(wiphy_to_cfg(wiphy), vif, type);
++ if (err) {
++ brcmf_err("iface validation failed: err=%d\n", err);
++ return err;
++ }
+ switch (type) {
+ case NL80211_IFTYPE_MONITOR:
+ case NL80211_IFTYPE_WDS:
--- /dev/null
+From: Franky Lin <frankyl@broadcom.com>
+Date: Thu, 20 Aug 2015 22:06:06 +0200
+Subject: [PATCH] brcmfmac: block the correct flowring when backup queue
+ overflow
+
+brcmf_flowring_block blocks the last active flowring under the same
+interface instead of the one provided by caller. This could lead to a
+dead lock of netif stop if there are more than one flowring under the
+interface and the traffic is high enough so brcmf_flowring_enqueue can
+not unblock the ring right away.
+
+Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
+Reviewed-by: Hante Meuleman <meuleman@broadcom.com>
+Signed-off-by: Franky Lin <frankyl@broadcom.com>
+Signed-off-by: Arend van Spriel <arend@broadcom.com>
+Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
+---
+
+--- a/drivers/net/wireless/brcm80211/brcmfmac/flowring.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/flowring.c
+@@ -194,11 +194,15 @@ static void brcmf_flowring_block(struct
+ spin_lock_irqsave(&flow->block_lock, flags);
+
+ ring = flow->rings[flowid];
++ if (ring->blocked == blocked) {
++ spin_unlock_irqrestore(&flow->block_lock, flags);
++ return;
++ }
+ ifidx = brcmf_flowring_ifidx_get(flow, flowid);
+
+ currently_blocked = false;
+ for (i = 0; i < flow->nrofrings; i++) {
+- if (flow->rings[i]) {
++ if ((flow->rings[i]) && (i != flowid)) {
+ ring = flow->rings[i];
+ if ((ring->status == RING_OPEN) &&
+ (brcmf_flowring_ifidx_get(flow, i) == ifidx)) {
+@@ -209,8 +213,8 @@ static void brcmf_flowring_block(struct
+ }
+ }
+ }
+- ring->blocked = blocked;
+- if (currently_blocked == blocked) {
++ flow->rings[flowid]->blocked = blocked;
++ if (currently_blocked) {
+ spin_unlock_irqrestore(&flow->block_lock, flags);
+ return;
+ }
--- /dev/null
+From: Arend van Spriel <arend@broadcom.com>
+Date: Thu, 20 Aug 2015 22:06:07 +0200
+Subject: [PATCH] brcmfmac: bump highest event number for 4339 firmware
+
+The event mask length is determined by the highest event number
+that is specified in the driver. When this length is shorter than
+firmware expects setting event mask will fail and device becomes
+pretty useless. This issue was reported with bcm4339 firmware that
+was recently released.
+
+Reported-by: Pontus Fuchs <pontusf@broadcom.com>
+Reviewed-by: Hante Meuleman <meuleman@broadcom.com>
+Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com>
+Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
+Reviewed-by: Pontus Fuchs <pontusf@broadcom.com>
+Signed-off-by: Arend van Spriel <arend@broadcom.com>
+Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
+---
+
+--- a/drivers/net/wireless/brcm80211/brcmfmac/fweh.h
++++ b/drivers/net/wireless/brcm80211/brcmfmac/fweh.h
+@@ -85,7 +85,6 @@ struct brcmf_event;
+ BRCMF_ENUM_DEF(IF, 54) \
+ BRCMF_ENUM_DEF(P2P_DISC_LISTEN_COMPLETE, 55) \
+ BRCMF_ENUM_DEF(RSSI, 56) \
+- BRCMF_ENUM_DEF(PFN_SCAN_COMPLETE, 57) \
+ BRCMF_ENUM_DEF(EXTLOG_MSG, 58) \
+ BRCMF_ENUM_DEF(ACTION_FRAME, 59) \
+ BRCMF_ENUM_DEF(ACTION_FRAME_COMPLETE, 60) \
+@@ -103,8 +102,7 @@ struct brcmf_event;
+ BRCMF_ENUM_DEF(FIFO_CREDIT_MAP, 74) \
+ BRCMF_ENUM_DEF(ACTION_FRAME_RX, 75) \
+ BRCMF_ENUM_DEF(TDLS_PEER_EVENT, 92) \
+- BRCMF_ENUM_DEF(BCMC_CREDIT_SUPPORT, 127) \
+- BRCMF_ENUM_DEF(PSTA_PRIMARY_INTF_IND, 128)
++ BRCMF_ENUM_DEF(BCMC_CREDIT_SUPPORT, 127)
+
+ #define BRCMF_ENUM_DEF(id, val) \
+ BRCMF_E_##id = (val),
+@@ -112,7 +110,11 @@ struct brcmf_event;
+ /* firmware event codes sent by the dongle */
+ enum brcmf_fweh_event_code {
+ BRCMF_FWEH_EVENT_ENUM_DEFLIST
+- BRCMF_E_LAST
++ /* this determines event mask length which must match
++ * minimum length check in device firmware so it is
++ * hard-coded here.
++ */
++ BRCMF_E_LAST = 139
+ };
+ #undef BRCMF_ENUM_DEF
+
--- /dev/null
+From: Arend van Spriel <arend@broadcom.com>
+Date: Wed, 26 Aug 2015 22:14:53 +0200
+Subject: [PATCH] brcmfmac: consolidate ifp lookup in driver core
+
+In rx path the firmware provide an interface index which is used to
+map to a struct brcmf_if instance. However, this involves some trick
+that is done in two places. This is changed by having driver core
+providing brcmf_get_ifp() function.
+
+Reviewed-by: Hante Meuleman <meuleman@broadcom.com>
+Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com>
+Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
+Signed-off-by: Arend van Spriel <arend@broadcom.com>
+---
+
+--- a/drivers/net/wireless/brcm80211/brcmfmac/bcdc.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/bcdc.c
+@@ -276,6 +276,7 @@ brcmf_proto_bcdc_hdrpull(struct brcmf_pu
+ struct sk_buff *pktbuf)
+ {
+ struct brcmf_proto_bcdc_header *h;
++ struct brcmf_if *ifp;
+
+ brcmf_dbg(BCDC, "Enter\n");
+
+@@ -289,30 +290,21 @@ brcmf_proto_bcdc_hdrpull(struct brcmf_pu
+ trace_brcmf_bcdchdr(pktbuf->data);
+ h = (struct brcmf_proto_bcdc_header *)(pktbuf->data);
+
+- *ifidx = BCDC_GET_IF_IDX(h);
+- if (*ifidx >= BRCMF_MAX_IFS) {
+- brcmf_err("rx data ifnum out of range (%d)\n", *ifidx);
++ ifp = brcmf_get_ifp(drvr, BCDC_GET_IF_IDX(h));
++ if (IS_ERR_OR_NULL(ifp)) {
++ brcmf_dbg(INFO, "no matching ifp found\n");
+ return -EBADE;
+ }
+- /* The ifidx is the idx to map to matching netdev/ifp. When receiving
+- * events this is easy because it contains the bssidx which maps
+- * 1-on-1 to the netdev/ifp. But for data frames the ifidx is rcvd.
+- * bssidx 1 is used for p2p0 and no data can be received or
+- * transmitted on it. Therefor bssidx is ifidx + 1 if ifidx > 0
+- */
+- if (*ifidx)
+- (*ifidx)++;
+-
+ if (((h->flags & BCDC_FLAG_VER_MASK) >> BCDC_FLAG_VER_SHIFT) !=
+ BCDC_PROTO_VER) {
+ brcmf_err("%s: non-BCDC packet received, flags 0x%x\n",
+- brcmf_ifname(drvr, *ifidx), h->flags);
++ brcmf_ifname(drvr, ifp->ifidx), h->flags);
+ return -EBADE;
+ }
+
+ if (h->flags & BCDC_FLAG_SUM_GOOD) {
+ brcmf_dbg(BCDC, "%s: BDC rcv, good checksum, flags 0x%x\n",
+- brcmf_ifname(drvr, *ifidx), h->flags);
++ brcmf_ifname(drvr, ifp->ifidx), h->flags);
+ pktbuf->ip_summed = CHECKSUM_UNNECESSARY;
+ }
+
+@@ -320,12 +312,15 @@ brcmf_proto_bcdc_hdrpull(struct brcmf_pu
+
+ skb_pull(pktbuf, BCDC_HEADER_LEN);
+ if (do_fws)
+- brcmf_fws_hdrpull(drvr, *ifidx, h->data_offset << 2, pktbuf);
++ brcmf_fws_hdrpull(drvr, ifp->ifidx, h->data_offset << 2,
++ pktbuf);
+ else
+ skb_pull(pktbuf, h->data_offset << 2);
+
+ if (pktbuf->len == 0)
+ return -ENODATA;
++
++ *ifidx = ifp->ifidx;
+ return 0;
+ }
+
+--- a/drivers/net/wireless/brcm80211/brcmfmac/core.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/core.c
+@@ -83,6 +83,25 @@ char *brcmf_ifname(struct brcmf_pub *drv
+ return "<if_none>";
+ }
+
++struct brcmf_if *brcmf_get_ifp(struct brcmf_pub *drvr, int ifidx)
++{
++ if (ifidx < 0 || ifidx >= BRCMF_MAX_IFS) {
++ brcmf_err("ifidx %d out of range\n", ifidx);
++ return ERR_PTR(-ERANGE);
++ }
++
++ /* The ifidx is the idx to map to matching netdev/ifp. When receiving
++ * events this is easy because it contains the bssidx which maps
++ * 1-on-1 to the netdev/ifp. But for data frames the ifidx is rcvd.
++ * bssidx 1 is used for p2p0 and no data can be received or
++ * transmitted on it. Therefor bssidx is ifidx + 1 if ifidx > 0
++ */
++ if (ifidx)
++ ifidx++;
++
++ return drvr->iflist[ifidx];
++}
++
+ static void _brcmf_set_multicast_list(struct work_struct *work)
+ {
+ struct brcmf_if *ifp;
+--- a/drivers/net/wireless/brcm80211/brcmfmac/core.h
++++ b/drivers/net/wireless/brcm80211/brcmfmac/core.h
+@@ -202,7 +202,7 @@ int brcmf_netdev_wait_pend8021x(struct b
+
+ /* Return pointer to interface name */
+ char *brcmf_ifname(struct brcmf_pub *drvr, int idx);
+-
++struct brcmf_if *brcmf_get_ifp(struct brcmf_pub *drvr, int ifidx);
+ int brcmf_net_attach(struct brcmf_if *ifp, bool rtnl_locked);
+ struct brcmf_if *brcmf_add_if(struct brcmf_pub *drvr, s32 bssidx, s32 ifidx,
+ char *name, u8 *mac_addr);
+--- a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c
+@@ -1081,16 +1081,8 @@ brcmf_msgbuf_rx_skb(struct brcmf_msgbuf
+ {
+ struct brcmf_if *ifp;
+
+- /* The ifidx is the idx to map to matching netdev/ifp. When receiving
+- * events this is easy because it contains the bssidx which maps
+- * 1-on-1 to the netdev/ifp. But for data frames the ifidx is rcvd.
+- * bssidx 1 is used for p2p0 and no data can be received or
+- * transmitted on it. Therefor bssidx is ifidx + 1 if ifidx > 0
+- */
+- if (ifidx)
+- (ifidx)++;
+- ifp = msgbuf->drvr->iflist[ifidx];
+- if (!ifp || !ifp->ndev) {
++ ifp = brcmf_get_ifp(msgbuf->drvr, ifidx);
++ if (IS_ERR_OR_NULL(ifp) || !ifp->ndev) {
+ brcmf_err("Received pkt for invalid ifidx %d\n", ifidx);
+ brcmu_pkt_buf_free_skb(skb);
+ return;
--- /dev/null
+From: Arend van Spriel <arend@broadcom.com>
+Date: Wed, 26 Aug 2015 22:14:54 +0200
+Subject: [PATCH] brcmfmac: make brcmf_proto_hdrpull() return struct
+ brcmf_if instance
+
+Avoid spreading the ifidx in the driver, but have it return the
+struct brcmf_if instance.
+
+Reviewed-by: Hante Meuleman <meuleman@broadcom.com>
+Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com>
+Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
+Signed-off-by: Arend van Spriel <arend@broadcom.com>
+---
+
+--- a/drivers/net/wireless/brcm80211/brcmfmac/bcdc.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/bcdc.c
+@@ -272,11 +272,11 @@ brcmf_proto_bcdc_hdrpush(struct brcmf_pu
+ }
+
+ static int
+-brcmf_proto_bcdc_hdrpull(struct brcmf_pub *drvr, bool do_fws, u8 *ifidx,
+- struct sk_buff *pktbuf)
++brcmf_proto_bcdc_hdrpull(struct brcmf_pub *drvr, bool do_fws,
++ struct sk_buff *pktbuf, struct brcmf_if **ifp)
+ {
+ struct brcmf_proto_bcdc_header *h;
+- struct brcmf_if *ifp;
++ struct brcmf_if *tmp_if;
+
+ brcmf_dbg(BCDC, "Enter\n");
+
+@@ -290,21 +290,21 @@ brcmf_proto_bcdc_hdrpull(struct brcmf_pu
+ trace_brcmf_bcdchdr(pktbuf->data);
+ h = (struct brcmf_proto_bcdc_header *)(pktbuf->data);
+
+- ifp = brcmf_get_ifp(drvr, BCDC_GET_IF_IDX(h));
+- if (IS_ERR_OR_NULL(ifp)) {
++ tmp_if = brcmf_get_ifp(drvr, BCDC_GET_IF_IDX(h));
++ if (!tmp_if) {
+ brcmf_dbg(INFO, "no matching ifp found\n");
+ return -EBADE;
+ }
+ if (((h->flags & BCDC_FLAG_VER_MASK) >> BCDC_FLAG_VER_SHIFT) !=
+ BCDC_PROTO_VER) {
+ brcmf_err("%s: non-BCDC packet received, flags 0x%x\n",
+- brcmf_ifname(drvr, ifp->ifidx), h->flags);
++ brcmf_ifname(drvr, tmp_if->ifidx), h->flags);
+ return -EBADE;
+ }
+
+ if (h->flags & BCDC_FLAG_SUM_GOOD) {
+ brcmf_dbg(BCDC, "%s: BDC rcv, good checksum, flags 0x%x\n",
+- brcmf_ifname(drvr, ifp->ifidx), h->flags);
++ brcmf_ifname(drvr, tmp_if->ifidx), h->flags);
+ pktbuf->ip_summed = CHECKSUM_UNNECESSARY;
+ }
+
+@@ -312,7 +312,7 @@ brcmf_proto_bcdc_hdrpull(struct brcmf_pu
+
+ skb_pull(pktbuf, BCDC_HEADER_LEN);
+ if (do_fws)
+- brcmf_fws_hdrpull(drvr, ifp->ifidx, h->data_offset << 2,
++ brcmf_fws_hdrpull(drvr, tmp_if->ifidx, h->data_offset << 2,
+ pktbuf);
+ else
+ skb_pull(pktbuf, h->data_offset << 2);
+@@ -320,7 +320,7 @@ brcmf_proto_bcdc_hdrpull(struct brcmf_pu
+ if (pktbuf->len == 0)
+ return -ENODATA;
+
+- *ifidx = ifp->ifidx;
++ *ifp = tmp_if;
+ return 0;
+ }
+
+--- a/drivers/net/wireless/brcm80211/brcmfmac/core.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/core.c
+@@ -87,7 +87,7 @@ struct brcmf_if *brcmf_get_ifp(struct br
+ {
+ if (ifidx < 0 || ifidx >= BRCMF_MAX_IFS) {
+ brcmf_err("ifidx %d out of range\n", ifidx);
+- return ERR_PTR(-ERANGE);
++ return NULL;
+ }
+
+ /* The ifidx is the idx to map to matching netdev/ifp. When receiving
+@@ -539,17 +539,15 @@ void brcmf_rx_frame(struct device *dev,
+ struct brcmf_bus *bus_if = dev_get_drvdata(dev);
+ struct brcmf_pub *drvr = bus_if->drvr;
+ struct brcmf_skb_reorder_data *rd;
+- u8 ifidx;
+ int ret;
+
+ brcmf_dbg(DATA, "Enter: %s: rxp=%p\n", dev_name(dev), skb);
+
+ /* process and remove protocol-specific header */
+- ret = brcmf_proto_hdrpull(drvr, true, &ifidx, skb);
+- ifp = drvr->iflist[ifidx];
++ ret = brcmf_proto_hdrpull(drvr, true, skb, &ifp);
+
+ if (ret || !ifp || !ifp->ndev) {
+- if ((ret != -ENODATA) && ifp)
++ if (ret != -ENODATA && ifp)
+ ifp->stats.rx_errors++;
+ brcmu_pkt_buf_free_skb(skb);
+ return;
+@@ -592,17 +590,17 @@ void brcmf_txcomplete(struct device *dev
+ {
+ struct brcmf_bus *bus_if = dev_get_drvdata(dev);
+ struct brcmf_pub *drvr = bus_if->drvr;
+- u8 ifidx;
++ struct brcmf_if *ifp;
+
+ /* await txstatus signal for firmware if active */
+ if (brcmf_fws_fc_active(drvr->fws)) {
+ if (!success)
+ brcmf_fws_bustxfail(drvr->fws, txp);
+ } else {
+- if (brcmf_proto_hdrpull(drvr, false, &ifidx, txp))
++ if (brcmf_proto_hdrpull(drvr, false, txp, &ifp))
+ brcmu_pkt_buf_free_skb(txp);
+ else
+- brcmf_txfinalize(drvr, txp, ifidx, success);
++ brcmf_txfinalize(drvr, txp, ifp->ifidx, success);
+ }
+ }
+
+--- a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c
+@@ -1448,7 +1448,7 @@ brcmf_fws_txs_process(struct brcmf_fws_i
+ struct sk_buff *skb;
+ struct brcmf_skbuff_cb *skcb;
+ struct brcmf_fws_mac_descriptor *entry = NULL;
+- u8 ifidx;
++ struct brcmf_if *ifp;
+
+ brcmf_dbg(DATA, "flags %d\n", flags);
+
+@@ -1497,15 +1497,16 @@ brcmf_fws_txs_process(struct brcmf_fws_i
+ }
+ brcmf_fws_macdesc_return_req_credit(skb);
+
+- if (brcmf_proto_hdrpull(fws->drvr, false, &ifidx, skb)) {
++ ret = brcmf_proto_hdrpull(fws->drvr, false, skb, &ifp);
++ if (ret) {
+ brcmu_pkt_buf_free_skb(skb);
+ return -EINVAL;
+ }
+ if (!remove_from_hanger)
+- ret = brcmf_fws_txstatus_suppressed(fws, fifo, skb, ifidx,
++ ret = brcmf_fws_txstatus_suppressed(fws, fifo, skb, ifp->ifidx,
+ genbit, seq);
+ if (remove_from_hanger || ret)
+- brcmf_txfinalize(fws->drvr, skb, ifidx, true);
++ brcmf_txfinalize(fws->drvr, skb, ifp->ifidx, true);
+
+ return 0;
+ }
+@@ -1848,7 +1849,7 @@ static int brcmf_fws_commit_skb(struct b
+ entry->transit_count--;
+ if (entry->suppressed)
+ entry->suppr_transit_count--;
+- brcmf_proto_hdrpull(fws->drvr, false, &ifidx, skb);
++ (void)brcmf_proto_hdrpull(fws->drvr, false, skb, NULL);
+ goto rollback;
+ }
+
+--- a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c
+@@ -522,7 +522,7 @@ static int brcmf_msgbuf_set_dcmd(struct
+
+
+ static int brcmf_msgbuf_hdrpull(struct brcmf_pub *drvr, bool do_fws,
+- u8 *ifidx, struct sk_buff *skb)
++ struct sk_buff *skb, struct brcmf_if **ifp)
+ {
+ return -ENODEV;
+ }
+@@ -1082,7 +1082,7 @@ brcmf_msgbuf_rx_skb(struct brcmf_msgbuf
+ struct brcmf_if *ifp;
+
+ ifp = brcmf_get_ifp(msgbuf->drvr, ifidx);
+- if (IS_ERR_OR_NULL(ifp) || !ifp->ndev) {
++ if (!ifp || !ifp->ndev) {
+ brcmf_err("Received pkt for invalid ifidx %d\n", ifidx);
+ brcmu_pkt_buf_free_skb(skb);
+ return;
+--- a/drivers/net/wireless/brcm80211/brcmfmac/proto.h
++++ b/drivers/net/wireless/brcm80211/brcmfmac/proto.h
+@@ -24,8 +24,8 @@ enum proto_addr_mode {
+
+
+ struct brcmf_proto {
+- int (*hdrpull)(struct brcmf_pub *drvr, bool do_fws, u8 *ifidx,
+- struct sk_buff *skb);
++ int (*hdrpull)(struct brcmf_pub *drvr, bool do_fws,
++ struct sk_buff *skb, struct brcmf_if **ifp);
+ int (*query_dcmd)(struct brcmf_pub *drvr, int ifidx, uint cmd,
+ void *buf, uint len);
+ int (*set_dcmd)(struct brcmf_pub *drvr, int ifidx, uint cmd, void *buf,
+@@ -46,9 +46,19 @@ int brcmf_proto_attach(struct brcmf_pub
+ void brcmf_proto_detach(struct brcmf_pub *drvr);
+
+ static inline int brcmf_proto_hdrpull(struct brcmf_pub *drvr, bool do_fws,
+- u8 *ifidx, struct sk_buff *skb)
++ struct sk_buff *skb,
++ struct brcmf_if **ifp)
+ {
+- return drvr->proto->hdrpull(drvr, do_fws, ifidx, skb);
++ struct brcmf_if *tmp = NULL;
++
++ /* assure protocol is always called with
++ * non-null initialized pointer.
++ */
++ if (ifp)
++ *ifp = NULL;
++ else
++ ifp = &tmp;
++ return drvr->proto->hdrpull(drvr, do_fws, skb, ifp);
+ }
+ static inline int brcmf_proto_query_dcmd(struct brcmf_pub *drvr, int ifidx,
+ uint cmd, void *buf, uint len)
--- /dev/null
+From: Arend van Spriel <arend@broadcom.com>
+Date: Wed, 26 Aug 2015 22:14:55 +0200
+Subject: [PATCH] brcmfmac: change parameters for
+ brcmf_remove_interface()
+
+Just pass the interface to be removed, ie. the struct brcmf_if instance.
+
+Reviewed-by: Hante Meuleman <meuleman@broadcom.com>
+Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com>
+Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
+Signed-off-by: Arend van Spriel <arend@broadcom.com>
+---
+
+--- a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c
+@@ -4982,7 +4982,7 @@ brcmf_notify_connect_status_ap(struct br
+ brcmf_dbg(CONN, "AP mode link down\n");
+ complete(&cfg->vif_disabled);
+ if (ifp->vif->mbss)
+- brcmf_remove_interface(ifp->drvr, ifp->bssidx);
++ brcmf_remove_interface(ifp);
+ return 0;
+ }
+
+--- a/drivers/net/wireless/brcm80211/brcmfmac/core.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/core.c
+@@ -887,12 +887,13 @@ static void brcmf_del_if(struct brcmf_pu
+ }
+ }
+
+-void brcmf_remove_interface(struct brcmf_pub *drvr, u32 bssidx)
++void brcmf_remove_interface(struct brcmf_if *ifp)
+ {
+- if (drvr->iflist[bssidx]) {
+- brcmf_fws_del_interface(drvr->iflist[bssidx]);
+- brcmf_del_if(drvr, bssidx);
+- }
++ if (!ifp || WARN_ON(ifp->drvr->iflist[ifp->bssidx] != ifp))
++ return;
++
++ brcmf_fws_del_interface(ifp);
++ brcmf_del_if(ifp->drvr, ifp->bssidx);
+ }
+
+ int brcmf_get_next_free_bsscfgidx(struct brcmf_pub *drvr)
+@@ -1122,7 +1123,7 @@ void brcmf_detach(struct device *dev)
+
+ /* make sure primary interface removed last */
+ for (i = BRCMF_MAX_IFS-1; i > -1; i--)
+- brcmf_remove_interface(drvr, i);
++ brcmf_remove_interface(drvr->iflist[i]);
+
+ brcmf_cfg80211_detach(drvr->config);
+
+--- a/drivers/net/wireless/brcm80211/brcmfmac/core.h
++++ b/drivers/net/wireless/brcm80211/brcmfmac/core.h
+@@ -206,7 +206,7 @@ struct brcmf_if *brcmf_get_ifp(struct br
+ int brcmf_net_attach(struct brcmf_if *ifp, bool rtnl_locked);
+ struct brcmf_if *brcmf_add_if(struct brcmf_pub *drvr, s32 bssidx, s32 ifidx,
+ char *name, u8 *mac_addr);
+-void brcmf_remove_interface(struct brcmf_pub *drvr, u32 bssidx);
++void brcmf_remove_interface(struct brcmf_if *ifp);
+ int brcmf_get_next_free_bsscfgidx(struct brcmf_pub *drvr);
+ void brcmf_txflowblock_if(struct brcmf_if *ifp,
+ enum brcmf_netif_stop_reason reason, bool state);
+--- a/drivers/net/wireless/brcm80211/brcmfmac/fweh.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/fweh.c
+@@ -222,7 +222,7 @@ static void brcmf_fweh_handle_if_event(s
+ err = brcmf_fweh_call_event_handler(ifp, emsg->event_code, emsg, data);
+
+ if (ifp && ifevent->action == BRCMF_E_IF_DEL)
+- brcmf_remove_interface(drvr, ifevent->bssidx);
++ brcmf_remove_interface(ifp);
+ }
+
+ /**
+--- a/drivers/net/wireless/brcm80211/brcmfmac/p2p.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/p2p.c
+@@ -2140,7 +2140,7 @@ static void brcmf_p2p_delete_p2pdev(stru
+ {
+ cfg80211_unregister_wdev(&vif->wdev);
+ p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif = NULL;
+- brcmf_remove_interface(vif->ifp->drvr, vif->ifp->bssidx);
++ brcmf_remove_interface(vif->ifp);
+ brcmf_free_vif(vif);
+ }
+
--- /dev/null
+From: Arend van Spriel <arend@broadcom.com>
+Date: Wed, 26 Aug 2015 22:14:56 +0200
+Subject: [PATCH] brcmfmac: only call brcmf_cfg80211_detach() when attach
+ was successful
+
+In brcmf_bus_start() the function brcmf_cfg80211_attach() is called which
+may fail. If this happens we should not call brcmf_cfg80211_detach() in
+the failure path as it will result in NULL pointer dereference:
+
+ brcmf_fweh_activate_events: Set event_msgs error (-5)
+ brcmf_bus_start: failed: -5
+ brcmf_sdio_firmware_callback: dongle is not responding
+ BUG: unable to handle kernel NULL pointer dereference at 0000000000000068
+ IP: [<ffffffff811e8f08>] kernfs_find_ns+0x18/0xd0
+ PGD 0
+ Oops: 0000 [#1] SMP
+ Modules linked in: brcmfmac(O) brcmutil(O) cfg80211 auth_rpcgss
+ CPU: 1 PID: 45 Comm: kworker/1:1 Tainted: G O
+ Hardware name: Dell Inc. Latitude E6410/07XJP9, BIOS A07 02/15/2011
+ Workqueue: events request_firmware_work_func
+ task: ffff880036c09ac0 ti: ffff880036dd4000 task.ti: ffff880036dd4000
+ RIP: 0010:[<ffffffff811e8f08>] [<ffffffff811e8f08>] kernfs_find_ns+0x18/0xd0
+ RSP: 0018:ffff880036dd7a28 EFLAGS: 00010246
+ RAX: ffff880036c09ac0 RBX: 0000000000000000 RCX: 000000007fffffff
+ RDX: 0000000000000000 RSI: ffffffff816578b9 RDI: 0000000000000000
+ RBP: ffff880036dd7a48 R08: 0000000000000000 R09: ffff880036c0b340
+ R10: 00000000000002ec R11: ffff880036dd7b08 R12: ffffffff816578b9
+ R13: 0000000000000000 R14: ffffffff816578b9 R15: ffff8800c6c87000
+ FS: 0000000000000000(0000) GS:ffff88012bc40000(0000) knlGS:0000000000000000
+ CS: 0010 DS: 0000 ES: 0000 CR0: 000000008005003b
+ CR2: 0000000000000068 CR3: 0000000001a0b000 CR4: 00000000000006e0
+ Stack:
+ 0000000000000000 ffffffff816578b9 0000000000000000 ffff8800c0d003c8
+ ffff880036dd7a78 ffffffff811e8ff5 0000000ffffffff1 ffffffff81a9b060
+ ffff8800c789f880 ffff8800c0d00000 ffff880036dd7a98 ffffffff811ebe0d
+ Call Trace:
+ [<ffffffff811e8ff5>] kernfs_find_and_get_ns+0x35/0x60
+ [<ffffffff811ebe0d>] sysfs_unmerge_group+0x1d/0x60
+ [<ffffffff81404ef2>] dpm_sysfs_remove+0x22/0x60
+ [<ffffffff813f9db9>] device_del+0x49/0x240
+ [<ffffffff815da768>] rfkill_unregister+0x58/0xc0
+ [<ffffffffa06bd91b>] wiphy_unregister+0xab/0x2f0 [cfg80211]
+ [<ffffffffa0742fe3>] brcmf_cfg80211_detach+0x23/0x50 [brcmfmac]
+ [<ffffffffa074d986>] brcmf_detach+0x86/0xe0 [brcmfmac]
+ [<ffffffffa0757de8>] brcmf_sdio_remove+0x48/0x120 [brcmfmac]
+ [<ffffffffa0758ed9>] brcmf_sdiod_remove+0x29/0xd0 [brcmfmac]
+ [<ffffffffa0759031>] brcmf_ops_sdio_remove+0xb1/0x110 [brcmfmac]
+ [<ffffffffa001c267>] sdio_bus_remove+0x37/0x100 [mmc_core]
+ [<ffffffff813fe026>] __device_release_driver+0x96/0x130
+ [<ffffffff813fe0e3>] device_release_driver+0x23/0x30
+ [<ffffffffa0754bc8>] brcmf_sdio_firmware_callback+0x2a8/0x5d0 [brcmfmac]
+ [<ffffffffa074deaf>] brcmf_fw_request_nvram_done+0x15f/0x5e0 [brcmfmac]
+ [<ffffffff8140142f>] ? devres_add+0x3f/0x50
+ [<ffffffff810642b5>] ? usermodehelper_read_unlock+0x15/0x20
+ [<ffffffff81400000>] ? platform_match+0x70/0xa0
+ [<ffffffff8140f400>] request_firmware_work_func+0x30/0x60
+ [<ffffffff8106828c>] process_one_work+0x14c/0x3d0
+ [<ffffffff8106862a>] worker_thread+0x11a/0x450
+ [<ffffffff81068510>] ? process_one_work+0x3d0/0x3d0
+ [<ffffffff8106d692>] kthread+0xd2/0xf0
+ [<ffffffff8106d5c0>] ? kthread_create_on_node+0x180/0x180
+ [<ffffffff815ed35f>] ret_from_fork+0x3f/0x70
+ [<ffffffff8106d5c0>] ? kthread_create_on_node+0x180/0x180
+ Code: e9 40 fe ff ff 48 89 d8 eb 87 66 0f 1f 84 00 00 00 00 00 66 66 66 66
+ 90 55 48 89 e5 41 56 49 89 f6 41 55 49 89 d5 31 d2 41 54 53 <0f> b7
+ 47 68 48 8b 5f 48 66 c1 e8 05 83 e0 01 4d 85 ed 0f b6 c8
+ RIP [<ffffffff811e8f08>] kernfs_find_ns+0x18/0xd0
+ RSP <ffff880036dd7a28>
+ CR2: 0000000000000068
+ ---[ end trace 87d6ec0d3fe46740 ]---
+
+Reported-by: Daniel (Deognyoun) Kim <dekim@broadcom.com>
+Reviewed-by: Hante Meuleman <meuleman@broadcom.com>
+Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com>
+Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
+Signed-off-by: Arend van Spriel <arend@broadcom.com>
+---
+
+--- a/drivers/net/wireless/brcm80211/brcmfmac/core.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/core.c
+@@ -1049,7 +1049,10 @@ int brcmf_bus_start(struct device *dev)
+ fail:
+ if (ret < 0) {
+ brcmf_err("failed: %d\n", ret);
+- brcmf_cfg80211_detach(drvr->config);
++ if (drvr->config) {
++ brcmf_cfg80211_detach(drvr->config);
++ drvr->config = NULL;
++ }
+ if (drvr->fws) {
+ brcmf_fws_del_interface(ifp);
+ brcmf_fws_deinit(drvr);
--- /dev/null
+From: Arend van Spriel <arend@broadcom.com>
+Date: Wed, 26 Aug 2015 22:14:57 +0200
+Subject: [PATCH] brcmfmac: correct detection of p2pdev interface event
+
+The p2pdev interface is setup in firmware resulting in a interface
+event. This event has role and no-if flag. When role is p2p client
+and no-if flag is set it indicates that this is the p2pdev interface.
+This info is used in handling the event and adding interface in the
+driver.
+
+Reviewed-by: Hante Meuleman <meuleman@broadcom.com>
+Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com>
+Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
+Signed-off-by: Arend van Spriel <arend@broadcom.com>
+---
+
+--- a/drivers/net/wireless/brcm80211/brcmfmac/core.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/core.c
+@@ -795,7 +795,7 @@ fail:
+ }
+
+ struct brcmf_if *brcmf_add_if(struct brcmf_pub *drvr, s32 bssidx, s32 ifidx,
+- char *name, u8 *mac_addr)
++ bool is_p2pdev, char *name, u8 *mac_addr)
+ {
+ struct brcmf_if *ifp;
+ struct net_device *ndev;
+@@ -821,7 +821,7 @@ struct brcmf_if *brcmf_add_if(struct brc
+ }
+ }
+
+- if (!brcmf_p2p_enable && bssidx == 1) {
++ if (!brcmf_p2p_enable && is_p2pdev) {
+ /* this is P2P_DEVICE interface */
+ brcmf_dbg(INFO, "allocate non-netdev interface\n");
+ ifp = kzalloc(sizeof(*ifp), GFP_KERNEL);
+@@ -999,12 +999,12 @@ int brcmf_bus_start(struct device *dev)
+ brcmf_dbg(TRACE, "\n");
+
+ /* add primary networking interface */
+- ifp = brcmf_add_if(drvr, 0, 0, "wlan%d", NULL);
++ ifp = brcmf_add_if(drvr, 0, 0, false, "wlan%d", NULL);
+ if (IS_ERR(ifp))
+ return PTR_ERR(ifp);
+
+ if (brcmf_p2p_enable)
+- p2p_ifp = brcmf_add_if(drvr, 1, 0, "p2p%d", NULL);
++ p2p_ifp = brcmf_add_if(drvr, 1, 0, false, "p2p%d", NULL);
+ else
+ p2p_ifp = NULL;
+ if (IS_ERR(p2p_ifp))
+--- a/drivers/net/wireless/brcm80211/brcmfmac/core.h
++++ b/drivers/net/wireless/brcm80211/brcmfmac/core.h
+@@ -205,7 +205,7 @@ char *brcmf_ifname(struct brcmf_pub *drv
+ struct brcmf_if *brcmf_get_ifp(struct brcmf_pub *drvr, int ifidx);
+ int brcmf_net_attach(struct brcmf_if *ifp, bool rtnl_locked);
+ struct brcmf_if *brcmf_add_if(struct brcmf_pub *drvr, s32 bssidx, s32 ifidx,
+- char *name, u8 *mac_addr);
++ bool is_p2pdev, char *name, u8 *mac_addr);
+ void brcmf_remove_interface(struct brcmf_if *ifp);
+ int brcmf_get_next_free_bsscfgidx(struct brcmf_pub *drvr);
+ void brcmf_txflowblock_if(struct brcmf_if *ifp,
+--- a/drivers/net/wireless/brcm80211/brcmfmac/fweh.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/fweh.c
+@@ -179,6 +179,7 @@ static void brcmf_fweh_handle_if_event(s
+ {
+ struct brcmf_if_event *ifevent = data;
+ struct brcmf_if *ifp;
++ bool is_p2pdev;
+ int err = 0;
+
+ brcmf_dbg(EVENT, "action: %u idx: %u bsscfg: %u flags: %u role: %u\n",
+@@ -186,18 +187,16 @@ static void brcmf_fweh_handle_if_event(s
+ ifevent->flags, ifevent->role);
+
+ /* The P2P Device interface event must not be ignored
+- * contrary to what firmware tells us. The only way to
+- * distinguish the P2P Device is by looking at the ifidx
+- * and bssidx received.
++ * contrary to what firmware tells us.
+ */
+- if (!(ifevent->ifidx == 0 && ifevent->bssidx == 1) &&
+- (ifevent->flags & BRCMF_E_IF_FLAG_NOIF)) {
++ is_p2pdev = (ifevent->flags & BRCMF_E_IF_FLAG_NOIF) &&
++ ifevent->role == BRCMF_E_IF_ROLE_P2P_CLIENT;
++ if (!is_p2pdev && (ifevent->flags & BRCMF_E_IF_FLAG_NOIF)) {
+ brcmf_dbg(EVENT, "event can be ignored\n");
+ return;
+ }
+ if (ifevent->ifidx >= BRCMF_MAX_IFS) {
+- brcmf_err("invalid interface index: %u\n",
+- ifevent->ifidx);
++ brcmf_err("invalid interface index: %u\n", ifevent->ifidx);
+ return;
+ }
+
+@@ -207,7 +206,7 @@ static void brcmf_fweh_handle_if_event(s
+ brcmf_dbg(EVENT, "adding %s (%pM)\n", emsg->ifname,
+ emsg->addr);
+ ifp = brcmf_add_if(drvr, ifevent->bssidx, ifevent->ifidx,
+- emsg->ifname, emsg->addr);
++ is_p2pdev, emsg->ifname, emsg->addr);
+ if (IS_ERR(ifp))
+ return;
+ brcmf_fws_add_interface(ifp);
--- /dev/null
+From: Arend van Spriel <arend@broadcom.com>
+Date: Wed, 26 Aug 2015 22:14:58 +0200
+Subject: [PATCH] brcmfmac: use brcmf_get_ifp() to map ifidx to struct
+ brcmf_if instance
+
+The knowledge on how to map the interface index to a struct brcmf_if
+instance is in brcmf_get_ifp() so use that function when only the
+interface index is known instead of accessing brcmf_pub::iflist
+directly.
+
+Reviewed-by: Hante Meuleman <meuleman@broadcom.com>
+Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com>
+Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
+Signed-off-by: Arend van Spriel <arend@broadcom.com>
+---
+
+--- a/drivers/net/wireless/brcm80211/brcmfmac/btcoex.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/btcoex.c
+@@ -149,7 +149,7 @@ static s32 brcmf_btcoex_params_read(stru
+ static void brcmf_btcoex_boost_wifi(struct brcmf_btcoex_info *btci,
+ bool trump_sco)
+ {
+- struct brcmf_if *ifp = btci->cfg->pub->iflist[0];
++ struct brcmf_if *ifp = brcmf_get_ifp(btci->cfg->pub, 0);
+
+ if (trump_sco && !btci->saved_regs_part2) {
+ /* this should reduce eSCO agressive
+@@ -468,7 +468,7 @@ int brcmf_btcoex_set_mode(struct brcmf_c
+ {
+ struct brcmf_cfg80211_info *cfg = wiphy_priv(vif->wdev.wiphy);
+ struct brcmf_btcoex_info *btci = cfg->btcoex;
+- struct brcmf_if *ifp = cfg->pub->iflist[0];
++ struct brcmf_if *ifp = brcmf_get_ifp(cfg->pub, 0);
+
+ switch (mode) {
+ case BRCMF_BTCOEX_DISABLED:
+--- a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c
+@@ -6212,7 +6212,7 @@ static void brcmf_free_wiphy(struct wiph
+ struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr,
+ struct device *busdev)
+ {
+- struct net_device *ndev = drvr->iflist[0]->ndev;
++ struct net_device *ndev = brcmf_get_ifp(drvr, 0)->ndev;
+ struct brcmf_cfg80211_info *cfg;
+ struct wiphy *wiphy;
+ struct brcmf_cfg80211_vif *vif;
+--- a/drivers/net/wireless/brcm80211/brcmfmac/feature.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/feature.c
+@@ -121,7 +121,7 @@ static void brcmf_feat_iovar_int_set(str
+
+ void brcmf_feat_attach(struct brcmf_pub *drvr)
+ {
+- struct brcmf_if *ifp = drvr->iflist[0];
++ struct brcmf_if *ifp = brcmf_get_ifp(drvr, 0);
+
+ brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_MCHAN, "mchan");
+ brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_PNO, "pfn");
+--- a/drivers/net/wireless/brcm80211/brcmfmac/flowring.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/flowring.c
+@@ -221,7 +221,7 @@ static void brcmf_flowring_block(struct
+
+ bus_if = dev_get_drvdata(flow->dev);
+ drvr = bus_if->drvr;
+- ifp = drvr->iflist[ifidx];
++ ifp = brcmf_get_ifp(drvr, ifidx);
+ brcmf_txflowblock_if(ifp, BRCMF_NETIF_STOP_REASON_FLOW, blocked);
+
+ spin_unlock_irqrestore(&flow->block_lock, flags);
+--- a/drivers/net/wireless/brcm80211/brcmfmac/fweh.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/fweh.c
+@@ -334,7 +334,7 @@ void brcmf_fweh_attach(struct brcmf_pub
+ void brcmf_fweh_detach(struct brcmf_pub *drvr)
+ {
+ struct brcmf_fweh_info *fweh = &drvr->fweh;
+- struct brcmf_if *ifp = drvr->iflist[0];
++ struct brcmf_if *ifp = brcmf_get_ifp(drvr, 0);
+ s8 eventmask[BRCMF_EVENTING_MASK_LEN];
+
+ if (ifp) {
+--- a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c
+@@ -972,7 +972,7 @@ static void
+ brcmf_fws_flow_control_check(struct brcmf_fws_info *fws, struct pktq *pq,
+ u8 if_id)
+ {
+- struct brcmf_if *ifp = fws->drvr->iflist[!if_id ? 0 : if_id + 1];
++ struct brcmf_if *ifp = brcmf_get_ifp(fws->drvr, if_id);
+
+ if (WARN_ON(!ifp))
+ return;
+@@ -2118,6 +2118,7 @@ static int brcmf_debugfs_fws_stats_read(
+ int brcmf_fws_init(struct brcmf_pub *drvr)
+ {
+ struct brcmf_fws_info *fws;
++ struct brcmf_if *ifp;
+ u32 tlv = BRCMF_FWS_FLAGS_RSSI_SIGNALS;
+ int rc;
+ u32 mode;
+@@ -2177,21 +2178,22 @@ int brcmf_fws_init(struct brcmf_pub *drv
+ * continue. Set mode back to none indicating not enabled.
+ */
+ fws->fw_signals = true;
+- if (brcmf_fil_iovar_int_set(drvr->iflist[0], "tlv", tlv)) {
++ ifp = brcmf_get_ifp(drvr, 0);
++ if (brcmf_fil_iovar_int_set(ifp, "tlv", tlv)) {
+ brcmf_err("failed to set bdcv2 tlv signaling\n");
+ fws->fcmode = BRCMF_FWS_FCMODE_NONE;
+ fws->fw_signals = false;
+ }
+
+- if (brcmf_fil_iovar_int_set(drvr->iflist[0], "ampdu_hostreorder", 1))
++ if (brcmf_fil_iovar_int_set(ifp, "ampdu_hostreorder", 1))
+ brcmf_dbg(INFO, "enabling AMPDU host-reorder failed\n");
+
+ /* Enable seq number reuse, if supported */
+- if (brcmf_fil_iovar_int_get(drvr->iflist[0], "wlfc_mode", &mode) == 0) {
++ if (brcmf_fil_iovar_int_get(ifp, "wlfc_mode", &mode) == 0) {
+ if (BRCMF_FWS_MODE_GET_REUSESEQ(mode)) {
+ mode = 0;
+ BRCMF_FWS_MODE_SET_REUSESEQ(mode, 1);
+- if (brcmf_fil_iovar_int_set(drvr->iflist[0],
++ if (brcmf_fil_iovar_int_set(ifp,
+ "wlfc_mode", mode) == 0) {
+ BRCMF_FWS_MODE_SET_REUSESEQ(fws->mode, 1);
+ }
--- /dev/null
+From: Arend van Spriel <arend@broadcom.com>
+Date: Wed, 26 Aug 2015 22:14:59 +0200
+Subject: [PATCH] brcmfmac: pass struct brcmf_if instance in
+ brcmf_txfinalize()
+
+Most call sites of brcmf_txfinalize already have struct brcmf_if
+instance so pass that to brcmf_txfinalize() as the function
+needs it anyway.
+
+Reviewed-by: Hante Meuleman <meuleman@broadcom.com>
+Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com>
+Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
+Signed-off-by: Arend van Spriel <arend@broadcom.com>
+---
+
+--- a/drivers/net/wireless/brcm80211/brcmfmac/core.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/core.c
+@@ -560,17 +560,11 @@ void brcmf_rx_frame(struct device *dev,
+ brcmf_netif_rx(ifp, skb);
+ }
+
+-void brcmf_txfinalize(struct brcmf_pub *drvr, struct sk_buff *txp, u8 ifidx,
+- bool success)
++void brcmf_txfinalize(struct brcmf_if *ifp, struct sk_buff *txp, bool success)
+ {
+- struct brcmf_if *ifp;
+ struct ethhdr *eh;
+ u16 type;
+
+- ifp = drvr->iflist[ifidx];
+- if (!ifp)
+- goto done;
+-
+ eh = (struct ethhdr *)(txp->data);
+ type = ntohs(eh->h_proto);
+
+@@ -582,7 +576,7 @@ void brcmf_txfinalize(struct brcmf_pub *
+
+ if (!success)
+ ifp->stats.tx_errors++;
+-done:
++
+ brcmu_pkt_buf_free_skb(txp);
+ }
+
+@@ -600,7 +594,7 @@ void brcmf_txcomplete(struct device *dev
+ if (brcmf_proto_hdrpull(drvr, false, txp, &ifp))
+ brcmu_pkt_buf_free_skb(txp);
+ else
+- brcmf_txfinalize(drvr, txp, ifp->ifidx, success);
++ brcmf_txfinalize(ifp, txp, success);
+ }
+ }
+
+--- a/drivers/net/wireless/brcm80211/brcmfmac/core.h
++++ b/drivers/net/wireless/brcm80211/brcmfmac/core.h
+@@ -210,8 +210,7 @@ void brcmf_remove_interface(struct brcmf
+ int brcmf_get_next_free_bsscfgidx(struct brcmf_pub *drvr);
+ void brcmf_txflowblock_if(struct brcmf_if *ifp,
+ enum brcmf_netif_stop_reason reason, bool state);
+-void brcmf_txfinalize(struct brcmf_pub *drvr, struct sk_buff *txp, u8 ifidx,
+- bool success);
++void brcmf_txfinalize(struct brcmf_if *ifp, struct sk_buff *txp, bool success);
+ void brcmf_netif_rx(struct brcmf_if *ifp, struct sk_buff *skb);
+
+ /* Sets dongle media info (drv_version, mac address). */
+--- a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c
+@@ -1506,7 +1506,7 @@ brcmf_fws_txs_process(struct brcmf_fws_i
+ ret = brcmf_fws_txstatus_suppressed(fws, fifo, skb, ifp->ifidx,
+ genbit, seq);
+ if (remove_from_hanger || ret)
+- brcmf_txfinalize(fws->drvr, skb, ifp->ifidx, true);
++ brcmf_txfinalize(ifp, skb, true);
+
+ return 0;
+ }
+@@ -1905,7 +1905,7 @@ int brcmf_fws_process_skb(struct brcmf_i
+ if (fws->avoid_queueing) {
+ rc = brcmf_proto_txdata(drvr, ifp->ifidx, 0, skb);
+ if (rc < 0)
+- brcmf_txfinalize(drvr, skb, ifp->ifidx, false);
++ brcmf_txfinalize(ifp, skb, false);
+ return rc;
+ }
+
+@@ -1929,7 +1929,7 @@ int brcmf_fws_process_skb(struct brcmf_i
+ brcmf_fws_schedule_deq(fws);
+ } else {
+ brcmf_err("drop skb: no hanger slot\n");
+- brcmf_txfinalize(drvr, skb, ifp->ifidx, false);
++ brcmf_txfinalize(ifp, skb, false);
+ rc = -ENOMEM;
+ }
+ brcmf_fws_unlock(fws);
+@@ -2009,8 +2009,9 @@ static void brcmf_fws_dequeue_worker(str
+ ret = brcmf_proto_txdata(drvr, ifidx, 0, skb);
+ brcmf_fws_lock(fws);
+ if (ret < 0)
+- brcmf_txfinalize(drvr, skb, ifidx,
+- false);
++ brcmf_txfinalize(brcmf_get_ifp(drvr,
++ ifidx),
++ skb, false);
+ if (fws->bus_flow_blocked)
+ break;
+ }
+--- a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c
+@@ -873,7 +873,11 @@ brcmf_msgbuf_process_txstatus(struct brc
+ commonring = msgbuf->flowrings[flowid];
+ atomic_dec(&commonring->outstanding_tx);
+
+- brcmf_txfinalize(msgbuf->drvr, skb, tx_status->msg.ifidx, true);
++ /* Hante: i believe this was a bug as tx_status->msg.ifidx was used
++ * in brcmf_txfinalize as index in drvr->iflist. Can you confirm/deny?
++ */
++ brcmf_txfinalize(brcmf_get_ifp(msgbuf->drvr, tx_status->msg.ifidx),
++ skb, true);
+ }
+
+
--- /dev/null
+From: Arend van Spriel <arend@broadcom.com>
+Date: Wed, 26 Aug 2015 22:15:00 +0200
+Subject: [PATCH] brcmfmac: add mapping for interface index to bsscfg
+ index
+
+Because the P2P Device interface in firmware uses the same interface
+index as the primary interface we use the bsscfg index as index in the
+struct brcmf_pub::iflist. However, in the data path we get the interface
+index and not the bsscfg index. So we need a mapping of interface index
+to bsscfg index, which can be determined upon handle adding the interface.
+
+Reviewed-by: Hante Meuleman <meuleman@broadcom.com>
+Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com>
+Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
+Signed-off-by: Arend van Spriel <arend@broadcom.com>
+---
+
+--- a/drivers/net/wireless/brcm80211/brcmfmac/core.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/core.c
+@@ -85,21 +85,20 @@ char *brcmf_ifname(struct brcmf_pub *drv
+
+ struct brcmf_if *brcmf_get_ifp(struct brcmf_pub *drvr, int ifidx)
+ {
++ struct brcmf_if *ifp;
++ s32 bssidx;
++
+ if (ifidx < 0 || ifidx >= BRCMF_MAX_IFS) {
+ brcmf_err("ifidx %d out of range\n", ifidx);
+ return NULL;
+ }
+
+- /* The ifidx is the idx to map to matching netdev/ifp. When receiving
+- * events this is easy because it contains the bssidx which maps
+- * 1-on-1 to the netdev/ifp. But for data frames the ifidx is rcvd.
+- * bssidx 1 is used for p2p0 and no data can be received or
+- * transmitted on it. Therefor bssidx is ifidx + 1 if ifidx > 0
+- */
+- if (ifidx)
+- ifidx++;
++ ifp = NULL;
++ bssidx = drvr->if2bss[ifidx];
++ if (bssidx >= 0)
++ ifp = drvr->iflist[bssidx];
+
+- return drvr->iflist[ifidx];
++ return ifp;
+ }
+
+ static void _brcmf_set_multicast_list(struct work_struct *work)
+@@ -831,6 +830,8 @@ struct brcmf_if *brcmf_add_if(struct brc
+
+ ifp = netdev_priv(ndev);
+ ifp->ndev = ndev;
++ /* store mapping ifidx to bssidx */
++ drvr->if2bss[ifidx] = bssidx;
+ }
+
+ ifp->drvr = drvr;
+@@ -855,6 +856,7 @@ static void brcmf_del_if(struct brcmf_pu
+ struct brcmf_if *ifp;
+
+ ifp = drvr->iflist[bssidx];
++ drvr->if2bss[ifp->ifidx] = -1;
+ drvr->iflist[bssidx] = NULL;
+ if (!ifp) {
+ brcmf_err("Null interface, idx=%d\n", bssidx);
+@@ -862,6 +864,7 @@ static void brcmf_del_if(struct brcmf_pu
+ }
+ brcmf_dbg(TRACE, "Enter, idx=%d, ifidx=%d\n", bssidx, ifp->ifidx);
+ if (ifp->ndev) {
++ drvr->if2bss[ifp->ifidx] = -1;
+ if (bssidx == 0) {
+ if (ifp->ndev->netdev_ops == &brcmf_netdev_ops_pri) {
+ rtnl_lock();
+@@ -926,6 +929,7 @@ int brcmf_attach(struct device *dev)
+ if (!drvr)
+ return -ENOMEM;
+
++ memset(drvr->if2bss, 0xFF, sizeof(drvr->if2bss));
+ mutex_init(&drvr->proto_block);
+
+ /* Link to bus module */
+--- a/drivers/net/wireless/brcm80211/brcmfmac/core.h
++++ b/drivers/net/wireless/brcm80211/brcmfmac/core.h
+@@ -122,6 +122,7 @@ struct brcmf_pub {
+ struct mac_address addresses[BRCMF_MAX_IFS];
+
+ struct brcmf_if *iflist[BRCMF_MAX_IFS];
++ s32 if2bss[BRCMF_MAX_IFS];
+
+ struct mutex proto_block;
+ unsigned char proto_buf[BRCMF_DCMD_MAXLEN];
--- /dev/null
+From: Arend van Spriel <arend@broadcom.com>
+Date: Wed, 26 Aug 2015 22:15:01 +0200
+Subject: [PATCH] brcmfmac: add dedicated debug level for firmware
+ console logging
+
+Both PCIe and SDIO devices have the possibility to log the firmware
+console output in kernel log. For PCIe it is logged when PCIE debug
+level is enabled. For SDIO it is logged when user specifies a non-zero
+console interval through debugfs. This patch tries to make it a
+bit more consistent. The firmware console output is only logged when
+FWCON debug level is enabled.
+
+Reviewed-by: Hante Meuleman <meuleman@broadcom.com>
+Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com>
+Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
+Reviewed-by: Pontus Fuchs <pontusf@broadcom.com>
+Signed-off-by: Arend van Spriel <arend@broadcom.com>
+---
+
+--- a/drivers/net/wireless/brcm80211/brcmfmac/debug.h
++++ b/drivers/net/wireless/brcm80211/brcmfmac/debug.h
+@@ -37,6 +37,7 @@
+ #define BRCMF_SDIO_VAL 0x00020000
+ #define BRCMF_MSGBUF_VAL 0x00040000
+ #define BRCMF_PCIE_VAL 0x00080000
++#define BRCMF_FWCON_VAL 0x00100000
+
+ /* set default print format */
+ #undef pr_fmt
+@@ -78,6 +79,7 @@ do { \
+ #define BRCMF_GLOM_ON() (brcmf_msg_level & BRCMF_GLOM_VAL)
+ #define BRCMF_EVENT_ON() (brcmf_msg_level & BRCMF_EVENT_VAL)
+ #define BRCMF_FIL_ON() (brcmf_msg_level & BRCMF_FIL_VAL)
++#define BRCMF_FWCON_ON() (brcmf_msg_level & BRCMF_FWCON_VAL)
+
+ #else /* defined(DEBUG) || defined(CPTCFG_BRCM_TRACING) */
+
+@@ -90,6 +92,7 @@ do { \
+ #define BRCMF_GLOM_ON() 0
+ #define BRCMF_EVENT_ON() 0
+ #define BRCMF_FIL_ON() 0
++#define BRCMF_FWCON_ON() 0
+
+ #endif /* defined(DEBUG) || defined(CPTCFG_BRCM_TRACING) */
+
+--- a/drivers/net/wireless/brcm80211/brcmfmac/pcie.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/pcie.c
+@@ -644,7 +644,7 @@ static void brcmf_pcie_bus_console_init(
+ addr = console->base_addr + BRCMF_CONSOLE_BUFSIZE_OFFSET;
+ console->bufsize = brcmf_pcie_read_tcm32(devinfo, addr);
+
+- brcmf_dbg(PCIE, "Console: base %x, buf %x, size %d\n",
++ brcmf_dbg(FWCON, "Console: base %x, buf %x, size %d\n",
+ console->base_addr, console->buf_addr, console->bufsize);
+ }
+
+@@ -656,6 +656,9 @@ static void brcmf_pcie_bus_console_read(
+ u8 ch;
+ u32 newidx;
+
++ if (!BRCMF_FWCON_ON())
++ return;
++
+ console = &devinfo->shared.console;
+ addr = console->base_addr + BRCMF_CONSOLE_WRITEIDX_OFFSET;
+ newidx = brcmf_pcie_read_tcm32(devinfo, addr);
+@@ -677,7 +680,7 @@ static void brcmf_pcie_bus_console_read(
+ }
+ if (ch == '\n') {
+ console->log_str[console->log_idx] = 0;
+- brcmf_dbg(PCIE, "CONSOLE: %s", console->log_str);
++ pr_debug("CONSOLE: %s", console->log_str);
+ console->log_idx = 0;
+ }
+ }
+--- a/drivers/net/wireless/brcm80211/brcmfmac/sdio.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c
+@@ -123,6 +123,7 @@ struct rte_console {
+
+ #define BRCMF_FIRSTREAD (1 << 6)
+
++#define BRCMF_CONSOLE 10 /* watchdog interval to poll console */
+
+ /* SBSDIO_DEVICE_CTL */
+
+@@ -3204,6 +3205,8 @@ static void brcmf_sdio_debugfs_create(st
+ if (IS_ERR_OR_NULL(dentry))
+ return;
+
++ bus->console_interval = BRCMF_CONSOLE;
++
+ brcmf_debugfs_add_entry(drvr, "forensics", brcmf_sdio_forensic_read);
+ brcmf_debugfs_add_entry(drvr, "counters",
+ brcmf_debugfs_sdio_count_read);
+@@ -3613,7 +3616,7 @@ static void brcmf_sdio_bus_watchdog(stru
+ }
+ #ifdef DEBUG
+ /* Poll for console output periodically */
+- if (bus->sdiodev->state == BRCMF_SDIOD_DATA &&
++ if (bus->sdiodev->state == BRCMF_SDIOD_DATA && BRCMF_FWCON_ON() &&
+ bus->console_interval != 0) {
+ bus->console.count += BRCMF_WD_POLL_MS;
+ if (bus->console.count >= bus->console_interval) {
--- /dev/null
+From: Arend van Spriel <arend@broadcom.com>
+Date: Wed, 26 Aug 2015 22:15:02 +0200
+Subject: [PATCH] brcmfmac: remove ifidx parameter from
+ brcmf_fws_txstatus_suppressed()
+
+The brcmf_fws_txstatus_suppressed() function prototype specifies an
+ifidx parameter which is not used within the function implementation.
+
+Reviewed-by: Hante Meuleman <meuleman@broadcom.com>
+Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com>
+Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
+Signed-off-by: Arend van Spriel <arend@broadcom.com>
+---
+
+--- a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c
+@@ -1398,7 +1398,7 @@ done:
+ }
+
+ static int brcmf_fws_txstatus_suppressed(struct brcmf_fws_info *fws, int fifo,
+- struct sk_buff *skb, u8 ifidx,
++ struct sk_buff *skb,
+ u32 genbit, u16 seq)
+ {
+ struct brcmf_fws_mac_descriptor *entry = brcmf_skbcb(skb)->mac;
+@@ -1503,7 +1503,7 @@ brcmf_fws_txs_process(struct brcmf_fws_i
+ return -EINVAL;
+ }
+ if (!remove_from_hanger)
+- ret = brcmf_fws_txstatus_suppressed(fws, fifo, skb, ifp->ifidx,
++ ret = brcmf_fws_txstatus_suppressed(fws, fifo, skb,
+ genbit, seq);
+ if (remove_from_hanger || ret)
+ brcmf_txfinalize(ifp, skb, true);
--- /dev/null
+From: Arend van Spriel <arend@broadcom.com>
+Date: Wed, 26 Aug 2015 22:15:03 +0200
+Subject: [PATCH] brcmfmac: change prototype for brcmf_fws_hdrpull()
+
+Instead of passing ifidx and drvr just pass struct brcmf_if pointer
+which holds both parameters.
+
+Reviewed-by: Hante Meuleman <meuleman@broadcom.com>
+Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com>
+Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
+Signed-off-by: Arend van Spriel <arend@broadcom.com>
+---
+
+--- a/drivers/net/wireless/brcm80211/brcmfmac/bcdc.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/bcdc.c
+@@ -312,8 +312,7 @@ brcmf_proto_bcdc_hdrpull(struct brcmf_pu
+
+ skb_pull(pktbuf, BCDC_HEADER_LEN);
+ if (do_fws)
+- brcmf_fws_hdrpull(drvr, tmp_if->ifidx, h->data_offset << 2,
+- pktbuf);
++ brcmf_fws_hdrpull(tmp_if, h->data_offset << 2, pktbuf);
+ else
+ skb_pull(pktbuf, h->data_offset << 2);
+
+--- a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c
+@@ -1616,11 +1616,10 @@ static int brcmf_fws_notify_bcmc_credit_
+ return 0;
+ }
+
+-int brcmf_fws_hdrpull(struct brcmf_pub *drvr, int ifidx, s16 signal_len,
+- struct sk_buff *skb)
++void brcmf_fws_hdrpull(struct brcmf_if *ifp, s16 siglen, struct sk_buff *skb)
+ {
+ struct brcmf_skb_reorder_data *rd;
+- struct brcmf_fws_info *fws = drvr->fws;
++ struct brcmf_fws_info *fws = ifp->drvr->fws;
+ u8 *signal_data;
+ s16 data_len;
+ u8 type;
+@@ -1630,20 +1629,20 @@ int brcmf_fws_hdrpull(struct brcmf_pub *
+ s32 err;
+
+ brcmf_dbg(HDRS, "enter: ifidx %d, skblen %u, sig %d\n",
+- ifidx, skb->len, signal_len);
++ ifp->ifidx, skb->len, siglen);
+
+- WARN_ON(signal_len > skb->len);
++ WARN_ON(siglen > skb->len);
+
+- if (!signal_len)
+- return 0;
++ if (!siglen)
++ return;
+ /* if flow control disabled, skip to packet data and leave */
+ if ((!fws) || (!fws->fw_signals)) {
+- skb_pull(skb, signal_len);
+- return 0;
++ skb_pull(skb, siglen);
++ return;
+ }
+
+ fws->stats.header_pulls++;
+- data_len = signal_len;
++ data_len = siglen;
+ signal_data = skb->data;
+
+ status = BRCMF_FWS_RET_OK_NOSCHEDULE;
+@@ -1731,14 +1730,12 @@ int brcmf_fws_hdrpull(struct brcmf_pub *
+ /* signalling processing result does
+ * not affect the actual ethernet packet.
+ */
+- skb_pull(skb, signal_len);
++ skb_pull(skb, siglen);
+
+ /* this may be a signal-only packet
+ */
+ if (skb->len == 0)
+ fws->stats.header_only_pkt++;
+-
+- return 0;
+ }
+
+ static u8 brcmf_fws_precommit_skb(struct brcmf_fws_info *fws, int fifo,
+--- a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.h
++++ b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.h
+@@ -21,8 +21,7 @@
+ int brcmf_fws_init(struct brcmf_pub *drvr);
+ void brcmf_fws_deinit(struct brcmf_pub *drvr);
+ bool brcmf_fws_fc_active(struct brcmf_fws_info *fws);
+-int brcmf_fws_hdrpull(struct brcmf_pub *drvr, int ifidx, s16 signal_len,
+- struct sk_buff *skb);
++void brcmf_fws_hdrpull(struct brcmf_if *ifp, s16 siglen, struct sk_buff *skb);
+ int brcmf_fws_process_skb(struct brcmf_if *ifp, struct sk_buff *skb);
+
+ void brcmf_fws_reset_interface(struct brcmf_if *ifp);
--- /dev/null
+From: Arend van Spriel <arend@broadcom.com>
+Date: Wed, 26 Aug 2015 22:15:04 +0200
+Subject: [PATCH] brcmfmac: introduce brcmf_net_detach() function
+
+In case of error during brcmf_bus_start() the network interfaces were
+freed using free_netdev(). However, the interfaces may have additional
+memory allocated which is not freed. The netdev has destructor set to
+brcmf_cfg80211_free_netdev() which frees the additional memory if
+allocated and call free_netdev(). The brcmf_net_detach() either calls
+brcmf_cfg80211_free_netdev() directly or uses unregister_netdev() when
+struct net_device::reg_state indicates the netdev was registered.
+
+Reported-by: Daniel (Deognyoun) Kim <dekim@broadcom.com>
+Reviewed-by: Hante Meuleman <meuleman@broadcom.com>
+Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com>
+Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
+Signed-off-by: Arend van Spriel <arend@broadcom.com>
+---
+
+--- a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c
+@@ -4746,7 +4746,8 @@ void brcmf_cfg80211_free_netdev(struct n
+ ifp = netdev_priv(ndev);
+ vif = ifp->vif;
+
+- brcmf_free_vif(vif);
++ if (vif)
++ brcmf_free_vif(vif);
+ free_netdev(ndev);
+ }
+
+--- a/drivers/net/wireless/brcm80211/brcmfmac/core.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/core.c
+@@ -718,8 +718,6 @@ int brcmf_net_attach(struct brcmf_if *if
+ }
+
+ brcmf_dbg(INFO, "%s: Broadcom Dongle Host Driver\n", ndev->name);
+-
+- ndev->destructor = brcmf_cfg80211_free_netdev;
+ return 0;
+
+ fail:
+@@ -729,6 +727,14 @@ fail:
+ return -EBADE;
+ }
+
++static void brcmf_net_detach(struct net_device *ndev)
++{
++ if (ndev->reg_state == NETREG_REGISTERED)
++ unregister_netdev(ndev);
++ else
++ brcmf_cfg80211_free_netdev(ndev);
++}
++
+ static int brcmf_net_p2p_open(struct net_device *ndev)
+ {
+ brcmf_dbg(TRACE, "Enter\n");
+@@ -805,8 +811,7 @@ struct brcmf_if *brcmf_add_if(struct brc
+ ifp->ndev->name);
+ if (ifidx) {
+ netif_stop_queue(ifp->ndev);
+- unregister_netdev(ifp->ndev);
+- free_netdev(ifp->ndev);
++ brcmf_net_detach(ifp->ndev);
+ drvr->iflist[bssidx] = NULL;
+ } else {
+ brcmf_err("ignore IF event\n");
+@@ -828,6 +833,7 @@ struct brcmf_if *brcmf_add_if(struct brc
+ if (!ndev)
+ return ERR_PTR(-ENOMEM);
+
++ ndev->destructor = brcmf_cfg80211_free_netdev;
+ ifp = netdev_priv(ndev);
+ ifp->ndev = ndev;
+ /* store mapping ifidx to bssidx */
+@@ -879,8 +885,7 @@ static void brcmf_del_if(struct brcmf_pu
+ cancel_work_sync(&ifp->setmacaddr_work);
+ cancel_work_sync(&ifp->multicast_work);
+ }
+- /* unregister will take care of freeing it */
+- unregister_netdev(ifp->ndev);
++ brcmf_net_detach(ifp->ndev);
+ }
+ }
+
+@@ -1056,11 +1061,11 @@ fail:
+ brcmf_fws_deinit(drvr);
+ }
+ if (drvr->iflist[0]) {
+- free_netdev(ifp->ndev);
++ brcmf_net_detach(ifp->ndev);
+ drvr->iflist[0] = NULL;
+ }
+ if (p2p_ifp) {
+- free_netdev(p2p_ifp->ndev);
++ brcmf_net_detach(p2p_ifp->ndev);
+ drvr->iflist[1] = NULL;
+ }
+ return ret;
--- /dev/null
+From: Hante Meuleman <meuleman@broadcom.com>
+Date: Thu, 27 Aug 2015 16:14:06 +0200
+Subject: [PATCH] brcmfmac: Reset PCIE devices after recognition.
+
+When PCIE type devices are being FW reloaded without being properly
+reset then the device ends up in a locked state, requiring the
+device to be completely powered down. This patch adds a reset
+through watchdog at the moment the device (cores) has been
+recognized. This will solve warm reboot issues.
+
+Cc: Rafal Milecki <zajec5@gmail.com>
+Reviewed-by: Arend Van Spriel <arend@broadcom.com>
+Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com>
+Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
+Signed-off-by: Hante Meuleman <meuleman@broadcom.com>
+Signed-off-by: Arend van Spriel <arend@broadcom.com>
+---
+
+--- a/drivers/net/wireless/brcm80211/brcmfmac/chip.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/chip.c
+@@ -101,6 +101,9 @@
+ /* ARM Cortex M3 core, ID 0x82a */
+ #define BCM4329_CORE_ARM_BASE 0x18002000
+
++/* Max possibly supported memory size (limited by IO mapped memory) */
++#define BRCMF_CHIP_MAX_MEMSIZE (4 * 1024 * 1024)
++
+ #define CORE_SB(base, field) \
+ (base + SBCONFIGOFF + offsetof(struct sbconfig, field))
+ #define SBCOREREV(sbidh) \
+@@ -687,6 +690,12 @@ static int brcmf_chip_get_raminfo(struct
+ brcmf_err("RAM size is undetermined\n");
+ return -ENOMEM;
+ }
++
++ if (ci->pub.ramsize > BRCMF_CHIP_MAX_MEMSIZE) {
++ brcmf_err("RAM size is incorrect\n");
++ return -ENOMEM;
++ }
++
+ return 0;
+ }
+
+@@ -899,6 +908,15 @@ static int brcmf_chip_recognition(struct
+
+ /* assure chip is passive for core access */
+ brcmf_chip_set_passive(&ci->pub);
++
++ /* Call bus specific reset function now. Cores have been determined
++ * but further access may require a chip specific reset at this point.
++ */
++ if (ci->ops->reset) {
++ ci->ops->reset(ci->ctx, &ci->pub);
++ brcmf_chip_set_passive(&ci->pub);
++ }
++
+ return brcmf_chip_get_raminfo(ci);
+ }
+
+--- a/drivers/net/wireless/brcm80211/brcmfmac/chip.h
++++ b/drivers/net/wireless/brcm80211/brcmfmac/chip.h
+@@ -73,6 +73,7 @@ struct brcmf_buscore_ops {
+ u32 (*read32)(void *ctx, u32 addr);
+ void (*write32)(void *ctx, u32 addr, u32 value);
+ int (*prepare)(void *ctx);
++ int (*reset)(void *ctx, struct brcmf_chip *chip);
+ int (*setup)(void *ctx, struct brcmf_chip *chip);
+ void (*activate)(void *ctx, struct brcmf_chip *chip, u32 rstvec);
+ };
+--- a/drivers/net/wireless/brcm80211/brcmfmac/pcie.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/pcie.c
+@@ -74,6 +74,8 @@ enum brcmf_pcie_state {
+ #define BRCMF_PCIE_REG_INTMASK 0x94
+ #define BRCMF_PCIE_REG_SBMBX 0x98
+
++#define BRCMF_PCIE_REG_LINK_STATUS_CTRL 0xBC
++
+ #define BRCMF_PCIE_PCIE2REG_INTMASK 0x24
+ #define BRCMF_PCIE_PCIE2REG_MAILBOXINT 0x48
+ #define BRCMF_PCIE_PCIE2REG_MAILBOXMASK 0x4C
+@@ -466,6 +468,7 @@ brcmf_pcie_select_core(struct brcmf_pcie
+
+ static void brcmf_pcie_reset_device(struct brcmf_pciedev_info *devinfo)
+ {
++ struct brcmf_core *core;
+ u16 cfg_offset[] = { BRCMF_PCIE_CFGREG_STATUS_CMD,
+ BRCMF_PCIE_CFGREG_PM_CSR,
+ BRCMF_PCIE_CFGREG_MSI_CAP,
+@@ -484,32 +487,38 @@ static void brcmf_pcie_reset_device(stru
+ if (!devinfo->ci)
+ return;
+
++ /* Disable ASPM */
+ brcmf_pcie_select_core(devinfo, BCMA_CORE_PCIE2);
+- brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_CONFIGADDR,
+- BRCMF_PCIE_CFGREG_LINK_STATUS_CTRL);
+- lsc = brcmf_pcie_read_reg32(devinfo, BRCMF_PCIE_PCIE2REG_CONFIGDATA);
++ pci_read_config_dword(devinfo->pdev, BRCMF_PCIE_REG_LINK_STATUS_CTRL,
++ &lsc);
+ val = lsc & (~BRCMF_PCIE_LINK_STATUS_CTRL_ASPM_ENAB);
+- brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_CONFIGDATA, val);
++ pci_write_config_dword(devinfo->pdev, BRCMF_PCIE_REG_LINK_STATUS_CTRL,
++ val);
+
++ /* Watchdog reset */
+ brcmf_pcie_select_core(devinfo, BCMA_CORE_CHIPCOMMON);
+ WRITECC32(devinfo, watchdog, 4);
+ msleep(100);
+
++ /* Restore ASPM */
+ brcmf_pcie_select_core(devinfo, BCMA_CORE_PCIE2);
+- brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_CONFIGADDR,
+- BRCMF_PCIE_CFGREG_LINK_STATUS_CTRL);
+- brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_CONFIGDATA, lsc);
++ pci_write_config_dword(devinfo->pdev, BRCMF_PCIE_REG_LINK_STATUS_CTRL,
++ lsc);
+
+- brcmf_pcie_select_core(devinfo, BCMA_CORE_PCIE2);
+- for (i = 0; i < ARRAY_SIZE(cfg_offset); i++) {
+- brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_CONFIGADDR,
+- cfg_offset[i]);
+- val = brcmf_pcie_read_reg32(devinfo,
+- BRCMF_PCIE_PCIE2REG_CONFIGDATA);
+- brcmf_dbg(PCIE, "config offset 0x%04x, value 0x%04x\n",
+- cfg_offset[i], val);
+- brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_CONFIGDATA,
+- val);
++ core = brcmf_chip_get_core(devinfo->ci, BCMA_CORE_PCIE2);
++ if (core->rev <= 13) {
++ for (i = 0; i < ARRAY_SIZE(cfg_offset); i++) {
++ brcmf_pcie_write_reg32(devinfo,
++ BRCMF_PCIE_PCIE2REG_CONFIGADDR,
++ cfg_offset[i]);
++ val = brcmf_pcie_read_reg32(devinfo,
++ BRCMF_PCIE_PCIE2REG_CONFIGDATA);
++ brcmf_dbg(PCIE, "config offset 0x%04x, value 0x%04x\n",
++ cfg_offset[i], val);
++ brcmf_pcie_write_reg32(devinfo,
++ BRCMF_PCIE_PCIE2REG_CONFIGDATA,
++ val);
++ }
+ }
+ }
+
+@@ -519,8 +528,6 @@ static void brcmf_pcie_attach(struct brc
+ u32 config;
+
+ brcmf_pcie_select_core(devinfo, BCMA_CORE_PCIE2);
+- if (brcmf_pcie_read_reg32(devinfo, BRCMF_PCIE_PCIE2REG_INTMASK) != 0)
+- brcmf_pcie_reset_device(devinfo);
+ /* BAR1 window may not be sized properly */
+ brcmf_pcie_select_core(devinfo, BCMA_CORE_PCIE2);
+ brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_CONFIGADDR, 0x4e0);
+@@ -1636,6 +1643,23 @@ static int brcmf_pcie_buscoreprep(void *
+ }
+
+
++static int brcmf_pcie_buscore_reset(void *ctx, struct brcmf_chip *chip)
++{
++ struct brcmf_pciedev_info *devinfo = (struct brcmf_pciedev_info *)ctx;
++ u32 val;
++
++ devinfo->ci = chip;
++ brcmf_pcie_reset_device(devinfo);
++
++ val = brcmf_pcie_read_reg32(devinfo, BRCMF_PCIE_PCIE2REG_MAILBOXINT);
++ if (val != 0xffffffff)
++ brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_MAILBOXINT,
++ val);
++
++ return 0;
++}
++
++
+ static void brcmf_pcie_buscore_activate(void *ctx, struct brcmf_chip *chip,
+ u32 rstvec)
+ {
+@@ -1647,6 +1671,7 @@ static void brcmf_pcie_buscore_activate(
+
+ static const struct brcmf_buscore_ops brcmf_pcie_buscore_ops = {
+ .prepare = brcmf_pcie_buscoreprep,
++ .reset = brcmf_pcie_buscore_reset,
+ .activate = brcmf_pcie_buscore_activate,
+ .read32 = brcmf_pcie_buscore_read32,
+ .write32 = brcmf_pcie_buscore_write32,
+@@ -1814,7 +1839,6 @@ brcmf_pcie_remove(struct pci_dev *pdev)
+ brcmf_pcie_intr_disable(devinfo);
+
+ brcmf_detach(&pdev->dev);
+- brcmf_pcie_reset_device(devinfo);
+
+ kfree(bus->bus_priv.pcie);
+ kfree(bus->msgbuf->flowrings);
--- /dev/null
+From: Felix Fietkau <nbd@openwrt.org>
+Date: Sun, 13 Sep 2015 22:26:10 +0200
+Subject: [PATCH] ath10k: fix DMA related firmware crashes on multiple devices
+
+Some platforms really don't like DMA bursts of 256 bytes, and this
+causes the firmware to crash when sending beacons.
+Also, changing this based on the firmware version does not seem to make
+much sense, so use 128 bytes for all versions.
+
+Cc: stable@vger.kernel.org
+Signed-off-by: Felix Fietkau <nbd@openwrt.org>
+---
+
+--- a/drivers/net/wireless/ath/ath10k/hw.h
++++ b/drivers/net/wireless/ath/ath10k/hw.h
+@@ -253,7 +253,7 @@ struct ath10k_pktlog_hdr {
+ #define TARGET_10X_MAX_FRAG_ENTRIES 0
+
+ /* 10.2 parameters */
+-#define TARGET_10_2_DMA_BURST_SIZE 1
++#define TARGET_10_2_DMA_BURST_SIZE 0
+
+ /* Target specific defines for WMI-TLV firmware */
+ #define TARGET_TLV_NUM_VDEVS 3
--- /dev/null
+From: Felix Fietkau <nbd@openwrt.org>
+Date: Thu, 24 Sep 2015 16:57:37 +0200
+Subject: [PATCH] ath9k: declare required extra tx headroom
+
+ath9k inserts padding between the 802.11 header and the data area (to
+align it). Since it didn't declare this extra required headroom, this
+led to some nasty issues like randomly dropped packets in some setups.
+
+Cc: stable@vger.kernel.org
+Signed-off-by: Felix Fietkau <nbd@openwrt.org>
+---
+
+--- a/drivers/net/wireless/ath/ath9k/init.c
++++ b/drivers/net/wireless/ath/ath9k/init.c
+@@ -867,6 +867,7 @@ static void ath9k_set_hw_capab(struct at
+ hw->max_rate_tries = 10;
+ hw->sta_data_size = sizeof(struct ath_node);
+ hw->vif_data_size = sizeof(struct ath_vif);
++ hw->extra_tx_headroom = 4;
+
+ hw->wiphy->available_antennas_rx = BIT(ah->caps.max_rxchains) - 1;
+ hw->wiphy->available_antennas_tx = BIT(ah->caps.max_txchains) - 1;
--- /dev/null
+From: Felix Fietkau <nbd@openwrt.org>
+Date: Mon, 5 Oct 2015 17:41:25 +0200
+Subject: [PATCH] mac80211: initialize tid field in struct ieee80211_txq
+
+Signed-off-by: Felix Fietkau <nbd@openwrt.org>
+---
+
+--- a/net/mac80211/util.c
++++ b/net/mac80211/util.c
+@@ -3323,9 +3323,11 @@ void ieee80211_init_tx_queue(struct ieee
+ if (sta) {
+ txqi->txq.sta = &sta->sta;
+ sta->sta.txq[tid] = &txqi->txq;
++ txqi->txq.tid = tid;
+ txqi->txq.ac = ieee802_1d_to_ac[tid & 7];
+ } else {
+ sdata->vif.txq = &txqi->txq;
++ txqi->txq.tid = 0;
+ txqi->txq.ac = IEEE80211_AC_BE;
+ }
+ }
+++ /dev/null
-From: Felix Fietkau <nbd@openwrt.org>
-Date: Tue, 2 Jun 2015 10:35:46 +0200
-Subject: [PATCH] ath9k: fix DMA stop sequence for AR9003+
-
-AR93xx and newer needs to stop rx before tx to avoid getting the DMA
-engine or MAC into a stuck state.
-This should reduce/fix the occurence of "Failed to stop Tx DMA" logspam.
-
-Cc: stable@vger.kernel.org
-Signed-off-by: Felix Fietkau <nbd@openwrt.org>
----
-
---- a/drivers/net/wireless/ath/ath9k/main.c
-+++ b/drivers/net/wireless/ath/ath9k/main.c
-@@ -216,11 +216,13 @@ static bool ath_prepare_reset(struct ath
- ath_stop_ani(sc);
- ath9k_hw_disable_interrupts(ah);
-
-- if (!ath_drain_all_txq(sc))
-- ret = false;
--
-- if (!ath_stoprecv(sc))
-- ret = false;
-+ if (AR_SREV_9300_20_OR_LATER(ah)) {
-+ ret &= ath_stoprecv(sc);
-+ ret &= ath_drain_all_txq(sc);
-+ } else {
-+ ret &= ath_drain_all_txq(sc);
-+ ret &= ath_stoprecv(sc);
-+ }
-
- return ret;
- }
+++ /dev/null
-From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <zajec5@gmail.com>
-Date: Thu, 28 May 2015 14:19:21 +0200
-Subject: [PATCH] brcmfmac: support NVRAMs containing pci devpaths (instead of
- pcie)
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Recently Broadcom added support for NVRAMs with entries for multiple
-PCIe devices. One of the supported formats is based on prefixes defined
-like: devpath0=pcie/1/4/ and entries like 0:foo=bar 0:baz=qux etc.
-
-Unfortunately there are also a bit older devices using different way of
-defining prefixes, e.g. SmartRG SR400ac (2 x BCM43602) with entries:
-devpath0=pci/1/1/
-devpath1=pci/2/1
-Broadcom stated this old format will never be used/supported by brcmfmac
-but given the simplicity of this patch I'll insist on supporting it.
-
-Signed-off-by: Rafał Miłecki <zajec5@gmail.com>
-Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
----
-
---- a/drivers/net/wireless/brcm80211/brcmfmac/firmware.c
-+++ b/drivers/net/wireless/brcm80211/brcmfmac/firmware.c
-@@ -232,6 +232,8 @@ static void brcmf_fw_strip_multi_v1(stru
- u16 bus_nr)
- {
- /* Device path with a leading '=' key-value separator */
-+ char pci_path[] = "=pci/?/?";
-+ size_t pci_len;
- char pcie_path[] = "=pcie/?/?";
- size_t pcie_len;
-
-@@ -251,6 +253,9 @@ static void brcmf_fw_strip_multi_v1(stru
- /* First search for the devpathX and see if it is the configuration
- * for domain_nr/bus_nr. Search complete nvp
- */
-+ snprintf(pci_path, sizeof(pci_path), "=pci/%d/%d", domain_nr,
-+ bus_nr);
-+ pci_len = strlen(pci_path);
- snprintf(pcie_path, sizeof(pcie_path), "=pcie/%d/%d", domain_nr,
- bus_nr);
- pcie_len = strlen(pcie_path);
-@@ -260,8 +265,9 @@ static void brcmf_fw_strip_multi_v1(stru
- /* Format: devpathX=pcie/Y/Z/
- * Y = domain_nr, Z = bus_nr, X = virtual ID
- */
-- if ((strncmp(&nvp->nvram[i], "devpath", 7) == 0) &&
-- (strncmp(&nvp->nvram[i + 8], pcie_path, pcie_len) == 0)) {
-+ if (strncmp(&nvp->nvram[i], "devpath", 7) == 0 &&
-+ (!strncmp(&nvp->nvram[i + 8], pci_path, pci_len) ||
-+ !strncmp(&nvp->nvram[i + 8], pcie_path, pcie_len))) {
- id = nvp->nvram[i + 7] - '0';
- found = true;
- break;
+++ /dev/null
-From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <zajec5@gmail.com>
-Date: Sun, 31 May 2015 02:52:26 +0200
-Subject: [PATCH] brcmfmac: set wiphy perm_addr to hardware MAC address
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-This allows e.g. user space to use /sys/class/ieee80211/*/macaddress
-
-Signed-off-by: Rafał Miłecki <zajec5@gmail.com>
-Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
----
-
---- a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c
-+++ b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c
-@@ -6070,6 +6070,7 @@ struct brcmf_cfg80211_info *brcmf_cfg802
- brcmf_err("Could not allocate wiphy device\n");
- return NULL;
- }
-+ memcpy(wiphy->perm_addr, drvr->mac, ETH_ALEN);
- set_wiphy_dev(wiphy, busdev);
-
- cfg = wiphy_priv(wiphy);
+++ /dev/null
-From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <zajec5@gmail.com>
-Date: Thu, 4 Jun 2015 22:11:07 +0200
-Subject: [PATCH] brcmfmac: use direct data pointer in NVRAM parser struct
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-As we plan to add support for platform NVRAM we should store direct
-data pointer without the extra struct firmware layer. This will allow
-us to support other sources with the only requirement being u8 buffer.
-
-Signed-off-by: Rafał Miłecki <zajec5@gmail.com>
-Acked-by: Arend van Spriel <arend@broadcom.com>
-Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
----
-
---- a/drivers/net/wireless/brcm80211/brcmfmac/firmware.c
-+++ b/drivers/net/wireless/brcm80211/brcmfmac/firmware.c
-@@ -43,7 +43,7 @@ enum nvram_parser_state {
- * struct nvram_parser - internal info for parser.
- *
- * @state: current parser state.
-- * @fwnv: input buffer being parsed.
-+ * @data: input buffer being parsed.
- * @nvram: output buffer with parse result.
- * @nvram_len: lenght of parse result.
- * @line: current line.
-@@ -55,7 +55,7 @@ enum nvram_parser_state {
- */
- struct nvram_parser {
- enum nvram_parser_state state;
-- const struct firmware *fwnv;
-+ const u8 *data;
- u8 *nvram;
- u32 nvram_len;
- u32 line;
-@@ -91,7 +91,7 @@ static enum nvram_parser_state brcmf_nvr
- {
- char c;
-
-- c = nvp->fwnv->data[nvp->pos];
-+ c = nvp->data[nvp->pos];
- if (c == '\n')
- return COMMENT;
- if (is_whitespace(c))
-@@ -115,16 +115,16 @@ static enum nvram_parser_state brcmf_nvr
- enum nvram_parser_state st = nvp->state;
- char c;
-
-- c = nvp->fwnv->data[nvp->pos];
-+ c = nvp->data[nvp->pos];
- if (c == '=') {
- /* ignore RAW1 by treating as comment */
-- if (strncmp(&nvp->fwnv->data[nvp->entry], "RAW1", 4) == 0)
-+ if (strncmp(&nvp->data[nvp->entry], "RAW1", 4) == 0)
- st = COMMENT;
- else
- st = VALUE;
-- if (strncmp(&nvp->fwnv->data[nvp->entry], "devpath", 7) == 0)
-+ if (strncmp(&nvp->data[nvp->entry], "devpath", 7) == 0)
- nvp->multi_dev_v1 = true;
-- if (strncmp(&nvp->fwnv->data[nvp->entry], "pcie/", 5) == 0)
-+ if (strncmp(&nvp->data[nvp->entry], "pcie/", 5) == 0)
- nvp->multi_dev_v2 = true;
- } else if (!is_nvram_char(c) || c == ' ') {
- brcmf_dbg(INFO, "warning: ln=%d:col=%d: '=' expected, skip invalid key entry\n",
-@@ -145,11 +145,11 @@ brcmf_nvram_handle_value(struct nvram_pa
- char *ekv;
- u32 cplen;
-
-- c = nvp->fwnv->data[nvp->pos];
-+ c = nvp->data[nvp->pos];
- if (!is_nvram_char(c)) {
- /* key,value pair complete */
-- ekv = (u8 *)&nvp->fwnv->data[nvp->pos];
-- skv = (u8 *)&nvp->fwnv->data[nvp->entry];
-+ ekv = (u8 *)&nvp->data[nvp->pos];
-+ skv = (u8 *)&nvp->data[nvp->entry];
- cplen = ekv - skv;
- if (nvp->nvram_len + cplen + 1 >= BRCMF_FW_MAX_NVRAM_SIZE)
- return END;
-@@ -170,7 +170,7 @@ brcmf_nvram_handle_comment(struct nvram_
- {
- char *eoc, *sol;
-
-- sol = (char *)&nvp->fwnv->data[nvp->pos];
-+ sol = (char *)&nvp->data[nvp->pos];
- eoc = strchr(sol, '\n');
- if (!eoc) {
- eoc = strchr(sol, '\0');
-@@ -201,17 +201,17 @@ static enum nvram_parser_state
- };
-
- static int brcmf_init_nvram_parser(struct nvram_parser *nvp,
-- const struct firmware *nv)
-+ const u8 *data, size_t data_len)
- {
- size_t size;
-
- memset(nvp, 0, sizeof(*nvp));
-- nvp->fwnv = nv;
-+ nvp->data = data;
- /* Limit size to MAX_NVRAM_SIZE, some files contain lot of comment */
-- if (nv->size > BRCMF_FW_MAX_NVRAM_SIZE)
-+ if (data_len > BRCMF_FW_MAX_NVRAM_SIZE)
- size = BRCMF_FW_MAX_NVRAM_SIZE;
- else
-- size = nv->size;
-+ size = data_len;
- /* Alloc for extra 0 byte + roundup by 4 + length field */
- size += 1 + 3 + sizeof(u32);
- nvp->nvram = kzalloc(size, GFP_KERNEL);
-@@ -362,18 +362,18 @@ fail:
- * and converts newlines to NULs. Shortens buffer as needed and pads with NULs.
- * End of buffer is completed with token identifying length of buffer.
- */
--static void *brcmf_fw_nvram_strip(const struct firmware *nv, u32 *new_length,
-- u16 domain_nr, u16 bus_nr)
-+static void *brcmf_fw_nvram_strip(const u8 *data, size_t data_len,
-+ u32 *new_length, u16 domain_nr, u16 bus_nr)
- {
- struct nvram_parser nvp;
- u32 pad;
- u32 token;
- __le32 token_le;
-
-- if (brcmf_init_nvram_parser(&nvp, nv) < 0)
-+ if (brcmf_init_nvram_parser(&nvp, data, data_len) < 0)
- return NULL;
-
-- while (nvp.pos < nv->size) {
-+ while (nvp.pos < data_len) {
- nvp.state = nv_parser_states[nvp.state](&nvp);
- if (nvp.state == END)
- break;
-@@ -432,7 +432,7 @@ static void brcmf_fw_request_nvram_done(
- goto fail;
-
- if (fw) {
-- nvram = brcmf_fw_nvram_strip(fw, &nvram_length,
-+ nvram = brcmf_fw_nvram_strip(fw->data, fw->size, &nvram_length,
- fwctx->domain_nr, fwctx->bus_nr);
- release_firmware(fw);
- if (!nvram && !(fwctx->flags & BRCMF_FW_REQ_NV_OPTIONAL))
+++ /dev/null
-From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <zajec5@gmail.com>
-Date: Sat, 6 Jun 2015 22:45:59 +0200
-Subject: [PATCH] b43: fix support for 14e4:4321 PCI dev with BCM4321 chipset
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-It seems Broadcom released two devices with conflicting device id. There
-are for sure 14e4:4321 PCI devices with BCM4321 (N-PHY) chipset, they
-can be found in routers, e.g. Netgear WNR834Bv2. However, according to
-Broadcom public sources 0x4321 is also used for 5 GHz BCM4306 (G-PHY).
-It's unsure if they meant PCI device id, or "virtual" id (from SPROM).
-To distinguish these devices lets check PHY type (G vs. N).
-
-Signed-off-by: Rafał Miłecki <zajec5@gmail.com>
-Cc: <stable@vger.kernel.org> # 3.16+
-Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
----
-
---- a/drivers/net/wireless/b43/main.c
-+++ b/drivers/net/wireless/b43/main.c
-@@ -5365,6 +5365,10 @@ static void b43_supported_bands(struct b
- *have_5ghz_phy = true;
- return;
- case 0x4321: /* BCM4306 */
-+ /* There are 14e4:4321 PCI devs with 2.4 GHz BCM4321 (N-PHY) */
-+ if (dev->phy.type != B43_PHYTYPE_G)
-+ break;
-+ /* fall through */
- case 0x4313: /* BCM4311 */
- case 0x431a: /* BCM4318 */
- case 0x432a: /* BCM4321 */
+++ /dev/null
-From: Felix Fietkau <nbd@openwrt.org>
-Date: Sun, 7 Jun 2015 13:53:35 +0200
-Subject: [PATCH] ath9k: force rx_clear when disabling rx
-
-This makes stopping Rx more reliable and should reduce the frequency of
-Rx related DMA stop warnings
-
-Cc: stable@vger.kernel.org
-Signed-off-by: Felix Fietkau <nbd@openwrt.org>
----
-
---- a/drivers/net/wireless/ath/ath9k/mac.c
-+++ b/drivers/net/wireless/ath/ath9k/mac.c
-@@ -677,13 +677,15 @@ void ath9k_hw_startpcureceive(struct ath
-
- ath9k_ani_reset(ah, is_scanning);
-
-- REG_CLR_BIT(ah, AR_DIAG_SW, (AR_DIAG_RX_DIS | AR_DIAG_RX_ABORT));
-+ REG_CLR_BIT(ah, AR_DIAG_SW,
-+ AR_DIAG_RX_DIS | AR_DIAG_RX_ABORT | AR_DIAG_FORCE_RX_CLEAR);
- }
- EXPORT_SYMBOL(ath9k_hw_startpcureceive);
-
- void ath9k_hw_abortpcurecv(struct ath_hw *ah)
- {
-- REG_SET_BIT(ah, AR_DIAG_SW, AR_DIAG_RX_ABORT | AR_DIAG_RX_DIS);
-+ REG_SET_BIT(ah, AR_DIAG_SW,
-+ AR_DIAG_RX_DIS | AR_DIAG_RX_ABORT | AR_DIAG_FORCE_RX_CLEAR);
-
- ath9k_hw_disable_mib_counters(ah);
- }
+++ /dev/null
-From: Hante Meuleman <meuleman@broadcom.com>
-Date: Mon, 8 Jun 2015 14:38:32 +0200
-Subject: [PATCH] brcmfmac: Update msgbuf read pointer quicker.
-
-On device to host data using msgbuf the read pointer gets updated
-once all data is processed. Updating this pointer more frequently
-allows the firmware to add more data quicker. This will result in
-slightly higher and more stable throughput on CPU bounded host
-processors.
-
-Reviewed-by: Arend Van Spriel <arend@broadcom.com>
-Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com>
-Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
-Signed-off-by: Hante Meuleman <meuleman@broadcom.com>
-Signed-off-by: Arend van Spriel <arend@broadcom.com>
-Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
----
-
---- a/drivers/net/wireless/brcm80211/brcmfmac/commonring.c
-+++ b/drivers/net/wireless/brcm80211/brcmfmac/commonring.c
-@@ -223,8 +223,6 @@ void brcmf_commonring_write_cancel(struc
- void *brcmf_commonring_get_read_ptr(struct brcmf_commonring *commonring,
- u16 *n_items)
- {
-- void *ret_addr;
--
- if (commonring->cr_update_wptr)
- commonring->cr_update_wptr(commonring->cr_ctx);
-
-@@ -235,19 +233,18 @@ void *brcmf_commonring_get_read_ptr(stru
- if (*n_items == 0)
- return NULL;
-
-- ret_addr = commonring->buf_addr +
-- (commonring->r_ptr * commonring->item_len);
--
-- commonring->r_ptr += *n_items;
-- if (commonring->r_ptr == commonring->depth)
-- commonring->r_ptr = 0;
--
-- return ret_addr;
-+ return commonring->buf_addr +
-+ (commonring->r_ptr * commonring->item_len);
- }
-
-
--int brcmf_commonring_read_complete(struct brcmf_commonring *commonring)
-+int brcmf_commonring_read_complete(struct brcmf_commonring *commonring,
-+ u16 n_items)
- {
-+ commonring->r_ptr += n_items;
-+ if (commonring->r_ptr == commonring->depth)
-+ commonring->r_ptr = 0;
-+
- if (commonring->cr_write_rptr)
- return commonring->cr_write_rptr(commonring->cr_ctx);
-
---- a/drivers/net/wireless/brcm80211/brcmfmac/commonring.h
-+++ b/drivers/net/wireless/brcm80211/brcmfmac/commonring.h
-@@ -62,7 +62,8 @@ void brcmf_commonring_write_cancel(struc
- u16 n_items);
- void *brcmf_commonring_get_read_ptr(struct brcmf_commonring *commonring,
- u16 *n_items);
--int brcmf_commonring_read_complete(struct brcmf_commonring *commonring);
-+int brcmf_commonring_read_complete(struct brcmf_commonring *commonring,
-+ u16 n_items);
-
- #define brcmf_commonring_n_items(commonring) (commonring->depth)
- #define brcmf_commonring_len_item(commonring) (commonring->item_len)
---- a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c
-+++ b/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c
-@@ -75,6 +75,8 @@
-
- #define BRCMF_MSGBUF_DELAY_TXWORKER_THRS 96
- #define BRCMF_MSGBUF_TRICKLE_TXWORKER_THRS 32
-+#define BRCMF_MSGBUF_UPDATE_RX_PTR_THRS 48
-+
-
- struct msgbuf_common_hdr {
- u8 msgtype;
-@@ -1257,19 +1259,27 @@ static void brcmf_msgbuf_process_rx(stru
- {
- void *buf;
- u16 count;
-+ u16 processed;
-
- again:
- buf = brcmf_commonring_get_read_ptr(commonring, &count);
- if (buf == NULL)
- return;
-
-+ processed = 0;
- while (count) {
- brcmf_msgbuf_process_msgtype(msgbuf,
- buf + msgbuf->rx_dataoffset);
- buf += brcmf_commonring_len_item(commonring);
-+ processed++;
-+ if (processed == BRCMF_MSGBUF_UPDATE_RX_PTR_THRS) {
-+ brcmf_commonring_read_complete(commonring, processed);
-+ processed = 0;
-+ }
- count--;
- }
-- brcmf_commonring_read_complete(commonring);
-+ if (processed)
-+ brcmf_commonring_read_complete(commonring, processed);
-
- if (commonring->r_ptr == 0)
- goto again;
+++ /dev/null
-From: Arend van Spriel <arend@broadcom.com>
-Date: Mon, 8 Jun 2015 14:38:33 +0200
-Subject: [PATCH] brcmfmac: remove chipinfo debugfs entry
-
-The information provided by chipinfo is also provided by the
-revinfo debugfs entry. Removing it from debugfs.
-
-Reviewed-by: Hante Meuleman <meuleman@broadcom.com>
-Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
-Signed-off-by: Arend van Spriel <arend@broadcom.com>
-Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
----
-
---- a/drivers/net/wireless/brcm80211/brcmfmac/debug.c
-+++ b/drivers/net/wireless/brcm80211/brcmfmac/debug.c
-@@ -41,15 +41,6 @@ void brcmf_debugfs_exit(void)
- root_folder = NULL;
- }
-
--static int brcmf_debugfs_chipinfo_read(struct seq_file *seq, void *data)
--{
-- struct brcmf_bus *bus = dev_get_drvdata(seq->private);
--
-- seq_printf(seq, "chip: %x(%u) rev %u\n",
-- bus->chip, bus->chip, bus->chiprev);
-- return 0;
--}
--
- int brcmf_debugfs_attach(struct brcmf_pub *drvr)
- {
- struct device *dev = drvr->bus_if->dev;
-@@ -58,7 +49,6 @@ int brcmf_debugfs_attach(struct brcmf_pu
- return -ENODEV;
-
- drvr->dbgfs_dir = debugfs_create_dir(dev_name(dev), root_folder);
-- brcmf_debugfs_add_entry(drvr, "chipinfo", brcmf_debugfs_chipinfo_read);
-
- return PTR_ERR_OR_ZERO(drvr->dbgfs_dir);
- }
+++ /dev/null
-From: Arend van Spriel <arend@broadcom.com>
-Date: Mon, 8 Jun 2015 14:38:34 +0200
-Subject: [PATCH] brcmfmac: remove watchdog reset from
- brcmf_pcie_buscoreprep()
-
-The watchdog reset as done in brcmf_pcie_buscoreprep() is not
-sufficient. It needs to modify PCIe core registers as well
-which is properly done by brcmf_pcie_reset_device() after the
-chip recognition is done. So the faulty watchdog reset can be
-removed as it was causing driver reload to fail and hang the
-system requiring a power-cycle. Instead the call to to the
-brcmf_pcie_reset_device() function is done twice in the unload.
-
-Reviewed-by: Hante Meuleman <meuleman@broadcom.com>
-Reviewed-by: Daniel (Deognyoun) Kim <dekim@broadcom.com>
-Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com>
-Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
-Signed-off-by: Arend van Spriel <arend@broadcom.com>
-Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
----
-
---- a/drivers/net/wireless/brcm80211/brcmfmac/pcie.c
-+++ b/drivers/net/wireless/brcm80211/brcmfmac/pcie.c
-@@ -1629,20 +1629,7 @@ static void brcmf_pcie_buscore_write32(v
-
- static int brcmf_pcie_buscoreprep(void *ctx)
- {
-- struct brcmf_pciedev_info *devinfo = (struct brcmf_pciedev_info *)ctx;
-- int err;
--
-- err = brcmf_pcie_get_resource(devinfo);
-- if (err == 0) {
-- /* Set CC watchdog to reset all the cores on the chip to bring
-- * back dongle to a sane state.
-- */
-- brcmf_pcie_buscore_write32(ctx, CORE_CC_REG(SI_ENUM_BASE,
-- watchdog), 4);
-- msleep(100);
-- }
--
-- return err;
-+ return brcmf_pcie_get_resource(ctx);
- }
-
-
-@@ -1824,6 +1811,7 @@ brcmf_pcie_remove(struct pci_dev *pdev)
- brcmf_pcie_intr_disable(devinfo);
-
- brcmf_detach(&pdev->dev);
-+ brcmf_pcie_reset_device(devinfo);
-
- kfree(bus->bus_priv.pcie);
- kfree(bus->msgbuf->flowrings);
+++ /dev/null
-From: Arend van Spriel <arend@broadcom.com>
-Date: Mon, 8 Jun 2015 14:38:35 +0200
-Subject: [PATCH] brcmfmac: use debugfs_create_devm_seqfile() helper
- function
-
-Some time ago the function debugfs_create_devm_seqfile() was
-introduced in debugfs. The caller simply needs to provide a
-device pointer and read function. The function brcmf_debugfs_add_entry()
-is now simply a wrapper only doing the work for CONFIG_BRCMDBG.
-
-Reviewed-by: Hante Meuleman <meuleman@broadcom.com>
-Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com>
-Reviewed-by: Daniel (Deognyoun) Kim <dekim@broadcom.com>
-Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
-Signed-off-by: Arend van Spriel <arend@broadcom.com>
-Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
----
-
---- a/drivers/net/wireless/brcm80211/brcmfmac/debug.c
-+++ b/drivers/net/wireless/brcm80211/brcmfmac/debug.c
-@@ -64,44 +64,12 @@ struct dentry *brcmf_debugfs_get_devdir(
- return drvr->dbgfs_dir;
- }
-
--struct brcmf_debugfs_entry {
-- int (*read)(struct seq_file *seq, void *data);
-- struct brcmf_pub *drvr;
--};
--
--static int brcmf_debugfs_entry_open(struct inode *inode, struct file *f)
--{
-- struct brcmf_debugfs_entry *entry = inode->i_private;
--
-- return single_open(f, entry->read, entry->drvr->bus_if->dev);
--}
--
--static const struct file_operations brcmf_debugfs_def_ops = {
-- .owner = THIS_MODULE,
-- .open = brcmf_debugfs_entry_open,
-- .release = single_release,
-- .read = seq_read,
-- .llseek = seq_lseek
--};
--
- 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);
-+ struct dentry *e;
-
-- return PTR_ERR_OR_ZERO(dentry);
-+ e = debugfs_create_devm_seqfile(drvr->bus_if->dev, fn,
-+ drvr->dbgfs_dir, read_fn);
-+ return PTR_ERR_OR_ZERO(e);
- }
+++ /dev/null
-From: Felix Fietkau <nbd@openwrt.org>
-Date: Sun, 21 Jun 2015 19:45:59 +0200
-Subject: [PATCH] ath9k_hw: fix device ID check for AR956x
-
-Because of the missing return, the macVersion value was being
-overwritten with an invalid register read
-
-Signed-off-by: Felix Fietkau <nbd@openwrt.org>
----
-
---- a/drivers/net/wireless/ath/ath9k/hw.c
-+++ b/drivers/net/wireless/ath/ath9k/hw.c
-@@ -278,6 +278,7 @@ static void ath9k_hw_read_revisions(stru
- return;
- case AR9300_DEVID_QCA956X:
- ah->hw_version.macVersion = AR_SREV_VERSION_9561;
-+ return;
- }
-
- val = REG_READ(ah, AR_SREV) & AR_SREV_ID;
+++ /dev/null
-From: Pontus Fuchs <pontusf@broadcom.com>
-Date: Thu, 11 Jun 2015 00:12:17 +0200
-Subject: [PATCH] brcmfmac: Check if firmware supports p2p
-
-Add a feature flag to reflect the firmware's p2p capability.
-
-Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
-Reviewed-by: Hante Meuleman <meuleman@broadcom.com>
-Reviewed-by: Arend Van Spriel <arend@broadcom.com>
-Signed-off-by: Pontus Fuchs <pontusf@broadcom.com>
-Signed-off-by: Arend van Spriel <arend@broadcom.com>
-Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
----
-
---- a/drivers/net/wireless/brcm80211/brcmfmac/feature.c
-+++ b/drivers/net/wireless/brcm80211/brcmfmac/feature.c
-@@ -129,6 +129,7 @@ void brcmf_feat_attach(struct brcmf_pub
- brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_WOWL, "wowl");
- if (drvr->bus_if->chip != BRCM_CC_43362_CHIP_ID)
- brcmf_feat_iovar_int_set(ifp, BRCMF_FEAT_MBSS, "mbss", 0);
-+ brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_P2P, "p2p");
-
- /* set chip related quirks */
- switch (drvr->bus_if->chip) {
---- a/drivers/net/wireless/brcm80211/brcmfmac/feature.h
-+++ b/drivers/net/wireless/brcm80211/brcmfmac/feature.h
-@@ -23,12 +23,14 @@
- * MCHAN: multi-channel for concurrent P2P.
- * PNO: preferred network offload.
- * WOWL: Wake-On-WLAN.
-+ * P2P: peer-to-peer
- */
- #define BRCMF_FEAT_LIST \
- BRCMF_FEAT_DEF(MBSS) \
- BRCMF_FEAT_DEF(MCHAN) \
- BRCMF_FEAT_DEF(PNO) \
-- BRCMF_FEAT_DEF(WOWL)
-+ BRCMF_FEAT_DEF(WOWL) \
-+ BRCMF_FEAT_DEF(P2P)
- /*
- * Quirks:
- *
+++ /dev/null
-From: Pontus Fuchs <pontusf@broadcom.com>
-Date: Thu, 11 Jun 2015 00:12:18 +0200
-Subject: [PATCH] brcmfmac: Build wiphy mode and interface combinations
- dynamically
-
-Switch from using semi hard coded interface combinations. This makes
-it easier to announce what the firmware actually supports. This fixes
-the case where brcmfmac announces p2p but the firmware doesn't
-support it.
-
-Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
-Reviewed-by: Hante Meuleman <meuleman@broadcom.com>
-Reviewed-by: Arend Van Spriel <arend@broadcom.com>
-Signed-off-by: Pontus Fuchs <pontusf@broadcom.com>
-Signed-off-by: Arend van Spriel <arend@broadcom.com>
-Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
----
-
---- a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c
-+++ b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c
-@@ -52,8 +52,6 @@
- #define BRCMF_PNO_SCAN_COMPLETE 1
- #define BRCMF_PNO_SCAN_INCOMPLETE 0
-
--#define BRCMF_IFACE_MAX_CNT 3
--
- #define WPA_OUI "\x00\x50\xF2" /* WPA OUI */
- #define WPA_OUI_TYPE 1
- #define RSN_OUI "\x00\x0F\xAC" /* RSN OUI */
-@@ -5639,53 +5637,6 @@ static int brcmf_setup_wiphybands(struct
- return 0;
- }
-
--static const struct ieee80211_iface_limit brcmf_iface_limits_mbss[] = {
-- {
-- .max = 1,
-- .types = BIT(NL80211_IFTYPE_STATION) |
-- BIT(NL80211_IFTYPE_ADHOC)
-- },
-- {
-- .max = 4,
-- .types = 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_limit brcmf_iface_limits_sbss[] = {
-- {
-- .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_sbss),
-- .limits = brcmf_iface_limits_sbss,
-- }
--};
--
- static const struct ieee80211_txrx_stypes
- brcmf_txrx_stypes[NUM_NL80211_IFTYPES] = {
- [NL80211_IFTYPE_STATION] = {
-@@ -5715,6 +5666,67 @@ brcmf_txrx_stypes[NUM_NL80211_IFTYPES] =
- }
- };
-
-+static int brcmf_setup_ifmodes(struct wiphy *wiphy, struct brcmf_if *ifp)
-+{
-+ struct ieee80211_iface_combination *combo = NULL;
-+ struct ieee80211_iface_limit *limits = NULL;
-+ int i = 0, max_iface_cnt;
-+
-+ combo = kzalloc(sizeof(*combo), GFP_KERNEL);
-+ if (!combo)
-+ goto err;
-+
-+ limits = kzalloc(sizeof(*limits) * 4, GFP_KERNEL);
-+ if (!limits)
-+ goto err;
-+
-+ wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
-+ BIT(NL80211_IFTYPE_ADHOC) |
-+ BIT(NL80211_IFTYPE_AP);
-+
-+ if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MCHAN))
-+ combo->num_different_channels = 2;
-+ else
-+ combo->num_different_channels = 1;
-+
-+ if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS)) {
-+ limits[i].max = 1;
-+ limits[i++].types = BIT(NL80211_IFTYPE_STATION);
-+ limits[i].max = 4;
-+ limits[i++].types = BIT(NL80211_IFTYPE_AP);
-+ max_iface_cnt = 5;
-+ } else {
-+ limits[i].max = 2;
-+ limits[i++].types = BIT(NL80211_IFTYPE_STATION) |
-+ BIT(NL80211_IFTYPE_AP);
-+ max_iface_cnt = 2;
-+ }
-+
-+ if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_P2P)) {
-+ wiphy->interface_modes |= BIT(NL80211_IFTYPE_P2P_CLIENT) |
-+ BIT(NL80211_IFTYPE_P2P_GO) |
-+ BIT(NL80211_IFTYPE_P2P_DEVICE);
-+ limits[i].max = 1;
-+ limits[i++].types = BIT(NL80211_IFTYPE_P2P_CLIENT) |
-+ BIT(NL80211_IFTYPE_P2P_GO);
-+ limits[i].max = 1;
-+ limits[i++].types = BIT(NL80211_IFTYPE_P2P_DEVICE);
-+ max_iface_cnt += 2;
-+ }
-+ combo->max_interfaces = max_iface_cnt;
-+ combo->limits = limits;
-+ combo->n_limits = i;
-+
-+ wiphy->iface_combinations = combo;
-+ wiphy->n_iface_combinations = 1;
-+ return 0;
-+
-+err:
-+ kfree(limits);
-+ kfree(combo);
-+ return -ENOMEM;
-+}
-+
- static void brcmf_wiphy_pno_params(struct wiphy *wiphy)
- {
- /* scheduled scan settings */
-@@ -5745,7 +5757,6 @@ static void brcmf_wiphy_wowl_params(stru
- static int brcmf_setup_wiphy(struct wiphy *wiphy, struct brcmf_if *ifp)
- {
- struct ieee80211_supported_band *band;
-- struct ieee80211_iface_combination ifc_combo;
- __le32 bandlist[3];
- u32 n_bands;
- int err, i;
-@@ -5753,24 +5764,11 @@ static int brcmf_setup_wiphy(struct wiph
- 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;
-- if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS)) {
-- ifc_combo.n_limits = ARRAY_SIZE(brcmf_iface_limits_mbss),
-- ifc_combo.limits = brcmf_iface_limits_mbss;
-- }
-- wiphy->iface_combinations = kmemdup(&ifc_combo,
-- sizeof(ifc_combo),
-- GFP_KERNEL);
-- wiphy->n_iface_combinations = ARRAY_SIZE(brcmf_iface_combos);
-+
-+ err = brcmf_setup_ifmodes(wiphy, ifp);
-+ if (err)
-+ return err;
-+
- wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
- wiphy->cipher_suites = __wl_cipher_suites;
- wiphy->n_cipher_suites = ARRAY_SIZE(__wl_cipher_suites);
-@@ -6035,6 +6033,8 @@ static void brcmf_free_wiphy(struct wiph
- if (!wiphy)
- return;
-
-+ if (wiphy->iface_combinations)
-+ kfree(wiphy->iface_combinations->limits);
- kfree(wiphy->iface_combinations);
- if (wiphy->bands[IEEE80211_BAND_2GHZ]) {
- kfree(wiphy->bands[IEEE80211_BAND_2GHZ]->channels);
+++ /dev/null
-From: Arend van Spriel <arend@broadcom.com>
-Date: Thu, 11 Jun 2015 00:12:19 +0200
-Subject: [PATCH] brcmfmac: rework .get_station() callback
-
-The .get_station() cfg80211 callback is used in several scenarios. In
-managed mode it can obtain information about the access-point and its
-BSS parameters. In managed mode it can also obtain information about
-TDLS peers. In AP mode it can obtain information about connected
-clients.
-
-Reviewed-by: Hante Meuleman <meuleman@broadcom.com>
-Reviewed-by: Daniel (Deognyoun) Kim <dekim@broadcom.com>
-Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com>
-Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
-Signed-off-by: Arend van Spriel <arend@broadcom.com>
-Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
----
-
---- a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c
-+++ b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c
-@@ -2395,27 +2395,80 @@ brcmf_cfg80211_reconfigure_wep(struct br
- brcmf_err("set wsec error (%d)\n", err);
- }
-
-+static void brcmf_convert_sta_flags(u32 fw_sta_flags, struct station_info *si)
-+{
-+ struct nl80211_sta_flag_update *sfu;
-+
-+ brcmf_dbg(TRACE, "flags %08x\n", fw_sta_flags);
-+ si->filled |= BIT(NL80211_STA_INFO_STA_FLAGS);
-+ sfu = &si->sta_flags;
-+ sfu->mask = BIT(NL80211_STA_FLAG_WME) |
-+ BIT(NL80211_STA_FLAG_AUTHENTICATED) |
-+ BIT(NL80211_STA_FLAG_ASSOCIATED) |
-+ BIT(NL80211_STA_FLAG_AUTHORIZED);
-+ if (fw_sta_flags & BRCMF_STA_WME)
-+ sfu->set |= BIT(NL80211_STA_FLAG_WME);
-+ if (fw_sta_flags & BRCMF_STA_AUTHE)
-+ sfu->set |= BIT(NL80211_STA_FLAG_AUTHENTICATED);
-+ if (fw_sta_flags & BRCMF_STA_ASSOC)
-+ sfu->set |= BIT(NL80211_STA_FLAG_ASSOCIATED);
-+ if (fw_sta_flags & BRCMF_STA_AUTHO)
-+ sfu->set |= BIT(NL80211_STA_FLAG_AUTHORIZED);
-+}
-+
-+static void brcmf_fill_bss_param(struct brcmf_if *ifp, struct station_info *si)
-+{
-+ struct {
-+ __le32 len;
-+ struct brcmf_bss_info_le bss_le;
-+ } *buf;
-+ u16 capability;
-+ int err;
-+
-+ buf = kzalloc(WL_BSS_INFO_MAX, GFP_KERNEL);
-+ if (!buf)
-+ return;
-+
-+ buf->len = cpu_to_le32(WL_BSS_INFO_MAX);
-+ err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BSS_INFO, buf,
-+ WL_BSS_INFO_MAX);
-+ if (err) {
-+ brcmf_err("Failed to get bss info (%d)\n", err);
-+ return;
-+ }
-+ si->filled |= BIT(NL80211_STA_INFO_BSS_PARAM);
-+ si->bss_param.beacon_interval = le16_to_cpu(buf->bss_le.beacon_period);
-+ si->bss_param.dtim_period = buf->bss_le.dtim_period;
-+ capability = le16_to_cpu(buf->bss_le.capability);
-+ if (capability & IEEE80211_HT_STBC_PARAM_DUAL_CTS_PROT)
-+ si->bss_param.flags |= BSS_PARAM_FLAGS_CTS_PROT;
-+ if (capability & WLAN_CAPABILITY_SHORT_PREAMBLE)
-+ si->bss_param.flags |= BSS_PARAM_FLAGS_SHORT_PREAMBLE;
-+ if (capability & WLAN_CAPABILITY_SHORT_SLOT_TIME)
-+ si->bss_param.flags |= BSS_PARAM_FLAGS_SHORT_SLOT_TIME;
-+}
-+
- static s32
- brcmf_cfg80211_get_station(struct wiphy *wiphy, struct net_device *ndev,
- const u8 *mac, struct station_info *sinfo)
- {
- struct brcmf_if *ifp = netdev_priv(ndev);
-- struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
-- struct brcmf_scb_val_le scb_val;
-- int rssi;
-- s32 rate;
- s32 err = 0;
-- u8 *bssid = profile->bssid;
- struct brcmf_sta_info_le sta_info_le;
-- u32 beacon_period;
-- u32 dtim_period;
-+ u32 sta_flags;
-+ u32 is_tdls_peer;
-
- brcmf_dbg(TRACE, "Enter, MAC %pM\n", mac);
- if (!check_vif_up(ifp->vif))
- return -EIO;
-
-- if (brcmf_is_apmode(ifp->vif)) {
-- memcpy(&sta_info_le, mac, ETH_ALEN);
-+ memset(&sta_info_le, 0, sizeof(sta_info_le));
-+ memcpy(&sta_info_le, mac, ETH_ALEN);
-+ err = brcmf_fil_iovar_data_get(ifp, "tdls_sta_info",
-+ &sta_info_le,
-+ sizeof(sta_info_le));
-+ is_tdls_peer = !err;
-+ if (err) {
- err = brcmf_fil_iovar_data_get(ifp, "sta_info",
- &sta_info_le,
- sizeof(sta_info_le));
-@@ -2423,73 +2476,48 @@ brcmf_cfg80211_get_station(struct wiphy
- brcmf_err("GET STA INFO failed, %d\n", err);
- goto done;
- }
-- sinfo->filled = BIT(NL80211_STA_INFO_INACTIVE_TIME);
-- sinfo->inactive_time = le32_to_cpu(sta_info_le.idle) * 1000;
-- if (le32_to_cpu(sta_info_le.flags) & BRCMF_STA_ASSOC) {
-- sinfo->filled |= BIT(NL80211_STA_INFO_CONNECTED_TIME);
-- sinfo->connected_time = le32_to_cpu(sta_info_le.in);
-- }
-- brcmf_dbg(TRACE, "STA idle time : %d ms, connected time :%d sec\n",
-- sinfo->inactive_time, sinfo->connected_time);
-- } else if (ifp->vif->wdev.iftype == NL80211_IFTYPE_STATION) {
-- if (memcmp(mac, bssid, ETH_ALEN)) {
-- brcmf_err("Wrong Mac address cfg_mac-%pM wl_bssid-%pM\n",
-- mac, bssid);
-- err = -ENOENT;
-- goto done;
-- }
-- /* Report the current tx rate */
-- err = brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_RATE, &rate);
-- if (err) {
-- brcmf_err("Could not get rate (%d)\n", err);
-- goto done;
-- } else {
-+ }
-+ brcmf_dbg(TRACE, "version %d\n", le16_to_cpu(sta_info_le.ver));
-+ sinfo->filled = BIT(NL80211_STA_INFO_INACTIVE_TIME);
-+ sinfo->inactive_time = le32_to_cpu(sta_info_le.idle) * 1000;
-+ sta_flags = le32_to_cpu(sta_info_le.flags);
-+ brcmf_convert_sta_flags(sta_flags, sinfo);
-+ sinfo->sta_flags.mask |= BIT(NL80211_STA_FLAG_TDLS_PEER);
-+ if (is_tdls_peer)
-+ sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_TDLS_PEER);
-+ else
-+ sinfo->sta_flags.set &= ~BIT(NL80211_STA_FLAG_TDLS_PEER);
-+ if (sta_flags & BRCMF_STA_ASSOC) {
-+ sinfo->filled |= BIT(NL80211_STA_INFO_CONNECTED_TIME);
-+ sinfo->connected_time = le32_to_cpu(sta_info_le.in);
-+ brcmf_fill_bss_param(ifp, sinfo);
-+ }
-+ if (sta_flags & BRCMF_STA_SCBSTATS) {
-+ sinfo->filled |= BIT(NL80211_STA_INFO_TX_FAILED);
-+ sinfo->tx_failed = le32_to_cpu(sta_info_le.tx_failures);
-+ sinfo->filled |= BIT(NL80211_STA_INFO_TX_PACKETS);
-+ sinfo->tx_packets = le32_to_cpu(sta_info_le.tx_pkts);
-+ sinfo->tx_packets += le32_to_cpu(sta_info_le.tx_mcast_pkts);
-+ sinfo->filled |= BIT(NL80211_STA_INFO_RX_PACKETS);
-+ sinfo->rx_packets = le32_to_cpu(sta_info_le.rx_ucast_pkts);
-+ sinfo->rx_packets += le32_to_cpu(sta_info_le.rx_mcast_pkts);
-+ if (sinfo->tx_packets) {
- sinfo->filled |= BIT(NL80211_STA_INFO_TX_BITRATE);
-- sinfo->txrate.legacy = rate * 5;
-- brcmf_dbg(CONN, "Rate %d Mbps\n", rate / 2);
-+ sinfo->txrate.legacy = le32_to_cpu(sta_info_le.tx_rate);
-+ sinfo->txrate.legacy /= 100;
- }
--
-- if (test_bit(BRCMF_VIF_STATUS_CONNECTED,
-- &ifp->vif->sme_state)) {
-- memset(&scb_val, 0, sizeof(scb_val));
-- err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_RSSI,
-- &scb_val, sizeof(scb_val));
-- if (err) {
-- brcmf_err("Could not get rssi (%d)\n", err);
-- goto done;
-- } else {
-- rssi = le32_to_cpu(scb_val.val);
-- sinfo->filled |= BIT(NL80211_STA_INFO_SIGNAL);
-- sinfo->signal = rssi;
-- brcmf_dbg(CONN, "RSSI %d dBm\n", rssi);
-- }
-- err = brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_BCNPRD,
-- &beacon_period);
-- if (err) {
-- brcmf_err("Could not get beacon period (%d)\n",
-- err);
-- goto done;
-- } else {
-- sinfo->bss_param.beacon_interval =
-- beacon_period;
-- brcmf_dbg(CONN, "Beacon peroid %d\n",
-- beacon_period);
-- }
-- err = brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_DTIMPRD,
-- &dtim_period);
-- if (err) {
-- brcmf_err("Could not get DTIM period (%d)\n",
-- err);
-- goto done;
-- } else {
-- sinfo->bss_param.dtim_period = dtim_period;
-- brcmf_dbg(CONN, "DTIM peroid %d\n",
-- dtim_period);
-- }
-- sinfo->filled |= BIT(NL80211_STA_INFO_BSS_PARAM);
-+ if (sinfo->rx_packets) {
-+ sinfo->filled |= BIT(NL80211_STA_INFO_RX_BITRATE);
-+ sinfo->rxrate.legacy = le32_to_cpu(sta_info_le.rx_rate);
-+ sinfo->rxrate.legacy /= 100;
-+ }
-+ if (le16_to_cpu(sta_info_le.ver) >= 4) {
-+ sinfo->filled |= BIT(NL80211_STA_INFO_TX_BYTES);
-+ sinfo->tx_bytes = le64_to_cpu(sta_info_le.tx_tot_bytes);
-+ sinfo->filled |= BIT(NL80211_STA_INFO_RX_BYTES);
-+ sinfo->rx_bytes = le64_to_cpu(sta_info_le.rx_tot_bytes);
- }
-- } else
-- err = -EPERM;
-+ }
- done:
- brcmf_dbg(TRACE, "Exit\n");
- return err;
---- a/drivers/net/wireless/brcm80211/brcmfmac/fwil_types.h
-+++ b/drivers/net/wireless/brcm80211/brcmfmac/fwil_types.h
-@@ -32,7 +32,11 @@
- #define BRCMF_BSS_INFO_VERSION 109 /* curr ver of brcmf_bss_info_le struct */
- #define BRCMF_BSS_RSSI_ON_CHANNEL 0x0002
-
--#define BRCMF_STA_ASSOC 0x10 /* Associated */
-+#define BRCMF_STA_WME 0x00000002 /* WMM association */
-+#define BRCMF_STA_AUTHE 0x00000008 /* Authenticated */
-+#define BRCMF_STA_ASSOC 0x00000010 /* Associated */
-+#define BRCMF_STA_AUTHO 0x00000020 /* Authorized */
-+#define BRCMF_STA_SCBSTATS 0x00004000 /* Per STA debug stats */
-
- /* size of brcmf_scan_params not including variable length array */
- #define BRCMF_SCAN_PARAMS_FIXED_SIZE 64
-@@ -113,6 +117,7 @@
- #define BRCMF_WOWL_MAXPATTERNSIZE 128
-
- #define BRCMF_COUNTRY_BUF_SZ 4
-+#define BRCMF_ANT_MAX 4
-
- /* join preference types for join_pref iovar */
- enum brcmf_join_pref_types {
-@@ -456,25 +461,61 @@ struct brcmf_channel_info_le {
- };
-
- struct brcmf_sta_info_le {
-- __le16 ver; /* version of this struct */
-- __le16 len; /* length in bytes of this structure */
-- __le16 cap; /* sta's advertised capabilities */
-- __le32 flags; /* flags defined below */
-- __le32 idle; /* time since data pkt rx'd from sta */
-- u8 ea[ETH_ALEN]; /* Station address */
-- __le32 count; /* # rates in this set */
-- u8 rates[BRCMF_MAXRATES_IN_SET]; /* rates in 500kbps units */
-+ __le16 ver; /* version of this struct */
-+ __le16 len; /* length in bytes of this structure */
-+ __le16 cap; /* sta's advertised capabilities */
-+ __le32 flags; /* flags defined below */
-+ __le32 idle; /* time since data pkt rx'd from sta */
-+ u8 ea[ETH_ALEN]; /* Station address */
-+ __le32 count; /* # rates in this set */
-+ u8 rates[BRCMF_MAXRATES_IN_SET]; /* rates in 500kbps units */
- /* w/hi bit set if basic */
-- __le32 in; /* seconds elapsed since associated */
-- __le32 listen_interval_inms; /* Min Listen interval in ms for STA */
-- __le32 tx_pkts; /* # of packets transmitted */
-- __le32 tx_failures; /* # of packets failed */
-- __le32 rx_ucast_pkts; /* # of unicast packets received */
-- __le32 rx_mcast_pkts; /* # of multicast packets received */
-- __le32 tx_rate; /* Rate of last successful tx frame */
-- __le32 rx_rate; /* Rate of last successful rx frame */
-- __le32 rx_decrypt_succeeds; /* # of packet decrypted successfully */
-- __le32 rx_decrypt_failures; /* # of packet decrypted failed */
-+ __le32 in; /* seconds elapsed since associated */
-+ __le32 listen_interval_inms; /* Min Listen interval in ms for STA */
-+ __le32 tx_pkts; /* # of packets transmitted */
-+ __le32 tx_failures; /* # of packets failed */
-+ __le32 rx_ucast_pkts; /* # of unicast packets received */
-+ __le32 rx_mcast_pkts; /* # of multicast packets received */
-+ __le32 tx_rate; /* Rate of last successful tx frame */
-+ __le32 rx_rate; /* Rate of last successful rx frame */
-+ __le32 rx_decrypt_succeeds; /* # of packet decrypted successfully */
-+ __le32 rx_decrypt_failures; /* # of packet decrypted failed */
-+ __le32 tx_tot_pkts; /* # of tx pkts (ucast + mcast) */
-+ __le32 rx_tot_pkts; /* # of data packets recvd (uni + mcast) */
-+ __le32 tx_mcast_pkts; /* # of mcast pkts txed */
-+ __le64 tx_tot_bytes; /* data bytes txed (ucast + mcast) */
-+ __le64 rx_tot_bytes; /* data bytes recvd (ucast + mcast) */
-+ __le64 tx_ucast_bytes; /* data bytes txed (ucast) */
-+ __le64 tx_mcast_bytes; /* # data bytes txed (mcast) */
-+ __le64 rx_ucast_bytes; /* data bytes recvd (ucast) */
-+ __le64 rx_mcast_bytes; /* data bytes recvd (mcast) */
-+ s8 rssi[BRCMF_ANT_MAX]; /* per antenna rssi */
-+ s8 nf[BRCMF_ANT_MAX]; /* per antenna noise floor */
-+ __le16 aid; /* association ID */
-+ __le16 ht_capabilities; /* advertised ht caps */
-+ __le16 vht_flags; /* converted vht flags */
-+ __le32 tx_pkts_retry_cnt; /* # of frames where a retry was
-+ * exhausted.
-+ */
-+ __le32 tx_pkts_retry_exhausted; /* # of user frames where a retry
-+ * was exhausted
-+ */
-+ s8 rx_lastpkt_rssi[BRCMF_ANT_MAX]; /* Per antenna RSSI of last
-+ * received data frame.
-+ */
-+ /* TX WLAN retry/failure statistics:
-+ * Separated for host requested frames and locally generated frames.
-+ * Include unicast frame only where the retries/failures can be counted.
-+ */
-+ __le32 tx_pkts_total; /* # user frames sent successfully */
-+ __le32 tx_pkts_retries; /* # user frames retries */
-+ __le32 tx_pkts_fw_total; /* # FW generated sent successfully */
-+ __le32 tx_pkts_fw_retries; /* # retries for FW generated frames */
-+ __le32 tx_pkts_fw_retry_exhausted; /* # FW generated where a retry
-+ * was exhausted
-+ */
-+ __le32 rx_pkts_retried; /* # rx with retry bit set */
-+ __le32 tx_rate_fallback; /* lowest fallback TX rate */
- };
-
- struct brcmf_chanspec_list {
+++ /dev/null
-From: Arend van Spriel <arend@broadcom.com>
-Date: Thu, 11 Jun 2015 00:12:20 +0200
-Subject: [PATCH] brcmfmac: have sdio return -EIO when device communication
- is not possible
-
-The bus interface functions txctl and rxctl may be used while the device
-can not be accessed, eg. upon driver .remove() callback. This patch will
-immediately return -EIO when this is the case which speeds up the module
-unload.
-
-Reviewed-by: Hante Meuleman <meuleman@broadcom.com>
-Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com>
-Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
-Signed-off-by: Arend van Spriel <arend@broadcom.com>
-Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
----
-
---- a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c
-+++ b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c
-@@ -988,6 +988,7 @@ static void brcmf_sdiod_freezer_detach(s
-
- static int brcmf_sdiod_remove(struct brcmf_sdio_dev *sdiodev)
- {
-+ sdiodev->state = BRCMF_SDIOD_DOWN;
- if (sdiodev->bus) {
- brcmf_sdio_remove(sdiodev->bus);
- sdiodev->bus = NULL;
---- a/drivers/net/wireless/brcm80211/brcmfmac/sdio.c
-+++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c
-@@ -2820,6 +2820,8 @@ static int brcmf_sdio_bus_txdata(struct
- struct brcmf_sdio *bus = sdiodev->bus;
-
- brcmf_dbg(TRACE, "Enter: pkt: data %p len %d\n", pkt->data, pkt->len);
-+ if (sdiodev->state != BRCMF_SDIOD_DATA)
-+ return -EIO;
-
- /* Add space for the header */
- skb_push(pkt, bus->tx_hdrlen);
-@@ -2948,6 +2950,8 @@ brcmf_sdio_bus_txctl(struct device *dev,
- int ret;
-
- brcmf_dbg(TRACE, "Enter\n");
-+ if (sdiodev->state != BRCMF_SDIOD_DATA)
-+ return -EIO;
-
- /* Send from dpc */
- bus->ctrl_frame_buf = msg;
-@@ -3238,6 +3242,8 @@ brcmf_sdio_bus_rxctl(struct device *dev,
- struct brcmf_sdio *bus = sdiodev->bus;
-
- brcmf_dbg(TRACE, "Enter\n");
-+ if (sdiodev->state != BRCMF_SDIOD_DATA)
-+ return -EIO;
-
- /* Wait until control frame is available */
- timeleft = brcmf_sdio_dcmd_resp_wait(bus, &bus->rxlen, &pending);
+++ /dev/null
-From: Felix Fietkau <nbd@openwrt.org>
-Date: Thu, 2 Jul 2015 13:35:05 +0200
-Subject: [PATCH] ath9k: make DMA stop related messages debug-only
-
-A long time ago, ath9k had issues during reset where the DMA engine
-would stay active and could potentially corrupt memory.
-To debug those issues, the driver would print warnings whenever they
-occur.
-
-Nowadays, these issues are gone and the primary cause of these messages
-is if the MAC is stuck during reset or busy processing a long
-transmission. This is fairly harmless, yet these messages continue to
-worry users.
-
-To reduce the number of bogus bug reports, turn these messages into
-debug messages and count their occurence in the "reset" debugfs file.
-
-Signed-off-by: Felix Fietkau <nbd@openwrt.org>
----
-
---- a/drivers/net/wireless/ath/ath9k/debug.c
-+++ b/drivers/net/wireless/ath/ath9k/debug.c
-@@ -765,6 +765,8 @@ static int read_file_reset(struct seq_fi
- [RESET_TYPE_BEACON_STUCK] = "Stuck Beacon",
- [RESET_TYPE_MCI] = "MCI Reset",
- [RESET_TYPE_CALIBRATION] = "Calibration error",
-+ [RESET_TX_DMA_ERROR] = "Tx DMA stop error",
-+ [RESET_RX_DMA_ERROR] = "Rx DMA stop error",
- };
- int i;
-
---- a/drivers/net/wireless/ath/ath9k/debug.h
-+++ b/drivers/net/wireless/ath/ath9k/debug.h
-@@ -50,6 +50,8 @@ enum ath_reset_type {
- RESET_TYPE_BEACON_STUCK,
- RESET_TYPE_MCI,
- RESET_TYPE_CALIBRATION,
-+ RESET_TX_DMA_ERROR,
-+ RESET_RX_DMA_ERROR,
- __RESET_TYPE_MAX
- };
-
---- a/drivers/net/wireless/ath/ath9k/recv.c
-+++ b/drivers/net/wireless/ath/ath9k/recv.c
-@@ -496,10 +496,9 @@ bool ath_stoprecv(struct ath_softc *sc)
-
- if (!(ah->ah_flags & AH_UNPLUGGED) &&
- unlikely(!stopped)) {
-- ath_err(ath9k_hw_common(sc->sc_ah),
-- "Could not stop RX, we could be "
-- "confusing the DMA engine when we start RX up\n");
-- ATH_DBG_WARN_ON_ONCE(!stopped);
-+ ath_dbg(ath9k_hw_common(sc->sc_ah), RESET,
-+ "Failed to stop Rx DMA\n");
-+ RESET_STAT_INC(sc, RESET_RX_DMA_ERROR);
- }
- return stopped && !reset;
- }
---- a/drivers/net/wireless/ath/ath9k/xmit.c
-+++ b/drivers/net/wireless/ath/ath9k/xmit.c
-@@ -1896,8 +1896,11 @@ bool ath_drain_all_txq(struct ath_softc
- npend |= BIT(i);
- }
-
-- if (npend)
-- ath_err(common, "Failed to stop TX DMA, queues=0x%03x!\n", npend);
-+ if (npend) {
-+ RESET_STAT_INC(sc, RESET_TX_DMA_ERROR);
-+ ath_dbg(common, RESET,
-+ "Failed to stop TX DMA, queues=0x%03x!\n", npend);
-+ }
-
- for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) {
- if (!ATH_TXQ_SETUP(sc, i))
+++ /dev/null
-From: Arend van Spriel <arend@broadcom.com>
-Date: Thu, 11 Jun 2015 00:12:21 +0200
-Subject: [PATCH] brcmfmac: free ifp for non-netdev interface in p2p module
-
-Making it more clear by freeing the ifp in same place where the
-vif object is freed.
-
-Reviewed-by: Hante Meuleman <meuleman@broadcom.com>
-Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com>
-Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
-Signed-off-by: Arend van Spriel <arend@broadcom.com>
-Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
----
-
---- a/drivers/net/wireless/brcm80211/brcmfmac/core.c
-+++ b/drivers/net/wireless/brcm80211/brcmfmac/core.c
-@@ -867,8 +867,6 @@ static void brcmf_del_if(struct brcmf_pu
- }
- /* unregister will take care of freeing it */
- unregister_netdev(ifp->ndev);
-- } else {
-- kfree(ifp);
- }
- }
-
---- a/drivers/net/wireless/brcm80211/brcmfmac/p2p.c
-+++ b/drivers/net/wireless/brcm80211/brcmfmac/p2p.c
-@@ -2238,6 +2238,7 @@ static void brcmf_p2p_delete_p2pdev(stru
- {
- cfg80211_unregister_wdev(&vif->wdev);
- p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif = NULL;
-+ kfree(vif->ifp);
- brcmf_free_vif(vif);
- }
-
-@@ -2361,6 +2362,8 @@ int brcmf_p2p_del_vif(struct wiphy *wiph
- break;
-
- case NL80211_IFTYPE_P2P_DEVICE:
-+ brcmf_p2p_cancel_remain_on_channel(vif->ifp);
-+ brcmf_p2p_deinit_discovery(p2p);
- brcmf_p2p_delete_p2pdev(p2p, vif);
- return 0;
- default:
+++ /dev/null
-From: Arend van Spriel <arend@broadcom.com>
-Date: Thu, 11 Jun 2015 00:12:22 +0200
-Subject: [PATCH] brcmfmac: move p2p attach/detach functions
-
-Moving two functions in p2p.c as is so next change will be
-easier to review.
-
-Reviewed-by: Hante Meuleman <meuleman@broadcom.com>
-Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com>
-Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
-Signed-off-by: Arend van Spriel <arend@broadcom.com>
-Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
----
-
---- a/drivers/net/wireless/brcm80211/brcmfmac/p2p.c
-+++ b/drivers/net/wireless/brcm80211/brcmfmac/p2p.c
-@@ -1908,105 +1908,6 @@ s32 brcmf_p2p_notify_rx_mgmt_p2p_probere
-
-
- /**
-- * brcmf_p2p_attach() - attach for P2P.
-- *
-- * @cfg: driver private data for cfg80211 interface.
-- */
--s32 brcmf_p2p_attach(struct brcmf_cfg80211_info *cfg)
--{
-- struct brcmf_if *pri_ifp;
-- struct brcmf_if *p2p_ifp;
-- struct brcmf_cfg80211_vif *p2p_vif;
-- struct brcmf_p2p_info *p2p;
-- struct brcmf_pub *drvr;
-- s32 bssidx;
-- s32 err = 0;
--
-- p2p = &cfg->p2p;
-- p2p->cfg = cfg;
--
-- drvr = cfg->pub;
--
-- pri_ifp = drvr->iflist[0];
-- p2p_ifp = drvr->iflist[1];
--
-- p2p->bss_idx[P2PAPI_BSSCFG_PRIMARY].vif = pri_ifp->vif;
--
-- if (p2p_ifp) {
-- p2p_vif = brcmf_alloc_vif(cfg, NL80211_IFTYPE_P2P_DEVICE,
-- false);
-- if (IS_ERR(p2p_vif)) {
-- brcmf_err("could not create discovery vif\n");
-- err = -ENOMEM;
-- goto exit;
-- }
--
-- p2p_vif->ifp = p2p_ifp;
-- p2p_ifp->vif = p2p_vif;
-- p2p_vif->wdev.netdev = p2p_ifp->ndev;
-- p2p_ifp->ndev->ieee80211_ptr = &p2p_vif->wdev;
-- SET_NETDEV_DEV(p2p_ifp->ndev, wiphy_dev(cfg->wiphy));
--
-- p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif = p2p_vif;
--
-- brcmf_p2p_generate_bss_mac(p2p, NULL);
-- memcpy(p2p_ifp->mac_addr, p2p->dev_addr, ETH_ALEN);
-- brcmf_p2p_set_firmware(pri_ifp, p2p->dev_addr);
--
-- /* Initialize P2P Discovery in the firmware */
-- err = brcmf_fil_iovar_int_set(pri_ifp, "p2p_disc", 1);
-- if (err < 0) {
-- brcmf_err("set p2p_disc error\n");
-- brcmf_free_vif(p2p_vif);
-- goto exit;
-- }
-- /* obtain bsscfg index for P2P discovery */
-- err = brcmf_fil_iovar_int_get(pri_ifp, "p2p_dev", &bssidx);
-- if (err < 0) {
-- brcmf_err("retrieving discover bsscfg index failed\n");
-- brcmf_free_vif(p2p_vif);
-- goto exit;
-- }
-- /* Verify that firmware uses same bssidx as driver !! */
-- if (p2p_ifp->bssidx != bssidx) {
-- brcmf_err("Incorrect bssidx=%d, compared to p2p_ifp->bssidx=%d\n",
-- bssidx, p2p_ifp->bssidx);
-- brcmf_free_vif(p2p_vif);
-- goto exit;
-- }
--
-- init_completion(&p2p->send_af_done);
-- INIT_WORK(&p2p->afx_hdl.afx_work, brcmf_p2p_afx_handler);
-- init_completion(&p2p->afx_hdl.act_frm_scan);
-- init_completion(&p2p->wait_next_af);
-- }
--exit:
-- return err;
--}
--
--
--/**
-- * brcmf_p2p_detach() - detach P2P.
-- *
-- * @p2p: P2P specific data.
-- */
--void brcmf_p2p_detach(struct brcmf_p2p_info *p2p)
--{
-- struct brcmf_cfg80211_vif *vif;
--
-- vif = p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif;
-- if (vif != NULL) {
-- brcmf_p2p_cancel_remain_on_channel(vif->ifp);
-- brcmf_p2p_deinit_discovery(p2p);
-- /* remove discovery interface */
-- brcmf_free_vif(vif);
-- p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif = NULL;
-- }
-- /* just set it all to zero */
-- memset(p2p, 0, sizeof(*p2p));
--}
--
--/**
- * brcmf_p2p_get_current_chanspec() - Get current operation channel.
- *
- * @p2p: P2P specific data.
-@@ -2425,3 +2326,102 @@ void brcmf_p2p_stop_device(struct wiphy
- clear_bit(BRCMF_VIF_STATUS_READY, &vif->sme_state);
- mutex_unlock(&cfg->usr_sync);
- }
-+
-+/**
-+ * brcmf_p2p_attach() - attach for P2P.
-+ *
-+ * @cfg: driver private data for cfg80211 interface.
-+ */
-+s32 brcmf_p2p_attach(struct brcmf_cfg80211_info *cfg)
-+{
-+ struct brcmf_if *pri_ifp;
-+ struct brcmf_if *p2p_ifp;
-+ struct brcmf_cfg80211_vif *p2p_vif;
-+ struct brcmf_p2p_info *p2p;
-+ struct brcmf_pub *drvr;
-+ s32 bssidx;
-+ s32 err = 0;
-+
-+ p2p = &cfg->p2p;
-+ p2p->cfg = cfg;
-+
-+ drvr = cfg->pub;
-+
-+ pri_ifp = drvr->iflist[0];
-+ p2p_ifp = drvr->iflist[1];
-+
-+ p2p->bss_idx[P2PAPI_BSSCFG_PRIMARY].vif = pri_ifp->vif;
-+
-+ if (p2p_ifp) {
-+ p2p_vif = brcmf_alloc_vif(cfg, NL80211_IFTYPE_P2P_DEVICE,
-+ false);
-+ if (IS_ERR(p2p_vif)) {
-+ brcmf_err("could not create discovery vif\n");
-+ err = -ENOMEM;
-+ goto exit;
-+ }
-+
-+ p2p_vif->ifp = p2p_ifp;
-+ p2p_ifp->vif = p2p_vif;
-+ p2p_vif->wdev.netdev = p2p_ifp->ndev;
-+ p2p_ifp->ndev->ieee80211_ptr = &p2p_vif->wdev;
-+ SET_NETDEV_DEV(p2p_ifp->ndev, wiphy_dev(cfg->wiphy));
-+
-+ p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif = p2p_vif;
-+
-+ brcmf_p2p_generate_bss_mac(p2p, NULL);
-+ memcpy(p2p_ifp->mac_addr, p2p->dev_addr, ETH_ALEN);
-+ brcmf_p2p_set_firmware(pri_ifp, p2p->dev_addr);
-+
-+ /* Initialize P2P Discovery in the firmware */
-+ err = brcmf_fil_iovar_int_set(pri_ifp, "p2p_disc", 1);
-+ if (err < 0) {
-+ brcmf_err("set p2p_disc error\n");
-+ brcmf_free_vif(p2p_vif);
-+ goto exit;
-+ }
-+ /* obtain bsscfg index for P2P discovery */
-+ err = brcmf_fil_iovar_int_get(pri_ifp, "p2p_dev", &bssidx);
-+ if (err < 0) {
-+ brcmf_err("retrieving discover bsscfg index failed\n");
-+ brcmf_free_vif(p2p_vif);
-+ goto exit;
-+ }
-+ /* Verify that firmware uses same bssidx as driver !! */
-+ if (p2p_ifp->bssidx != bssidx) {
-+ brcmf_err("Incorrect bssidx=%d, compared to p2p_ifp->bssidx=%d\n",
-+ bssidx, p2p_ifp->bssidx);
-+ brcmf_free_vif(p2p_vif);
-+ goto exit;
-+ }
-+
-+ init_completion(&p2p->send_af_done);
-+ INIT_WORK(&p2p->afx_hdl.afx_work, brcmf_p2p_afx_handler);
-+ init_completion(&p2p->afx_hdl.act_frm_scan);
-+ init_completion(&p2p->wait_next_af);
-+ }
-+exit:
-+ return err;
-+}
-+
-+/**
-+ * brcmf_p2p_detach() - detach P2P.
-+ *
-+ * @p2p: P2P specific data.
-+ */
-+void brcmf_p2p_detach(struct brcmf_p2p_info *p2p)
-+{
-+ struct brcmf_cfg80211_vif *vif;
-+
-+ vif = p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif;
-+ if (vif != NULL) {
-+ brcmf_p2p_cancel_remain_on_channel(vif->ifp);
-+ brcmf_p2p_deinit_discovery(p2p);
-+ /* remove discovery interface */
-+ brcmf_free_vif(vif);
-+ p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif = NULL;
-+ }
-+ /* just set it all to zero */
-+ memset(p2p, 0, sizeof(*p2p));
-+}
-+
+++ /dev/null
-From: Arend van Spriel <arend@broadcom.com>
-Date: Thu, 11 Jun 2015 00:12:23 +0200
-Subject: [PATCH] brcmfmac: assure p2pdev is unregistered upon driver
- unload
-
-When unloading the driver with a p2pdev interface it resulted in
-a warning upon calling wiphy_unregister() and subsequently a crash
-in the driver. This patch assures the p2pdev is unregistered calling
-unregister_wdev() before doing the wiphy_unregister().
-
-Reviewed-by: Hante Meuleman <meuleman@broadcom.com>
-Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com>
-Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
-Signed-off-by: Arend van Spriel <arend@broadcom.com>
-Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
----
-
---- a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c
-+++ b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c
-@@ -6206,10 +6206,8 @@ void brcmf_cfg80211_detach(struct brcmf_
- if (!cfg)
- return;
-
-- WARN_ON(!list_empty(&cfg->vif_list));
-- wiphy_unregister(cfg->wiphy);
- brcmf_btcoex_detach(cfg);
-- brcmf_p2p_detach(&cfg->p2p);
-+ wiphy_unregister(cfg->wiphy);
- wl_deinit_priv(cfg);
- brcmf_free_wiphy(cfg->wiphy);
- }
---- a/drivers/net/wireless/brcm80211/brcmfmac/core.c
-+++ b/drivers/net/wireless/brcm80211/brcmfmac/core.c
-@@ -1098,6 +1098,7 @@ void brcmf_detach(struct device *dev)
-
- /* stop firmware event handling */
- brcmf_fweh_detach(drvr);
-+ brcmf_p2p_detach(&drvr->config->p2p);
-
- brcmf_bus_change_state(bus_if, BRCMF_BUS_DOWN);
-
---- a/drivers/net/wireless/brcm80211/brcmfmac/p2p.c
-+++ b/drivers/net/wireless/brcm80211/brcmfmac/p2p.c
-@@ -16,6 +16,7 @@
- #include <linux/slab.h>
- #include <linux/netdevice.h>
- #include <linux/etherdevice.h>
-+#include <linux/rtnetlink.h>
- #include <net/cfg80211.h>
-
- #include <brcmu_wifi.h>
-@@ -2418,8 +2419,9 @@ void brcmf_p2p_detach(struct brcmf_p2p_i
- brcmf_p2p_cancel_remain_on_channel(vif->ifp);
- brcmf_p2p_deinit_discovery(p2p);
- /* remove discovery interface */
-- brcmf_free_vif(vif);
-- p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif = NULL;
-+ rtnl_lock();
-+ brcmf_p2p_delete_p2pdev(p2p, vif);
-+ rtnl_unlock();
- }
- /* just set it all to zero */
- memset(p2p, 0, sizeof(*p2p));
+++ /dev/null
-From: Arend van Spriel <arend@broadcom.com>
-Date: Mon, 15 Jun 2015 22:48:38 +0200
-Subject: [PATCH] brcmfmac: fix double free of p2pdev interface
-
-When freeing the driver ifp pointer it should also be removed from
-the driver interface list, which is what brcmf_remove_interface()
-does. Otherwise, the ifp pointer will be freed twice triggering
-a kernel oops.
-
-Fixes: f37d69a4babc ("brcmfmac: free ifp for non-netdev interface in p2p module")
-Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
-Reviewed-by: Hante Meuleman <meuleman@broadcom.com>
-Signed-off-by: Arend van Spriel <arend@broadcom.com>
-Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
----
-
---- a/drivers/net/wireless/brcm80211/brcmfmac/p2p.c
-+++ b/drivers/net/wireless/brcm80211/brcmfmac/p2p.c
-@@ -2140,7 +2140,7 @@ static void brcmf_p2p_delete_p2pdev(stru
- {
- cfg80211_unregister_wdev(&vif->wdev);
- p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif = NULL;
-- kfree(vif->ifp);
-+ brcmf_remove_interface(vif->ifp->drvr, vif->ifp->bssidx);
- brcmf_free_vif(vif);
- }
-
+++ /dev/null
-From: Arend van Spriel <arend@broadcom.com>
-Date: Mon, 15 Jun 2015 22:48:39 +0200
-Subject: [PATCH] brcmfmac: make brcmf_p2p_detach() call conditional
-
-During verification of error handling in brcmf_cfg80211_attach() a
-null pointer dereference occurred upon calling brcmf_p2p_detach()
-from brcmf_detach(). This should only be called when the
-brcmf_cfg80211_attach() has succeeded.
-
-Fixes: f7a40873d2fa ("brcmfmac: assure p2pdev is unregistered upon driver unload")
-Reviewed-by: Hante Meuleman <meuleman@broadcom.com>
-Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com>
-Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
-Signed-off-by: Arend van Spriel <arend@broadcom.com>
-Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
----
-
---- a/drivers/net/wireless/brcm80211/brcmfmac/core.c
-+++ b/drivers/net/wireless/brcm80211/brcmfmac/core.c
-@@ -1098,7 +1098,8 @@ void brcmf_detach(struct device *dev)
-
- /* stop firmware event handling */
- brcmf_fweh_detach(drvr);
-- brcmf_p2p_detach(&drvr->config->p2p);
-+ if (drvr->config)
-+ brcmf_p2p_detach(&drvr->config->p2p);
-
- brcmf_bus_change_state(bus_if, BRCMF_BUS_DOWN);
-
+++ /dev/null
-From: Rafa? Mi?ecki <zajec5@gmail.com>
-Date: Thu, 9 Jul 2015 17:07:08 +0200
-Subject: [PATCH] brcmfmac: set wiphy's addresses to provide valid MACs
-
-Broadcom's firmware requires every BSS to use MAC address with unique
-last few bits. The amount of bits may depend on a particular firmware,
-it was verified to be 2 for BCM43602 one.
-If this condition won't be fulfilled firmware will reject such MAC:
-brcmfmac: _brcmf_set_mac_address: Setting cur_etheraddr failed, -52
-
-We don't want to simply set addr_mask as it would also disallow using
-locally administrated bit. Instead let's build a list of addresses
-manually enabling 0x2 bit for extra interfaces.
-
-Signed-off-by: Rafa? Mi?ecki <zajec5@gmail.com>
-Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
----
-
---- a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c
-+++ b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c
-@@ -5784,6 +5784,7 @@ static void brcmf_wiphy_wowl_params(stru
-
- static int brcmf_setup_wiphy(struct wiphy *wiphy, struct brcmf_if *ifp)
- {
-+ struct brcmf_pub *drvr = ifp->drvr;
- struct ieee80211_supported_band *band;
- __le32 bandlist[3];
- u32 n_bands;
-@@ -5797,6 +5798,19 @@ static int brcmf_setup_wiphy(struct wiph
- if (err)
- return err;
-
-+ for (i = 0; i < wiphy->iface_combinations->max_interfaces &&
-+ i < ARRAY_SIZE(drvr->addresses); i++) {
-+ u8 *addr = drvr->addresses[i].addr;
-+
-+ memcpy(addr, drvr->mac, ETH_ALEN);
-+ if (i) {
-+ addr[0] |= BIT(1);
-+ addr[ETH_ALEN - 1] ^= i;
-+ }
-+ }
-+ wiphy->addresses = drvr->addresses;
-+ wiphy->n_addresses = i;
-+
- wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
- wiphy->cipher_suites = __wl_cipher_suites;
- wiphy->n_cipher_suites = ARRAY_SIZE(__wl_cipher_suites);
---- a/drivers/net/wireless/brcm80211/brcmfmac/core.h
-+++ b/drivers/net/wireless/brcm80211/brcmfmac/core.h
-@@ -21,6 +21,7 @@
- #ifndef BRCMFMAC_CORE_H
- #define BRCMFMAC_CORE_H
-
-+#include <net/cfg80211.h>
- #include "fweh.h"
-
- #define TOE_TX_CSUM_OL 0x00000001
-@@ -118,6 +119,8 @@ struct brcmf_pub {
- /* Multicast data packets sent to dongle */
- unsigned long tx_multicast;
-
-+ struct mac_address addresses[BRCMF_MAX_IFS];
-+
- struct brcmf_if *iflist[BRCMF_MAX_IFS];
-
- struct mutex proto_block;
+++ /dev/null
-From: Vineet Gupta <Vineet.Gupta1@synopsys.com>
-Date: Thu, 9 Jul 2015 13:43:18 +0530
-Subject: [PATCH] brcmfmac: dhd_sdio.c: use existing atomic_or primitive
-
-There's already a generic implementation so use that instead.
-
-Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
----
-
---- a/drivers/net/wireless/brcm80211/brcmfmac/sdio.c
-+++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c
-@@ -2564,15 +2564,6 @@ static inline void brcmf_sdio_clrintr(st
- }
- }
-
--static void atomic_orr(int val, atomic_t *v)
--{
-- int old_val;
--
-- old_val = atomic_read(v);
-- while (atomic_cmpxchg(v, old_val, val | old_val) != old_val)
-- old_val = atomic_read(v);
--}
--
- static int brcmf_sdio_intr_rstatus(struct brcmf_sdio *bus)
- {
- struct brcmf_core *buscore;
-@@ -2595,7 +2586,7 @@ static int brcmf_sdio_intr_rstatus(struc
- if (val) {
- brcmf_sdiod_regwl(bus->sdiodev, addr, val, &ret);
- bus->sdcnt.f1regdata++;
-- atomic_orr(val, &bus->intstatus);
-+ atomic_or(val, &bus->intstatus);
- }
-
- return ret;
-@@ -2712,7 +2703,7 @@ static void brcmf_sdio_dpc(struct brcmf_
-
- /* Keep still-pending events for next scheduling */
- if (intstatus)
-- atomic_orr(intstatus, &bus->intstatus);
-+ atomic_or(intstatus, &bus->intstatus);
-
- brcmf_sdio_clrintr(bus);
-
+++ /dev/null
-From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <zajec5@gmail.com>
-Date: Thu, 20 Aug 2015 00:16:42 +0200
-Subject: [PATCH] brcmfmac: check all combinations when setting wiphy's
- addresses
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Broadcom is working on better reflection of interface combinations. With
-upcoming patches we may have 1st combination supporting less interfaces
-than others.
-To don't run out of addresses check all combinations to find the one
-with the greatest max_interfaces value.
-
-Signed-off-by: Rafał Miłecki <zajec5@gmail.com>
-Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
----
-
---- a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c
-+++ b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c
-@@ -5785,7 +5785,9 @@ static void brcmf_wiphy_wowl_params(stru
- static int brcmf_setup_wiphy(struct wiphy *wiphy, struct brcmf_if *ifp)
- {
- struct brcmf_pub *drvr = ifp->drvr;
-+ const struct ieee80211_iface_combination *combo;
- struct ieee80211_supported_band *band;
-+ u16 max_interfaces = 0;
- __le32 bandlist[3];
- u32 n_bands;
- int err, i;
-@@ -5798,8 +5800,13 @@ static int brcmf_setup_wiphy(struct wiph
- if (err)
- return err;
-
-- for (i = 0; i < wiphy->iface_combinations->max_interfaces &&
-- i < ARRAY_SIZE(drvr->addresses); i++) {
-+ for (i = 0, combo = wiphy->iface_combinations;
-+ i < wiphy->n_iface_combinations; i++, combo++) {
-+ max_interfaces = max(max_interfaces, combo->max_interfaces);
-+ }
-+
-+ for (i = 0; i < max_interfaces && i < ARRAY_SIZE(drvr->addresses);
-+ i++) {
- u8 *addr = drvr->addresses[i].addr;
-
- memcpy(addr, drvr->mac, ETH_ALEN);
+++ /dev/null
-From: Arend van Spriel <arend@broadcom.com>
-Date: Thu, 20 Aug 2015 22:06:03 +0200
-Subject: [PATCH] brcmfmac: correct interface combination info
-
-The interface combination provided by brcmfmac did not truly reflect
-the combinations supported by driver and/or firmware.
-
-Reviewed-by: Hante Meuleman <meuleman@broadcom.com>
-Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com>
-Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
-Reviewed-by: Pontus Fuchs <pontusf@broadcom.com>
-Signed-off-by: Arend van Spriel <arend@broadcom.com>
-Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
----
-
---- a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c
-+++ b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c
-@@ -5694,63 +5694,132 @@ brcmf_txrx_stypes[NUM_NL80211_IFTYPES] =
- }
- };
-
-+/**
-+ * brcmf_setup_ifmodes() - determine interface modes and combinations.
-+ *
-+ * @wiphy: wiphy object.
-+ * @ifp: interface object needed for feat module api.
-+ *
-+ * The interface modes and combinations are determined dynamically here
-+ * based on firmware functionality.
-+ *
-+ * no p2p and no mbss:
-+ *
-+ * #STA <= 1, #AP <= 1, channels = 1, 2 total
-+ *
-+ * no p2p and mbss:
-+ *
-+ * #STA <= 1, #AP <= 1, channels = 1, 2 total
-+ * #AP <= 4, matching BI, channels = 1, 4 total
-+ *
-+ * p2p, no mchan, and mbss:
-+ *
-+ * #STA <= 1, #P2P-DEV <= 1, #{P2P-CL, P2P-GO} <= 1, channels = 1, 3 total
-+ * #STA <= 1, #P2P-DEV <= 1, #AP <= 1, #P2P-CL <= 1, channels = 1, 4 total
-+ * #AP <= 4, matching BI, channels = 1, 4 total
-+ *
-+ * p2p, mchan, and mbss:
-+ *
-+ * #STA <= 1, #P2P-DEV <= 1, #{P2P-CL, P2P-GO} <= 1, channels = 2, 3 total
-+ * #STA <= 1, #P2P-DEV <= 1, #AP <= 1, #P2P-CL <= 1, channels = 1, 4 total
-+ * #AP <= 4, matching BI, channels = 1, 4 total
-+ */
- static int brcmf_setup_ifmodes(struct wiphy *wiphy, struct brcmf_if *ifp)
- {
- struct ieee80211_iface_combination *combo = NULL;
-- struct ieee80211_iface_limit *limits = NULL;
-- int i = 0, max_iface_cnt;
-+ struct ieee80211_iface_limit *c0_limits = NULL;
-+ struct ieee80211_iface_limit *p2p_limits = NULL;
-+ struct ieee80211_iface_limit *mbss_limits = NULL;
-+ bool mbss, p2p;
-+ int i, c, n_combos;
-
-- combo = kzalloc(sizeof(*combo), GFP_KERNEL);
-+ mbss = brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS);
-+ p2p = brcmf_feat_is_enabled(ifp, BRCMF_FEAT_P2P);
-+
-+ n_combos = 1 + !!p2p + !!mbss;
-+ combo = kcalloc(n_combos, sizeof(*combo), GFP_KERNEL);
- if (!combo)
- goto err;
-
-- limits = kzalloc(sizeof(*limits) * 4, GFP_KERNEL);
-- if (!limits)
-+ c0_limits = kcalloc(p2p ? 3 : 2, sizeof(*c0_limits), GFP_KERNEL);
-+ if (!c0_limits)
- goto err;
-
-+ if (p2p) {
-+ p2p_limits = kcalloc(4, sizeof(*p2p_limits), GFP_KERNEL);
-+ if (!p2p_limits)
-+ goto err;
-+ }
-+
-+ if (mbss) {
-+ mbss_limits = kcalloc(1, sizeof(*mbss_limits), GFP_KERNEL);
-+ if (!mbss_limits)
-+ goto err;
-+ }
-+
- wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
- BIT(NL80211_IFTYPE_ADHOC) |
- BIT(NL80211_IFTYPE_AP);
-
-- if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MCHAN))
-- combo->num_different_channels = 2;
-- else
-- combo->num_different_channels = 1;
--
-- if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS)) {
-- limits[i].max = 1;
-- limits[i++].types = BIT(NL80211_IFTYPE_STATION);
-- limits[i].max = 4;
-- limits[i++].types = BIT(NL80211_IFTYPE_AP);
-- max_iface_cnt = 5;
-- } else {
-- limits[i].max = 2;
-- limits[i++].types = BIT(NL80211_IFTYPE_STATION) |
-- BIT(NL80211_IFTYPE_AP);
-- max_iface_cnt = 2;
-- }
--
-- if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_P2P)) {
-+ c = 0;
-+ i = 0;
-+ combo[c].num_different_channels = 1;
-+ c0_limits[i].max = 1;
-+ c0_limits[i++].types = BIT(NL80211_IFTYPE_STATION);
-+ if (p2p) {
-+ if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MCHAN))
-+ combo[c].num_different_channels = 2;
- wiphy->interface_modes |= BIT(NL80211_IFTYPE_P2P_CLIENT) |
- BIT(NL80211_IFTYPE_P2P_GO) |
- BIT(NL80211_IFTYPE_P2P_DEVICE);
-- limits[i].max = 1;
-- limits[i++].types = BIT(NL80211_IFTYPE_P2P_CLIENT) |
-- BIT(NL80211_IFTYPE_P2P_GO);
-- limits[i].max = 1;
-- limits[i++].types = BIT(NL80211_IFTYPE_P2P_DEVICE);
-- max_iface_cnt += 2;
-- }
-- combo->max_interfaces = max_iface_cnt;
-- combo->limits = limits;
-- combo->n_limits = i;
-+ c0_limits[i].max = 1;
-+ c0_limits[i++].types = BIT(NL80211_IFTYPE_P2P_DEVICE);
-+ c0_limits[i].max = 1;
-+ c0_limits[i++].types = BIT(NL80211_IFTYPE_P2P_CLIENT) |
-+ BIT(NL80211_IFTYPE_P2P_GO);
-+ } else {
-+ c0_limits[i].max = 1;
-+ c0_limits[i++].types = BIT(NL80211_IFTYPE_AP);
-+ }
-+ combo[c].max_interfaces = i;
-+ combo[c].n_limits = i;
-+ combo[c].limits = c0_limits;
-+
-+ if (p2p) {
-+ c++;
-+ i = 0;
-+ combo[c].num_different_channels = 1;
-+ p2p_limits[i].max = 1;
-+ p2p_limits[i++].types = BIT(NL80211_IFTYPE_STATION);
-+ p2p_limits[i].max = 1;
-+ p2p_limits[i++].types = BIT(NL80211_IFTYPE_AP);
-+ p2p_limits[i].max = 1;
-+ p2p_limits[i++].types = BIT(NL80211_IFTYPE_P2P_CLIENT);
-+ p2p_limits[i].max = 1;
-+ p2p_limits[i++].types = BIT(NL80211_IFTYPE_P2P_DEVICE);
-+ combo[c].max_interfaces = i;
-+ combo[c].n_limits = i;
-+ combo[c].limits = p2p_limits;
-+ }
-
-+ if (mbss) {
-+ c++;
-+ combo[c].beacon_int_infra_match = true;
-+ combo[c].num_different_channels = 1;
-+ mbss_limits[0].max = 4;
-+ mbss_limits[0].types = BIT(NL80211_IFTYPE_AP);
-+ combo[c].max_interfaces = 4;
-+ combo[c].n_limits = 1;
-+ combo[c].limits = mbss_limits;
-+ }
-+ wiphy->n_iface_combinations = n_combos;
- wiphy->iface_combinations = combo;
-- wiphy->n_iface_combinations = 1;
- return 0;
-
- err:
-- kfree(limits);
-+ kfree(c0_limits);
-+ kfree(p2p_limits);
-+ kfree(mbss_limits);
- kfree(combo);
- return -ENOMEM;
- }
-@@ -6079,11 +6148,15 @@ static void brcmf_cfg80211_reg_notifier(
-
- static void brcmf_free_wiphy(struct wiphy *wiphy)
- {
-+ int i;
-+
- if (!wiphy)
- return;
-
-- if (wiphy->iface_combinations)
-- kfree(wiphy->iface_combinations->limits);
-+ if (wiphy->iface_combinations) {
-+ for (i = 0; i < wiphy->n_iface_combinations; i++)
-+ kfree(wiphy->iface_combinations[i].limits);
-+ }
- kfree(wiphy->iface_combinations);
- if (wiphy->bands[IEEE80211_BAND_2GHZ]) {
- kfree(wiphy->bands[IEEE80211_BAND_2GHZ]->channels);
+++ /dev/null
-From: Franky Lin <frankyl@broadcom.com>
-Date: Thu, 20 Aug 2015 22:06:04 +0200
-Subject: [PATCH] brcmfmac: add debugfs entry for msgbuf statistics
-
-Expose ring buffer read/write pointers and other useful statistics
-through debugfs.
-
-Reviewed-by: Arend Van Spriel <arend@broadcom.com>
-Reviewed-by: Hante Meuleman <meuleman@broadcom.com>
-Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
-Signed-off-by: Franky Lin <frankyl@broadcom.com>
-Signed-off-by: Arend van Spriel <arend@broadcom.com>
-Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
----
-
---- a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c
-+++ b/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c
-@@ -1360,6 +1360,60 @@ void brcmf_msgbuf_delete_flowring(struct
- }
- }
-
-+#ifdef DEBUG
-+static int brcmf_msgbuf_stats_read(struct seq_file *seq, void *data)
-+{
-+ struct brcmf_bus *bus_if = dev_get_drvdata(seq->private);
-+ struct brcmf_pub *drvr = bus_if->drvr;
-+ struct brcmf_msgbuf *msgbuf = (struct brcmf_msgbuf *)drvr->proto->pd;
-+ struct brcmf_commonring *commonring;
-+ u16 i;
-+ struct brcmf_flowring_ring *ring;
-+ struct brcmf_flowring_hash *hash;
-+
-+ commonring = msgbuf->commonrings[BRCMF_H2D_MSGRING_CONTROL_SUBMIT];
-+ seq_printf(seq, "h2d_ctl_submit: rp %4u, wp %4u, depth %4u\n",
-+ commonring->r_ptr, commonring->w_ptr, commonring->depth);
-+ commonring = msgbuf->commonrings[BRCMF_H2D_MSGRING_RXPOST_SUBMIT];
-+ seq_printf(seq, "h2d_rx_submit: rp %4u, wp %4u, depth %4u\n",
-+ commonring->r_ptr, commonring->w_ptr, commonring->depth);
-+ commonring = msgbuf->commonrings[BRCMF_D2H_MSGRING_CONTROL_COMPLETE];
-+ seq_printf(seq, "d2h_ctl_cmplt: rp %4u, wp %4u, depth %4u\n",
-+ commonring->r_ptr, commonring->w_ptr, commonring->depth);
-+ commonring = msgbuf->commonrings[BRCMF_D2H_MSGRING_TX_COMPLETE];
-+ seq_printf(seq, "d2h_tx_cmplt: rp %4u, wp %4u, depth %4u\n",
-+ commonring->r_ptr, commonring->w_ptr, commonring->depth);
-+ commonring = msgbuf->commonrings[BRCMF_D2H_MSGRING_RX_COMPLETE];
-+ seq_printf(seq, "d2h_rx_cmplt: rp %4u, wp %4u, depth %4u\n",
-+ commonring->r_ptr, commonring->w_ptr, commonring->depth);
-+
-+ seq_printf(seq, "\nh2d_flowrings: depth %u\n",
-+ BRCMF_H2D_TXFLOWRING_MAX_ITEM);
-+ seq_puts(seq, "Active flowrings:\n");
-+ hash = msgbuf->flow->hash;
-+ for (i = 0; i < msgbuf->flow->nrofrings; i++) {
-+ if (!msgbuf->flow->rings[i])
-+ continue;
-+ ring = msgbuf->flow->rings[i];
-+ if (ring->status != RING_OPEN)
-+ continue;
-+ commonring = msgbuf->flowrings[i];
-+ hash = &msgbuf->flow->hash[ring->hash_id];
-+ seq_printf(seq, "id %3u: rp %4u, wp %4u, qlen %4u, blocked %u\n"
-+ " ifidx %u, fifo %u, da %pM\n",
-+ i, commonring->r_ptr, commonring->w_ptr,
-+ skb_queue_len(&ring->skblist), ring->blocked,
-+ hash->ifidx, hash->fifo, hash->mac);
-+ }
-+
-+ return 0;
-+}
-+#else
-+static int brcmf_msgbuf_stats_read(struct seq_file *seq, void *data)
-+{
-+ return 0;
-+}
-+#endif
-
- int brcmf_proto_msgbuf_attach(struct brcmf_pub *drvr)
- {
-@@ -1460,6 +1514,8 @@ int brcmf_proto_msgbuf_attach(struct brc
- spin_lock_init(&msgbuf->flowring_work_lock);
- INIT_LIST_HEAD(&msgbuf->work_queue);
-
-+ brcmf_debugfs_add_entry(drvr, "msgbuf_stats", brcmf_msgbuf_stats_read);
-+
- return 0;
-
- fail:
+++ /dev/null
-From: Arend van Spriel <arend@broadcom.com>
-Date: Thu, 20 Aug 2015 22:06:05 +0200
-Subject: [PATCH] brcmfmac: make use of cfg80211_check_combinations()
-
-Use cfg80211_check_combinations() so we can bail out early when an
-interface add or change results in an invalid combination.
-
-Reviewed-by: Hante Meuleman <meuleman@broadcom.com>
-Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com>
-Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
-Signed-off-by: Arend van Spriel <arend@broadcom.com>
-Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
----
-
---- a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c
-+++ b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c
-@@ -469,6 +469,36 @@ brcmf_find_wpsie(const u8 *parse, u32 le
- return NULL;
- }
-
-+static int brcmf_vif_change_validate(struct brcmf_cfg80211_info *cfg,
-+ struct brcmf_cfg80211_vif *vif,
-+ enum nl80211_iftype new_type)
-+{
-+ int iftype_num[NUM_NL80211_IFTYPES];
-+ struct brcmf_cfg80211_vif *pos;
-+
-+ memset(&iftype_num[0], 0, sizeof(iftype_num));
-+ list_for_each_entry(pos, &cfg->vif_list, list)
-+ if (pos == vif)
-+ iftype_num[new_type]++;
-+ else
-+ iftype_num[pos->wdev.iftype]++;
-+
-+ return cfg80211_check_combinations(cfg->wiphy, 1, 0, iftype_num);
-+}
-+
-+static int brcmf_vif_add_validate(struct brcmf_cfg80211_info *cfg,
-+ enum nl80211_iftype new_type)
-+{
-+ int iftype_num[NUM_NL80211_IFTYPES];
-+ struct brcmf_cfg80211_vif *pos;
-+
-+ memset(&iftype_num[0], 0, sizeof(iftype_num));
-+ list_for_each_entry(pos, &cfg->vif_list, list)
-+ iftype_num[pos->wdev.iftype]++;
-+
-+ iftype_num[new_type]++;
-+ return cfg80211_check_combinations(cfg->wiphy, 1, 0, iftype_num);
-+}
-
- static void convert_key_from_CPU(struct brcmf_wsec_key *key,
- struct brcmf_wsec_key_le *key_le)
-@@ -662,8 +692,14 @@ static struct wireless_dev *brcmf_cfg802
- struct vif_params *params)
- {
- struct wireless_dev *wdev;
-+ int err;
-
- brcmf_dbg(TRACE, "enter: %s type %d\n", name, type);
-+ err = brcmf_vif_add_validate(wiphy_to_cfg(wiphy), type);
-+ if (err) {
-+ brcmf_err("iface validation failed: err=%d\n", err);
-+ return ERR_PTR(err);
-+ }
- switch (type) {
- case NL80211_IFTYPE_ADHOC:
- case NL80211_IFTYPE_STATION:
-@@ -822,8 +858,12 @@ brcmf_cfg80211_change_iface(struct wiphy
- s32 ap = 0;
- s32 err = 0;
-
-- brcmf_dbg(TRACE, "Enter, ndev=%p, type=%d\n", ndev, type);
--
-+ brcmf_dbg(TRACE, "Enter, idx=%d, type=%d\n", ifp->bssidx, type);
-+ err = brcmf_vif_change_validate(wiphy_to_cfg(wiphy), vif, type);
-+ if (err) {
-+ brcmf_err("iface validation failed: err=%d\n", err);
-+ return err;
-+ }
- switch (type) {
- case NL80211_IFTYPE_MONITOR:
- case NL80211_IFTYPE_WDS:
+++ /dev/null
-From: Franky Lin <frankyl@broadcom.com>
-Date: Thu, 20 Aug 2015 22:06:06 +0200
-Subject: [PATCH] brcmfmac: block the correct flowring when backup queue
- overflow
-
-brcmf_flowring_block blocks the last active flowring under the same
-interface instead of the one provided by caller. This could lead to a
-dead lock of netif stop if there are more than one flowring under the
-interface and the traffic is high enough so brcmf_flowring_enqueue can
-not unblock the ring right away.
-
-Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
-Reviewed-by: Hante Meuleman <meuleman@broadcom.com>
-Signed-off-by: Franky Lin <frankyl@broadcom.com>
-Signed-off-by: Arend van Spriel <arend@broadcom.com>
-Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
----
-
---- a/drivers/net/wireless/brcm80211/brcmfmac/flowring.c
-+++ b/drivers/net/wireless/brcm80211/brcmfmac/flowring.c
-@@ -194,11 +194,15 @@ static void brcmf_flowring_block(struct
- spin_lock_irqsave(&flow->block_lock, flags);
-
- ring = flow->rings[flowid];
-+ if (ring->blocked == blocked) {
-+ spin_unlock_irqrestore(&flow->block_lock, flags);
-+ return;
-+ }
- ifidx = brcmf_flowring_ifidx_get(flow, flowid);
-
- currently_blocked = false;
- for (i = 0; i < flow->nrofrings; i++) {
-- if (flow->rings[i]) {
-+ if ((flow->rings[i]) && (i != flowid)) {
- ring = flow->rings[i];
- if ((ring->status == RING_OPEN) &&
- (brcmf_flowring_ifidx_get(flow, i) == ifidx)) {
-@@ -209,8 +213,8 @@ static void brcmf_flowring_block(struct
- }
- }
- }
-- ring->blocked = blocked;
-- if (currently_blocked == blocked) {
-+ flow->rings[flowid]->blocked = blocked;
-+ if (currently_blocked) {
- spin_unlock_irqrestore(&flow->block_lock, flags);
- return;
- }
+++ /dev/null
-From: Arend van Spriel <arend@broadcom.com>
-Date: Thu, 20 Aug 2015 22:06:07 +0200
-Subject: [PATCH] brcmfmac: bump highest event number for 4339 firmware
-
-The event mask length is determined by the highest event number
-that is specified in the driver. When this length is shorter than
-firmware expects setting event mask will fail and device becomes
-pretty useless. This issue was reported with bcm4339 firmware that
-was recently released.
-
-Reported-by: Pontus Fuchs <pontusf@broadcom.com>
-Reviewed-by: Hante Meuleman <meuleman@broadcom.com>
-Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com>
-Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
-Reviewed-by: Pontus Fuchs <pontusf@broadcom.com>
-Signed-off-by: Arend van Spriel <arend@broadcom.com>
-Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
----
-
---- a/drivers/net/wireless/brcm80211/brcmfmac/fweh.h
-+++ b/drivers/net/wireless/brcm80211/brcmfmac/fweh.h
-@@ -85,7 +85,6 @@ struct brcmf_event;
- BRCMF_ENUM_DEF(IF, 54) \
- BRCMF_ENUM_DEF(P2P_DISC_LISTEN_COMPLETE, 55) \
- BRCMF_ENUM_DEF(RSSI, 56) \
-- BRCMF_ENUM_DEF(PFN_SCAN_COMPLETE, 57) \
- BRCMF_ENUM_DEF(EXTLOG_MSG, 58) \
- BRCMF_ENUM_DEF(ACTION_FRAME, 59) \
- BRCMF_ENUM_DEF(ACTION_FRAME_COMPLETE, 60) \
-@@ -103,8 +102,7 @@ struct brcmf_event;
- BRCMF_ENUM_DEF(FIFO_CREDIT_MAP, 74) \
- BRCMF_ENUM_DEF(ACTION_FRAME_RX, 75) \
- BRCMF_ENUM_DEF(TDLS_PEER_EVENT, 92) \
-- BRCMF_ENUM_DEF(BCMC_CREDIT_SUPPORT, 127) \
-- BRCMF_ENUM_DEF(PSTA_PRIMARY_INTF_IND, 128)
-+ BRCMF_ENUM_DEF(BCMC_CREDIT_SUPPORT, 127)
-
- #define BRCMF_ENUM_DEF(id, val) \
- BRCMF_E_##id = (val),
-@@ -112,7 +110,11 @@ struct brcmf_event;
- /* firmware event codes sent by the dongle */
- enum brcmf_fweh_event_code {
- BRCMF_FWEH_EVENT_ENUM_DEFLIST
-- BRCMF_E_LAST
-+ /* this determines event mask length which must match
-+ * minimum length check in device firmware so it is
-+ * hard-coded here.
-+ */
-+ BRCMF_E_LAST = 139
- };
- #undef BRCMF_ENUM_DEF
-
+++ /dev/null
-From: Arend van Spriel <arend@broadcom.com>
-Date: Wed, 26 Aug 2015 22:14:53 +0200
-Subject: [PATCH] brcmfmac: consolidate ifp lookup in driver core
-
-In rx path the firmware provide an interface index which is used to
-map to a struct brcmf_if instance. However, this involves some trick
-that is done in two places. This is changed by having driver core
-providing brcmf_get_ifp() function.
-
-Reviewed-by: Hante Meuleman <meuleman@broadcom.com>
-Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com>
-Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
-Signed-off-by: Arend van Spriel <arend@broadcom.com>
----
-
---- a/drivers/net/wireless/brcm80211/brcmfmac/bcdc.c
-+++ b/drivers/net/wireless/brcm80211/brcmfmac/bcdc.c
-@@ -276,6 +276,7 @@ brcmf_proto_bcdc_hdrpull(struct brcmf_pu
- struct sk_buff *pktbuf)
- {
- struct brcmf_proto_bcdc_header *h;
-+ struct brcmf_if *ifp;
-
- brcmf_dbg(BCDC, "Enter\n");
-
-@@ -289,30 +290,21 @@ brcmf_proto_bcdc_hdrpull(struct brcmf_pu
- trace_brcmf_bcdchdr(pktbuf->data);
- h = (struct brcmf_proto_bcdc_header *)(pktbuf->data);
-
-- *ifidx = BCDC_GET_IF_IDX(h);
-- if (*ifidx >= BRCMF_MAX_IFS) {
-- brcmf_err("rx data ifnum out of range (%d)\n", *ifidx);
-+ ifp = brcmf_get_ifp(drvr, BCDC_GET_IF_IDX(h));
-+ if (IS_ERR_OR_NULL(ifp)) {
-+ brcmf_dbg(INFO, "no matching ifp found\n");
- return -EBADE;
- }
-- /* The ifidx is the idx to map to matching netdev/ifp. When receiving
-- * events this is easy because it contains the bssidx which maps
-- * 1-on-1 to the netdev/ifp. But for data frames the ifidx is rcvd.
-- * bssidx 1 is used for p2p0 and no data can be received or
-- * transmitted on it. Therefor bssidx is ifidx + 1 if ifidx > 0
-- */
-- if (*ifidx)
-- (*ifidx)++;
--
- if (((h->flags & BCDC_FLAG_VER_MASK) >> BCDC_FLAG_VER_SHIFT) !=
- BCDC_PROTO_VER) {
- brcmf_err("%s: non-BCDC packet received, flags 0x%x\n",
-- brcmf_ifname(drvr, *ifidx), h->flags);
-+ brcmf_ifname(drvr, ifp->ifidx), h->flags);
- return -EBADE;
- }
-
- if (h->flags & BCDC_FLAG_SUM_GOOD) {
- brcmf_dbg(BCDC, "%s: BDC rcv, good checksum, flags 0x%x\n",
-- brcmf_ifname(drvr, *ifidx), h->flags);
-+ brcmf_ifname(drvr, ifp->ifidx), h->flags);
- pktbuf->ip_summed = CHECKSUM_UNNECESSARY;
- }
-
-@@ -320,12 +312,15 @@ brcmf_proto_bcdc_hdrpull(struct brcmf_pu
-
- skb_pull(pktbuf, BCDC_HEADER_LEN);
- if (do_fws)
-- brcmf_fws_hdrpull(drvr, *ifidx, h->data_offset << 2, pktbuf);
-+ brcmf_fws_hdrpull(drvr, ifp->ifidx, h->data_offset << 2,
-+ pktbuf);
- else
- skb_pull(pktbuf, h->data_offset << 2);
-
- if (pktbuf->len == 0)
- return -ENODATA;
-+
-+ *ifidx = ifp->ifidx;
- return 0;
- }
-
---- a/drivers/net/wireless/brcm80211/brcmfmac/core.c
-+++ b/drivers/net/wireless/brcm80211/brcmfmac/core.c
-@@ -83,6 +83,25 @@ char *brcmf_ifname(struct brcmf_pub *drv
- return "<if_none>";
- }
-
-+struct brcmf_if *brcmf_get_ifp(struct brcmf_pub *drvr, int ifidx)
-+{
-+ if (ifidx < 0 || ifidx >= BRCMF_MAX_IFS) {
-+ brcmf_err("ifidx %d out of range\n", ifidx);
-+ return ERR_PTR(-ERANGE);
-+ }
-+
-+ /* The ifidx is the idx to map to matching netdev/ifp. When receiving
-+ * events this is easy because it contains the bssidx which maps
-+ * 1-on-1 to the netdev/ifp. But for data frames the ifidx is rcvd.
-+ * bssidx 1 is used for p2p0 and no data can be received or
-+ * transmitted on it. Therefor bssidx is ifidx + 1 if ifidx > 0
-+ */
-+ if (ifidx)
-+ ifidx++;
-+
-+ return drvr->iflist[ifidx];
-+}
-+
- static void _brcmf_set_multicast_list(struct work_struct *work)
- {
- struct brcmf_if *ifp;
---- a/drivers/net/wireless/brcm80211/brcmfmac/core.h
-+++ b/drivers/net/wireless/brcm80211/brcmfmac/core.h
-@@ -202,7 +202,7 @@ int brcmf_netdev_wait_pend8021x(struct b
-
- /* Return pointer to interface name */
- char *brcmf_ifname(struct brcmf_pub *drvr, int idx);
--
-+struct brcmf_if *brcmf_get_ifp(struct brcmf_pub *drvr, int ifidx);
- int brcmf_net_attach(struct brcmf_if *ifp, bool rtnl_locked);
- struct brcmf_if *brcmf_add_if(struct brcmf_pub *drvr, s32 bssidx, s32 ifidx,
- char *name, u8 *mac_addr);
---- a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c
-+++ b/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c
-@@ -1081,16 +1081,8 @@ brcmf_msgbuf_rx_skb(struct brcmf_msgbuf
- {
- struct brcmf_if *ifp;
-
-- /* The ifidx is the idx to map to matching netdev/ifp. When receiving
-- * events this is easy because it contains the bssidx which maps
-- * 1-on-1 to the netdev/ifp. But for data frames the ifidx is rcvd.
-- * bssidx 1 is used for p2p0 and no data can be received or
-- * transmitted on it. Therefor bssidx is ifidx + 1 if ifidx > 0
-- */
-- if (ifidx)
-- (ifidx)++;
-- ifp = msgbuf->drvr->iflist[ifidx];
-- if (!ifp || !ifp->ndev) {
-+ ifp = brcmf_get_ifp(msgbuf->drvr, ifidx);
-+ if (IS_ERR_OR_NULL(ifp) || !ifp->ndev) {
- brcmf_err("Received pkt for invalid ifidx %d\n", ifidx);
- brcmu_pkt_buf_free_skb(skb);
- return;
+++ /dev/null
-From: Arend van Spriel <arend@broadcom.com>
-Date: Wed, 26 Aug 2015 22:14:54 +0200
-Subject: [PATCH] brcmfmac: make brcmf_proto_hdrpull() return struct
- brcmf_if instance
-
-Avoid spreading the ifidx in the driver, but have it return the
-struct brcmf_if instance.
-
-Reviewed-by: Hante Meuleman <meuleman@broadcom.com>
-Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com>
-Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
-Signed-off-by: Arend van Spriel <arend@broadcom.com>
----
-
---- a/drivers/net/wireless/brcm80211/brcmfmac/bcdc.c
-+++ b/drivers/net/wireless/brcm80211/brcmfmac/bcdc.c
-@@ -272,11 +272,11 @@ brcmf_proto_bcdc_hdrpush(struct brcmf_pu
- }
-
- static int
--brcmf_proto_bcdc_hdrpull(struct brcmf_pub *drvr, bool do_fws, u8 *ifidx,
-- struct sk_buff *pktbuf)
-+brcmf_proto_bcdc_hdrpull(struct brcmf_pub *drvr, bool do_fws,
-+ struct sk_buff *pktbuf, struct brcmf_if **ifp)
- {
- struct brcmf_proto_bcdc_header *h;
-- struct brcmf_if *ifp;
-+ struct brcmf_if *tmp_if;
-
- brcmf_dbg(BCDC, "Enter\n");
-
-@@ -290,21 +290,21 @@ brcmf_proto_bcdc_hdrpull(struct brcmf_pu
- trace_brcmf_bcdchdr(pktbuf->data);
- h = (struct brcmf_proto_bcdc_header *)(pktbuf->data);
-
-- ifp = brcmf_get_ifp(drvr, BCDC_GET_IF_IDX(h));
-- if (IS_ERR_OR_NULL(ifp)) {
-+ tmp_if = brcmf_get_ifp(drvr, BCDC_GET_IF_IDX(h));
-+ if (!tmp_if) {
- brcmf_dbg(INFO, "no matching ifp found\n");
- return -EBADE;
- }
- if (((h->flags & BCDC_FLAG_VER_MASK) >> BCDC_FLAG_VER_SHIFT) !=
- BCDC_PROTO_VER) {
- brcmf_err("%s: non-BCDC packet received, flags 0x%x\n",
-- brcmf_ifname(drvr, ifp->ifidx), h->flags);
-+ brcmf_ifname(drvr, tmp_if->ifidx), h->flags);
- return -EBADE;
- }
-
- if (h->flags & BCDC_FLAG_SUM_GOOD) {
- brcmf_dbg(BCDC, "%s: BDC rcv, good checksum, flags 0x%x\n",
-- brcmf_ifname(drvr, ifp->ifidx), h->flags);
-+ brcmf_ifname(drvr, tmp_if->ifidx), h->flags);
- pktbuf->ip_summed = CHECKSUM_UNNECESSARY;
- }
-
-@@ -312,7 +312,7 @@ brcmf_proto_bcdc_hdrpull(struct brcmf_pu
-
- skb_pull(pktbuf, BCDC_HEADER_LEN);
- if (do_fws)
-- brcmf_fws_hdrpull(drvr, ifp->ifidx, h->data_offset << 2,
-+ brcmf_fws_hdrpull(drvr, tmp_if->ifidx, h->data_offset << 2,
- pktbuf);
- else
- skb_pull(pktbuf, h->data_offset << 2);
-@@ -320,7 +320,7 @@ brcmf_proto_bcdc_hdrpull(struct brcmf_pu
- if (pktbuf->len == 0)
- return -ENODATA;
-
-- *ifidx = ifp->ifidx;
-+ *ifp = tmp_if;
- return 0;
- }
-
---- a/drivers/net/wireless/brcm80211/brcmfmac/core.c
-+++ b/drivers/net/wireless/brcm80211/brcmfmac/core.c
-@@ -87,7 +87,7 @@ struct brcmf_if *brcmf_get_ifp(struct br
- {
- if (ifidx < 0 || ifidx >= BRCMF_MAX_IFS) {
- brcmf_err("ifidx %d out of range\n", ifidx);
-- return ERR_PTR(-ERANGE);
-+ return NULL;
- }
-
- /* The ifidx is the idx to map to matching netdev/ifp. When receiving
-@@ -539,17 +539,15 @@ void brcmf_rx_frame(struct device *dev,
- struct brcmf_bus *bus_if = dev_get_drvdata(dev);
- struct brcmf_pub *drvr = bus_if->drvr;
- struct brcmf_skb_reorder_data *rd;
-- u8 ifidx;
- int ret;
-
- brcmf_dbg(DATA, "Enter: %s: rxp=%p\n", dev_name(dev), skb);
-
- /* process and remove protocol-specific header */
-- ret = brcmf_proto_hdrpull(drvr, true, &ifidx, skb);
-- ifp = drvr->iflist[ifidx];
-+ ret = brcmf_proto_hdrpull(drvr, true, skb, &ifp);
-
- if (ret || !ifp || !ifp->ndev) {
-- if ((ret != -ENODATA) && ifp)
-+ if (ret != -ENODATA && ifp)
- ifp->stats.rx_errors++;
- brcmu_pkt_buf_free_skb(skb);
- return;
-@@ -592,17 +590,17 @@ void brcmf_txcomplete(struct device *dev
- {
- struct brcmf_bus *bus_if = dev_get_drvdata(dev);
- struct brcmf_pub *drvr = bus_if->drvr;
-- u8 ifidx;
-+ struct brcmf_if *ifp;
-
- /* await txstatus signal for firmware if active */
- if (brcmf_fws_fc_active(drvr->fws)) {
- if (!success)
- brcmf_fws_bustxfail(drvr->fws, txp);
- } else {
-- if (brcmf_proto_hdrpull(drvr, false, &ifidx, txp))
-+ if (brcmf_proto_hdrpull(drvr, false, txp, &ifp))
- brcmu_pkt_buf_free_skb(txp);
- else
-- brcmf_txfinalize(drvr, txp, ifidx, success);
-+ brcmf_txfinalize(drvr, txp, ifp->ifidx, success);
- }
- }
-
---- a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c
-+++ b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c
-@@ -1448,7 +1448,7 @@ brcmf_fws_txs_process(struct brcmf_fws_i
- struct sk_buff *skb;
- struct brcmf_skbuff_cb *skcb;
- struct brcmf_fws_mac_descriptor *entry = NULL;
-- u8 ifidx;
-+ struct brcmf_if *ifp;
-
- brcmf_dbg(DATA, "flags %d\n", flags);
-
-@@ -1497,15 +1497,16 @@ brcmf_fws_txs_process(struct brcmf_fws_i
- }
- brcmf_fws_macdesc_return_req_credit(skb);
-
-- if (brcmf_proto_hdrpull(fws->drvr, false, &ifidx, skb)) {
-+ ret = brcmf_proto_hdrpull(fws->drvr, false, skb, &ifp);
-+ if (ret) {
- brcmu_pkt_buf_free_skb(skb);
- return -EINVAL;
- }
- if (!remove_from_hanger)
-- ret = brcmf_fws_txstatus_suppressed(fws, fifo, skb, ifidx,
-+ ret = brcmf_fws_txstatus_suppressed(fws, fifo, skb, ifp->ifidx,
- genbit, seq);
- if (remove_from_hanger || ret)
-- brcmf_txfinalize(fws->drvr, skb, ifidx, true);
-+ brcmf_txfinalize(fws->drvr, skb, ifp->ifidx, true);
-
- return 0;
- }
-@@ -1848,7 +1849,7 @@ static int brcmf_fws_commit_skb(struct b
- entry->transit_count--;
- if (entry->suppressed)
- entry->suppr_transit_count--;
-- brcmf_proto_hdrpull(fws->drvr, false, &ifidx, skb);
-+ (void)brcmf_proto_hdrpull(fws->drvr, false, skb, NULL);
- goto rollback;
- }
-
---- a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c
-+++ b/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c
-@@ -522,7 +522,7 @@ static int brcmf_msgbuf_set_dcmd(struct
-
-
- static int brcmf_msgbuf_hdrpull(struct brcmf_pub *drvr, bool do_fws,
-- u8 *ifidx, struct sk_buff *skb)
-+ struct sk_buff *skb, struct brcmf_if **ifp)
- {
- return -ENODEV;
- }
-@@ -1082,7 +1082,7 @@ brcmf_msgbuf_rx_skb(struct brcmf_msgbuf
- struct brcmf_if *ifp;
-
- ifp = brcmf_get_ifp(msgbuf->drvr, ifidx);
-- if (IS_ERR_OR_NULL(ifp) || !ifp->ndev) {
-+ if (!ifp || !ifp->ndev) {
- brcmf_err("Received pkt for invalid ifidx %d\n", ifidx);
- brcmu_pkt_buf_free_skb(skb);
- return;
---- a/drivers/net/wireless/brcm80211/brcmfmac/proto.h
-+++ b/drivers/net/wireless/brcm80211/brcmfmac/proto.h
-@@ -24,8 +24,8 @@ enum proto_addr_mode {
-
-
- struct brcmf_proto {
-- int (*hdrpull)(struct brcmf_pub *drvr, bool do_fws, u8 *ifidx,
-- struct sk_buff *skb);
-+ int (*hdrpull)(struct brcmf_pub *drvr, bool do_fws,
-+ struct sk_buff *skb, struct brcmf_if **ifp);
- int (*query_dcmd)(struct brcmf_pub *drvr, int ifidx, uint cmd,
- void *buf, uint len);
- int (*set_dcmd)(struct brcmf_pub *drvr, int ifidx, uint cmd, void *buf,
-@@ -46,9 +46,19 @@ int brcmf_proto_attach(struct brcmf_pub
- void brcmf_proto_detach(struct brcmf_pub *drvr);
-
- static inline int brcmf_proto_hdrpull(struct brcmf_pub *drvr, bool do_fws,
-- u8 *ifidx, struct sk_buff *skb)
-+ struct sk_buff *skb,
-+ struct brcmf_if **ifp)
- {
-- return drvr->proto->hdrpull(drvr, do_fws, ifidx, skb);
-+ struct brcmf_if *tmp = NULL;
-+
-+ /* assure protocol is always called with
-+ * non-null initialized pointer.
-+ */
-+ if (ifp)
-+ *ifp = NULL;
-+ else
-+ ifp = &tmp;
-+ return drvr->proto->hdrpull(drvr, do_fws, skb, ifp);
- }
- static inline int brcmf_proto_query_dcmd(struct brcmf_pub *drvr, int ifidx,
- uint cmd, void *buf, uint len)
+++ /dev/null
-From: Arend van Spriel <arend@broadcom.com>
-Date: Wed, 26 Aug 2015 22:14:55 +0200
-Subject: [PATCH] brcmfmac: change parameters for
- brcmf_remove_interface()
-
-Just pass the interface to be removed, ie. the struct brcmf_if instance.
-
-Reviewed-by: Hante Meuleman <meuleman@broadcom.com>
-Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com>
-Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
-Signed-off-by: Arend van Spriel <arend@broadcom.com>
----
-
---- a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c
-+++ b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c
-@@ -4982,7 +4982,7 @@ brcmf_notify_connect_status_ap(struct br
- brcmf_dbg(CONN, "AP mode link down\n");
- complete(&cfg->vif_disabled);
- if (ifp->vif->mbss)
-- brcmf_remove_interface(ifp->drvr, ifp->bssidx);
-+ brcmf_remove_interface(ifp);
- return 0;
- }
-
---- a/drivers/net/wireless/brcm80211/brcmfmac/core.c
-+++ b/drivers/net/wireless/brcm80211/brcmfmac/core.c
-@@ -887,12 +887,13 @@ static void brcmf_del_if(struct brcmf_pu
- }
- }
-
--void brcmf_remove_interface(struct brcmf_pub *drvr, u32 bssidx)
-+void brcmf_remove_interface(struct brcmf_if *ifp)
- {
-- if (drvr->iflist[bssidx]) {
-- brcmf_fws_del_interface(drvr->iflist[bssidx]);
-- brcmf_del_if(drvr, bssidx);
-- }
-+ if (!ifp || WARN_ON(ifp->drvr->iflist[ifp->bssidx] != ifp))
-+ return;
-+
-+ brcmf_fws_del_interface(ifp);
-+ brcmf_del_if(ifp->drvr, ifp->bssidx);
- }
-
- int brcmf_get_next_free_bsscfgidx(struct brcmf_pub *drvr)
-@@ -1122,7 +1123,7 @@ void brcmf_detach(struct device *dev)
-
- /* make sure primary interface removed last */
- for (i = BRCMF_MAX_IFS-1; i > -1; i--)
-- brcmf_remove_interface(drvr, i);
-+ brcmf_remove_interface(drvr->iflist[i]);
-
- brcmf_cfg80211_detach(drvr->config);
-
---- a/drivers/net/wireless/brcm80211/brcmfmac/core.h
-+++ b/drivers/net/wireless/brcm80211/brcmfmac/core.h
-@@ -206,7 +206,7 @@ struct brcmf_if *brcmf_get_ifp(struct br
- int brcmf_net_attach(struct brcmf_if *ifp, bool rtnl_locked);
- struct brcmf_if *brcmf_add_if(struct brcmf_pub *drvr, s32 bssidx, s32 ifidx,
- char *name, u8 *mac_addr);
--void brcmf_remove_interface(struct brcmf_pub *drvr, u32 bssidx);
-+void brcmf_remove_interface(struct brcmf_if *ifp);
- int brcmf_get_next_free_bsscfgidx(struct brcmf_pub *drvr);
- void brcmf_txflowblock_if(struct brcmf_if *ifp,
- enum brcmf_netif_stop_reason reason, bool state);
---- a/drivers/net/wireless/brcm80211/brcmfmac/fweh.c
-+++ b/drivers/net/wireless/brcm80211/brcmfmac/fweh.c
-@@ -222,7 +222,7 @@ static void brcmf_fweh_handle_if_event(s
- err = brcmf_fweh_call_event_handler(ifp, emsg->event_code, emsg, data);
-
- if (ifp && ifevent->action == BRCMF_E_IF_DEL)
-- brcmf_remove_interface(drvr, ifevent->bssidx);
-+ brcmf_remove_interface(ifp);
- }
-
- /**
---- a/drivers/net/wireless/brcm80211/brcmfmac/p2p.c
-+++ b/drivers/net/wireless/brcm80211/brcmfmac/p2p.c
-@@ -2140,7 +2140,7 @@ static void brcmf_p2p_delete_p2pdev(stru
- {
- cfg80211_unregister_wdev(&vif->wdev);
- p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif = NULL;
-- brcmf_remove_interface(vif->ifp->drvr, vif->ifp->bssidx);
-+ brcmf_remove_interface(vif->ifp);
- brcmf_free_vif(vif);
- }
-
+++ /dev/null
-From: Arend van Spriel <arend@broadcom.com>
-Date: Wed, 26 Aug 2015 22:14:56 +0200
-Subject: [PATCH] brcmfmac: only call brcmf_cfg80211_detach() when attach
- was successful
-
-In brcmf_bus_start() the function brcmf_cfg80211_attach() is called which
-may fail. If this happens we should not call brcmf_cfg80211_detach() in
-the failure path as it will result in NULL pointer dereference:
-
- brcmf_fweh_activate_events: Set event_msgs error (-5)
- brcmf_bus_start: failed: -5
- brcmf_sdio_firmware_callback: dongle is not responding
- BUG: unable to handle kernel NULL pointer dereference at 0000000000000068
- IP: [<ffffffff811e8f08>] kernfs_find_ns+0x18/0xd0
- PGD 0
- Oops: 0000 [#1] SMP
- Modules linked in: brcmfmac(O) brcmutil(O) cfg80211 auth_rpcgss
- CPU: 1 PID: 45 Comm: kworker/1:1 Tainted: G O
- Hardware name: Dell Inc. Latitude E6410/07XJP9, BIOS A07 02/15/2011
- Workqueue: events request_firmware_work_func
- task: ffff880036c09ac0 ti: ffff880036dd4000 task.ti: ffff880036dd4000
- RIP: 0010:[<ffffffff811e8f08>] [<ffffffff811e8f08>] kernfs_find_ns+0x18/0xd0
- RSP: 0018:ffff880036dd7a28 EFLAGS: 00010246
- RAX: ffff880036c09ac0 RBX: 0000000000000000 RCX: 000000007fffffff
- RDX: 0000000000000000 RSI: ffffffff816578b9 RDI: 0000000000000000
- RBP: ffff880036dd7a48 R08: 0000000000000000 R09: ffff880036c0b340
- R10: 00000000000002ec R11: ffff880036dd7b08 R12: ffffffff816578b9
- R13: 0000000000000000 R14: ffffffff816578b9 R15: ffff8800c6c87000
- FS: 0000000000000000(0000) GS:ffff88012bc40000(0000) knlGS:0000000000000000
- CS: 0010 DS: 0000 ES: 0000 CR0: 000000008005003b
- CR2: 0000000000000068 CR3: 0000000001a0b000 CR4: 00000000000006e0
- Stack:
- 0000000000000000 ffffffff816578b9 0000000000000000 ffff8800c0d003c8
- ffff880036dd7a78 ffffffff811e8ff5 0000000ffffffff1 ffffffff81a9b060
- ffff8800c789f880 ffff8800c0d00000 ffff880036dd7a98 ffffffff811ebe0d
- Call Trace:
- [<ffffffff811e8ff5>] kernfs_find_and_get_ns+0x35/0x60
- [<ffffffff811ebe0d>] sysfs_unmerge_group+0x1d/0x60
- [<ffffffff81404ef2>] dpm_sysfs_remove+0x22/0x60
- [<ffffffff813f9db9>] device_del+0x49/0x240
- [<ffffffff815da768>] rfkill_unregister+0x58/0xc0
- [<ffffffffa06bd91b>] wiphy_unregister+0xab/0x2f0 [cfg80211]
- [<ffffffffa0742fe3>] brcmf_cfg80211_detach+0x23/0x50 [brcmfmac]
- [<ffffffffa074d986>] brcmf_detach+0x86/0xe0 [brcmfmac]
- [<ffffffffa0757de8>] brcmf_sdio_remove+0x48/0x120 [brcmfmac]
- [<ffffffffa0758ed9>] brcmf_sdiod_remove+0x29/0xd0 [brcmfmac]
- [<ffffffffa0759031>] brcmf_ops_sdio_remove+0xb1/0x110 [brcmfmac]
- [<ffffffffa001c267>] sdio_bus_remove+0x37/0x100 [mmc_core]
- [<ffffffff813fe026>] __device_release_driver+0x96/0x130
- [<ffffffff813fe0e3>] device_release_driver+0x23/0x30
- [<ffffffffa0754bc8>] brcmf_sdio_firmware_callback+0x2a8/0x5d0 [brcmfmac]
- [<ffffffffa074deaf>] brcmf_fw_request_nvram_done+0x15f/0x5e0 [brcmfmac]
- [<ffffffff8140142f>] ? devres_add+0x3f/0x50
- [<ffffffff810642b5>] ? usermodehelper_read_unlock+0x15/0x20
- [<ffffffff81400000>] ? platform_match+0x70/0xa0
- [<ffffffff8140f400>] request_firmware_work_func+0x30/0x60
- [<ffffffff8106828c>] process_one_work+0x14c/0x3d0
- [<ffffffff8106862a>] worker_thread+0x11a/0x450
- [<ffffffff81068510>] ? process_one_work+0x3d0/0x3d0
- [<ffffffff8106d692>] kthread+0xd2/0xf0
- [<ffffffff8106d5c0>] ? kthread_create_on_node+0x180/0x180
- [<ffffffff815ed35f>] ret_from_fork+0x3f/0x70
- [<ffffffff8106d5c0>] ? kthread_create_on_node+0x180/0x180
- Code: e9 40 fe ff ff 48 89 d8 eb 87 66 0f 1f 84 00 00 00 00 00 66 66 66 66
- 90 55 48 89 e5 41 56 49 89 f6 41 55 49 89 d5 31 d2 41 54 53 <0f> b7
- 47 68 48 8b 5f 48 66 c1 e8 05 83 e0 01 4d 85 ed 0f b6 c8
- RIP [<ffffffff811e8f08>] kernfs_find_ns+0x18/0xd0
- RSP <ffff880036dd7a28>
- CR2: 0000000000000068
- ---[ end trace 87d6ec0d3fe46740 ]---
-
-Reported-by: Daniel (Deognyoun) Kim <dekim@broadcom.com>
-Reviewed-by: Hante Meuleman <meuleman@broadcom.com>
-Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com>
-Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
-Signed-off-by: Arend van Spriel <arend@broadcom.com>
----
-
---- a/drivers/net/wireless/brcm80211/brcmfmac/core.c
-+++ b/drivers/net/wireless/brcm80211/brcmfmac/core.c
-@@ -1049,7 +1049,10 @@ int brcmf_bus_start(struct device *dev)
- fail:
- if (ret < 0) {
- brcmf_err("failed: %d\n", ret);
-- brcmf_cfg80211_detach(drvr->config);
-+ if (drvr->config) {
-+ brcmf_cfg80211_detach(drvr->config);
-+ drvr->config = NULL;
-+ }
- if (drvr->fws) {
- brcmf_fws_del_interface(ifp);
- brcmf_fws_deinit(drvr);
+++ /dev/null
-From: Arend van Spriel <arend@broadcom.com>
-Date: Wed, 26 Aug 2015 22:14:57 +0200
-Subject: [PATCH] brcmfmac: correct detection of p2pdev interface event
-
-The p2pdev interface is setup in firmware resulting in a interface
-event. This event has role and no-if flag. When role is p2p client
-and no-if flag is set it indicates that this is the p2pdev interface.
-This info is used in handling the event and adding interface in the
-driver.
-
-Reviewed-by: Hante Meuleman <meuleman@broadcom.com>
-Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com>
-Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
-Signed-off-by: Arend van Spriel <arend@broadcom.com>
----
-
---- a/drivers/net/wireless/brcm80211/brcmfmac/core.c
-+++ b/drivers/net/wireless/brcm80211/brcmfmac/core.c
-@@ -795,7 +795,7 @@ fail:
- }
-
- struct brcmf_if *brcmf_add_if(struct brcmf_pub *drvr, s32 bssidx, s32 ifidx,
-- char *name, u8 *mac_addr)
-+ bool is_p2pdev, char *name, u8 *mac_addr)
- {
- struct brcmf_if *ifp;
- struct net_device *ndev;
-@@ -821,7 +821,7 @@ struct brcmf_if *brcmf_add_if(struct brc
- }
- }
-
-- if (!brcmf_p2p_enable && bssidx == 1) {
-+ if (!brcmf_p2p_enable && is_p2pdev) {
- /* this is P2P_DEVICE interface */
- brcmf_dbg(INFO, "allocate non-netdev interface\n");
- ifp = kzalloc(sizeof(*ifp), GFP_KERNEL);
-@@ -999,12 +999,12 @@ int brcmf_bus_start(struct device *dev)
- brcmf_dbg(TRACE, "\n");
-
- /* add primary networking interface */
-- ifp = brcmf_add_if(drvr, 0, 0, "wlan%d", NULL);
-+ ifp = brcmf_add_if(drvr, 0, 0, false, "wlan%d", NULL);
- if (IS_ERR(ifp))
- return PTR_ERR(ifp);
-
- if (brcmf_p2p_enable)
-- p2p_ifp = brcmf_add_if(drvr, 1, 0, "p2p%d", NULL);
-+ p2p_ifp = brcmf_add_if(drvr, 1, 0, false, "p2p%d", NULL);
- else
- p2p_ifp = NULL;
- if (IS_ERR(p2p_ifp))
---- a/drivers/net/wireless/brcm80211/brcmfmac/core.h
-+++ b/drivers/net/wireless/brcm80211/brcmfmac/core.h
-@@ -205,7 +205,7 @@ char *brcmf_ifname(struct brcmf_pub *drv
- struct brcmf_if *brcmf_get_ifp(struct brcmf_pub *drvr, int ifidx);
- int brcmf_net_attach(struct brcmf_if *ifp, bool rtnl_locked);
- struct brcmf_if *brcmf_add_if(struct brcmf_pub *drvr, s32 bssidx, s32 ifidx,
-- char *name, u8 *mac_addr);
-+ bool is_p2pdev, char *name, u8 *mac_addr);
- void brcmf_remove_interface(struct brcmf_if *ifp);
- int brcmf_get_next_free_bsscfgidx(struct brcmf_pub *drvr);
- void brcmf_txflowblock_if(struct brcmf_if *ifp,
---- a/drivers/net/wireless/brcm80211/brcmfmac/fweh.c
-+++ b/drivers/net/wireless/brcm80211/brcmfmac/fweh.c
-@@ -179,6 +179,7 @@ static void brcmf_fweh_handle_if_event(s
- {
- struct brcmf_if_event *ifevent = data;
- struct brcmf_if *ifp;
-+ bool is_p2pdev;
- int err = 0;
-
- brcmf_dbg(EVENT, "action: %u idx: %u bsscfg: %u flags: %u role: %u\n",
-@@ -186,18 +187,16 @@ static void brcmf_fweh_handle_if_event(s
- ifevent->flags, ifevent->role);
-
- /* The P2P Device interface event must not be ignored
-- * contrary to what firmware tells us. The only way to
-- * distinguish the P2P Device is by looking at the ifidx
-- * and bssidx received.
-+ * contrary to what firmware tells us.
- */
-- if (!(ifevent->ifidx == 0 && ifevent->bssidx == 1) &&
-- (ifevent->flags & BRCMF_E_IF_FLAG_NOIF)) {
-+ is_p2pdev = (ifevent->flags & BRCMF_E_IF_FLAG_NOIF) &&
-+ ifevent->role == BRCMF_E_IF_ROLE_P2P_CLIENT;
-+ if (!is_p2pdev && (ifevent->flags & BRCMF_E_IF_FLAG_NOIF)) {
- brcmf_dbg(EVENT, "event can be ignored\n");
- return;
- }
- if (ifevent->ifidx >= BRCMF_MAX_IFS) {
-- brcmf_err("invalid interface index: %u\n",
-- ifevent->ifidx);
-+ brcmf_err("invalid interface index: %u\n", ifevent->ifidx);
- return;
- }
-
-@@ -207,7 +206,7 @@ static void brcmf_fweh_handle_if_event(s
- brcmf_dbg(EVENT, "adding %s (%pM)\n", emsg->ifname,
- emsg->addr);
- ifp = brcmf_add_if(drvr, ifevent->bssidx, ifevent->ifidx,
-- emsg->ifname, emsg->addr);
-+ is_p2pdev, emsg->ifname, emsg->addr);
- if (IS_ERR(ifp))
- return;
- brcmf_fws_add_interface(ifp);
+++ /dev/null
-From: Arend van Spriel <arend@broadcom.com>
-Date: Wed, 26 Aug 2015 22:14:58 +0200
-Subject: [PATCH] brcmfmac: use brcmf_get_ifp() to map ifidx to struct
- brcmf_if instance
-
-The knowledge on how to map the interface index to a struct brcmf_if
-instance is in brcmf_get_ifp() so use that function when only the
-interface index is known instead of accessing brcmf_pub::iflist
-directly.
-
-Reviewed-by: Hante Meuleman <meuleman@broadcom.com>
-Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com>
-Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
-Signed-off-by: Arend van Spriel <arend@broadcom.com>
----
-
---- a/drivers/net/wireless/brcm80211/brcmfmac/btcoex.c
-+++ b/drivers/net/wireless/brcm80211/brcmfmac/btcoex.c
-@@ -149,7 +149,7 @@ static s32 brcmf_btcoex_params_read(stru
- static void brcmf_btcoex_boost_wifi(struct brcmf_btcoex_info *btci,
- bool trump_sco)
- {
-- struct brcmf_if *ifp = btci->cfg->pub->iflist[0];
-+ struct brcmf_if *ifp = brcmf_get_ifp(btci->cfg->pub, 0);
-
- if (trump_sco && !btci->saved_regs_part2) {
- /* this should reduce eSCO agressive
-@@ -468,7 +468,7 @@ int brcmf_btcoex_set_mode(struct brcmf_c
- {
- struct brcmf_cfg80211_info *cfg = wiphy_priv(vif->wdev.wiphy);
- struct brcmf_btcoex_info *btci = cfg->btcoex;
-- struct brcmf_if *ifp = cfg->pub->iflist[0];
-+ struct brcmf_if *ifp = brcmf_get_ifp(cfg->pub, 0);
-
- switch (mode) {
- case BRCMF_BTCOEX_DISABLED:
---- a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c
-+++ b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c
-@@ -6212,7 +6212,7 @@ static void brcmf_free_wiphy(struct wiph
- struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr,
- struct device *busdev)
- {
-- struct net_device *ndev = drvr->iflist[0]->ndev;
-+ struct net_device *ndev = brcmf_get_ifp(drvr, 0)->ndev;
- struct brcmf_cfg80211_info *cfg;
- struct wiphy *wiphy;
- struct brcmf_cfg80211_vif *vif;
---- a/drivers/net/wireless/brcm80211/brcmfmac/feature.c
-+++ b/drivers/net/wireless/brcm80211/brcmfmac/feature.c
-@@ -121,7 +121,7 @@ static void brcmf_feat_iovar_int_set(str
-
- void brcmf_feat_attach(struct brcmf_pub *drvr)
- {
-- struct brcmf_if *ifp = drvr->iflist[0];
-+ struct brcmf_if *ifp = brcmf_get_ifp(drvr, 0);
-
- brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_MCHAN, "mchan");
- brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_PNO, "pfn");
---- a/drivers/net/wireless/brcm80211/brcmfmac/flowring.c
-+++ b/drivers/net/wireless/brcm80211/brcmfmac/flowring.c
-@@ -221,7 +221,7 @@ static void brcmf_flowring_block(struct
-
- bus_if = dev_get_drvdata(flow->dev);
- drvr = bus_if->drvr;
-- ifp = drvr->iflist[ifidx];
-+ ifp = brcmf_get_ifp(drvr, ifidx);
- brcmf_txflowblock_if(ifp, BRCMF_NETIF_STOP_REASON_FLOW, blocked);
-
- spin_unlock_irqrestore(&flow->block_lock, flags);
---- a/drivers/net/wireless/brcm80211/brcmfmac/fweh.c
-+++ b/drivers/net/wireless/brcm80211/brcmfmac/fweh.c
-@@ -334,7 +334,7 @@ void brcmf_fweh_attach(struct brcmf_pub
- void brcmf_fweh_detach(struct brcmf_pub *drvr)
- {
- struct brcmf_fweh_info *fweh = &drvr->fweh;
-- struct brcmf_if *ifp = drvr->iflist[0];
-+ struct brcmf_if *ifp = brcmf_get_ifp(drvr, 0);
- s8 eventmask[BRCMF_EVENTING_MASK_LEN];
-
- if (ifp) {
---- a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c
-+++ b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c
-@@ -972,7 +972,7 @@ static void
- brcmf_fws_flow_control_check(struct brcmf_fws_info *fws, struct pktq *pq,
- u8 if_id)
- {
-- struct brcmf_if *ifp = fws->drvr->iflist[!if_id ? 0 : if_id + 1];
-+ struct brcmf_if *ifp = brcmf_get_ifp(fws->drvr, if_id);
-
- if (WARN_ON(!ifp))
- return;
-@@ -2118,6 +2118,7 @@ static int brcmf_debugfs_fws_stats_read(
- int brcmf_fws_init(struct brcmf_pub *drvr)
- {
- struct brcmf_fws_info *fws;
-+ struct brcmf_if *ifp;
- u32 tlv = BRCMF_FWS_FLAGS_RSSI_SIGNALS;
- int rc;
- u32 mode;
-@@ -2177,21 +2178,22 @@ int brcmf_fws_init(struct brcmf_pub *drv
- * continue. Set mode back to none indicating not enabled.
- */
- fws->fw_signals = true;
-- if (brcmf_fil_iovar_int_set(drvr->iflist[0], "tlv", tlv)) {
-+ ifp = brcmf_get_ifp(drvr, 0);
-+ if (brcmf_fil_iovar_int_set(ifp, "tlv", tlv)) {
- brcmf_err("failed to set bdcv2 tlv signaling\n");
- fws->fcmode = BRCMF_FWS_FCMODE_NONE;
- fws->fw_signals = false;
- }
-
-- if (brcmf_fil_iovar_int_set(drvr->iflist[0], "ampdu_hostreorder", 1))
-+ if (brcmf_fil_iovar_int_set(ifp, "ampdu_hostreorder", 1))
- brcmf_dbg(INFO, "enabling AMPDU host-reorder failed\n");
-
- /* Enable seq number reuse, if supported */
-- if (brcmf_fil_iovar_int_get(drvr->iflist[0], "wlfc_mode", &mode) == 0) {
-+ if (brcmf_fil_iovar_int_get(ifp, "wlfc_mode", &mode) == 0) {
- if (BRCMF_FWS_MODE_GET_REUSESEQ(mode)) {
- mode = 0;
- BRCMF_FWS_MODE_SET_REUSESEQ(mode, 1);
-- if (brcmf_fil_iovar_int_set(drvr->iflist[0],
-+ if (brcmf_fil_iovar_int_set(ifp,
- "wlfc_mode", mode) == 0) {
- BRCMF_FWS_MODE_SET_REUSESEQ(fws->mode, 1);
- }
+++ /dev/null
-From: Arend van Spriel <arend@broadcom.com>
-Date: Wed, 26 Aug 2015 22:14:59 +0200
-Subject: [PATCH] brcmfmac: pass struct brcmf_if instance in
- brcmf_txfinalize()
-
-Most call sites of brcmf_txfinalize already have struct brcmf_if
-instance so pass that to brcmf_txfinalize() as the function
-needs it anyway.
-
-Reviewed-by: Hante Meuleman <meuleman@broadcom.com>
-Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com>
-Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
-Signed-off-by: Arend van Spriel <arend@broadcom.com>
----
-
---- a/drivers/net/wireless/brcm80211/brcmfmac/core.c
-+++ b/drivers/net/wireless/brcm80211/brcmfmac/core.c
-@@ -560,17 +560,11 @@ void brcmf_rx_frame(struct device *dev,
- brcmf_netif_rx(ifp, skb);
- }
-
--void brcmf_txfinalize(struct brcmf_pub *drvr, struct sk_buff *txp, u8 ifidx,
-- bool success)
-+void brcmf_txfinalize(struct brcmf_if *ifp, struct sk_buff *txp, bool success)
- {
-- struct brcmf_if *ifp;
- struct ethhdr *eh;
- u16 type;
-
-- ifp = drvr->iflist[ifidx];
-- if (!ifp)
-- goto done;
--
- eh = (struct ethhdr *)(txp->data);
- type = ntohs(eh->h_proto);
-
-@@ -582,7 +576,7 @@ void brcmf_txfinalize(struct brcmf_pub *
-
- if (!success)
- ifp->stats.tx_errors++;
--done:
-+
- brcmu_pkt_buf_free_skb(txp);
- }
-
-@@ -600,7 +594,7 @@ void brcmf_txcomplete(struct device *dev
- if (brcmf_proto_hdrpull(drvr, false, txp, &ifp))
- brcmu_pkt_buf_free_skb(txp);
- else
-- brcmf_txfinalize(drvr, txp, ifp->ifidx, success);
-+ brcmf_txfinalize(ifp, txp, success);
- }
- }
-
---- a/drivers/net/wireless/brcm80211/brcmfmac/core.h
-+++ b/drivers/net/wireless/brcm80211/brcmfmac/core.h
-@@ -210,8 +210,7 @@ void brcmf_remove_interface(struct brcmf
- int brcmf_get_next_free_bsscfgidx(struct brcmf_pub *drvr);
- void brcmf_txflowblock_if(struct brcmf_if *ifp,
- enum brcmf_netif_stop_reason reason, bool state);
--void brcmf_txfinalize(struct brcmf_pub *drvr, struct sk_buff *txp, u8 ifidx,
-- bool success);
-+void brcmf_txfinalize(struct brcmf_if *ifp, struct sk_buff *txp, bool success);
- void brcmf_netif_rx(struct brcmf_if *ifp, struct sk_buff *skb);
-
- /* Sets dongle media info (drv_version, mac address). */
---- a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c
-+++ b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c
-@@ -1506,7 +1506,7 @@ brcmf_fws_txs_process(struct brcmf_fws_i
- ret = brcmf_fws_txstatus_suppressed(fws, fifo, skb, ifp->ifidx,
- genbit, seq);
- if (remove_from_hanger || ret)
-- brcmf_txfinalize(fws->drvr, skb, ifp->ifidx, true);
-+ brcmf_txfinalize(ifp, skb, true);
-
- return 0;
- }
-@@ -1905,7 +1905,7 @@ int brcmf_fws_process_skb(struct brcmf_i
- if (fws->avoid_queueing) {
- rc = brcmf_proto_txdata(drvr, ifp->ifidx, 0, skb);
- if (rc < 0)
-- brcmf_txfinalize(drvr, skb, ifp->ifidx, false);
-+ brcmf_txfinalize(ifp, skb, false);
- return rc;
- }
-
-@@ -1929,7 +1929,7 @@ int brcmf_fws_process_skb(struct brcmf_i
- brcmf_fws_schedule_deq(fws);
- } else {
- brcmf_err("drop skb: no hanger slot\n");
-- brcmf_txfinalize(drvr, skb, ifp->ifidx, false);
-+ brcmf_txfinalize(ifp, skb, false);
- rc = -ENOMEM;
- }
- brcmf_fws_unlock(fws);
-@@ -2009,8 +2009,9 @@ static void brcmf_fws_dequeue_worker(str
- ret = brcmf_proto_txdata(drvr, ifidx, 0, skb);
- brcmf_fws_lock(fws);
- if (ret < 0)
-- brcmf_txfinalize(drvr, skb, ifidx,
-- false);
-+ brcmf_txfinalize(brcmf_get_ifp(drvr,
-+ ifidx),
-+ skb, false);
- if (fws->bus_flow_blocked)
- break;
- }
---- a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c
-+++ b/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c
-@@ -873,7 +873,11 @@ brcmf_msgbuf_process_txstatus(struct brc
- commonring = msgbuf->flowrings[flowid];
- atomic_dec(&commonring->outstanding_tx);
-
-- brcmf_txfinalize(msgbuf->drvr, skb, tx_status->msg.ifidx, true);
-+ /* Hante: i believe this was a bug as tx_status->msg.ifidx was used
-+ * in brcmf_txfinalize as index in drvr->iflist. Can you confirm/deny?
-+ */
-+ brcmf_txfinalize(brcmf_get_ifp(msgbuf->drvr, tx_status->msg.ifidx),
-+ skb, true);
- }
-
-
+++ /dev/null
-From: Arend van Spriel <arend@broadcom.com>
-Date: Wed, 26 Aug 2015 22:15:00 +0200
-Subject: [PATCH] brcmfmac: add mapping for interface index to bsscfg
- index
-
-Because the P2P Device interface in firmware uses the same interface
-index as the primary interface we use the bsscfg index as index in the
-struct brcmf_pub::iflist. However, in the data path we get the interface
-index and not the bsscfg index. So we need a mapping of interface index
-to bsscfg index, which can be determined upon handle adding the interface.
-
-Reviewed-by: Hante Meuleman <meuleman@broadcom.com>
-Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com>
-Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
-Signed-off-by: Arend van Spriel <arend@broadcom.com>
----
-
---- a/drivers/net/wireless/brcm80211/brcmfmac/core.c
-+++ b/drivers/net/wireless/brcm80211/brcmfmac/core.c
-@@ -85,21 +85,20 @@ char *brcmf_ifname(struct brcmf_pub *drv
-
- struct brcmf_if *brcmf_get_ifp(struct brcmf_pub *drvr, int ifidx)
- {
-+ struct brcmf_if *ifp;
-+ s32 bssidx;
-+
- if (ifidx < 0 || ifidx >= BRCMF_MAX_IFS) {
- brcmf_err("ifidx %d out of range\n", ifidx);
- return NULL;
- }
-
-- /* The ifidx is the idx to map to matching netdev/ifp. When receiving
-- * events this is easy because it contains the bssidx which maps
-- * 1-on-1 to the netdev/ifp. But for data frames the ifidx is rcvd.
-- * bssidx 1 is used for p2p0 and no data can be received or
-- * transmitted on it. Therefor bssidx is ifidx + 1 if ifidx > 0
-- */
-- if (ifidx)
-- ifidx++;
-+ ifp = NULL;
-+ bssidx = drvr->if2bss[ifidx];
-+ if (bssidx >= 0)
-+ ifp = drvr->iflist[bssidx];
-
-- return drvr->iflist[ifidx];
-+ return ifp;
- }
-
- static void _brcmf_set_multicast_list(struct work_struct *work)
-@@ -831,6 +830,8 @@ struct brcmf_if *brcmf_add_if(struct brc
-
- ifp = netdev_priv(ndev);
- ifp->ndev = ndev;
-+ /* store mapping ifidx to bssidx */
-+ drvr->if2bss[ifidx] = bssidx;
- }
-
- ifp->drvr = drvr;
-@@ -855,6 +856,7 @@ static void brcmf_del_if(struct brcmf_pu
- struct brcmf_if *ifp;
-
- ifp = drvr->iflist[bssidx];
-+ drvr->if2bss[ifp->ifidx] = -1;
- drvr->iflist[bssidx] = NULL;
- if (!ifp) {
- brcmf_err("Null interface, idx=%d\n", bssidx);
-@@ -862,6 +864,7 @@ static void brcmf_del_if(struct brcmf_pu
- }
- brcmf_dbg(TRACE, "Enter, idx=%d, ifidx=%d\n", bssidx, ifp->ifidx);
- if (ifp->ndev) {
-+ drvr->if2bss[ifp->ifidx] = -1;
- if (bssidx == 0) {
- if (ifp->ndev->netdev_ops == &brcmf_netdev_ops_pri) {
- rtnl_lock();
-@@ -926,6 +929,7 @@ int brcmf_attach(struct device *dev)
- if (!drvr)
- return -ENOMEM;
-
-+ memset(drvr->if2bss, 0xFF, sizeof(drvr->if2bss));
- mutex_init(&drvr->proto_block);
-
- /* Link to bus module */
---- a/drivers/net/wireless/brcm80211/brcmfmac/core.h
-+++ b/drivers/net/wireless/brcm80211/brcmfmac/core.h
-@@ -122,6 +122,7 @@ struct brcmf_pub {
- struct mac_address addresses[BRCMF_MAX_IFS];
-
- struct brcmf_if *iflist[BRCMF_MAX_IFS];
-+ s32 if2bss[BRCMF_MAX_IFS];
-
- struct mutex proto_block;
- unsigned char proto_buf[BRCMF_DCMD_MAXLEN];
+++ /dev/null
-From: Arend van Spriel <arend@broadcom.com>
-Date: Wed, 26 Aug 2015 22:15:01 +0200
-Subject: [PATCH] brcmfmac: add dedicated debug level for firmware
- console logging
-
-Both PCIe and SDIO devices have the possibility to log the firmware
-console output in kernel log. For PCIe it is logged when PCIE debug
-level is enabled. For SDIO it is logged when user specifies a non-zero
-console interval through debugfs. This patch tries to make it a
-bit more consistent. The firmware console output is only logged when
-FWCON debug level is enabled.
-
-Reviewed-by: Hante Meuleman <meuleman@broadcom.com>
-Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com>
-Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
-Reviewed-by: Pontus Fuchs <pontusf@broadcom.com>
-Signed-off-by: Arend van Spriel <arend@broadcom.com>
----
-
---- a/drivers/net/wireless/brcm80211/brcmfmac/debug.h
-+++ b/drivers/net/wireless/brcm80211/brcmfmac/debug.h
-@@ -37,6 +37,7 @@
- #define BRCMF_SDIO_VAL 0x00020000
- #define BRCMF_MSGBUF_VAL 0x00040000
- #define BRCMF_PCIE_VAL 0x00080000
-+#define BRCMF_FWCON_VAL 0x00100000
-
- /* set default print format */
- #undef pr_fmt
-@@ -78,6 +79,7 @@ do { \
- #define BRCMF_GLOM_ON() (brcmf_msg_level & BRCMF_GLOM_VAL)
- #define BRCMF_EVENT_ON() (brcmf_msg_level & BRCMF_EVENT_VAL)
- #define BRCMF_FIL_ON() (brcmf_msg_level & BRCMF_FIL_VAL)
-+#define BRCMF_FWCON_ON() (brcmf_msg_level & BRCMF_FWCON_VAL)
-
- #else /* defined(DEBUG) || defined(CPTCFG_BRCM_TRACING) */
-
-@@ -90,6 +92,7 @@ do { \
- #define BRCMF_GLOM_ON() 0
- #define BRCMF_EVENT_ON() 0
- #define BRCMF_FIL_ON() 0
-+#define BRCMF_FWCON_ON() 0
-
- #endif /* defined(DEBUG) || defined(CPTCFG_BRCM_TRACING) */
-
---- a/drivers/net/wireless/brcm80211/brcmfmac/pcie.c
-+++ b/drivers/net/wireless/brcm80211/brcmfmac/pcie.c
-@@ -644,7 +644,7 @@ static void brcmf_pcie_bus_console_init(
- addr = console->base_addr + BRCMF_CONSOLE_BUFSIZE_OFFSET;
- console->bufsize = brcmf_pcie_read_tcm32(devinfo, addr);
-
-- brcmf_dbg(PCIE, "Console: base %x, buf %x, size %d\n",
-+ brcmf_dbg(FWCON, "Console: base %x, buf %x, size %d\n",
- console->base_addr, console->buf_addr, console->bufsize);
- }
-
-@@ -656,6 +656,9 @@ static void brcmf_pcie_bus_console_read(
- u8 ch;
- u32 newidx;
-
-+ if (!BRCMF_FWCON_ON())
-+ return;
-+
- console = &devinfo->shared.console;
- addr = console->base_addr + BRCMF_CONSOLE_WRITEIDX_OFFSET;
- newidx = brcmf_pcie_read_tcm32(devinfo, addr);
-@@ -677,7 +680,7 @@ static void brcmf_pcie_bus_console_read(
- }
- if (ch == '\n') {
- console->log_str[console->log_idx] = 0;
-- brcmf_dbg(PCIE, "CONSOLE: %s", console->log_str);
-+ pr_debug("CONSOLE: %s", console->log_str);
- console->log_idx = 0;
- }
- }
---- a/drivers/net/wireless/brcm80211/brcmfmac/sdio.c
-+++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c
-@@ -123,6 +123,7 @@ struct rte_console {
-
- #define BRCMF_FIRSTREAD (1 << 6)
-
-+#define BRCMF_CONSOLE 10 /* watchdog interval to poll console */
-
- /* SBSDIO_DEVICE_CTL */
-
-@@ -3204,6 +3205,8 @@ static void brcmf_sdio_debugfs_create(st
- if (IS_ERR_OR_NULL(dentry))
- return;
-
-+ bus->console_interval = BRCMF_CONSOLE;
-+
- brcmf_debugfs_add_entry(drvr, "forensics", brcmf_sdio_forensic_read);
- brcmf_debugfs_add_entry(drvr, "counters",
- brcmf_debugfs_sdio_count_read);
-@@ -3613,7 +3616,7 @@ static void brcmf_sdio_bus_watchdog(stru
- }
- #ifdef DEBUG
- /* Poll for console output periodically */
-- if (bus->sdiodev->state == BRCMF_SDIOD_DATA &&
-+ if (bus->sdiodev->state == BRCMF_SDIOD_DATA && BRCMF_FWCON_ON() &&
- bus->console_interval != 0) {
- bus->console.count += BRCMF_WD_POLL_MS;
- if (bus->console.count >= bus->console_interval) {
+++ /dev/null
-From: Arend van Spriel <arend@broadcom.com>
-Date: Wed, 26 Aug 2015 22:15:02 +0200
-Subject: [PATCH] brcmfmac: remove ifidx parameter from
- brcmf_fws_txstatus_suppressed()
-
-The brcmf_fws_txstatus_suppressed() function prototype specifies an
-ifidx parameter which is not used within the function implementation.
-
-Reviewed-by: Hante Meuleman <meuleman@broadcom.com>
-Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com>
-Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
-Signed-off-by: Arend van Spriel <arend@broadcom.com>
----
-
---- a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c
-+++ b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c
-@@ -1398,7 +1398,7 @@ done:
- }
-
- static int brcmf_fws_txstatus_suppressed(struct brcmf_fws_info *fws, int fifo,
-- struct sk_buff *skb, u8 ifidx,
-+ struct sk_buff *skb,
- u32 genbit, u16 seq)
- {
- struct brcmf_fws_mac_descriptor *entry = brcmf_skbcb(skb)->mac;
-@@ -1503,7 +1503,7 @@ brcmf_fws_txs_process(struct brcmf_fws_i
- return -EINVAL;
- }
- if (!remove_from_hanger)
-- ret = brcmf_fws_txstatus_suppressed(fws, fifo, skb, ifp->ifidx,
-+ ret = brcmf_fws_txstatus_suppressed(fws, fifo, skb,
- genbit, seq);
- if (remove_from_hanger || ret)
- brcmf_txfinalize(ifp, skb, true);
+++ /dev/null
-From: Arend van Spriel <arend@broadcom.com>
-Date: Wed, 26 Aug 2015 22:15:03 +0200
-Subject: [PATCH] brcmfmac: change prototype for brcmf_fws_hdrpull()
-
-Instead of passing ifidx and drvr just pass struct brcmf_if pointer
-which holds both parameters.
-
-Reviewed-by: Hante Meuleman <meuleman@broadcom.com>
-Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com>
-Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
-Signed-off-by: Arend van Spriel <arend@broadcom.com>
----
-
---- a/drivers/net/wireless/brcm80211/brcmfmac/bcdc.c
-+++ b/drivers/net/wireless/brcm80211/brcmfmac/bcdc.c
-@@ -312,8 +312,7 @@ brcmf_proto_bcdc_hdrpull(struct brcmf_pu
-
- skb_pull(pktbuf, BCDC_HEADER_LEN);
- if (do_fws)
-- brcmf_fws_hdrpull(drvr, tmp_if->ifidx, h->data_offset << 2,
-- pktbuf);
-+ brcmf_fws_hdrpull(tmp_if, h->data_offset << 2, pktbuf);
- else
- skb_pull(pktbuf, h->data_offset << 2);
-
---- a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c
-+++ b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c
-@@ -1616,11 +1616,10 @@ static int brcmf_fws_notify_bcmc_credit_
- return 0;
- }
-
--int brcmf_fws_hdrpull(struct brcmf_pub *drvr, int ifidx, s16 signal_len,
-- struct sk_buff *skb)
-+void brcmf_fws_hdrpull(struct brcmf_if *ifp, s16 siglen, struct sk_buff *skb)
- {
- struct brcmf_skb_reorder_data *rd;
-- struct brcmf_fws_info *fws = drvr->fws;
-+ struct brcmf_fws_info *fws = ifp->drvr->fws;
- u8 *signal_data;
- s16 data_len;
- u8 type;
-@@ -1630,20 +1629,20 @@ int brcmf_fws_hdrpull(struct brcmf_pub *
- s32 err;
-
- brcmf_dbg(HDRS, "enter: ifidx %d, skblen %u, sig %d\n",
-- ifidx, skb->len, signal_len);
-+ ifp->ifidx, skb->len, siglen);
-
-- WARN_ON(signal_len > skb->len);
-+ WARN_ON(siglen > skb->len);
-
-- if (!signal_len)
-- return 0;
-+ if (!siglen)
-+ return;
- /* if flow control disabled, skip to packet data and leave */
- if ((!fws) || (!fws->fw_signals)) {
-- skb_pull(skb, signal_len);
-- return 0;
-+ skb_pull(skb, siglen);
-+ return;
- }
-
- fws->stats.header_pulls++;
-- data_len = signal_len;
-+ data_len = siglen;
- signal_data = skb->data;
-
- status = BRCMF_FWS_RET_OK_NOSCHEDULE;
-@@ -1731,14 +1730,12 @@ int brcmf_fws_hdrpull(struct brcmf_pub *
- /* signalling processing result does
- * not affect the actual ethernet packet.
- */
-- skb_pull(skb, signal_len);
-+ skb_pull(skb, siglen);
-
- /* this may be a signal-only packet
- */
- if (skb->len == 0)
- fws->stats.header_only_pkt++;
--
-- return 0;
- }
-
- static u8 brcmf_fws_precommit_skb(struct brcmf_fws_info *fws, int fifo,
---- a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.h
-+++ b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.h
-@@ -21,8 +21,7 @@
- int brcmf_fws_init(struct brcmf_pub *drvr);
- void brcmf_fws_deinit(struct brcmf_pub *drvr);
- bool brcmf_fws_fc_active(struct brcmf_fws_info *fws);
--int brcmf_fws_hdrpull(struct brcmf_pub *drvr, int ifidx, s16 signal_len,
-- struct sk_buff *skb);
-+void brcmf_fws_hdrpull(struct brcmf_if *ifp, s16 siglen, struct sk_buff *skb);
- int brcmf_fws_process_skb(struct brcmf_if *ifp, struct sk_buff *skb);
-
- void brcmf_fws_reset_interface(struct brcmf_if *ifp);
+++ /dev/null
-From: Arend van Spriel <arend@broadcom.com>
-Date: Wed, 26 Aug 2015 22:15:04 +0200
-Subject: [PATCH] brcmfmac: introduce brcmf_net_detach() function
-
-In case of error during brcmf_bus_start() the network interfaces were
-freed using free_netdev(). However, the interfaces may have additional
-memory allocated which is not freed. The netdev has destructor set to
-brcmf_cfg80211_free_netdev() which frees the additional memory if
-allocated and call free_netdev(). The brcmf_net_detach() either calls
-brcmf_cfg80211_free_netdev() directly or uses unregister_netdev() when
-struct net_device::reg_state indicates the netdev was registered.
-
-Reported-by: Daniel (Deognyoun) Kim <dekim@broadcom.com>
-Reviewed-by: Hante Meuleman <meuleman@broadcom.com>
-Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com>
-Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
-Signed-off-by: Arend van Spriel <arend@broadcom.com>
----
-
---- a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c
-+++ b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c
-@@ -4746,7 +4746,8 @@ void brcmf_cfg80211_free_netdev(struct n
- ifp = netdev_priv(ndev);
- vif = ifp->vif;
-
-- brcmf_free_vif(vif);
-+ if (vif)
-+ brcmf_free_vif(vif);
- free_netdev(ndev);
- }
-
---- a/drivers/net/wireless/brcm80211/brcmfmac/core.c
-+++ b/drivers/net/wireless/brcm80211/brcmfmac/core.c
-@@ -718,8 +718,6 @@ int brcmf_net_attach(struct brcmf_if *if
- }
-
- brcmf_dbg(INFO, "%s: Broadcom Dongle Host Driver\n", ndev->name);
--
-- ndev->destructor = brcmf_cfg80211_free_netdev;
- return 0;
-
- fail:
-@@ -729,6 +727,14 @@ fail:
- return -EBADE;
- }
-
-+static void brcmf_net_detach(struct net_device *ndev)
-+{
-+ if (ndev->reg_state == NETREG_REGISTERED)
-+ unregister_netdev(ndev);
-+ else
-+ brcmf_cfg80211_free_netdev(ndev);
-+}
-+
- static int brcmf_net_p2p_open(struct net_device *ndev)
- {
- brcmf_dbg(TRACE, "Enter\n");
-@@ -805,8 +811,7 @@ struct brcmf_if *brcmf_add_if(struct brc
- ifp->ndev->name);
- if (ifidx) {
- netif_stop_queue(ifp->ndev);
-- unregister_netdev(ifp->ndev);
-- free_netdev(ifp->ndev);
-+ brcmf_net_detach(ifp->ndev);
- drvr->iflist[bssidx] = NULL;
- } else {
- brcmf_err("ignore IF event\n");
-@@ -828,6 +833,7 @@ struct brcmf_if *brcmf_add_if(struct brc
- if (!ndev)
- return ERR_PTR(-ENOMEM);
-
-+ ndev->destructor = brcmf_cfg80211_free_netdev;
- ifp = netdev_priv(ndev);
- ifp->ndev = ndev;
- /* store mapping ifidx to bssidx */
-@@ -879,8 +885,7 @@ static void brcmf_del_if(struct brcmf_pu
- cancel_work_sync(&ifp->setmacaddr_work);
- cancel_work_sync(&ifp->multicast_work);
- }
-- /* unregister will take care of freeing it */
-- unregister_netdev(ifp->ndev);
-+ brcmf_net_detach(ifp->ndev);
- }
- }
-
-@@ -1056,11 +1061,11 @@ fail:
- brcmf_fws_deinit(drvr);
- }
- if (drvr->iflist[0]) {
-- free_netdev(ifp->ndev);
-+ brcmf_net_detach(ifp->ndev);
- drvr->iflist[0] = NULL;
- }
- if (p2p_ifp) {
-- free_netdev(p2p_ifp->ndev);
-+ brcmf_net_detach(p2p_ifp->ndev);
- drvr->iflist[1] = NULL;
- }
- return ret;
+++ /dev/null
-From: Hante Meuleman <meuleman@broadcom.com>
-Date: Thu, 27 Aug 2015 16:14:06 +0200
-Subject: [PATCH] brcmfmac: Reset PCIE devices after recognition.
-
-When PCIE type devices are being FW reloaded without being properly
-reset then the device ends up in a locked state, requiring the
-device to be completely powered down. This patch adds a reset
-through watchdog at the moment the device (cores) has been
-recognized. This will solve warm reboot issues.
-
-Cc: Rafal Milecki <zajec5@gmail.com>
-Reviewed-by: Arend Van Spriel <arend@broadcom.com>
-Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com>
-Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
-Signed-off-by: Hante Meuleman <meuleman@broadcom.com>
-Signed-off-by: Arend van Spriel <arend@broadcom.com>
----
-
---- a/drivers/net/wireless/brcm80211/brcmfmac/chip.c
-+++ b/drivers/net/wireless/brcm80211/brcmfmac/chip.c
-@@ -101,6 +101,9 @@
- /* ARM Cortex M3 core, ID 0x82a */
- #define BCM4329_CORE_ARM_BASE 0x18002000
-
-+/* Max possibly supported memory size (limited by IO mapped memory) */
-+#define BRCMF_CHIP_MAX_MEMSIZE (4 * 1024 * 1024)
-+
- #define CORE_SB(base, field) \
- (base + SBCONFIGOFF + offsetof(struct sbconfig, field))
- #define SBCOREREV(sbidh) \
-@@ -687,6 +690,12 @@ static int brcmf_chip_get_raminfo(struct
- brcmf_err("RAM size is undetermined\n");
- return -ENOMEM;
- }
-+
-+ if (ci->pub.ramsize > BRCMF_CHIP_MAX_MEMSIZE) {
-+ brcmf_err("RAM size is incorrect\n");
-+ return -ENOMEM;
-+ }
-+
- return 0;
- }
-
-@@ -899,6 +908,15 @@ static int brcmf_chip_recognition(struct
-
- /* assure chip is passive for core access */
- brcmf_chip_set_passive(&ci->pub);
-+
-+ /* Call bus specific reset function now. Cores have been determined
-+ * but further access may require a chip specific reset at this point.
-+ */
-+ if (ci->ops->reset) {
-+ ci->ops->reset(ci->ctx, &ci->pub);
-+ brcmf_chip_set_passive(&ci->pub);
-+ }
-+
- return brcmf_chip_get_raminfo(ci);
- }
-
---- a/drivers/net/wireless/brcm80211/brcmfmac/chip.h
-+++ b/drivers/net/wireless/brcm80211/brcmfmac/chip.h
-@@ -73,6 +73,7 @@ struct brcmf_buscore_ops {
- u32 (*read32)(void *ctx, u32 addr);
- void (*write32)(void *ctx, u32 addr, u32 value);
- int (*prepare)(void *ctx);
-+ int (*reset)(void *ctx, struct brcmf_chip *chip);
- int (*setup)(void *ctx, struct brcmf_chip *chip);
- void (*activate)(void *ctx, struct brcmf_chip *chip, u32 rstvec);
- };
---- a/drivers/net/wireless/brcm80211/brcmfmac/pcie.c
-+++ b/drivers/net/wireless/brcm80211/brcmfmac/pcie.c
-@@ -74,6 +74,8 @@ enum brcmf_pcie_state {
- #define BRCMF_PCIE_REG_INTMASK 0x94
- #define BRCMF_PCIE_REG_SBMBX 0x98
-
-+#define BRCMF_PCIE_REG_LINK_STATUS_CTRL 0xBC
-+
- #define BRCMF_PCIE_PCIE2REG_INTMASK 0x24
- #define BRCMF_PCIE_PCIE2REG_MAILBOXINT 0x48
- #define BRCMF_PCIE_PCIE2REG_MAILBOXMASK 0x4C
-@@ -466,6 +468,7 @@ brcmf_pcie_select_core(struct brcmf_pcie
-
- static void brcmf_pcie_reset_device(struct brcmf_pciedev_info *devinfo)
- {
-+ struct brcmf_core *core;
- u16 cfg_offset[] = { BRCMF_PCIE_CFGREG_STATUS_CMD,
- BRCMF_PCIE_CFGREG_PM_CSR,
- BRCMF_PCIE_CFGREG_MSI_CAP,
-@@ -484,32 +487,38 @@ static void brcmf_pcie_reset_device(stru
- if (!devinfo->ci)
- return;
-
-+ /* Disable ASPM */
- brcmf_pcie_select_core(devinfo, BCMA_CORE_PCIE2);
-- brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_CONFIGADDR,
-- BRCMF_PCIE_CFGREG_LINK_STATUS_CTRL);
-- lsc = brcmf_pcie_read_reg32(devinfo, BRCMF_PCIE_PCIE2REG_CONFIGDATA);
-+ pci_read_config_dword(devinfo->pdev, BRCMF_PCIE_REG_LINK_STATUS_CTRL,
-+ &lsc);
- val = lsc & (~BRCMF_PCIE_LINK_STATUS_CTRL_ASPM_ENAB);
-- brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_CONFIGDATA, val);
-+ pci_write_config_dword(devinfo->pdev, BRCMF_PCIE_REG_LINK_STATUS_CTRL,
-+ val);
-
-+ /* Watchdog reset */
- brcmf_pcie_select_core(devinfo, BCMA_CORE_CHIPCOMMON);
- WRITECC32(devinfo, watchdog, 4);
- msleep(100);
-
-+ /* Restore ASPM */
- brcmf_pcie_select_core(devinfo, BCMA_CORE_PCIE2);
-- brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_CONFIGADDR,
-- BRCMF_PCIE_CFGREG_LINK_STATUS_CTRL);
-- brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_CONFIGDATA, lsc);
-+ pci_write_config_dword(devinfo->pdev, BRCMF_PCIE_REG_LINK_STATUS_CTRL,
-+ lsc);
-
-- brcmf_pcie_select_core(devinfo, BCMA_CORE_PCIE2);
-- for (i = 0; i < ARRAY_SIZE(cfg_offset); i++) {
-- brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_CONFIGADDR,
-- cfg_offset[i]);
-- val = brcmf_pcie_read_reg32(devinfo,
-- BRCMF_PCIE_PCIE2REG_CONFIGDATA);
-- brcmf_dbg(PCIE, "config offset 0x%04x, value 0x%04x\n",
-- cfg_offset[i], val);
-- brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_CONFIGDATA,
-- val);
-+ core = brcmf_chip_get_core(devinfo->ci, BCMA_CORE_PCIE2);
-+ if (core->rev <= 13) {
-+ for (i = 0; i < ARRAY_SIZE(cfg_offset); i++) {
-+ brcmf_pcie_write_reg32(devinfo,
-+ BRCMF_PCIE_PCIE2REG_CONFIGADDR,
-+ cfg_offset[i]);
-+ val = brcmf_pcie_read_reg32(devinfo,
-+ BRCMF_PCIE_PCIE2REG_CONFIGDATA);
-+ brcmf_dbg(PCIE, "config offset 0x%04x, value 0x%04x\n",
-+ cfg_offset[i], val);
-+ brcmf_pcie_write_reg32(devinfo,
-+ BRCMF_PCIE_PCIE2REG_CONFIGDATA,
-+ val);
-+ }
- }
- }
-
-@@ -519,8 +528,6 @@ static void brcmf_pcie_attach(struct brc
- u32 config;
-
- brcmf_pcie_select_core(devinfo, BCMA_CORE_PCIE2);
-- if (brcmf_pcie_read_reg32(devinfo, BRCMF_PCIE_PCIE2REG_INTMASK) != 0)
-- brcmf_pcie_reset_device(devinfo);
- /* BAR1 window may not be sized properly */
- brcmf_pcie_select_core(devinfo, BCMA_CORE_PCIE2);
- brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_CONFIGADDR, 0x4e0);
-@@ -1636,6 +1643,23 @@ static int brcmf_pcie_buscoreprep(void *
- }
-
-
-+static int brcmf_pcie_buscore_reset(void *ctx, struct brcmf_chip *chip)
-+{
-+ struct brcmf_pciedev_info *devinfo = (struct brcmf_pciedev_info *)ctx;
-+ u32 val;
-+
-+ devinfo->ci = chip;
-+ brcmf_pcie_reset_device(devinfo);
-+
-+ val = brcmf_pcie_read_reg32(devinfo, BRCMF_PCIE_PCIE2REG_MAILBOXINT);
-+ if (val != 0xffffffff)
-+ brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_MAILBOXINT,
-+ val);
-+
-+ return 0;
-+}
-+
-+
- static void brcmf_pcie_buscore_activate(void *ctx, struct brcmf_chip *chip,
- u32 rstvec)
- {
-@@ -1647,6 +1671,7 @@ static void brcmf_pcie_buscore_activate(
-
- static const struct brcmf_buscore_ops brcmf_pcie_buscore_ops = {
- .prepare = brcmf_pcie_buscoreprep,
-+ .reset = brcmf_pcie_buscore_reset,
- .activate = brcmf_pcie_buscore_activate,
- .read32 = brcmf_pcie_buscore_read32,
- .write32 = brcmf_pcie_buscore_write32,
-@@ -1814,7 +1839,6 @@ brcmf_pcie_remove(struct pci_dev *pdev)
- brcmf_pcie_intr_disable(devinfo);
-
- brcmf_detach(&pdev->dev);
-- brcmf_pcie_reset_device(devinfo);
-
- kfree(bus->bus_priv.pcie);
- kfree(bus->msgbuf->flowrings);
+++ /dev/null
-From: Felix Fietkau <nbd@openwrt.org>
-Date: Sun, 13 Sep 2015 22:26:10 +0200
-Subject: [PATCH] ath10k: fix DMA related firmware crashes on multiple devices
-
-Some platforms really don't like DMA bursts of 256 bytes, and this
-causes the firmware to crash when sending beacons.
-Also, changing this based on the firmware version does not seem to make
-much sense, so use 128 bytes for all versions.
-
-Cc: stable@vger.kernel.org
-Signed-off-by: Felix Fietkau <nbd@openwrt.org>
----
-
---- a/drivers/net/wireless/ath/ath10k/hw.h
-+++ b/drivers/net/wireless/ath/ath10k/hw.h
-@@ -253,7 +253,7 @@ struct ath10k_pktlog_hdr {
- #define TARGET_10X_MAX_FRAG_ENTRIES 0
-
- /* 10.2 parameters */
--#define TARGET_10_2_DMA_BURST_SIZE 1
-+#define TARGET_10_2_DMA_BURST_SIZE 0
-
- /* Target specific defines for WMI-TLV firmware */
- #define TARGET_TLV_NUM_VDEVS 3
+++ /dev/null
-From: Felix Fietkau <nbd@openwrt.org>
-Date: Thu, 24 Sep 2015 16:57:37 +0200
-Subject: [PATCH] ath9k: declare required extra tx headroom
-
-ath9k inserts padding between the 802.11 header and the data area (to
-align it). Since it didn't declare this extra required headroom, this
-led to some nasty issues like randomly dropped packets in some setups.
-
-Cc: stable@vger.kernel.org
-Signed-off-by: Felix Fietkau <nbd@openwrt.org>
----
-
---- a/drivers/net/wireless/ath/ath9k/init.c
-+++ b/drivers/net/wireless/ath/ath9k/init.c
-@@ -867,6 +867,7 @@ static void ath9k_set_hw_capab(struct at
- hw->max_rate_tries = 10;
- hw->sta_data_size = sizeof(struct ath_node);
- hw->vif_data_size = sizeof(struct ath_vif);
-+ hw->extra_tx_headroom = 4;
-
- hw->wiphy->available_antennas_rx = BIT(ah->caps.max_rxchains) - 1;
- hw->wiphy->available_antennas_tx = BIT(ah->caps.max_txchains) - 1;
+++ /dev/null
-From: Felix Fietkau <nbd@openwrt.org>
-Date: Mon, 5 Oct 2015 17:41:25 +0200
-Subject: [PATCH] mac80211: initialize tid field in struct ieee80211_txq
-
-Signed-off-by: Felix Fietkau <nbd@openwrt.org>
----
-
---- a/net/mac80211/util.c
-+++ b/net/mac80211/util.c
-@@ -3323,9 +3323,11 @@ void ieee80211_init_tx_queue(struct ieee
- if (sta) {
- txqi->txq.sta = &sta->sta;
- sta->sta.txq[tid] = &txqi->txq;
-+ txqi->txq.tid = tid;
- txqi->txq.ac = ieee802_1d_to_ac[tid & 7];
- } else {
- sdata->vif.txq = &txqi->txq;
-+ txqi->txq.tid = 0;
- txqi->txq.ac = IEEE80211_AC_BE;
- }
- }