aboutsummaryrefslogtreecommitdiffstats
path: root/package/kernel/mac80211
diff options
context:
space:
mode:
authorRafał Miłecki <zajec5@gmail.com>2015-06-21 12:07:33 +0000
committerRafał Miłecki <zajec5@gmail.com>2015-06-21 12:07:33 +0000
commit1909a42ad4ad297c4cee989c9e9dd5f99e37af4b (patch)
treef336d0da2c2f118185c8ffbcda4cf284b5ce2035 /package/kernel/mac80211
parent167a2dc12ec879763b3f176e290523914679ea5c (diff)
downloadupstream-1909a42ad4ad297c4cee989c9e9dd5f99e37af4b.tar.gz
upstream-1909a42ad4ad297c4cee989c9e9dd5f99e37af4b.tar.bz2
upstream-1909a42ad4ad297c4cee989c9e9dd5f99e37af4b.zip
mac80211: backport some brcmfmac patches
There are two important patches in this patchset: updating read pointer quicker & rework of .get_station(). There are few more upstream patches that are p2p-related and weren't backported in this commit. Signed-off-by: Rafał Miłecki <zajec5@gmail.com> SVN-Revision: 46084
Diffstat (limited to 'package/kernel/mac80211')
-rw-r--r--package/kernel/mac80211/patches/385-brcmfmac-Update-msgbuf-read-pointer-quicker.patch109
-rw-r--r--package/kernel/mac80211/patches/386-brcmfmac-remove-chipinfo-debugfs-entry.patch39
-rw-r--r--package/kernel/mac80211/patches/387-brcmfmac-remove-watchdog-reset-from-brcmf_pcie_busco.patch53
-rw-r--r--package/kernel/mac80211/patches/388-brcmfmac-use-debugfs_create_devm_seqfile-helper-func.patch69
-rw-r--r--package/kernel/mac80211/patches/389-brcmfmac-Check-if-firmware-supports-p2p.patch42
-rw-r--r--package/kernel/mac80211/patches/390-brcmfmac-Build-wiphy-mode-and-interface-combinations.patch198
-rw-r--r--package/kernel/mac80211/patches/391-brcmfmac-rework-.get_station-callback.patch326
-rw-r--r--package/kernel/mac80211/patches/392-brcmfmac-have-sdio-return-EIO-when-device-communicat.patch56
8 files changed, 892 insertions, 0 deletions
diff --git a/package/kernel/mac80211/patches/385-brcmfmac-Update-msgbuf-read-pointer-quicker.patch b/package/kernel/mac80211/patches/385-brcmfmac-Update-msgbuf-read-pointer-quicker.patch
new file mode 100644
index 0000000000..74df9f93f7
--- /dev/null
+++ b/package/kernel/mac80211/patches/385-brcmfmac-Update-msgbuf-read-pointer-quicker.patch
@@ -0,0 +1,109 @@
+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;
diff --git a/package/kernel/mac80211/patches/386-brcmfmac-remove-chipinfo-debugfs-entry.patch b/package/kernel/mac80211/patches/386-brcmfmac-remove-chipinfo-debugfs-entry.patch
new file mode 100644
index 0000000000..9e5b48608d
--- /dev/null
+++ b/package/kernel/mac80211/patches/386-brcmfmac-remove-chipinfo-debugfs-entry.patch
@@ -0,0 +1,39 @@
+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);
+ }
diff --git a/package/kernel/mac80211/patches/387-brcmfmac-remove-watchdog-reset-from-brcmf_pcie_busco.patch b/package/kernel/mac80211/patches/387-brcmfmac-remove-watchdog-reset-from-brcmf_pcie_busco.patch
new file mode 100644
index 0000000000..c38b2cd150
--- /dev/null
+++ b/package/kernel/mac80211/patches/387-brcmfmac-remove-watchdog-reset-from-brcmf_pcie_busco.patch
@@ -0,0 +1,53 @@
+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);
diff --git a/package/kernel/mac80211/patches/388-brcmfmac-use-debugfs_create_devm_seqfile-helper-func.patch b/package/kernel/mac80211/patches/388-brcmfmac-use-debugfs_create_devm_seqfile-helper-func.patch
new file mode 100644
index 0000000000..756fbb2cb7
--- /dev/null
+++ b/package/kernel/mac80211/patches/388-brcmfmac-use-debugfs_create_devm_seqfile-helper-func.patch
@@ -0,0 +1,69 @@
+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);
+ }
diff --git a/package/kernel/mac80211/patches/389-brcmfmac-Check-if-firmware-supports-p2p.patch b/package/kernel/mac80211/patches/389-brcmfmac-Check-if-firmware-supports-p2p.patch
new file mode 100644
index 0000000000..ff24a4a06f
--- /dev/null
+++ b/package/kernel/mac80211/patches/389-brcmfmac-Check-if-firmware-supports-p2p.patch
@@ -0,0 +1,42 @@
+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:
+ *
diff --git a/package/kernel/mac80211/patches/390-brcmfmac-Build-wiphy-mode-and-interface-combinations.patch b/package/kernel/mac80211/patches/390-brcmfmac-Build-wiphy-mode-and-interface-combinations.patch
new file mode 100644
index 0000000000..3876ba01da
--- /dev/null
+++ b/package/kernel/mac80211/patches/390-brcmfmac-Build-wiphy-mode-and-interface-combinations.patch
@@ -0,0 +1,198 @@
+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);
diff --git a/package/kernel/mac80211/patches/391-brcmfmac-rework-.get_station-callback.patch b/package/kernel/mac80211/patches/391-brcmfmac-rework-.get_station-callback.patch
new file mode 100644
index 0000000000..7bd06869da
--- /dev/null
+++ b/package/kernel/mac80211/patches/391-brcmfmac-rework-.get_station-callback.patch
@@ -0,0 +1,326 @@
+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 {
diff --git a/package/kernel/mac80211/patches/392-brcmfmac-have-sdio-return-EIO-when-device-communicat.patch b/package/kernel/mac80211/patches/392-brcmfmac-have-sdio-return-EIO-when-device-communicat.patch
new file mode 100644
index 0000000000..302bc3ed15
--- /dev/null
+++ b/package/kernel/mac80211/patches/392-brcmfmac-have-sdio-return-EIO-when-device-communicat.patch
@@ -0,0 +1,56 @@
+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);