--- /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;
--- a/drivers/net/wireless/brcm80211/brcmfmac/core.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/core.c
-@@ -1189,6 +1189,7 @@ static int __init brcmfmac_module_init(v
+@@ -1213,6 +1213,7 @@ static int __init brcmfmac_module_init(v
#endif
if (!schedule_work(&brcmf_driver_work))
return -EBUSY;
--- a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c
-@@ -661,8 +661,36 @@ static struct wireless_dev *brcmf_cfg80211_add_iface(struct wiphy *wiphy,
+@@ -691,9 +691,37 @@ static struct wireless_dev *brcmf_cfg802
u32 *flags,
struct vif_params *params)
{
+ struct net_device *dev;
struct wireless_dev *wdev;
+ int err;
+ /*
+ * There is a bug with in-firmware BSS management. When adding virtual
+ }
+
brcmf_dbg(TRACE, "enter: %s type %d\n", name, type);
- switch (type) {
- case NL80211_IFTYPE_ADHOC:
+ err = brcmf_vif_add_validate(wiphy_to_cfg(wiphy), type);
+ if (err) {
+++ /dev/null
-From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <zajec5@gmail.com>
-Date: Thu, 9 Jul 2015 16:53:30 +0200
-Subject: [PATCH] brcmfmac: set wiphy's addresses to provide valid MACs
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-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>
----
-
---- a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c
-+++ b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c
-@@ -5812,6 +5812,7 @@ static void brcmf_wiphy_wowl_params(struct wiphy *wiphy)
-
- 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;
-@@ -5825,6 +5826,19 @@ static int brcmf_setup_wiphy(struct wiphy *wiphy, struct brcmf_if *ifp)
- 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;