Merge commit '4e6ce4dc7ce71d0886908d55129d5d6482a27ff9' of git://git.kernel.org/pub...
authorJohn W. Linville <linville@tuxdriver.com>
Wed, 19 Nov 2014 20:38:48 +0000 (15:38 -0500)
committerJohn W. Linville <linville@tuxdriver.com>
Wed, 19 Nov 2014 20:38:48 +0000 (15:38 -0500)
22 files changed:
1  2 
drivers/bcma/main.c
drivers/net/wireless/ath/ath.h
drivers/net/wireless/ath/ath9k/ar9003_phy.c
drivers/net/wireless/ath/ath9k/common.c
drivers/net/wireless/ath/ath9k/debug.c
drivers/net/wireless/ath/ath9k/hw.c
drivers/net/wireless/ath/ath9k/init.c
drivers/net/wireless/ath/ath9k/main.c
drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c
drivers/net/wireless/brcm80211/brcmfmac/sdio.c
drivers/net/wireless/iwlwifi/mvm/mac80211.c
drivers/net/wireless/iwlwifi/mvm/mvm.h
drivers/net/wireless/iwlwifi/mvm/ops.c
drivers/net/wireless/iwlwifi/mvm/scan.c
drivers/net/wireless/iwlwifi/pcie/trans.c
drivers/net/wireless/mac80211_hwsim.c
drivers/net/wireless/mwifiex/main.h
drivers/net/wireless/rtlwifi/rtl8821ae/phy.c
net/mac80211/ieee80211_i.h
net/mac80211/iface.c
net/mac80211/mlme.c
net/mac80211/rx.c

diff --combined drivers/bcma/main.c
index 122086ef9fe1b78accfeffecb97ccbbcd35e4751,1000955ce09d870db23979e3950d75e32b75cfc1..534e1337766d2d0ba9d7e8d220a30434da23ef5d
@@@ -11,7 -11,6 +11,7 @@@
  #include <linux/bcma/bcma.h>
  #include <linux/slab.h>
  #include <linux/of_address.h>
 +#include <linux/of_irq.h>
  
  MODULE_DESCRIPTION("Broadcom's specific AMBA driver");
  MODULE_LICENSE("GPL");
@@@ -133,7 -132,7 +133,7 @@@ static bool bcma_is_core_needed_early(u
        return false;
  }
  
- #ifdef CONFIG_OF
+ #if defined(CONFIG_OF) && defined(CONFIG_OF_ADDRESS)
  static struct device_node *bcma_of_find_child_device(struct platform_device *parent,
                                                     struct bcma_device *core)
  {
        return NULL;
  }
  
 +static int bcma_of_irq_parse(struct platform_device *parent,
 +                           struct bcma_device *core,
 +                           struct of_phandle_args *out_irq, int num)
 +{
 +      __be32 laddr[1];
 +      int rc;
 +
 +      if (core->dev.of_node) {
 +              rc = of_irq_parse_one(core->dev.of_node, num, out_irq);
 +              if (!rc)
 +                      return rc;
 +      }
 +
 +      out_irq->np = parent->dev.of_node;
 +      out_irq->args_count = 1;
 +      out_irq->args[0] = num;
 +
 +      laddr[0] = cpu_to_be32(core->addr);
 +      return of_irq_parse_raw(laddr, out_irq);
 +}
 +
 +static unsigned int bcma_of_get_irq(struct platform_device *parent,
 +                                  struct bcma_device *core, int num)
 +{
 +      struct of_phandle_args out_irq;
 +      int ret;
 +
 +      if (!parent || !parent->dev.of_node)
 +              return 0;
 +
 +      ret = bcma_of_irq_parse(parent, core, &out_irq, num);
 +      if (ret) {
 +              bcma_debug(core->bus, "bcma_of_get_irq() failed with rc=%d\n",
 +                         ret);
 +              return 0;
 +      }
 +
 +      return irq_create_of_mapping(&out_irq);
 +}
 +
  static void bcma_of_fill_device(struct platform_device *parent,
                                struct bcma_device *core)
  {
        node = bcma_of_find_child_device(parent, core);
        if (node)
                core->dev.of_node = node;
 +
 +      core->irq = bcma_of_get_irq(parent, core, 0);
  }
  #else
  static void bcma_of_fill_device(struct platform_device *parent,
                                struct bcma_device *core)
  {
  }
 +static inline unsigned int bcma_of_get_irq(struct platform_device *parent,
 +                                         struct bcma_device *core, int num)
 +{
 +      return 0;
 +}
  #endif /* CONFIG_OF */
  
 -static void bcma_register_core(struct bcma_bus *bus, struct bcma_device *core)
 +unsigned int bcma_core_irq(struct bcma_device *core, int num)
  {
 -      int err;
 +      struct bcma_bus *bus = core->bus;
 +      unsigned int mips_irq;
 +
 +      switch (bus->hosttype) {
 +      case BCMA_HOSTTYPE_PCI:
 +              return bus->host_pci->irq;
 +      case BCMA_HOSTTYPE_SOC:
 +              if (bus->drv_mips.core && num == 0) {
 +                      mips_irq = bcma_core_mips_irq(core);
 +                      return mips_irq <= 4 ? mips_irq + 2 : 0;
 +              }
 +              if (bus->host_pdev)
 +                      return bcma_of_get_irq(bus->host_pdev, core, num);
 +              return 0;
 +      case BCMA_HOSTTYPE_SDIO:
 +              return 0;
 +      }
  
 +      return 0;
 +}
 +EXPORT_SYMBOL(bcma_core_irq);
 +
 +void bcma_prepare_core(struct bcma_bus *bus, struct bcma_device *core)
 +{
        core->dev.release = bcma_release_core_dev;
        core->dev.bus = &bcma_bus_type;
        dev_set_name(&core->dev, "bcma%d:%d", bus->num, core->core_index);
        case BCMA_HOSTTYPE_SDIO:
                break;
        }
 +}
 +
 +static void bcma_register_core(struct bcma_bus *bus, struct bcma_device *core)
 +{
 +      int err;
  
        err = device_register(&core->dev);
        if (err) {
index 76668dc79c0e4b444a2239925bed0ea0876d7587,86907e5ba6ca21a5e7e6d8ff8b6b20499e004cce..ccba4fea7269e35ecf2df7d95fb8b6af1aee507a
@@@ -80,6 -80,7 +80,7 @@@ struct reg_dmn_pair_mapping 
  
  struct ath_regulatory {
        char alpha2[2];
+       enum nl80211_dfs_regions region;
        u16 country_code;
        u16 max_power_level;
        u16 current_rd;
@@@ -134,11 -135,6 +135,11 @@@ struct ath_ops 
  struct ath_common;
  struct ath_bus_ops;
  
 +struct ath_ps_ops {
 +      void (*wakeup)(struct ath_common *common);
 +      void (*restore)(struct ath_common *common);
 +};
 +
  struct ath_common {
        void *ah;
        void *priv;
        u16 cachelsz;
        u16 curaid;
        u8 macaddr[ETH_ALEN];
 -      u8 curbssid[ETH_ALEN];
 +      u8 curbssid[ETH_ALEN] __aligned(2);
        u8 bssidmask[ETH_ALEN];
  
        u32 rx_bufsize;
        struct ath_regulatory reg_world_copy;
        const struct ath_ops *ops;
        const struct ath_bus_ops *bus_ops;
 +      const struct ath_ps_ops *ps_ops;
  
        bool btcoex_enabled;
        bool disable_ani;
        struct ieee80211_supported_band sbands[IEEE80211_NUM_BANDS];
  };
  
 +static inline const struct ath_ps_ops *ath_ps_ops(struct ath_common *common)
 +{
 +      return common->ps_ops;
 +}
 +
  struct sk_buff *ath_rxbuf_alloc(struct ath_common *common,
                                u32 len,
                                gfp_t gfp_mask);
index 9bdaa0afc37f7bed18594e7cab050e1467bfb279,1e8ea5e4d4ca71ecd7598aea30d5a76f4749e85a..2df6d2ee70c25283f3c03b3957e8e4f0fce6f618
@@@ -664,6 -664,19 +664,19 @@@ static void ar9003_hw_override_ini(stru
                ah->enabled_cals |= TX_CL_CAL;
        else
                ah->enabled_cals &= ~TX_CL_CAL;
+       if (AR_SREV_9340(ah) || AR_SREV_9531(ah) || AR_SREV_9550(ah)) {
+               if (ah->is_clk_25mhz) {
+                       REG_WRITE(ah, AR_RTC_DERIVED_CLK, 0x17c << 1);
+                       REG_WRITE(ah, AR_SLP32_MODE, 0x0010f3d7);
+                       REG_WRITE(ah, AR_SLP32_INC, 0x0001e7ae);
+               } else {
+                       REG_WRITE(ah, AR_RTC_DERIVED_CLK, 0x261 << 1);
+                       REG_WRITE(ah, AR_SLP32_MODE, 0x0010f400);
+                       REG_WRITE(ah, AR_SLP32_INC, 0x0001e800);
+               }
+               udelay(100);
+       }
  }
  
  static void ar9003_hw_prog_ini(struct ath_hw *ah,
@@@ -1348,7 -1361,7 +1361,7 @@@ static void ar9003_hw_set_radar_params(
                                       struct ath_hw_radar_conf *conf)
  {
        unsigned int regWrites = 0;
 -      u32 radar_0 = 0, radar_1 = 0;
 +      u32 radar_0 = 0, radar_1;
  
        if (!conf) {
                REG_CLR_BIT(ah, AR_PHY_RADAR_0, AR_PHY_RADAR_0_ENA);
        radar_0 |= SM(conf->pulse_rssi, AR_PHY_RADAR_0_PRSSI);
        radar_0 |= SM(conf->pulse_inband, AR_PHY_RADAR_0_INBAND);
  
 +      radar_1 = REG_READ(ah, AR_PHY_RADAR_1);
 +      radar_1 &= ~(AR_PHY_RADAR_1_MAXLEN | AR_PHY_RADAR_1_RELSTEP_THRESH |
 +                   AR_PHY_RADAR_1_RELPWR_THRESH);
        radar_1 |= AR_PHY_RADAR_1_MAX_RRSSI;
        radar_1 |= AR_PHY_RADAR_1_BLOCK_CHECK;
        radar_1 |= SM(conf->pulse_maxlen, AR_PHY_RADAR_1_MAXLEN);
@@@ -1391,7 -1401,7 +1404,7 @@@ static void ar9003_hw_set_radar_conf(st
        conf->fir_power = -28;
        conf->radar_rssi = 0;
        conf->pulse_height = 10;
 -      conf->pulse_rssi = 24;
 +      conf->pulse_rssi = 15;
        conf->pulse_inband = 8;
        conf->pulse_maxlen = 255;
        conf->pulse_inband_step = 12;
index eb62c58dd0f7ade60b018cc54207dd57ce47a121,33b0c7aef2ea39f527962f483a8014a052ac10ce..e8c699446470cb9e33dc5f407ebfc81b5197af1b
@@@ -159,7 -159,7 +159,7 @@@ void ath9k_cmn_rx_skb_postprocess(struc
                if (test_bit(keyix, common->keymap))
                        rxs->flag |= RX_FLAG_DECRYPTED;
        }
 -      if (ah->sw_mgmt_crypto &&
 +      if (ah->sw_mgmt_crypto_rx &&
            (rxs->flag & RX_FLAG_DECRYPTED) &&
            ieee80211_is_mgmt(fc))
                /* Use software decrypt for management frames. */
@@@ -368,11 -368,11 +368,11 @@@ void ath9k_cmn_update_txpow(struct ath_
  {
        struct ath_regulatory *reg = ath9k_hw_regulatory(ah);
  
-       if (reg->power_limit != new_txpow) {
+       if (reg->power_limit != new_txpow)
                ath9k_hw_set_txpowerlimit(ah, new_txpow, false);
-               /* read back in case value is clamped */
-               *txpower = reg->max_power_level;
-       }
+       /* read back in case value is clamped */
+       *txpower = reg->max_power_level;
  }
  EXPORT_SYMBOL(ath9k_cmn_update_txpow);
  
index 3f21b1bbc52e9efa4e2c019487e9d4269587b3e6,5c45e787814ed6a9b65f9f3e523626f282201ddb..696e3d5309c6bdfc5f890c6dd2189ceee28f51c1
@@@ -455,7 -455,7 +455,7 @@@ static ssize_t read_file_dma(struct fil
                         "%2d          %2x      %1x     %2x           %2x\n",
                         i, (*qcuBase & (0x7 << qcuOffset)) >> qcuOffset,
                         (*qcuBase & (0x8 << qcuOffset)) >> (qcuOffset + 3),
-                        val[2] & (0x7 << (i * 3)) >> (i * 3),
+                        (val[2] & (0x7 << (i * 3))) >> (i * 3),
                         (*dcuBase & (0x1f << dcuOffset)) >> dcuOffset);
        }
  
@@@ -828,14 -828,13 +828,14 @@@ static ssize_t read_file_misc(struct fi
  
        i = 0;
        ath_for_each_chanctx(sc, ctx) {
 -              if (!ctx->assigned || list_empty(&ctx->vifs))
 +              if (list_empty(&ctx->vifs))
                        continue;
                ath9k_calculate_iter_data(sc, ctx, &iter_data);
  
                len += scnprintf(buf + len, sizeof(buf) - len,
 -                      "VIF-COUNTS: CTX %i AP: %i STA: %i MESH: %i WDS: %i",
 -                      i++, iter_data.naps, iter_data.nstations,
 +                      "VIFS: CTX %i(%i) AP: %i STA: %i MESH: %i WDS: %i",
 +                      i++, (int)(ctx->assigned), iter_data.naps,
 +                      iter_data.nstations,
                        iter_data.nmeshes, iter_data.nwds);
                len += scnprintf(buf + len, sizeof(buf) - len,
                        " ADHOC: %i TOTAL: %hi BEACON-VIF: %hi\n",
@@@ -853,31 -852,36 +853,31 @@@ static ssize_t read_file_reset(struct f
                               size_t count, loff_t *ppos)
  {
        struct ath_softc *sc = file->private_data;
 +      static const char * const reset_cause[__RESET_TYPE_MAX] = {
 +              [RESET_TYPE_BB_HANG] = "Baseband Hang",
 +              [RESET_TYPE_BB_WATCHDOG] = "Baseband Watchdog",
 +              [RESET_TYPE_FATAL_INT] = "Fatal HW Error",
 +              [RESET_TYPE_TX_ERROR] = "TX HW error",
 +              [RESET_TYPE_TX_GTT] = "Transmit timeout",
 +              [RESET_TYPE_TX_HANG] = "TX Path Hang",
 +              [RESET_TYPE_PLL_HANG] = "PLL RX Hang",
 +              [RESET_TYPE_MAC_HANG] = "MAC Hang",
 +              [RESET_TYPE_BEACON_STUCK] = "Stuck Beacon",
 +              [RESET_TYPE_MCI] = "MCI Reset",
 +              [RESET_TYPE_CALIBRATION] = "Calibration error",
 +      };
        char buf[512];
        unsigned int len = 0;
 +      int i;
  
 -      len += scnprintf(buf + len, sizeof(buf) - len,
 -                       "%17s: %2d\n", "Baseband Hang",
 -                       sc->debug.stats.reset[RESET_TYPE_BB_HANG]);
 -      len += scnprintf(buf + len, sizeof(buf) - len,
 -                       "%17s: %2d\n", "Baseband Watchdog",
 -                       sc->debug.stats.reset[RESET_TYPE_BB_WATCHDOG]);
 -      len += scnprintf(buf + len, sizeof(buf) - len,
 -                       "%17s: %2d\n", "Fatal HW Error",
 -                       sc->debug.stats.reset[RESET_TYPE_FATAL_INT]);
 -      len += scnprintf(buf + len, sizeof(buf) - len,
 -                       "%17s: %2d\n", "TX HW error",
 -                       sc->debug.stats.reset[RESET_TYPE_TX_ERROR]);
 -      len += scnprintf(buf + len, sizeof(buf) - len,
 -                       "%17s: %2d\n", "TX Path Hang",
 -                       sc->debug.stats.reset[RESET_TYPE_TX_HANG]);
 -      len += scnprintf(buf + len, sizeof(buf) - len,
 -                       "%17s: %2d\n", "PLL RX Hang",
 -                       sc->debug.stats.reset[RESET_TYPE_PLL_HANG]);
 -      len += scnprintf(buf + len, sizeof(buf) - len,
 -                       "%17s: %2d\n", "MAC Hang",
 -                       sc->debug.stats.reset[RESET_TYPE_MAC_HANG]);
 -      len += scnprintf(buf + len, sizeof(buf) - len,
 -                       "%17s: %2d\n", "Stuck Beacon",
 -                       sc->debug.stats.reset[RESET_TYPE_BEACON_STUCK]);
 -      len += scnprintf(buf + len, sizeof(buf) - len,
 -                       "%17s: %2d\n", "MCI Reset",
 -                       sc->debug.stats.reset[RESET_TYPE_MCI]);
 +      for (i = 0; i < ARRAY_SIZE(reset_cause); i++) {
 +              if (!reset_cause[i])
 +                  continue;
 +
 +              len += scnprintf(buf + len, sizeof(buf) - len,
 +                               "%17s: %2d\n", reset_cause[i],
 +                               sc->debug.stats.reset[i]);
 +      }
  
        if (len > sizeof(buf))
                len = sizeof(buf);
@@@ -1311,7 -1315,7 +1311,7 @@@ void ath9k_get_et_stats(struct ieee8021
  
  void ath9k_deinit_debug(struct ath_softc *sc)
  {
 -      ath9k_spectral_deinit_debug(sc);
 +      ath9k_cmn_spectral_deinit_debug(&sc->spec_priv);
  }
  
  int ath9k_init_debug(struct ath_hw *ah)
  
        ath9k_dfs_init_debug(sc);
        ath9k_tx99_init_debug(sc);
 -      ath9k_spectral_init_debug(sc);
 +      ath9k_cmn_spectral_init_debug(&sc->spec_priv, sc->debug.debugfs_phy);
  
        debugfs_create_file("dma", S_IRUSR, sc->debug.debugfs_phy, sc,
                            &fops_dma);
index fbc78d80c559c3676cea7bb6d29533c4edf2b8e4,2ad605760e2136584a56c25680cb14324fa3d30c..6d4b273469b1914ab019c7cdeb4ec00f1ddd329a
@@@ -19,7 -19,6 +19,7 @@@
  #include <linux/module.h>
  #include <linux/time.h>
  #include <linux/bitops.h>
 +#include <linux/etherdevice.h>
  #include <asm/unaligned.h>
  
  #include "hw.h"
@@@ -447,16 -446,8 +447,16 @@@ static int ath9k_hw_init_macaddr(struc
                common->macaddr[2 * i] = eeval >> 8;
                common->macaddr[2 * i + 1] = eeval & 0xff;
        }
 -      if (sum == 0 || sum == 0xffff * 3)
 -              return -EADDRNOTAVAIL;
 +      if (!is_valid_ether_addr(common->macaddr)) {
 +              ath_err(common,
 +                      "eeprom contains invalid mac address: %pM\n",
 +                      common->macaddr);
 +
 +              random_ether_addr(common->macaddr);
 +              ath_err(common,
 +                      "random mac address will be used: %pM\n",
 +                      common->macaddr);
 +      }
  
        return 0;
  }
@@@ -870,19 -861,6 +870,6 @@@ static void ath9k_hw_init_pll(struct at
        udelay(RTC_PLL_SETTLE_DELAY);
  
        REG_WRITE(ah, AR_RTC_SLEEP_CLK, AR_RTC_FORCE_DERIVED_CLK);
-       if (AR_SREV_9340(ah) || AR_SREV_9550(ah)) {
-               if (ah->is_clk_25mhz) {
-                       REG_WRITE(ah, AR_RTC_DERIVED_CLK, 0x17c << 1);
-                       REG_WRITE(ah, AR_SLP32_MODE, 0x0010f3d7);
-                       REG_WRITE(ah,  AR_SLP32_INC, 0x0001e7ae);
-               } else {
-                       REG_WRITE(ah, AR_RTC_DERIVED_CLK, 0x261 << 1);
-                       REG_WRITE(ah, AR_SLP32_MODE, 0x0010f400);
-                       REG_WRITE(ah,  AR_SLP32_INC, 0x0001e800);
-               }
-               udelay(100);
-       }
  }
  
  static void ath9k_hw_init_interrupt_masks(struct ath_hw *ah,
@@@ -1598,22 -1576,16 +1585,22 @@@ static void ath9k_hw_init_mfp(struct at
                 * frames when constructing CCMP AAD. */
                REG_RMW_FIELD(ah, AR_AES_MUTE_MASK1, AR_AES_MUTE_MASK1_FC_MGMT,
                              0xc7ff);
 -              ah->sw_mgmt_crypto = false;
 +              if (AR_SREV_9271(ah) || AR_DEVID_7010(ah))
 +                      ah->sw_mgmt_crypto_tx = true;
 +              else
 +                      ah->sw_mgmt_crypto_tx = false;
 +              ah->sw_mgmt_crypto_rx = false;
        } else if (AR_SREV_9160_10_OR_LATER(ah)) {
                /* Disable hardware crypto for management frames */
                REG_CLR_BIT(ah, AR_PCU_MISC_MODE2,
                            AR_PCU_MISC_MODE2_MGMT_CRYPTO_ENABLE);
                REG_SET_BIT(ah, AR_PCU_MISC_MODE2,
                            AR_PCU_MISC_MODE2_NO_CRYPTO_FOR_NON_DATA_PKT);
 -              ah->sw_mgmt_crypto = true;
 +              ah->sw_mgmt_crypto_tx = true;
 +              ah->sw_mgmt_crypto_rx = true;
        } else {
 -              ah->sw_mgmt_crypto = true;
 +              ah->sw_mgmt_crypto_tx = true;
 +              ah->sw_mgmt_crypto_rx = true;
        }
  }
  
@@@ -1960,8 -1932,6 +1947,8 @@@ int ath9k_hw_reset(struct ath_hw *ah, s
  
        REGWRITE_BUFFER_FLUSH(ah);
  
 +      ath9k_hw_gen_timer_start_tsf2(ah);
 +
        ath9k_hw_init_desc(ah);
  
        if (ath9k_hw_btcoex_is_enabled(ah))
        if (ath9k_hw_mci_is_enabled(ah))
                ar9003_mci_check_bt(ah);
  
 -      ath9k_hw_loadnf(ah, chan);
 -      ath9k_hw_start_nfcal(ah, true);
 +      if (AR_SREV_9300_20_OR_LATER(ah)) {
 +              ath9k_hw_loadnf(ah, chan);
 +              ath9k_hw_start_nfcal(ah, true);
 +      }
  
        if (AR_SREV_9300_20_OR_LATER(ah))
                ar9003_hw_bb_watchdog_config(ah);
@@@ -2341,6 -2309,7 +2328,6 @@@ int ath9k_hw_fill_cap_info(struct ath_h
        struct ath9k_hw_capabilities *pCap = &ah->caps;
        struct ath_regulatory *regulatory = ath9k_hw_regulatory(ah);
        struct ath_common *common = ath9k_hw_common(ah);
 -      unsigned int chip_chainmask;
  
        u16 eeval;
        u8 ant_div_ctl1, tx_chainmask, rx_chainmask;
        }
  
        eeval = ah->eep_ops->get_eeprom(ah, EEP_OP_MODE);
 -      if ((eeval & (AR5416_OPFLAGS_11G | AR5416_OPFLAGS_11A)) == 0) {
 -              ath_err(common,
 -                      "no band has been marked as supported in EEPROM\n");
 -              return -EINVAL;
 +
 +      if (eeval & AR5416_OPFLAGS_11A) {
 +              if (ah->disable_5ghz)
 +                      ath_warn(common, "disabling 5GHz band\n");
 +              else
 +                      pCap->hw_caps |= ATH9K_HW_CAP_5GHZ;
        }
  
 -      if (eeval & AR5416_OPFLAGS_11A)
 -              pCap->hw_caps |= ATH9K_HW_CAP_5GHZ;
 +      if (eeval & AR5416_OPFLAGS_11G) {
 +              if (ah->disable_2ghz)
 +                      ath_warn(common, "disabling 2GHz band\n");
 +              else
 +                      pCap->hw_caps |= ATH9K_HW_CAP_2GHZ;
 +      }
  
 -      if (eeval & AR5416_OPFLAGS_11G)
 -              pCap->hw_caps |= ATH9K_HW_CAP_2GHZ;
 +      if ((pCap->hw_caps & (ATH9K_HW_CAP_2GHZ | ATH9K_HW_CAP_5GHZ)) == 0) {
 +              ath_err(common, "both bands are disabled\n");
 +              return -EINVAL;
 +      }
  
        if (AR_SREV_9485(ah) ||
            AR_SREV_9285(ah) ||
            AR_SREV_9330(ah) ||
            AR_SREV_9565(ah))
 -              chip_chainmask = 1;
 -      else if (AR_SREV_9462(ah))
 -              chip_chainmask = 3;
 +              pCap->chip_chainmask = 1;
        else if (!AR_SREV_9280_20_OR_LATER(ah))
 -              chip_chainmask = 7;
 -      else if (!AR_SREV_9300_20_OR_LATER(ah) || AR_SREV_9340(ah))
 -              chip_chainmask = 3;
 +              pCap->chip_chainmask = 7;
 +      else if (!AR_SREV_9300_20_OR_LATER(ah) ||
 +               AR_SREV_9340(ah) ||
 +               AR_SREV_9462(ah) ||
 +               AR_SREV_9531(ah))
 +              pCap->chip_chainmask = 3;
        else
 -              chip_chainmask = 7;
 +              pCap->chip_chainmask = 7;
  
        pCap->tx_chainmask = ah->eep_ops->get_eeprom(ah, EEP_TX_MASK);
        /*
                /* Use rx_chainmask from EEPROM. */
                pCap->rx_chainmask = ah->eep_ops->get_eeprom(ah, EEP_RX_MASK);
  
 -      pCap->tx_chainmask = fixup_chainmask(chip_chainmask, pCap->tx_chainmask);
 -      pCap->rx_chainmask = fixup_chainmask(chip_chainmask, pCap->rx_chainmask);
 +      pCap->tx_chainmask = fixup_chainmask(pCap->chip_chainmask, pCap->tx_chainmask);
 +      pCap->rx_chainmask = fixup_chainmask(pCap->chip_chainmask, pCap->rx_chainmask);
        ah->txchainmask = pCap->tx_chainmask;
        ah->rxchainmask = pCap->rx_chainmask;
  
@@@ -2926,16 -2886,6 +2913,16 @@@ u32 ath9k_hw_gettsf32(struct ath_hw *ah
  }
  EXPORT_SYMBOL(ath9k_hw_gettsf32);
  
 +void ath9k_hw_gen_timer_start_tsf2(struct ath_hw *ah)
 +{
 +      struct ath_gen_timer_table *timer_table = &ah->hw_gen_timers;
 +
 +      if (timer_table->tsf2_enabled) {
 +              REG_SET_BIT(ah, AR_DIRECT_CONNECT, AR_DC_AP_STA_EN);
 +              REG_SET_BIT(ah, AR_RESET_TSF, AR_RESET_TSF2_ONCE);
 +      }
 +}
 +
  struct ath_gen_timer *ath_gen_timer_alloc(struct ath_hw *ah,
                                          void (*trigger)(void *),
                                          void (*overflow)(void *),
        struct ath_gen_timer *timer;
  
        if ((timer_index < AR_FIRST_NDP_TIMER) ||
 -              (timer_index >= ATH_MAX_GEN_TIMER))
 +          (timer_index >= ATH_MAX_GEN_TIMER))
 +              return NULL;
 +
 +      if ((timer_index > AR_FIRST_NDP_TIMER) &&
 +          !AR_SREV_9300_20_OR_LATER(ah))
                return NULL;
  
        timer = kzalloc(sizeof(struct ath_gen_timer), GFP_KERNEL);
        timer->overflow = overflow;
        timer->arg = arg;
  
 +      if ((timer_index > AR_FIRST_NDP_TIMER) && !timer_table->tsf2_enabled) {
 +              timer_table->tsf2_enabled = true;
 +              ath9k_hw_gen_timer_start_tsf2(ah);
 +      }
 +
        return timer;
  }
  EXPORT_SYMBOL(ath_gen_timer_alloc);
index 39157ca723d6108992b9dca46a9593048546a63e,3bd030494986a087bfd9744873649442fa67b572..59d679cebc89e1132fdb313e4fea7e953a35728d
@@@ -88,21 -88,6 +88,21 @@@ static const struct ieee80211_tpt_blin
  
  static void ath9k_deinit_softc(struct ath_softc *sc);
  
 +static void ath9k_op_ps_wakeup(struct ath_common *common)
 +{
 +      ath9k_ps_wakeup((struct ath_softc *) common->priv);
 +}
 +
 +static void ath9k_op_ps_restore(struct ath_common *common)
 +{
 +      ath9k_ps_restore((struct ath_softc *) common->priv);
 +}
 +
 +static struct ath_ps_ops ath9k_ps_ops = {
 +      .wakeup = ath9k_op_ps_wakeup,
 +      .restore = ath9k_op_ps_restore,
 +};
 +
  /*
   * Read and write, they both share the same lock. We do this to serialize
   * reads and writes on Atheros 802.11n PCI devices only. This is required
@@@ -187,20 -172,17 +187,20 @@@ static void ath9k_reg_notifier(struct w
        ath_reg_notifier_apply(wiphy, request, reg);
  
        /* Set tx power */
 -      if (ah->curchan) {
 -              sc->cur_chan->txpower = 2 * ah->curchan->chan->max_power;
 -              ath9k_ps_wakeup(sc);
 -              ath9k_hw_set_txpowerlimit(ah, sc->cur_chan->txpower, false);
 -              sc->curtxpow = ath9k_hw_regulatory(ah)->power_limit;
 -              /* synchronize DFS detector if regulatory domain changed */
 -              if (sc->dfs_detector != NULL)
 -                      sc->dfs_detector->set_dfs_domain(sc->dfs_detector,
 -                                                       request->dfs_region);
 -              ath9k_ps_restore(sc);
 -      }
 +      if (!ah->curchan)
 +              return;
 +
 +      sc->cur_chan->txpower = 2 * ah->curchan->chan->max_power;
 +      ath9k_ps_wakeup(sc);
 +      ath9k_hw_set_txpowerlimit(ah, sc->cur_chan->txpower, false);
 +      ath9k_cmn_update_txpow(ah, sc->cur_chan->cur_txpower,
 +                             sc->cur_chan->txpower,
 +                             &sc->cur_chan->cur_txpower);
 +      /* synchronize DFS detector if regulatory domain changed */
 +      if (sc->dfs_detector != NULL)
 +              sc->dfs_detector->set_dfs_domain(sc->dfs_detector,
 +                                               request->dfs_region);
 +      ath9k_ps_restore(sc);
  }
  
  /*
@@@ -366,13 -348,12 +366,13 @@@ static void ath9k_init_misc(struct ath_
        if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_ANT_DIV_COMB)
                sc->ant_comb.count = ATH_ANT_DIV_COMB_INIT_COUNT;
  
 -      sc->spec_config.enabled = 0;
 -      sc->spec_config.short_repeat = true;
 -      sc->spec_config.count = 8;
 -      sc->spec_config.endless = false;
 -      sc->spec_config.period = 0xFF;
 -      sc->spec_config.fft_period = 0xF;
 +      sc->spec_priv.ah = sc->sc_ah;
 +      sc->spec_priv.spec_config.enabled = 0;
 +      sc->spec_priv.spec_config.short_repeat = true;
 +      sc->spec_priv.spec_config.count = 8;
 +      sc->spec_priv.spec_config.endless = false;
 +      sc->spec_priv.spec_config.period = 0xFF;
 +      sc->spec_priv.spec_config.fft_period = 0xF;
  }
  
  static void ath9k_init_pcoem_platform(struct ath_softc *sc)
        struct ath9k_hw_capabilities *pCap = &ah->caps;
        struct ath_common *common = ath9k_hw_common(ah);
  
 +      if (!IS_ENABLED(CONFIG_ATH9K_PCOEM))
 +              return;
 +
        if (common->bus_ops->ath_bus_type != ATH_PCI)
                return;
  
                ah->config.no_pll_pwrsave = true;
                ath_info(common, "Disable PLL PowerSave\n");
        }
 +
 +      if (sc->driver_data & ATH9K_PCI_LED_ACT_HI)
 +              ah->config.led_active_high = true;
  }
  
  static void ath9k_eeprom_request_cb(const struct firmware *eeprom_blob,
@@@ -553,15 -528,10 +553,15 @@@ static int ath9k_init_softc(u16 devid, 
                ah->is_clk_25mhz = pdata->is_clk_25mhz;
                ah->get_mac_revision = pdata->get_mac_revision;
                ah->external_reset = pdata->external_reset;
 +              ah->disable_2ghz = pdata->disable_2ghz;
 +              ah->disable_5ghz = pdata->disable_5ghz;
 +              if (!pdata->endian_check)
 +                      ah->ah_flags |= AH_NO_EEP_SWAP;
        }
  
        common->ops = &ah->reg_ops;
        common->bus_ops = bus_ops;
 +      common->ps_ops = &ath9k_ps_ops;
        common->ah = ah;
        common->hw = sc->hw;
        common->priv = sc;
@@@ -764,6 -734,32 +764,32 @@@ static const struct ieee80211_iface_com
  #endif
  };
  
+ #ifdef CONFIG_ATH9K_CHANNEL_CONTEXT
+ static void ath9k_set_mcc_capab(struct ath_softc *sc, struct ieee80211_hw *hw)
+ {
+       struct ath_hw *ah = sc->sc_ah;
+       struct ath_common *common = ath9k_hw_common(ah);
+       if (!ath9k_is_chanctx_enabled())
+               return;
+       hw->flags |= IEEE80211_HW_QUEUE_CONTROL;
+       hw->queues = ATH9K_NUM_TX_QUEUES;
+       hw->offchannel_tx_hw_queue = hw->queues - 1;
+       hw->wiphy->interface_modes &= ~ BIT(NL80211_IFTYPE_WDS);
+       hw->wiphy->iface_combinations = if_comb_multi;
+       hw->wiphy->n_iface_combinations = ARRAY_SIZE(if_comb_multi);
+       hw->wiphy->max_scan_ssids = 255;
+       hw->wiphy->max_scan_ie_len = IEEE80211_MAX_DATA_LEN;
+       hw->wiphy->max_remain_on_channel_duration = 10000;
+       hw->chanctx_data_size = sizeof(void *);
+       hw->extra_beacon_tailroom =
+               sizeof(struct ieee80211_p2p_noa_attr) + 9;
+       ath_dbg(common, CHAN_CTX, "Use channel contexts\n");
+ }
+ #endif /* CONFIG_ATH9K_CHANNEL_CONTEXT */
  static void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw)
  {
        struct ath_hw *ah = sc->sc_ah;
                IEEE80211_HW_SPECTRUM_MGMT |
                IEEE80211_HW_REPORTS_TX_ACK_STATUS |
                IEEE80211_HW_SUPPORTS_RC_TABLE |
-               IEEE80211_HW_QUEUE_CONTROL |
                IEEE80211_HW_SUPPORTS_HT_CCK_RATES;
  
        if (ath9k_ps_enable)
                        hw->wiphy->n_iface_combinations = ARRAY_SIZE(if_comb);
        }
  
- #ifdef CONFIG_ATH9K_CHANNEL_CONTEXT
-       if (ath9k_is_chanctx_enabled()) {
-               hw->wiphy->interface_modes &= ~ BIT(NL80211_IFTYPE_WDS);
-               hw->wiphy->iface_combinations = if_comb_multi;
-               hw->wiphy->n_iface_combinations = ARRAY_SIZE(if_comb_multi);
-               hw->wiphy->max_scan_ssids = 255;
-               hw->wiphy->max_scan_ie_len = IEEE80211_MAX_DATA_LEN;
-               hw->wiphy->max_remain_on_channel_duration = 10000;
-               hw->chanctx_data_size = sizeof(void *);
-               hw->extra_beacon_tailroom =
-                       sizeof(struct ieee80211_p2p_noa_attr) + 9;
-               ath_dbg(common, CHAN_CTX, "Use channel contexts\n");
-       }
- #endif /* CONFIG_ATH9K_CHANNEL_CONTEXT */
        hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT;
  
        hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN;
        hw->wiphy->flags |= WIPHY_FLAG_HAS_CHANNEL_SWITCH;
        hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD;
  
-       /* allow 4 queues per channel context +
-        * 1 cab queue + 1 offchannel tx queue
-        */
-       hw->queues = ATH9K_NUM_TX_QUEUES;
-       /* last queue for offchannel */
-       hw->offchannel_tx_hw_queue = hw->queues - 1;
+       hw->queues = 4;
        hw->max_rates = 4;
        hw->max_listen_interval = 10;
        hw->max_rate_tries = 10;
                hw->wiphy->bands[IEEE80211_BAND_5GHZ] =
                        &common->sbands[IEEE80211_BAND_5GHZ];
  
+ #ifdef CONFIG_ATH9K_CHANNEL_CONTEXT
+       ath9k_set_mcc_capab(sc, hw);
+ #endif
        ath9k_init_wow(hw);
        ath9k_cmn_reload_chainmask(ah);
  
index 5f16630f26ce27b7d44a632031169b6f38032531,30c66dfcd7a04b8489186c82db741b87f0afbad0..f95283f3c350c4e060031145f34dd210bf44ccb7
@@@ -54,8 -54,7 +54,8 @@@ u8 ath9k_parse_mpdudensity(u8 mpdudensi
        }
  }
  
 -static bool ath9k_has_pending_frames(struct ath_softc *sc, struct ath_txq *txq)
 +static bool ath9k_has_pending_frames(struct ath_softc *sc, struct ath_txq *txq,
 +                                   bool sw_pending)
  {
        bool pending = false;
  
@@@ -66,9 -65,6 +66,9 @@@
                goto out;
        }
  
 +      if (!sw_pending)
 +              goto out;
 +
        if (txq->mac80211_qnum >= 0) {
                struct list_head *list;
  
@@@ -233,9 -229,8 +233,9 @@@ static bool ath_complete_reset(struct a
  
        ath9k_calculate_summary_state(sc, sc->cur_chan);
        ath_startrecv(sc);
 -      ath9k_cmn_update_txpow(ah, sc->curtxpow,
 -                             sc->cur_chan->txpower, &sc->curtxpow);
 +      ath9k_cmn_update_txpow(ah, sc->cur_chan->cur_txpower,
 +                             sc->cur_chan->txpower,
 +                             &sc->cur_chan->cur_txpower);
        clear_bit(ATH_OP_HW_RESET, &common->op_flags);
  
        if (!sc->cur_chan->offchannel && start) {
        return true;
  }
  
 -int ath_reset_internal(struct ath_softc *sc, struct ath9k_channel *hchan)
 +static int ath_reset_internal(struct ath_softc *sc, struct ath9k_channel *hchan)
  {
        struct ath_hw *ah = sc->sc_ah;
        struct ath_common *common = ath9k_hw_common(ah);
        __ath_cancel_work(sc);
  
        tasklet_disable(&sc->intr_tq);
 +      tasklet_disable(&sc->bcon_tasklet);
        spin_lock_bh(&sc->sc_pcu_lock);
  
        if (!sc->cur_chan->offchannel) {
  
  out:
        spin_unlock_bh(&sc->sc_pcu_lock);
 +      tasklet_enable(&sc->bcon_tasklet);
        tasklet_enable(&sc->intr_tq);
  
        return r;
@@@ -546,10 -539,11 +546,10 @@@ irqreturn_t ath_isr(int irq, void *dev
                sched = true;
  
        /*
 -       * If a FATAL or RXORN interrupt is received, we have to reset the
 -       * chip immediately.
 +       * If a FATAL interrupt is received, we have to reset the chip
 +       * immediately.
         */
 -      if ((status & ATH9K_INT_FATAL) || ((status & ATH9K_INT_RXORN) &&
 -          !(ah->caps.hw_caps & ATH9K_HW_CAP_EDMA)))
 +      if (status & ATH9K_INT_FATAL)
                goto chip_reset;
  
        if ((ah->config.hw_hang_checks & HW_BB_WATCHDOG) &&
@@@ -604,29 -598,17 +604,29 @@@ chip_reset
  #undef SCHED_INTR
  }
  
 -int ath_reset(struct ath_softc *sc)
 +/*
 + * This function is called when a HW reset cannot be deferred
 + * and has to be immediate.
 + */
 +int ath_reset(struct ath_softc *sc, struct ath9k_channel *hchan)
  {
 +      struct ath_common *common = ath9k_hw_common(sc->sc_ah);
        int r;
  
 +      set_bit(ATH_OP_HW_RESET, &common->op_flags);
 +
        ath9k_ps_wakeup(sc);
 -      r = ath_reset_internal(sc, NULL);
 +      r = ath_reset_internal(sc, hchan);
        ath9k_ps_restore(sc);
  
        return r;
  }
  
 +/*
 + * When a HW reset can be deferred, it is added to the
 + * hw_reset_work workqueue, but we set ATH_OP_HW_RESET before
 + * queueing.
 + */
  void ath9k_queue_reset(struct ath_softc *sc, enum ath_reset_type type)
  {
        struct ath_common *common = ath9k_hw_common(sc->sc_ah);
@@@ -641,9 -623,7 +641,9 @@@ void ath_reset_work(struct work_struct 
  {
        struct ath_softc *sc = container_of(work, struct ath_softc, hw_reset_work);
  
 -      ath_reset(sc);
 +      ath9k_ps_wakeup(sc);
 +      ath_reset_internal(sc, NULL);
 +      ath9k_ps_restore(sc);
  }
  
  /**********************/
@@@ -727,8 -707,7 +727,8 @@@ static int ath9k_start(struct ieee80211
        if (ah->led_pin >= 0) {
                ath9k_hw_cfg_output(ah, ah->led_pin,
                                    AR_GPIO_OUTPUT_MUX_AS_OUTPUT);
 -              ath9k_hw_set_gpio(ah, ah->led_pin, 0);
 +              ath9k_hw_set_gpio(ah, ah->led_pin,
 +                                (ah->config.led_active_high) ? 1 : 0);
        }
  
        /*
@@@ -870,8 -849,7 +870,8 @@@ static void ath9k_stop(struct ieee80211
        spin_lock_bh(&sc->sc_pcu_lock);
  
        if (ah->led_pin >= 0) {
 -              ath9k_hw_set_gpio(ah, ah->led_pin, 1);
 +              ath9k_hw_set_gpio(ah, ah->led_pin,
 +                                (ah->config.led_active_high) ? 0 : 1);
                ath9k_hw_cfg_gpio_input(ah, ah->led_pin);
        }
  
@@@ -1059,7 -1037,7 +1059,7 @@@ static void ath9k_set_offchannel_state(
  
        eth_zero_addr(common->curbssid);
        eth_broadcast_addr(common->bssidmask);
 -      ether_addr_copy(common->macaddr, vif->addr);
 +      memcpy(common->macaddr, vif->addr, ETH_ALEN);
        common->curaid = 0;
        ah->opmode = vif->type;
        ah->imask &= ~ATH9K_INT_SWBA;
@@@ -1100,7 -1078,7 +1100,7 @@@ void ath9k_calculate_summary_state(stru
        ath9k_calculate_iter_data(sc, ctx, &iter_data);
  
        if (iter_data.has_hw_macaddr)
 -              ether_addr_copy(common->macaddr, iter_data.hw_macaddr);
 +              memcpy(common->macaddr, iter_data.hw_macaddr, ETH_ALEN);
  
        memcpy(common->bssidmask, iter_data.mask, ETH_ALEN);
        ath_hw_setbssidmask(common);
@@@ -1184,11 -1162,13 +1184,14 @@@ static void ath9k_assign_hw_queues(stru
  {
        int i;
  
+       if (!ath9k_is_chanctx_enabled())
+               return;
        for (i = 0; i < IEEE80211_NUM_ACS; i++)
                vif->hw_queue[i] = i;
  
 -      if (vif->type == NL80211_IFTYPE_AP)
 +      if (vif->type == NL80211_IFTYPE_AP ||
 +          vif->type == NL80211_IFTYPE_MESH_POINT)
                vif->cab_queue = hw->queues - 2;
        else
                vif->cab_queue = IEEE80211_INVAL_HW_QUEUE;
@@@ -1340,6 -1320,78 +1343,6 @@@ static void ath9k_disable_ps(struct ath
        ath_dbg(common, PS, "PowerSave disabled\n");
  }
  
 -void ath9k_spectral_scan_trigger(struct ieee80211_hw *hw)
 -{
 -      struct ath_softc *sc = hw->priv;
 -      struct ath_hw *ah = sc->sc_ah;
 -      struct ath_common *common = ath9k_hw_common(ah);
 -      u32 rxfilter;
 -
 -      if (config_enabled(CONFIG_ATH9K_TX99))
 -              return;
 -
 -      if (!ath9k_hw_ops(ah)->spectral_scan_trigger) {
 -              ath_err(common, "spectrum analyzer not implemented on this hardware\n");
 -              return;
 -      }
 -
 -      ath9k_ps_wakeup(sc);
 -      rxfilter = ath9k_hw_getrxfilter(ah);
 -      ath9k_hw_setrxfilter(ah, rxfilter |
 -                               ATH9K_RX_FILTER_PHYRADAR |
 -                               ATH9K_RX_FILTER_PHYERR);
 -
 -      /* TODO: usually this should not be neccesary, but for some reason
 -       * (or in some mode?) the trigger must be called after the
 -       * configuration, otherwise the register will have its values reset
 -       * (on my ar9220 to value 0x01002310)
 -       */
 -      ath9k_spectral_scan_config(hw, sc->spectral_mode);
 -      ath9k_hw_ops(ah)->spectral_scan_trigger(ah);
 -      ath9k_ps_restore(sc);
 -}
 -
 -int ath9k_spectral_scan_config(struct ieee80211_hw *hw,
 -                             enum spectral_mode spectral_mode)
 -{
 -      struct ath_softc *sc = hw->priv;
 -      struct ath_hw *ah = sc->sc_ah;
 -      struct ath_common *common = ath9k_hw_common(ah);
 -
 -      if (!ath9k_hw_ops(ah)->spectral_scan_trigger) {
 -              ath_err(common, "spectrum analyzer not implemented on this hardware\n");
 -              return -1;
 -      }
 -
 -      switch (spectral_mode) {
 -      case SPECTRAL_DISABLED:
 -              sc->spec_config.enabled = 0;
 -              break;
 -      case SPECTRAL_BACKGROUND:
 -              /* send endless samples.
 -               * TODO: is this really useful for "background"?
 -               */
 -              sc->spec_config.endless = 1;
 -              sc->spec_config.enabled = 1;
 -              break;
 -      case SPECTRAL_CHANSCAN:
 -      case SPECTRAL_MANUAL:
 -              sc->spec_config.endless = 0;
 -              sc->spec_config.enabled = 1;
 -              break;
 -      default:
 -              return -1;
 -      }
 -
 -      ath9k_ps_wakeup(sc);
 -      ath9k_hw_ops(ah)->spectral_scan_config(ah, &sc->spec_config);
 -      ath9k_ps_restore(sc);
 -
 -      sc->spectral_mode = spectral_mode;
 -
 -      return 0;
 -}
 -
  static int ath9k_config(struct ieee80211_hw *hw, u32 changed)
  {
        struct ath_softc *sc = hw->priv;
        if (changed & IEEE80211_CONF_CHANGE_POWER) {
                ath_dbg(common, CONFIG, "Set power: %d\n", conf->power_level);
                sc->cur_chan->txpower = 2 * conf->power_level;
 -              ath9k_cmn_update_txpow(ah, sc->curtxpow,
 -                                     sc->cur_chan->txpower, &sc->curtxpow);
 +              ath9k_cmn_update_txpow(ah, sc->cur_chan->cur_txpower,
 +                                     sc->cur_chan->txpower,
 +                                     &sc->cur_chan->cur_txpower);
        }
  
        mutex_unlock(&sc->mutex);
@@@ -1499,40 -1550,6 +1502,40 @@@ static int ath9k_sta_remove(struct ieee
        return 0;
  }
  
 +static int ath9k_sta_state(struct ieee80211_hw *hw,
 +                         struct ieee80211_vif *vif,
 +                         struct ieee80211_sta *sta,
 +                         enum ieee80211_sta_state old_state,
 +                         enum ieee80211_sta_state new_state)
 +{
 +      struct ath_softc *sc = hw->priv;
 +      struct ath_common *common = ath9k_hw_common(sc->sc_ah);
 +      int ret = 0;
 +
 +      if (old_state == IEEE80211_STA_AUTH &&
 +          new_state == IEEE80211_STA_ASSOC) {
 +              ret = ath9k_sta_add(hw, vif, sta);
 +              ath_dbg(common, CONFIG,
 +                      "Add station: %pM\n", sta->addr);
 +      } else if (old_state == IEEE80211_STA_ASSOC &&
 +                 new_state == IEEE80211_STA_AUTH) {
 +              ret = ath9k_sta_remove(hw, vif, sta);
 +              ath_dbg(common, CONFIG,
 +                      "Remove station: %pM\n", sta->addr);
 +      }
 +
 +      if (ath9k_is_chanctx_enabled()) {
 +              if (vif->type == NL80211_IFTYPE_STATION) {
 +                      if (old_state == IEEE80211_STA_ASSOC &&
 +                          new_state == IEEE80211_STA_AUTHORIZED)
 +                              ath_chanctx_event(sc, vif,
 +                                                ATH_CHANCTX_EVENT_AUTHORIZED);
 +              }
 +      }
 +
 +      return ret;
 +}
 +
  static void ath9k_sta_set_tx_filter(struct ath_hw *ah,
                                    struct ath_node *an,
                                    bool set)
@@@ -1657,7 -1674,7 +1660,7 @@@ static int ath9k_set_key(struct ieee802
                        key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
                        if (key->cipher == WLAN_CIPHER_SUITE_TKIP)
                                key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC;
 -                      if (sc->sc_ah->sw_mgmt_crypto &&
 +                      if (sc->sc_ah->sw_mgmt_crypto_tx &&
                            key->cipher == WLAN_CIPHER_SUITE_CCMP)
                                key->flags |= IEEE80211_KEY_FLAG_SW_MGMT_TX;
                        ret = 0;
@@@ -1717,11 -1734,17 +1720,11 @@@ static void ath9k_bss_info_changed(stru
                ath_dbg(common, CONFIG, "BSSID %pM Changed ASSOC %d\n",
                        bss_conf->bssid, bss_conf->assoc);
  
 -              ether_addr_copy(avp->bssid, bss_conf->bssid);
 +              memcpy(avp->bssid, bss_conf->bssid, ETH_ALEN);
                avp->aid = bss_conf->aid;
                avp->assoc = bss_conf->assoc;
  
                ath9k_calculate_summary_state(sc, avp->chanctx);
 -
 -              if (ath9k_is_chanctx_enabled()) {
 -                      if (bss_conf->assoc)
 -                              ath_chanctx_event(sc, vif,
 -                                                ATH_CHANCTX_EVENT_ASSOC);
 -              }
        }
  
        if (changed & BSS_CHANGED_IBSS) {
@@@ -1817,7 -1840,6 +1820,7 @@@ static int ath9k_ampdu_action(struct ie
                              u16 tid, u16 *ssn, u8 buf_size)
  {
        struct ath_softc *sc = hw->priv;
 +      struct ath_common *common = ath9k_hw_common(sc->sc_ah);
        bool flush = false;
        int ret = 0;
  
        case IEEE80211_AMPDU_RX_STOP:
                break;
        case IEEE80211_AMPDU_TX_START:
 +              if (ath9k_is_chanctx_enabled()) {
 +                      if (test_bit(ATH_OP_SCANNING, &common->op_flags)) {
 +                              ret = -EBUSY;
 +                              break;
 +                      }
 +              }
                ath9k_ps_wakeup(sc);
                ret = ath_tx_aggr_start(sc, sta, tid, ssn);
                if (!ret)
@@@ -1948,8 -1964,7 +1951,8 @@@ static void ath9k_set_coverage_class(st
        mutex_unlock(&sc->mutex);
  }
  
 -static bool ath9k_has_tx_pending(struct ath_softc *sc)
 +static bool ath9k_has_tx_pending(struct ath_softc *sc,
 +                               bool sw_pending)
  {
        int i, npend = 0;
  
                if (!ATH_TXQ_SETUP(sc, i))
                        continue;
  
 -              npend = ath9k_has_pending_frames(sc, &sc->tx.txq[i]);
 +              npend = ath9k_has_pending_frames(sc, &sc->tx.txq[i],
 +                                               sw_pending);
                if (npend)
                        break;
        }
@@@ -1970,38 -1984,18 +1973,38 @@@ static void ath9k_flush(struct ieee8021
                        u32 queues, bool drop)
  {
        struct ath_softc *sc = hw->priv;
 +      struct ath_common *common = ath9k_hw_common(sc->sc_ah);
 +
 +      if (ath9k_is_chanctx_enabled()) {
 +              if (!test_bit(ATH_OP_MULTI_CHANNEL, &common->op_flags))
 +                      goto flush;
  
 +              /*
 +               * If MCC is active, extend the flush timeout
 +               * and wait for the HW/SW queues to become
 +               * empty. This needs to be done outside the
 +               * sc->mutex lock to allow the channel scheduler
 +               * to switch channel contexts.
 +               *
 +               * The vif queues have been stopped in mac80211,
 +               * so there won't be any incoming frames.
 +               */
 +              __ath9k_flush(hw, queues, drop, true, true);
 +              return;
 +      }
 +flush:
        mutex_lock(&sc->mutex);
 -      __ath9k_flush(hw, queues, drop);
 +      __ath9k_flush(hw, queues, drop, true, false);
        mutex_unlock(&sc->mutex);
  }
  
 -void __ath9k_flush(struct ieee80211_hw *hw, u32 queues, bool drop)
 +void __ath9k_flush(struct ieee80211_hw *hw, u32 queues, bool drop,
 +                 bool sw_pending, bool timeout_override)
  {
        struct ath_softc *sc = hw->priv;
        struct ath_hw *ah = sc->sc_ah;
        struct ath_common *common = ath9k_hw_common(ah);
 -      int timeout = HZ / 5; /* 200 ms */
 +      int timeout;
        bool drain_txq;
  
        cancel_delayed_work_sync(&sc->tx_complete_work);
                return;
        }
  
 -      if (wait_event_timeout(sc->tx_wait, !ath9k_has_tx_pending(sc),
 +      spin_lock_bh(&sc->chan_lock);
 +      if (timeout_override)
 +              timeout = HZ / 5;
 +      else
 +              timeout = sc->cur_chan->flush_timeout;
 +      spin_unlock_bh(&sc->chan_lock);
 +
 +      ath_dbg(common, CHAN_CTX,
 +              "Flush timeout: %d\n", jiffies_to_msecs(timeout));
 +
 +      if (wait_event_timeout(sc->tx_wait, !ath9k_has_tx_pending(sc, sw_pending),
                               timeout) > 0)
                drop = false;
  
                spin_unlock_bh(&sc->sc_pcu_lock);
  
                if (!drain_txq)
 -                      ath_reset(sc);
 +                      ath_reset(sc, NULL);
  
                ath9k_ps_restore(sc);
        }
@@@ -2049,7 -2033,7 +2052,7 @@@ static bool ath9k_tx_frames_pending(str
  {
        struct ath_softc *sc = hw->priv;
  
 -      return ath9k_has_tx_pending(sc);
 +      return ath9k_has_tx_pending(sc, true);
  }
  
  static int ath9k_tx_last_beacon(struct ieee80211_hw *hw)
@@@ -2196,28 -2180,6 +2199,28 @@@ static void ath9k_sw_scan_complete(stru
  
  #ifdef CONFIG_ATH9K_CHANNEL_CONTEXT
  
 +static void ath9k_cancel_pending_offchannel(struct ath_softc *sc)
 +{
 +      struct ath_common *common = ath9k_hw_common(sc->sc_ah);
 +
 +      if (sc->offchannel.roc_vif) {
 +              ath_dbg(common, CHAN_CTX,
 +                      "%s: Aborting RoC\n", __func__);
 +
 +              del_timer_sync(&sc->offchannel.timer);
 +              if (sc->offchannel.state >= ATH_OFFCHANNEL_ROC_START)
 +                      ath_roc_complete(sc, true);
 +      }
 +
 +      if (test_bit(ATH_OP_SCANNING, &common->op_flags)) {
 +              ath_dbg(common, CHAN_CTX,
 +                      "%s: Aborting HW scan\n", __func__);
 +
 +              del_timer_sync(&sc->offchannel.timer);
 +              ath_scan_complete(sc, true);
 +      }
 +}
 +
  static int ath9k_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
                         struct ieee80211_scan_request *hw_req)
  {
@@@ -2348,6 -2310,7 +2351,6 @@@ static int ath9k_add_chanctx(struct iee
                        conf->def.chan->center_freq);
  
                ath_chanctx_set_channel(sc, ctx, &conf->def);
 -              ath_chanctx_event(sc, NULL, ATH_CHANCTX_EVENT_ASSIGN);
  
                mutex_unlock(&sc->mutex);
                return 0;
@@@ -2404,8 -2367,6 +2407,8 @@@ static int ath9k_assign_vif_chanctx(str
        struct ath_chanctx *ctx = ath_chanctx_get(conf);
        int i;
  
 +      ath9k_cancel_pending_offchannel(sc);
 +
        mutex_lock(&sc->mutex);
  
        ath_dbg(common, CHAN_CTX,
@@@ -2435,8 -2396,6 +2438,8 @@@ static void ath9k_unassign_vif_chanctx(
        struct ath_chanctx *ctx = ath_chanctx_get(conf);
        int ac;
  
 +      ath9k_cancel_pending_offchannel(sc);
 +
        mutex_lock(&sc->mutex);
  
        ath_dbg(common, CHAN_CTX,
@@@ -2460,11 -2419,7 +2463,11 @@@ static void ath9k_mgd_prepare_tx(struc
        struct ath_softc *sc = hw->priv;
        struct ath_common *common = ath9k_hw_common(sc->sc_ah);
        struct ath_vif *avp = (struct ath_vif *) vif->drv_priv;
 +      struct ath_beacon_config *cur_conf;
 +      struct ath_chanctx *go_ctx;
 +      unsigned long timeout;
        bool changed = false;
 +      u32 beacon_int;
  
        if (!test_bit(ATH_OP_MULTI_CHANNEL, &common->op_flags))
                return;
        mutex_lock(&sc->mutex);
  
        spin_lock_bh(&sc->chan_lock);
 -      if (sc->next_chan || (sc->cur_chan != avp->chanctx)) {
 -              sc->next_chan = avp->chanctx;
 +      if (sc->next_chan || (sc->cur_chan != avp->chanctx))
                changed = true;
 +      spin_unlock_bh(&sc->chan_lock);
 +
 +      if (!changed)
 +              goto out;
 +
 +      ath9k_cancel_pending_offchannel(sc);
 +
 +      go_ctx = ath_is_go_chanctx_present(sc);
 +
 +      if (go_ctx) {
 +              /*
 +               * Wait till the GO interface gets a chance
 +               * to send out an NoA.
 +               */
 +              spin_lock_bh(&sc->chan_lock);
 +              sc->sched.mgd_prepare_tx = true;
 +              cur_conf = &go_ctx->beacon;
 +              beacon_int = TU_TO_USEC(cur_conf->beacon_interval);
 +              spin_unlock_bh(&sc->chan_lock);
 +
 +              timeout = usecs_to_jiffies(beacon_int * 2);
 +              init_completion(&sc->go_beacon);
 +
 +              mutex_unlock(&sc->mutex);
 +
 +              if (wait_for_completion_timeout(&sc->go_beacon,
 +                                              timeout) == 0) {
 +                      ath_dbg(common, CHAN_CTX,
 +                              "Failed to send new NoA\n");
 +
 +                      spin_lock_bh(&sc->chan_lock);
 +                      sc->sched.mgd_prepare_tx = false;
 +                      spin_unlock_bh(&sc->chan_lock);
 +              }
 +
 +              mutex_lock(&sc->mutex);
        }
 +
        ath_dbg(common, CHAN_CTX,
 -              "%s: Set chanctx state to FORCE_ACTIVE, changed: %d\n",
 -              __func__, changed);
 +              "%s: Set chanctx state to FORCE_ACTIVE for vif: %pM\n",
 +              __func__, vif->addr);
 +
 +      spin_lock_bh(&sc->chan_lock);
 +      sc->next_chan = avp->chanctx;
        sc->sched.state = ATH_CHANCTX_STATE_FORCE_ACTIVE;
        spin_unlock_bh(&sc->chan_lock);
  
 -      if (changed)
 -              ath_chanctx_set_next(sc, true);
 -
 +      ath_chanctx_set_next(sc, true);
 +out:
        mutex_unlock(&sc->mutex);
  }
  
@@@ -2548,24 -2465,6 +2551,24 @@@ void ath9k_fill_chanctx_ops(void
  
  #endif
  
 +static int ath9k_get_txpower(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
 +                           int *dbm)
 +{
 +      struct ath_softc *sc = hw->priv;
 +      struct ath_vif *avp = (void *)vif->drv_priv;
 +
 +      mutex_lock(&sc->mutex);
 +      if (avp->chanctx)
 +              *dbm = avp->chanctx->cur_txpower;
 +      else
 +              *dbm = sc->cur_chan->cur_txpower;
 +      mutex_unlock(&sc->mutex);
 +
 +      *dbm /= 2;
 +
 +      return 0;
 +}
 +
  struct ieee80211_ops ath9k_ops = {
        .tx                 = ath9k_tx,
        .start              = ath9k_start,
        .remove_interface   = ath9k_remove_interface,
        .config             = ath9k_config,
        .configure_filter   = ath9k_configure_filter,
 -      .sta_add            = ath9k_sta_add,
 -      .sta_remove         = ath9k_sta_remove,
 +      .sta_state          = ath9k_sta_state,
        .sta_notify         = ath9k_sta_notify,
        .conf_tx            = ath9k_conf_tx,
        .bss_info_changed   = ath9k_bss_info_changed,
  #endif
        .sw_scan_start      = ath9k_sw_scan_start,
        .sw_scan_complete   = ath9k_sw_scan_complete,
 +      .get_txpower        = ath9k_get_txpower,
  };
index e418969679c9c558cb5045691d9d195957625542,0000000000000000000000000000000000000000..f8a9dfa657ba6a4a2e4a54d2ba218c0ff862be26
mode 100644,000000..100644
--- /dev/null
@@@ -1,5863 -1,0 +1,5869 @@@
 +/*
 + * Copyright (c) 2010 Broadcom Corporation
 + *
 + * Permission to use, copy, modify, and/or distribute this software for any
 + * purpose with or without fee is hereby granted, provided that the above
 + * copyright notice and this permission notice appear in all copies.
 + *
 + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
 + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
 + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
 + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 + */
 +
 +/* Toplevel file. Relies on dhd_linux.c to send commands to the dongle. */
 +
 +#include <linux/kernel.h>
 +#include <linux/etherdevice.h>
 +#include <linux/module.h>
 +#include <linux/vmalloc.h>
 +#include <net/cfg80211.h>
 +#include <net/netlink.h>
 +
 +#include <brcmu_utils.h>
 +#include <defs.h>
 +#include <brcmu_wifi.h>
 +#include "core.h"
 +#include "debug.h"
 +#include "tracepoint.h"
 +#include "fwil_types.h"
 +#include "p2p.h"
 +#include "btcoex.h"
 +#include "cfg80211.h"
 +#include "feature.h"
 +#include "fwil.h"
 +#include "proto.h"
 +#include "vendor.h"
 +#include "bus.h"
 +
 +#define BRCMF_SCAN_IE_LEN_MAX         2048
 +#define BRCMF_PNO_VERSION             2
 +#define BRCMF_PNO_TIME                        30
 +#define BRCMF_PNO_REPEAT              4
 +#define BRCMF_PNO_FREQ_EXPO_MAX               3
 +#define BRCMF_PNO_MAX_PFN_COUNT               16
 +#define BRCMF_PNO_ENABLE_ADAPTSCAN_BIT        6
 +#define BRCMF_PNO_HIDDEN_BIT          2
 +#define BRCMF_PNO_WPA_AUTH_ANY                0xFFFFFFFF
 +#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 */
 +#define       WME_OUI_TYPE                    2
 +#define WPS_OUI_TYPE                  4
 +
 +#define VS_IE_FIXED_HDR_LEN           6
 +#define WPA_IE_VERSION_LEN            2
 +#define WPA_IE_MIN_OUI_LEN            4
 +#define WPA_IE_SUITE_COUNT_LEN                2
 +
 +#define WPA_CIPHER_NONE                       0       /* None */
 +#define WPA_CIPHER_WEP_40             1       /* WEP (40-bit) */
 +#define WPA_CIPHER_TKIP                       2       /* TKIP: default for WPA */
 +#define WPA_CIPHER_AES_CCM            4       /* AES (CCM) */
 +#define WPA_CIPHER_WEP_104            5       /* WEP (104-bit) */
 +
 +#define RSN_AKM_NONE                  0       /* None (IBSS) */
 +#define RSN_AKM_UNSPECIFIED           1       /* Over 802.1x */
 +#define RSN_AKM_PSK                   2       /* Pre-shared Key */
 +#define RSN_CAP_LEN                   2       /* Length of RSN capabilities */
 +#define RSN_CAP_PTK_REPLAY_CNTR_MASK  0x000C
 +
 +#define VNDR_IE_CMD_LEN                       4       /* length of the set command
 +                                               * string :"add", "del" (+ NUL)
 +                                               */
 +#define VNDR_IE_COUNT_OFFSET          4
 +#define VNDR_IE_PKTFLAG_OFFSET                8
 +#define VNDR_IE_VSIE_OFFSET           12
 +#define VNDR_IE_HDR_SIZE              12
 +#define VNDR_IE_PARSE_LIMIT           5
 +
 +#define       DOT11_MGMT_HDR_LEN              24      /* d11 management header len */
 +#define       DOT11_BCN_PRB_FIXED_LEN         12      /* beacon/probe fixed length */
 +
 +#define BRCMF_SCAN_JOIN_ACTIVE_DWELL_TIME_MS  320
 +#define BRCMF_SCAN_JOIN_PASSIVE_DWELL_TIME_MS 400
 +#define BRCMF_SCAN_JOIN_PROBE_INTERVAL_MS     20
 +
 +#define BRCMF_ASSOC_PARAMS_FIXED_SIZE \
 +      (sizeof(struct brcmf_assoc_params_le) - sizeof(u16))
 +
 +static bool check_vif_up(struct brcmf_cfg80211_vif *vif)
 +{
 +      if (!test_bit(BRCMF_VIF_STATUS_READY, &vif->sme_state)) {
 +              brcmf_dbg(INFO, "device is not ready : status (%lu)\n",
 +                        vif->sme_state);
 +              return false;
 +      }
 +      return true;
 +}
 +
 +#define RATE_TO_BASE100KBPS(rate)   (((rate) * 10) / 2)
 +#define RATETAB_ENT(_rateid, _flags) \
 +      {                                                               \
 +              .bitrate        = RATE_TO_BASE100KBPS(_rateid),     \
 +              .hw_value       = (_rateid),                            \
 +              .flags          = (_flags),                             \
 +      }
 +
 +static struct ieee80211_rate __wl_rates[] = {
 +      RATETAB_ENT(BRCM_RATE_1M, 0),
 +      RATETAB_ENT(BRCM_RATE_2M, IEEE80211_RATE_SHORT_PREAMBLE),
 +      RATETAB_ENT(BRCM_RATE_5M5, IEEE80211_RATE_SHORT_PREAMBLE),
 +      RATETAB_ENT(BRCM_RATE_11M, IEEE80211_RATE_SHORT_PREAMBLE),
 +      RATETAB_ENT(BRCM_RATE_6M, 0),
 +      RATETAB_ENT(BRCM_RATE_9M, 0),
 +      RATETAB_ENT(BRCM_RATE_12M, 0),
 +      RATETAB_ENT(BRCM_RATE_18M, 0),
 +      RATETAB_ENT(BRCM_RATE_24M, 0),
 +      RATETAB_ENT(BRCM_RATE_36M, 0),
 +      RATETAB_ENT(BRCM_RATE_48M, 0),
 +      RATETAB_ENT(BRCM_RATE_54M, 0),
 +};
 +
 +#define wl_a_rates            (__wl_rates + 4)
 +#define wl_a_rates_size       8
 +#define wl_g_rates            (__wl_rates + 0)
 +#define wl_g_rates_size       12
 +
 +/* Band templates duplicated per wiphy. The channel info
 + * is filled in after querying the device.
 + */
 +static const struct ieee80211_supported_band __wl_band_2ghz = {
 +      .band = IEEE80211_BAND_2GHZ,
 +      .bitrates = wl_g_rates,
 +      .n_bitrates = wl_g_rates_size,
 +};
 +
 +static const struct ieee80211_supported_band __wl_band_5ghz_a = {
 +      .band = IEEE80211_BAND_5GHZ,
 +      .bitrates = wl_a_rates,
 +      .n_bitrates = wl_a_rates_size,
 +};
 +
 +/* This is to override regulatory domains defined in cfg80211 module (reg.c)
 + * By default world regulatory domain defined in reg.c puts the flags
 + * NL80211_RRF_NO_IR for 5GHz channels (for * 36..48 and 149..165).
 + * With respect to these flags, wpa_supplicant doesn't * start p2p
 + * operations on 5GHz channels. All the changes in world regulatory
 + * domain are to be done here.
 + */
 +static const struct ieee80211_regdomain brcmf_regdom = {
 +      .n_reg_rules = 4,
 +      .alpha2 =  "99",
 +      .reg_rules = {
 +              /* IEEE 802.11b/g, channels 1..11 */
 +              REG_RULE(2412-10, 2472+10, 40, 6, 20, 0),
 +              /* If any */
 +              /* IEEE 802.11 channel 14 - Only JP enables
 +               * this and for 802.11b only
 +               */
 +              REG_RULE(2484-10, 2484+10, 20, 6, 20, 0),
 +              /* IEEE 802.11a, channel 36..64 */
 +              REG_RULE(5150-10, 5350+10, 80, 6, 20, 0),
 +              /* IEEE 802.11a, channel 100..165 */
 +              REG_RULE(5470-10, 5850+10, 80, 6, 20, 0), }
 +};
 +
 +static const u32 __wl_cipher_suites[] = {
 +      WLAN_CIPHER_SUITE_WEP40,
 +      WLAN_CIPHER_SUITE_WEP104,
 +      WLAN_CIPHER_SUITE_TKIP,
 +      WLAN_CIPHER_SUITE_CCMP,
 +      WLAN_CIPHER_SUITE_AES_CMAC,
 +};
 +
 +/* Vendor specific ie. id = 221, oui and type defines exact ie */
 +struct brcmf_vs_tlv {
 +      u8 id;
 +      u8 len;
 +      u8 oui[3];
 +      u8 oui_type;
 +};
 +
 +struct parsed_vndr_ie_info {
 +      u8 *ie_ptr;
 +      u32 ie_len;     /* total length including id & length field */
 +      struct brcmf_vs_tlv vndrie;
 +};
 +
 +struct parsed_vndr_ies {
 +      u32 count;
 +      struct parsed_vndr_ie_info ie_info[VNDR_IE_PARSE_LIMIT];
 +};
 +
 +static int brcmf_roamoff;
 +module_param_named(roamoff, brcmf_roamoff, int, S_IRUSR);
 +MODULE_PARM_DESC(roamoff, "do not use internal roaming engine");
 +
 +/* Quarter dBm units to mW
 + * Table starts at QDBM_OFFSET, so the first entry is mW for qdBm=153
 + * Table is offset so the last entry is largest mW value that fits in
 + * a u16.
 + */
 +
 +#define QDBM_OFFSET 153               /* Offset for first entry */
 +#define QDBM_TABLE_LEN 40     /* Table size */
 +
 +/* Smallest mW value that will round up to the first table entry, QDBM_OFFSET.
 + * Value is ( mW(QDBM_OFFSET - 1) + mW(QDBM_OFFSET) ) / 2
 + */
 +#define QDBM_TABLE_LOW_BOUND 6493     /* Low bound */
 +
 +/* Largest mW value that will round down to the last table entry,
 + * QDBM_OFFSET + QDBM_TABLE_LEN-1.
 + * Value is ( mW(QDBM_OFFSET + QDBM_TABLE_LEN - 1) +
 + * mW(QDBM_OFFSET + QDBM_TABLE_LEN) ) / 2.
 + */
 +#define QDBM_TABLE_HIGH_BOUND 64938   /* High bound */
 +
 +static const u16 nqdBm_to_mW_map[QDBM_TABLE_LEN] = {
 +/* qdBm:      +0      +1      +2      +3      +4      +5      +6      +7 */
 +/* 153: */ 6683, 7079, 7499, 7943, 8414, 8913, 9441, 10000,
 +/* 161: */ 10593, 11220, 11885, 12589, 13335, 14125, 14962, 15849,
 +/* 169: */ 16788, 17783, 18836, 19953, 21135, 22387, 23714, 25119,
 +/* 177: */ 26607, 28184, 29854, 31623, 33497, 35481, 37584, 39811,
 +/* 185: */ 42170, 44668, 47315, 50119, 53088, 56234, 59566, 63096
 +};
 +
 +static u16 brcmf_qdbm_to_mw(u8 qdbm)
 +{
 +      uint factor = 1;
 +      int idx = qdbm - QDBM_OFFSET;
 +
 +      if (idx >= QDBM_TABLE_LEN)
 +              /* clamp to max u16 mW value */
 +              return 0xFFFF;
 +
 +      /* scale the qdBm index up to the range of the table 0-40
 +       * where an offset of 40 qdBm equals a factor of 10 mW.
 +       */
 +      while (idx < 0) {
 +              idx += 40;
 +              factor *= 10;
 +      }
 +
 +      /* return the mW value scaled down to the correct factor of 10,
 +       * adding in factor/2 to get proper rounding.
 +       */
 +      return (nqdBm_to_mW_map[idx] + factor / 2) / factor;
 +}
 +
 +static u8 brcmf_mw_to_qdbm(u16 mw)
 +{
 +      u8 qdbm;
 +      int offset;
 +      uint mw_uint = mw;
 +      uint boundary;
 +
 +      /* handle boundary case */
 +      if (mw_uint <= 1)
 +              return 0;
 +
 +      offset = QDBM_OFFSET;
 +
 +      /* move mw into the range of the table */
 +      while (mw_uint < QDBM_TABLE_LOW_BOUND) {
 +              mw_uint *= 10;
 +              offset -= 40;
 +      }
 +
 +      for (qdbm = 0; qdbm < QDBM_TABLE_LEN - 1; qdbm++) {
 +              boundary = nqdBm_to_mW_map[qdbm] + (nqdBm_to_mW_map[qdbm + 1] -
 +                                                  nqdBm_to_mW_map[qdbm]) / 2;
 +              if (mw_uint < boundary)
 +                      break;
 +      }
 +
 +      qdbm += (u8) offset;
 +
 +      return qdbm;
 +}
 +
 +static u16 chandef_to_chanspec(struct brcmu_d11inf *d11inf,
 +                             struct cfg80211_chan_def *ch)
 +{
 +      struct brcmu_chan ch_inf;
 +      s32 primary_offset;
 +
 +      brcmf_dbg(TRACE, "chandef: control %d center %d width %d\n",
 +                ch->chan->center_freq, ch->center_freq1, ch->width);
 +      ch_inf.chnum = ieee80211_frequency_to_channel(ch->center_freq1);
 +      primary_offset = ch->center_freq1 - ch->chan->center_freq;
 +      switch (ch->width) {
 +      case NL80211_CHAN_WIDTH_20:
++      case NL80211_CHAN_WIDTH_20_NOHT:
 +              ch_inf.bw = BRCMU_CHAN_BW_20;
 +              WARN_ON(primary_offset != 0);
 +              break;
 +      case NL80211_CHAN_WIDTH_40:
 +              ch_inf.bw = BRCMU_CHAN_BW_40;
 +              if (primary_offset < 0)
 +                      ch_inf.sb = BRCMU_CHAN_SB_U;
 +              else
 +                      ch_inf.sb = BRCMU_CHAN_SB_L;
 +              break;
 +      case NL80211_CHAN_WIDTH_80:
 +              ch_inf.bw = BRCMU_CHAN_BW_80;
 +              if (primary_offset < 0) {
 +                      if (primary_offset < -CH_10MHZ_APART)
 +                              ch_inf.sb = BRCMU_CHAN_SB_UU;
 +                      else
 +                              ch_inf.sb = BRCMU_CHAN_SB_UL;
 +              } else {
 +                      if (primary_offset > CH_10MHZ_APART)
 +                              ch_inf.sb = BRCMU_CHAN_SB_LL;
 +                      else
 +                              ch_inf.sb = BRCMU_CHAN_SB_LU;
 +              }
 +              break;
++      case NL80211_CHAN_WIDTH_80P80:
++      case NL80211_CHAN_WIDTH_160:
++      case NL80211_CHAN_WIDTH_5:
++      case NL80211_CHAN_WIDTH_10:
 +      default:
 +              WARN_ON_ONCE(1);
 +      }
 +      switch (ch->chan->band) {
 +      case IEEE80211_BAND_2GHZ:
 +              ch_inf.band = BRCMU_CHAN_BAND_2G;
 +              break;
 +      case IEEE80211_BAND_5GHZ:
 +              ch_inf.band = BRCMU_CHAN_BAND_5G;
 +              break;
++      case IEEE80211_BAND_60GHZ:
 +      default:
 +              WARN_ON_ONCE(1);
 +      }
 +      d11inf->encchspec(&ch_inf);
 +
 +      return ch_inf.chspec;
 +}
 +
 +u16 channel_to_chanspec(struct brcmu_d11inf *d11inf,
 +                      struct ieee80211_channel *ch)
 +{
 +      struct brcmu_chan ch_inf;
 +
 +      ch_inf.chnum = ieee80211_frequency_to_channel(ch->center_freq);
 +      ch_inf.bw = BRCMU_CHAN_BW_20;
 +      d11inf->encchspec(&ch_inf);
 +
 +      return ch_inf.chspec;
 +}
 +
 +/* Traverse a string of 1-byte tag/1-byte length/variable-length value
 + * triples, returning a pointer to the substring whose first element
 + * matches tag
 + */
 +const struct brcmf_tlv *
 +brcmf_parse_tlvs(const void *buf, int buflen, uint key)
 +{
 +      const struct brcmf_tlv *elt = buf;
 +      int totlen = buflen;
 +
 +      /* find tagged parameter */
 +      while (totlen >= TLV_HDR_LEN) {
 +              int len = elt->len;
 +
 +              /* validate remaining totlen */
 +              if ((elt->id == key) && (totlen >= (len + TLV_HDR_LEN)))
 +                      return elt;
 +
 +              elt = (struct brcmf_tlv *)((u8 *)elt + (len + TLV_HDR_LEN));
 +              totlen -= (len + TLV_HDR_LEN);
 +      }
 +
 +      return NULL;
 +}
 +
 +/* Is any of the tlvs the expected entry? If
 + * not update the tlvs buffer pointer/length.
 + */
 +static bool
 +brcmf_tlv_has_ie(const u8 *ie, const u8 **tlvs, u32 *tlvs_len,
 +               const u8 *oui, u32 oui_len, u8 type)
 +{
 +      /* If the contents match the OUI and the type */
 +      if (ie[TLV_LEN_OFF] >= oui_len + 1 &&
 +          !memcmp(&ie[TLV_BODY_OFF], oui, oui_len) &&
 +          type == ie[TLV_BODY_OFF + oui_len]) {
 +              return true;
 +      }
 +
 +      if (tlvs == NULL)
 +              return false;
 +      /* point to the next ie */
 +      ie += ie[TLV_LEN_OFF] + TLV_HDR_LEN;
 +      /* calculate the length of the rest of the buffer */
 +      *tlvs_len -= (int)(ie - *tlvs);
 +      /* update the pointer to the start of the buffer */
 +      *tlvs = ie;
 +
 +      return false;
 +}
 +
 +static struct brcmf_vs_tlv *
 +brcmf_find_wpaie(const u8 *parse, u32 len)
 +{
 +      const struct brcmf_tlv *ie;
 +
 +      while ((ie = brcmf_parse_tlvs(parse, len, WLAN_EID_VENDOR_SPECIFIC))) {
 +              if (brcmf_tlv_has_ie((const u8 *)ie, &parse, &len,
 +                                   WPA_OUI, TLV_OUI_LEN, WPA_OUI_TYPE))
 +                      return (struct brcmf_vs_tlv *)ie;
 +      }
 +      return NULL;
 +}
 +
 +static struct brcmf_vs_tlv *
 +brcmf_find_wpsie(const u8 *parse, u32 len)
 +{
 +      const struct brcmf_tlv *ie;
 +
 +      while ((ie = brcmf_parse_tlvs(parse, len, WLAN_EID_VENDOR_SPECIFIC))) {
 +              if (brcmf_tlv_has_ie((u8 *)ie, &parse, &len,
 +                                   WPA_OUI, TLV_OUI_LEN, WPS_OUI_TYPE))
 +                      return (struct brcmf_vs_tlv *)ie;
 +      }
 +      return NULL;
 +}
 +
 +
 +static void convert_key_from_CPU(struct brcmf_wsec_key *key,
 +                               struct brcmf_wsec_key_le *key_le)
 +{
 +      key_le->index = cpu_to_le32(key->index);
 +      key_le->len = cpu_to_le32(key->len);
 +      key_le->algo = cpu_to_le32(key->algo);
 +      key_le->flags = cpu_to_le32(key->flags);
 +      key_le->rxiv.hi = cpu_to_le32(key->rxiv.hi);
 +      key_le->rxiv.lo = cpu_to_le16(key->rxiv.lo);
 +      key_le->iv_initialized = cpu_to_le32(key->iv_initialized);
 +      memcpy(key_le->data, key->data, sizeof(key->data));
 +      memcpy(key_le->ea, key->ea, sizeof(key->ea));
 +}
 +
 +static int
 +send_key_to_dongle(struct net_device *ndev, struct brcmf_wsec_key *key)
 +{
 +      int err;
 +      struct brcmf_wsec_key_le key_le;
 +
 +      convert_key_from_CPU(key, &key_le);
 +
 +      brcmf_netdev_wait_pend8021x(ndev);
 +
 +      err = brcmf_fil_bsscfg_data_set(netdev_priv(ndev), "wsec_key", &key_le,
 +                                      sizeof(key_le));
 +
 +      if (err)
 +              brcmf_err("wsec_key error (%d)\n", err);
 +      return err;
 +}
 +
 +static s32
 +brcmf_configure_arp_offload(struct brcmf_if *ifp, bool enable)
 +{
 +      s32 err;
 +      u32 mode;
 +
 +      if (enable)
 +              mode = BRCMF_ARP_OL_AGENT | BRCMF_ARP_OL_PEER_AUTO_REPLY;
 +      else
 +              mode = 0;
 +
 +      /* Try to set and enable ARP offload feature, this may fail, then it  */
 +      /* is simply not supported and err 0 will be returned                 */
 +      err = brcmf_fil_iovar_int_set(ifp, "arp_ol", mode);
 +      if (err) {
 +              brcmf_dbg(TRACE, "failed to set ARP offload mode to 0x%x, err = %d\n",
 +                        mode, err);
 +              err = 0;
 +      } else {
 +              err = brcmf_fil_iovar_int_set(ifp, "arpoe", enable);
 +              if (err) {
 +                      brcmf_dbg(TRACE, "failed to configure (%d) ARP offload err = %d\n",
 +                                enable, err);
 +                      err = 0;
 +              } else
 +                      brcmf_dbg(TRACE, "successfully configured (%d) ARP offload to 0x%x\n",
 +                                enable, mode);
 +      }
 +
 +      return err;
 +}
 +
 +static void
 +brcmf_cfg80211_update_proto_addr_mode(struct wireless_dev *wdev)
 +{
 +      struct brcmf_cfg80211_vif *vif;
 +      struct brcmf_if *ifp;
 +
 +      vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
 +      ifp = vif->ifp;
 +
 +      if ((wdev->iftype == NL80211_IFTYPE_ADHOC) ||
 +          (wdev->iftype == NL80211_IFTYPE_AP) ||
 +          (wdev->iftype == NL80211_IFTYPE_P2P_GO))
 +              brcmf_proto_configure_addr_mode(ifp->drvr, ifp->ifidx,
 +                                              ADDR_DIRECT);
 +      else
 +              brcmf_proto_configure_addr_mode(ifp->drvr, ifp->ifidx,
 +                                              ADDR_INDIRECT);
 +}
 +
 +static bool brcmf_is_apmode(struct brcmf_cfg80211_vif *vif)
 +{
 +      enum nl80211_iftype iftype;
 +
 +      iftype = vif->wdev.iftype;
 +      return iftype == NL80211_IFTYPE_AP || iftype == NL80211_IFTYPE_P2P_GO;
 +}
 +
 +static bool brcmf_is_ibssmode(struct brcmf_cfg80211_vif *vif)
 +{
 +      return vif->wdev.iftype == NL80211_IFTYPE_ADHOC;
 +}
 +
 +static struct wireless_dev *brcmf_cfg80211_add_iface(struct wiphy *wiphy,
 +                                                   const char *name,
 +                                                   enum nl80211_iftype type,
 +                                                   u32 *flags,
 +                                                   struct vif_params *params)
 +{
 +      struct wireless_dev *wdev;
 +
 +      brcmf_dbg(TRACE, "enter: %s type %d\n", name, type);
 +      switch (type) {
 +      case NL80211_IFTYPE_ADHOC:
 +      case NL80211_IFTYPE_STATION:
 +      case NL80211_IFTYPE_AP:
 +      case NL80211_IFTYPE_AP_VLAN:
 +      case NL80211_IFTYPE_WDS:
 +      case NL80211_IFTYPE_MONITOR:
 +      case NL80211_IFTYPE_MESH_POINT:
 +              return ERR_PTR(-EOPNOTSUPP);
 +      case NL80211_IFTYPE_P2P_CLIENT:
 +      case NL80211_IFTYPE_P2P_GO:
 +      case NL80211_IFTYPE_P2P_DEVICE:
 +              wdev = brcmf_p2p_add_vif(wiphy, name, type, flags, params);
 +              if (!IS_ERR(wdev))
 +                      brcmf_cfg80211_update_proto_addr_mode(wdev);
 +              return wdev;
 +      case NL80211_IFTYPE_UNSPECIFIED:
 +      default:
 +              return ERR_PTR(-EINVAL);
 +      }
 +}
 +
 +static void brcmf_scan_config_mpc(struct brcmf_if *ifp, int mpc)
 +{
 +      if (brcmf_feat_is_quirk_enabled(ifp, BRCMF_FEAT_QUIRK_NEED_MPC))
 +              brcmf_set_mpc(ifp, mpc);
 +}
 +
 +void brcmf_set_mpc(struct brcmf_if *ifp, int mpc)
 +{
 +      s32 err = 0;
 +
 +      if (check_vif_up(ifp->vif)) {
 +              err = brcmf_fil_iovar_int_set(ifp, "mpc", mpc);
 +              if (err) {
 +                      brcmf_err("fail to set mpc\n");
 +                      return;
 +              }
 +              brcmf_dbg(INFO, "MPC : %d\n", mpc);
 +      }
 +}
 +
 +s32 brcmf_notify_escan_complete(struct brcmf_cfg80211_info *cfg,
 +                              struct brcmf_if *ifp, bool aborted,
 +                              bool fw_abort)
 +{
 +      struct brcmf_scan_params_le params_le;
 +      struct cfg80211_scan_request *scan_request;
 +      s32 err = 0;
 +
 +      brcmf_dbg(SCAN, "Enter\n");
 +
 +      /* clear scan request, because the FW abort can cause a second call */
 +      /* to this functon and might cause a double cfg80211_scan_done      */
 +      scan_request = cfg->scan_request;
 +      cfg->scan_request = NULL;
 +
 +      if (timer_pending(&cfg->escan_timeout))
 +              del_timer_sync(&cfg->escan_timeout);
 +
 +      if (fw_abort) {
 +              /* Do a scan abort to stop the driver's scan engine */
 +              brcmf_dbg(SCAN, "ABORT scan in firmware\n");
 +              memset(&params_le, 0, sizeof(params_le));
 +              memset(params_le.bssid, 0xFF, ETH_ALEN);
 +              params_le.bss_type = DOT11_BSSTYPE_ANY;
 +              params_le.scan_type = 0;
 +              params_le.channel_num = cpu_to_le32(1);
 +              params_le.nprobes = cpu_to_le32(1);
 +              params_le.active_time = cpu_to_le32(-1);
 +              params_le.passive_time = cpu_to_le32(-1);
 +              params_le.home_time = cpu_to_le32(-1);
 +              /* Scan is aborted by setting channel_list[0] to -1 */
 +              params_le.channel_list[0] = cpu_to_le16(-1);
 +              /* E-Scan (or anyother type) can be aborted by SCAN */
 +              err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SCAN,
 +                                           &params_le, sizeof(params_le));
 +              if (err)
 +                      brcmf_err("Scan abort  failed\n");
 +      }
 +
 +      brcmf_scan_config_mpc(ifp, 1);
 +
 +      /*
 +       * e-scan can be initiated by scheduled scan
 +       * which takes precedence.
 +       */
 +      if (cfg->sched_escan) {
 +              brcmf_dbg(SCAN, "scheduled scan completed\n");
 +              cfg->sched_escan = false;
 +              if (!aborted)
 +                      cfg80211_sched_scan_results(cfg_to_wiphy(cfg));
 +      } else if (scan_request) {
 +              brcmf_dbg(SCAN, "ESCAN Completed scan: %s\n",
 +                        aborted ? "Aborted" : "Done");
 +              cfg80211_scan_done(scan_request, aborted);
 +      }
 +      if (!test_and_clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status))
 +              brcmf_dbg(SCAN, "Scan complete, probably P2P scan\n");
 +
 +      return err;
 +}
 +
 +static
 +int brcmf_cfg80211_del_iface(struct wiphy *wiphy, struct wireless_dev *wdev)
 +{
 +      struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy);
 +      struct net_device *ndev = wdev->netdev;
 +
 +      /* vif event pending in firmware */
 +      if (brcmf_cfg80211_vif_event_armed(cfg))
 +              return -EBUSY;
 +
 +      if (ndev) {
 +              if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status) &&
 +                  cfg->escan_info.ifp == netdev_priv(ndev))
 +                      brcmf_notify_escan_complete(cfg, netdev_priv(ndev),
 +                                                  true, true);
 +
 +              brcmf_fil_iovar_int_set(netdev_priv(ndev), "mpc", 1);
 +      }
 +
 +      switch (wdev->iftype) {
 +      case NL80211_IFTYPE_ADHOC:
 +      case NL80211_IFTYPE_STATION:
 +      case NL80211_IFTYPE_AP:
 +      case NL80211_IFTYPE_AP_VLAN:
 +      case NL80211_IFTYPE_WDS:
 +      case NL80211_IFTYPE_MONITOR:
 +      case NL80211_IFTYPE_MESH_POINT:
 +              return -EOPNOTSUPP;
 +      case NL80211_IFTYPE_P2P_CLIENT:
 +      case NL80211_IFTYPE_P2P_GO:
 +      case NL80211_IFTYPE_P2P_DEVICE:
 +              return brcmf_p2p_del_vif(wiphy, wdev);
 +      case NL80211_IFTYPE_UNSPECIFIED:
 +      default:
 +              return -EINVAL;
 +      }
 +      return -EOPNOTSUPP;
 +}
 +
 +static s32
 +brcmf_cfg80211_change_iface(struct wiphy *wiphy, struct net_device *ndev,
 +                       enum nl80211_iftype type, u32 *flags,
 +                       struct vif_params *params)
 +{
 +      struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy);
 +      struct brcmf_if *ifp = netdev_priv(ndev);
 +      struct brcmf_cfg80211_vif *vif = ifp->vif;
 +      s32 infra = 0;
 +      s32 ap = 0;
 +      s32 err = 0;
 +
 +      brcmf_dbg(TRACE, "Enter, ndev=%p, type=%d\n", ndev, type);
 +
 +      switch (type) {
 +      case NL80211_IFTYPE_MONITOR:
 +      case NL80211_IFTYPE_WDS:
 +              brcmf_err("type (%d) : currently we do not support this type\n",
 +                        type);
 +              return -EOPNOTSUPP;
 +      case NL80211_IFTYPE_ADHOC:
 +              infra = 0;
 +              break;
 +      case NL80211_IFTYPE_STATION:
 +              /* Ignore change for p2p IF. Unclear why supplicant does this */
 +              if ((vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT) ||
 +                  (vif->wdev.iftype == NL80211_IFTYPE_P2P_GO)) {
 +                      brcmf_dbg(TRACE, "Ignoring cmd for p2p if\n");
 +                      /* WAR: It is unexpected to get a change of VIF for P2P
 +                       * IF, but it happens. The request can not be handled
 +                       * but returning EPERM causes a crash. Returning 0
 +                       * without setting ieee80211_ptr->iftype causes trace
 +                       * (WARN_ON) but it works with wpa_supplicant
 +                       */
 +                      return 0;
 +              }
 +              infra = 1;
 +              break;
 +      case NL80211_IFTYPE_AP:
 +      case NL80211_IFTYPE_P2P_GO:
 +              ap = 1;
 +              break;
 +      default:
 +              err = -EINVAL;
 +              goto done;
 +      }
 +
 +      if (ap) {
 +              if (type == NL80211_IFTYPE_P2P_GO) {
 +                      brcmf_dbg(INFO, "IF Type = P2P GO\n");
 +                      err = brcmf_p2p_ifchange(cfg, BRCMF_FIL_P2P_IF_GO);
 +              }
 +              if (!err) {
 +                      set_bit(BRCMF_VIF_STATUS_AP_CREATING, &vif->sme_state);
 +                      brcmf_dbg(INFO, "IF Type = AP\n");
 +              }
 +      } else {
 +              err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_INFRA, infra);
 +              if (err) {
 +                      brcmf_err("WLC_SET_INFRA error (%d)\n", err);
 +                      err = -EAGAIN;
 +                      goto done;
 +              }
 +              brcmf_dbg(INFO, "IF Type = %s\n", brcmf_is_ibssmode(vif) ?
 +                        "Adhoc" : "Infra");
 +      }
 +      ndev->ieee80211_ptr->iftype = type;
 +
 +      brcmf_cfg80211_update_proto_addr_mode(&vif->wdev);
 +
 +done:
 +      brcmf_dbg(TRACE, "Exit\n");
 +
 +      return err;
 +}
 +
 +static void brcmf_escan_prep(struct brcmf_cfg80211_info *cfg,
 +                           struct brcmf_scan_params_le *params_le,
 +                           struct cfg80211_scan_request *request)
 +{
 +      u32 n_ssids;
 +      u32 n_channels;
 +      s32 i;
 +      s32 offset;
 +      u16 chanspec;
 +      char *ptr;
 +      struct brcmf_ssid_le ssid_le;
 +
 +      memset(params_le->bssid, 0xFF, ETH_ALEN);
 +      params_le->bss_type = DOT11_BSSTYPE_ANY;
 +      params_le->scan_type = 0;
 +      params_le->channel_num = 0;
 +      params_le->nprobes = cpu_to_le32(-1);
 +      params_le->active_time = cpu_to_le32(-1);
 +      params_le->passive_time = cpu_to_le32(-1);
 +      params_le->home_time = cpu_to_le32(-1);
 +      memset(&params_le->ssid_le, 0, sizeof(params_le->ssid_le));
 +
 +      /* if request is null exit so it will be all channel broadcast scan */
 +      if (!request)
 +              return;
 +
 +      n_ssids = request->n_ssids;
 +      n_channels = request->n_channels;
 +      /* Copy channel array if applicable */
 +      brcmf_dbg(SCAN, "### List of channelspecs to scan ### %d\n",
 +                n_channels);
 +      if (n_channels > 0) {
 +              for (i = 0; i < n_channels; i++) {
 +                      chanspec = channel_to_chanspec(&cfg->d11inf,
 +                                                     request->channels[i]);
 +                      brcmf_dbg(SCAN, "Chan : %d, Channel spec: %x\n",
 +                                request->channels[i]->hw_value, chanspec);
 +                      params_le->channel_list[i] = cpu_to_le16(chanspec);
 +              }
 +      } else {
 +              brcmf_dbg(SCAN, "Scanning all channels\n");
 +      }
 +      /* Copy ssid array if applicable */
 +      brcmf_dbg(SCAN, "### List of SSIDs to scan ### %d\n", n_ssids);
 +      if (n_ssids > 0) {
 +              offset = offsetof(struct brcmf_scan_params_le, channel_list) +
 +                              n_channels * sizeof(u16);
 +              offset = roundup(offset, sizeof(u32));
 +              ptr = (char *)params_le + offset;
 +              for (i = 0; i < n_ssids; i++) {
 +                      memset(&ssid_le, 0, sizeof(ssid_le));
 +                      ssid_le.SSID_len =
 +                                      cpu_to_le32(request->ssids[i].ssid_len);
 +                      memcpy(ssid_le.SSID, request->ssids[i].ssid,
 +                             request->ssids[i].ssid_len);
 +                      if (!ssid_le.SSID_len)
 +                              brcmf_dbg(SCAN, "%d: Broadcast scan\n", i);
 +                      else
 +                              brcmf_dbg(SCAN, "%d: scan for  %s size =%d\n",
 +                                        i, ssid_le.SSID, ssid_le.SSID_len);
 +                      memcpy(ptr, &ssid_le, sizeof(ssid_le));
 +                      ptr += sizeof(ssid_le);
 +              }
 +      } else {
 +              brcmf_dbg(SCAN, "Broadcast scan %p\n", request->ssids);
 +              if ((request->ssids) && request->ssids->ssid_len) {
 +                      brcmf_dbg(SCAN, "SSID %s len=%d\n",
 +                                params_le->ssid_le.SSID,
 +                                request->ssids->ssid_len);
 +                      params_le->ssid_le.SSID_len =
 +                              cpu_to_le32(request->ssids->ssid_len);
 +                      memcpy(&params_le->ssid_le.SSID, request->ssids->ssid,
 +                              request->ssids->ssid_len);
 +              }
 +      }
 +      /* Adding mask to channel numbers */
 +      params_le->channel_num =
 +              cpu_to_le32((n_ssids << BRCMF_SCAN_PARAMS_NSSID_SHIFT) |
 +                      (n_channels & BRCMF_SCAN_PARAMS_COUNT_MASK));
 +}
 +
 +static s32
 +brcmf_run_escan(struct brcmf_cfg80211_info *cfg, struct brcmf_if *ifp,
 +              struct cfg80211_scan_request *request, u16 action)
 +{
 +      s32 params_size = BRCMF_SCAN_PARAMS_FIXED_SIZE +
 +                        offsetof(struct brcmf_escan_params_le, params_le);
 +      struct brcmf_escan_params_le *params;
 +      s32 err = 0;
 +
 +      brcmf_dbg(SCAN, "E-SCAN START\n");
 +
 +      if (request != NULL) {
 +              /* Allocate space for populating ssids in struct */
 +              params_size += sizeof(u32) * ((request->n_channels + 1) / 2);
 +
 +              /* Allocate space for populating ssids in struct */
 +              params_size += sizeof(struct brcmf_ssid) * request->n_ssids;
 +      }
 +
 +      params = kzalloc(params_size, GFP_KERNEL);
 +      if (!params) {
 +              err = -ENOMEM;
 +              goto exit;
 +      }
 +      BUG_ON(params_size + sizeof("escan") >= BRCMF_DCMD_MEDLEN);
 +      brcmf_escan_prep(cfg, &params->params_le, request);
 +      params->version = cpu_to_le32(BRCMF_ESCAN_REQ_VERSION);
 +      params->action = cpu_to_le16(action);
 +      params->sync_id = cpu_to_le16(0x1234);
 +
 +      err = brcmf_fil_iovar_data_set(ifp, "escan", params, params_size);
 +      if (err) {
 +              if (err == -EBUSY)
 +                      brcmf_dbg(INFO, "system busy : escan canceled\n");
 +              else
 +                      brcmf_err("error (%d)\n", err);
 +      }
 +
 +      kfree(params);
 +exit:
 +      return err;
 +}
 +
 +static s32
 +brcmf_do_escan(struct brcmf_cfg80211_info *cfg, struct wiphy *wiphy,
 +             struct brcmf_if *ifp, struct cfg80211_scan_request *request)
 +{
 +      s32 err;
 +      u32 passive_scan;
 +      struct brcmf_scan_results *results;
 +      struct escan_info *escan = &cfg->escan_info;
 +
 +      brcmf_dbg(SCAN, "Enter\n");
 +      escan->ifp = ifp;
 +      escan->wiphy = wiphy;
 +      escan->escan_state = WL_ESCAN_STATE_SCANNING;
 +      passive_scan = cfg->active_scan ? 0 : 1;
 +      err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PASSIVE_SCAN,
 +                                  passive_scan);
 +      if (err) {
 +              brcmf_err("error (%d)\n", err);
 +              return err;
 +      }
 +      brcmf_scan_config_mpc(ifp, 0);
 +      results = (struct brcmf_scan_results *)cfg->escan_info.escan_buf;
 +      results->version = 0;
 +      results->count = 0;
 +      results->buflen = WL_ESCAN_RESULTS_FIXED_SIZE;
 +
 +      err = escan->run(cfg, ifp, request, WL_ESCAN_ACTION_START);
 +      if (err)
 +              brcmf_scan_config_mpc(ifp, 1);
 +      return err;
 +}
 +
 +static s32
 +brcmf_cfg80211_escan(struct wiphy *wiphy, struct brcmf_cfg80211_vif *vif,
 +                   struct cfg80211_scan_request *request,
 +                   struct cfg80211_ssid *this_ssid)
 +{
 +      struct brcmf_if *ifp = vif->ifp;
 +      struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
 +      struct cfg80211_ssid *ssids;
 +      struct brcmf_cfg80211_scan_req *sr = &cfg->scan_req_int;
 +      u32 passive_scan;
 +      bool escan_req;
 +      bool spec_scan;
 +      s32 err;
 +      u32 SSID_len;
 +
 +      brcmf_dbg(SCAN, "START ESCAN\n");
 +
 +      if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) {
 +              brcmf_err("Scanning already: status (%lu)\n", cfg->scan_status);
 +              return -EAGAIN;
 +      }
 +      if (test_bit(BRCMF_SCAN_STATUS_ABORT, &cfg->scan_status)) {
 +              brcmf_err("Scanning being aborted: status (%lu)\n",
 +                        cfg->scan_status);
 +              return -EAGAIN;
 +      }
 +      if (test_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status)) {
 +              brcmf_err("Scanning suppressed: status (%lu)\n",
 +                        cfg->scan_status);
 +              return -EAGAIN;
 +      }
 +      if (test_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state)) {
 +              brcmf_err("Connecting: status (%lu)\n", ifp->vif->sme_state);
 +              return -EAGAIN;
 +      }
 +
 +      /* If scan req comes for p2p0, send it over primary I/F */
 +      if (vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif)
 +              vif = cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif;
 +
 +      /* Arm scan timeout timer */
 +      mod_timer(&cfg->escan_timeout, jiffies +
 +                      WL_ESCAN_TIMER_INTERVAL_MS * HZ / 1000);
 +
 +      escan_req = false;
 +      if (request) {
 +              /* scan bss */
 +              ssids = request->ssids;
 +              escan_req = true;
 +      } else {
 +              /* scan in ibss */
 +              /* we don't do escan in ibss */
 +              ssids = this_ssid;
 +      }
 +
 +      cfg->scan_request = request;
 +      set_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
 +      if (escan_req) {
 +              cfg->escan_info.run = brcmf_run_escan;
 +              err = brcmf_p2p_scan_prep(wiphy, request, vif);
 +              if (err)
 +                      goto scan_out;
 +
 +              err = brcmf_do_escan(cfg, wiphy, vif->ifp, request);
 +              if (err)
 +                      goto scan_out;
 +      } else {
 +              brcmf_dbg(SCAN, "ssid \"%s\", ssid_len (%d)\n",
 +                        ssids->ssid, ssids->ssid_len);
 +              memset(&sr->ssid_le, 0, sizeof(sr->ssid_le));
 +              SSID_len = min_t(u8, sizeof(sr->ssid_le.SSID), ssids->ssid_len);
 +              sr->ssid_le.SSID_len = cpu_to_le32(0);
 +              spec_scan = false;
 +              if (SSID_len) {
 +                      memcpy(sr->ssid_le.SSID, ssids->ssid, SSID_len);
 +                      sr->ssid_le.SSID_len = cpu_to_le32(SSID_len);
 +                      spec_scan = true;
 +              } else
 +                      brcmf_dbg(SCAN, "Broadcast scan\n");
 +
 +              passive_scan = cfg->active_scan ? 0 : 1;
 +              err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PASSIVE_SCAN,
 +                                          passive_scan);
 +              if (err) {
 +                      brcmf_err("WLC_SET_PASSIVE_SCAN error (%d)\n", err);
 +                      goto scan_out;
 +              }
 +              brcmf_scan_config_mpc(ifp, 0);
 +              err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SCAN,
 +                                           &sr->ssid_le, sizeof(sr->ssid_le));
 +              if (err) {
 +                      if (err == -EBUSY)
 +                              brcmf_dbg(INFO, "BUSY: scan for \"%s\" canceled\n",
 +                                        sr->ssid_le.SSID);
 +                      else
 +                              brcmf_err("WLC_SCAN error (%d)\n", err);
 +
 +                      brcmf_scan_config_mpc(ifp, 1);
 +                      goto scan_out;
 +              }
 +      }
 +
 +      return 0;
 +
 +scan_out:
 +      clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
 +      if (timer_pending(&cfg->escan_timeout))
 +              del_timer_sync(&cfg->escan_timeout);
 +      cfg->scan_request = NULL;
 +      return err;
 +}
 +
 +static s32
 +brcmf_cfg80211_scan(struct wiphy *wiphy, struct cfg80211_scan_request *request)
 +{
 +      struct brcmf_cfg80211_vif *vif;
 +      s32 err = 0;
 +
 +      brcmf_dbg(TRACE, "Enter\n");
 +      vif = container_of(request->wdev, struct brcmf_cfg80211_vif, wdev);
 +      if (!check_vif_up(vif))
 +              return -EIO;
 +
 +      err = brcmf_cfg80211_escan(wiphy, vif, request, NULL);
 +
 +      if (err)
 +              brcmf_err("scan error (%d)\n", err);
 +
 +      brcmf_dbg(TRACE, "Exit\n");
 +      return err;
 +}
 +
 +static s32 brcmf_set_rts(struct net_device *ndev, u32 rts_threshold)
 +{
 +      s32 err = 0;
 +
 +      err = brcmf_fil_iovar_int_set(netdev_priv(ndev), "rtsthresh",
 +                                    rts_threshold);
 +      if (err)
 +              brcmf_err("Error (%d)\n", err);
 +
 +      return err;
 +}
 +
 +static s32 brcmf_set_frag(struct net_device *ndev, u32 frag_threshold)
 +{
 +      s32 err = 0;
 +
 +      err = brcmf_fil_iovar_int_set(netdev_priv(ndev), "fragthresh",
 +                                    frag_threshold);
 +      if (err)
 +              brcmf_err("Error (%d)\n", err);
 +
 +      return err;
 +}
 +
 +static s32 brcmf_set_retry(struct net_device *ndev, u32 retry, bool l)
 +{
 +      s32 err = 0;
 +      u32 cmd = (l ? BRCMF_C_SET_LRL : BRCMF_C_SET_SRL);
 +
 +      err = brcmf_fil_cmd_int_set(netdev_priv(ndev), cmd, retry);
 +      if (err) {
 +              brcmf_err("cmd (%d) , error (%d)\n", cmd, err);
 +              return err;
 +      }
 +      return err;
 +}
 +
 +static s32 brcmf_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)
 +{
 +      struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
 +      struct net_device *ndev = cfg_to_ndev(cfg);
 +      struct brcmf_if *ifp = netdev_priv(ndev);
 +      s32 err = 0;
 +
 +      brcmf_dbg(TRACE, "Enter\n");
 +      if (!check_vif_up(ifp->vif))
 +              return -EIO;
 +
 +      if (changed & WIPHY_PARAM_RTS_THRESHOLD &&
 +          (cfg->conf->rts_threshold != wiphy->rts_threshold)) {
 +              cfg->conf->rts_threshold = wiphy->rts_threshold;
 +              err = brcmf_set_rts(ndev, cfg->conf->rts_threshold);
 +              if (!err)
 +                      goto done;
 +      }
 +      if (changed & WIPHY_PARAM_FRAG_THRESHOLD &&
 +          (cfg->conf->frag_threshold != wiphy->frag_threshold)) {
 +              cfg->conf->frag_threshold = wiphy->frag_threshold;
 +              err = brcmf_set_frag(ndev, cfg->conf->frag_threshold);
 +              if (!err)
 +                      goto done;
 +      }
 +      if (changed & WIPHY_PARAM_RETRY_LONG
 +          && (cfg->conf->retry_long != wiphy->retry_long)) {
 +              cfg->conf->retry_long = wiphy->retry_long;
 +              err = brcmf_set_retry(ndev, cfg->conf->retry_long, true);
 +              if (!err)
 +                      goto done;
 +      }
 +      if (changed & WIPHY_PARAM_RETRY_SHORT
 +          && (cfg->conf->retry_short != wiphy->retry_short)) {
 +              cfg->conf->retry_short = wiphy->retry_short;
 +              err = brcmf_set_retry(ndev, cfg->conf->retry_short, false);
 +              if (!err)
 +                      goto done;
 +      }
 +
 +done:
 +      brcmf_dbg(TRACE, "Exit\n");
 +      return err;
 +}
 +
 +static void brcmf_init_prof(struct brcmf_cfg80211_profile *prof)
 +{
 +      memset(prof, 0, sizeof(*prof));
 +}
 +
 +static void brcmf_link_down(struct brcmf_cfg80211_vif *vif)
 +{
 +      struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(vif->wdev.wiphy);
 +      s32 err = 0;
 +
 +      brcmf_dbg(TRACE, "Enter\n");
 +
 +      if (test_bit(BRCMF_VIF_STATUS_CONNECTED, &vif->sme_state)) {
 +              brcmf_dbg(INFO, "Call WLC_DISASSOC to stop excess roaming\n ");
 +              err = brcmf_fil_cmd_data_set(vif->ifp,
 +                                           BRCMF_C_DISASSOC, NULL, 0);
 +              if (err) {
 +                      brcmf_err("WLC_DISASSOC failed (%d)\n", err);
 +              }
 +              clear_bit(BRCMF_VIF_STATUS_CONNECTED, &vif->sme_state);
 +              cfg80211_disconnected(vif->wdev.netdev, 0, NULL, 0, GFP_KERNEL);
 +
 +      }
 +      clear_bit(BRCMF_VIF_STATUS_CONNECTING, &vif->sme_state);
 +      clear_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status);
 +      brcmf_btcoex_set_mode(vif, BRCMF_BTCOEX_ENABLED, 0);
 +      brcmf_dbg(TRACE, "Exit\n");
 +}
 +
 +static s32
 +brcmf_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *ndev,
 +                    struct cfg80211_ibss_params *params)
 +{
 +      struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
 +      struct brcmf_if *ifp = netdev_priv(ndev);
 +      struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
 +      struct brcmf_join_params join_params;
 +      size_t join_params_size = 0;
 +      s32 err = 0;
 +      s32 wsec = 0;
 +      s32 bcnprd;
 +      u16 chanspec;
 +
 +      brcmf_dbg(TRACE, "Enter\n");
 +      if (!check_vif_up(ifp->vif))
 +              return -EIO;
 +
 +      if (params->ssid)
 +              brcmf_dbg(CONN, "SSID: %s\n", params->ssid);
 +      else {
 +              brcmf_dbg(CONN, "SSID: NULL, Not supported\n");
 +              return -EOPNOTSUPP;
 +      }
 +
 +      set_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);
 +
 +      if (params->bssid)
 +              brcmf_dbg(CONN, "BSSID: %pM\n", params->bssid);
 +      else
 +              brcmf_dbg(CONN, "No BSSID specified\n");
 +
 +      if (params->chandef.chan)
 +              brcmf_dbg(CONN, "channel: %d\n",
 +                        params->chandef.chan->center_freq);
 +      else
 +              brcmf_dbg(CONN, "no channel specified\n");
 +
 +      if (params->channel_fixed)
 +              brcmf_dbg(CONN, "fixed channel required\n");
 +      else
 +              brcmf_dbg(CONN, "no fixed channel required\n");
 +
 +      if (params->ie && params->ie_len)
 +              brcmf_dbg(CONN, "ie len: %d\n", params->ie_len);
 +      else
 +              brcmf_dbg(CONN, "no ie specified\n");
 +
 +      if (params->beacon_interval)
 +              brcmf_dbg(CONN, "beacon interval: %d\n",
 +                        params->beacon_interval);
 +      else
 +              brcmf_dbg(CONN, "no beacon interval specified\n");
 +
 +      if (params->basic_rates)
 +              brcmf_dbg(CONN, "basic rates: %08X\n", params->basic_rates);
 +      else
 +              brcmf_dbg(CONN, "no basic rates specified\n");
 +
 +      if (params->privacy)
 +              brcmf_dbg(CONN, "privacy required\n");
 +      else
 +              brcmf_dbg(CONN, "no privacy required\n");
 +
 +      /* Configure Privacy for starter */
 +      if (params->privacy)
 +              wsec |= WEP_ENABLED;
 +
 +      err = brcmf_fil_iovar_int_set(ifp, "wsec", wsec);
 +      if (err) {
 +              brcmf_err("wsec failed (%d)\n", err);
 +              goto done;
 +      }
 +
 +      /* Configure Beacon Interval for starter */
 +      if (params->beacon_interval)
 +              bcnprd = params->beacon_interval;
 +      else
 +              bcnprd = 100;
 +
 +      err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_BCNPRD, bcnprd);
 +      if (err) {
 +              brcmf_err("WLC_SET_BCNPRD failed (%d)\n", err);
 +              goto done;
 +      }
 +
 +      /* Configure required join parameter */
 +      memset(&join_params, 0, sizeof(struct brcmf_join_params));
 +
 +      /* SSID */
 +      profile->ssid.SSID_len = min_t(u32, params->ssid_len, 32);
 +      memcpy(profile->ssid.SSID, params->ssid, profile->ssid.SSID_len);
 +      memcpy(join_params.ssid_le.SSID, params->ssid, profile->ssid.SSID_len);
 +      join_params.ssid_le.SSID_len = cpu_to_le32(profile->ssid.SSID_len);
 +      join_params_size = sizeof(join_params.ssid_le);
 +
 +      /* BSSID */
 +      if (params->bssid) {
 +              memcpy(join_params.params_le.bssid, params->bssid, ETH_ALEN);
 +              join_params_size = sizeof(join_params.ssid_le) +
 +                                 BRCMF_ASSOC_PARAMS_FIXED_SIZE;
 +              memcpy(profile->bssid, params->bssid, ETH_ALEN);
 +      } else {
 +              memset(join_params.params_le.bssid, 0xFF, ETH_ALEN);
 +              memset(profile->bssid, 0, ETH_ALEN);
 +      }
 +
 +      /* Channel */
 +      if (params->chandef.chan) {
 +              u32 target_channel;
 +
 +              cfg->channel =
 +                      ieee80211_frequency_to_channel(
 +                              params->chandef.chan->center_freq);
 +              if (params->channel_fixed) {
 +                      /* adding chanspec */
 +                      chanspec = chandef_to_chanspec(&cfg->d11inf,
 +                                                     &params->chandef);
 +                      join_params.params_le.chanspec_list[0] =
 +                              cpu_to_le16(chanspec);
 +                      join_params.params_le.chanspec_num = cpu_to_le32(1);
 +                      join_params_size += sizeof(join_params.params_le);
 +              }
 +
 +              /* set channel for starter */
 +              target_channel = cfg->channel;
 +              err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_CHANNEL,
 +                                          target_channel);
 +              if (err) {
 +                      brcmf_err("WLC_SET_CHANNEL failed (%d)\n", err);
 +                      goto done;
 +              }
 +      } else
 +              cfg->channel = 0;
 +
 +      cfg->ibss_starter = false;
 +
 +
 +      err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID,
 +                                   &join_params, join_params_size);
 +      if (err) {
 +              brcmf_err("WLC_SET_SSID failed (%d)\n", err);
 +              goto done;
 +      }
 +
 +done:
 +      if (err)
 +              clear_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);
 +      brcmf_dbg(TRACE, "Exit\n");
 +      return err;
 +}
 +
 +static s32
 +brcmf_cfg80211_leave_ibss(struct wiphy *wiphy, struct net_device *ndev)
 +{
 +      struct brcmf_if *ifp = netdev_priv(ndev);
 +
 +      brcmf_dbg(TRACE, "Enter\n");
 +      if (!check_vif_up(ifp->vif))
 +              return -EIO;
 +
 +      brcmf_link_down(ifp->vif);
 +
 +      brcmf_dbg(TRACE, "Exit\n");
 +
 +      return 0;
 +}
 +
 +static s32 brcmf_set_wpa_version(struct net_device *ndev,
 +                               struct cfg80211_connect_params *sme)
 +{
 +      struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
 +      struct brcmf_cfg80211_security *sec;
 +      s32 val = 0;
 +      s32 err = 0;
 +
 +      if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_1)
 +              val = WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED;
 +      else if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_2)
 +              val = WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED;
 +      else
 +              val = WPA_AUTH_DISABLED;
 +      brcmf_dbg(CONN, "setting wpa_auth to 0x%0x\n", val);
 +      err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "wpa_auth", val);
 +      if (err) {
 +              brcmf_err("set wpa_auth failed (%d)\n", err);
 +              return err;
 +      }
 +      sec = &profile->sec;
 +      sec->wpa_versions = sme->crypto.wpa_versions;
 +      return err;
 +}
 +
 +static s32 brcmf_set_auth_type(struct net_device *ndev,
 +                             struct cfg80211_connect_params *sme)
 +{
 +      struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
 +      struct brcmf_cfg80211_security *sec;
 +      s32 val = 0;
 +      s32 err = 0;
 +
 +      switch (sme->auth_type) {
 +      case NL80211_AUTHTYPE_OPEN_SYSTEM:
 +              val = 0;
 +              brcmf_dbg(CONN, "open system\n");
 +              break;
 +      case NL80211_AUTHTYPE_SHARED_KEY:
 +              val = 1;
 +              brcmf_dbg(CONN, "shared key\n");
 +              break;
 +      case NL80211_AUTHTYPE_AUTOMATIC:
 +              val = 2;
 +              brcmf_dbg(CONN, "automatic\n");
 +              break;
 +      case NL80211_AUTHTYPE_NETWORK_EAP:
 +              brcmf_dbg(CONN, "network eap\n");
 +      default:
 +              val = 2;
 +              brcmf_err("invalid auth type (%d)\n", sme->auth_type);
 +              break;
 +      }
 +
 +      err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "auth", val);
 +      if (err) {
 +              brcmf_err("set auth failed (%d)\n", err);
 +              return err;
 +      }
 +      sec = &profile->sec;
 +      sec->auth_type = sme->auth_type;
 +      return err;
 +}
 +
 +static s32
 +brcmf_set_wsec_mode(struct net_device *ndev,
 +                   struct cfg80211_connect_params *sme, bool mfp)
 +{
 +      struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
 +      struct brcmf_cfg80211_security *sec;
 +      s32 pval = 0;
 +      s32 gval = 0;
 +      s32 wsec;
 +      s32 err = 0;
 +
 +      if (sme->crypto.n_ciphers_pairwise) {
 +              switch (sme->crypto.ciphers_pairwise[0]) {
 +              case WLAN_CIPHER_SUITE_WEP40:
 +              case WLAN_CIPHER_SUITE_WEP104:
 +                      pval = WEP_ENABLED;
 +                      break;
 +              case WLAN_CIPHER_SUITE_TKIP:
 +                      pval = TKIP_ENABLED;
 +                      break;
 +              case WLAN_CIPHER_SUITE_CCMP:
 +                      pval = AES_ENABLED;
 +                      break;
 +              case WLAN_CIPHER_SUITE_AES_CMAC:
 +                      pval = AES_ENABLED;
 +                      break;
 +              default:
 +                      brcmf_err("invalid cipher pairwise (%d)\n",
 +                                sme->crypto.ciphers_pairwise[0]);
 +                      return -EINVAL;
 +              }
 +      }
 +      if (sme->crypto.cipher_group) {
 +              switch (sme->crypto.cipher_group) {
 +              case WLAN_CIPHER_SUITE_WEP40:
 +              case WLAN_CIPHER_SUITE_WEP104:
 +                      gval = WEP_ENABLED;
 +                      break;
 +              case WLAN_CIPHER_SUITE_TKIP:
 +                      gval = TKIP_ENABLED;
 +                      break;
 +              case WLAN_CIPHER_SUITE_CCMP:
 +                      gval = AES_ENABLED;
 +                      break;
 +              case WLAN_CIPHER_SUITE_AES_CMAC:
 +                      gval = AES_ENABLED;
 +                      break;
 +              default:
 +                      brcmf_err("invalid cipher group (%d)\n",
 +                                sme->crypto.cipher_group);
 +                      return -EINVAL;
 +              }
 +      }
 +
 +      brcmf_dbg(CONN, "pval (%d) gval (%d)\n", pval, gval);
 +      /* In case of privacy, but no security and WPS then simulate */
 +      /* setting AES. WPS-2.0 allows no security                   */
 +      if (brcmf_find_wpsie(sme->ie, sme->ie_len) && !pval && !gval &&
 +          sme->privacy)
 +              pval = AES_ENABLED;
 +
 +      if (mfp)
 +              wsec = pval | gval | MFP_CAPABLE;
 +      else
 +              wsec = pval | gval;
 +      err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "wsec", wsec);
 +      if (err) {
 +              brcmf_err("error (%d)\n", err);
 +              return err;
 +      }
 +
 +      sec = &profile->sec;
 +      sec->cipher_pairwise = sme->crypto.ciphers_pairwise[0];
 +      sec->cipher_group = sme->crypto.cipher_group;
 +
 +      return err;
 +}
 +
 +static s32
 +brcmf_set_key_mgmt(struct net_device *ndev, struct cfg80211_connect_params *sme)
 +{
 +      struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
 +      struct brcmf_cfg80211_security *sec;
 +      s32 val = 0;
 +      s32 err = 0;
 +
 +      if (sme->crypto.n_akm_suites) {
 +              err = brcmf_fil_bsscfg_int_get(netdev_priv(ndev),
 +                                             "wpa_auth", &val);
 +              if (err) {
 +                      brcmf_err("could not get wpa_auth (%d)\n", err);
 +                      return err;
 +              }
 +              if (val & (WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED)) {
 +                      switch (sme->crypto.akm_suites[0]) {
 +                      case WLAN_AKM_SUITE_8021X:
 +                              val = WPA_AUTH_UNSPECIFIED;
 +                              break;
 +                      case WLAN_AKM_SUITE_PSK:
 +                              val = WPA_AUTH_PSK;
 +                              break;
 +                      default:
 +                              brcmf_err("invalid cipher group (%d)\n",
 +                                        sme->crypto.cipher_group);
 +                              return -EINVAL;
 +                      }
 +              } else if (val & (WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED)) {
 +                      switch (sme->crypto.akm_suites[0]) {
 +                      case WLAN_AKM_SUITE_8021X:
 +                              val = WPA2_AUTH_UNSPECIFIED;
 +                              break;
 +                      case WLAN_AKM_SUITE_PSK:
 +                              val = WPA2_AUTH_PSK;
 +                              break;
 +                      default:
 +                              brcmf_err("invalid cipher group (%d)\n",
 +                                        sme->crypto.cipher_group);
 +                              return -EINVAL;
 +                      }
 +              }
 +
 +              brcmf_dbg(CONN, "setting wpa_auth to %d\n", val);
 +              err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev),
 +                                             "wpa_auth", val);
 +              if (err) {
 +                      brcmf_err("could not set wpa_auth (%d)\n", err);
 +                      return err;
 +              }
 +      }
 +      sec = &profile->sec;
 +      sec->wpa_auth = sme->crypto.akm_suites[0];
 +
 +      return err;
 +}
 +
 +static s32
 +brcmf_set_sharedkey(struct net_device *ndev,
 +                  struct cfg80211_connect_params *sme)
 +{
 +      struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
 +      struct brcmf_cfg80211_security *sec;
 +      struct brcmf_wsec_key key;
 +      s32 val;
 +      s32 err = 0;
 +
 +      brcmf_dbg(CONN, "key len (%d)\n", sme->key_len);
 +
 +      if (sme->key_len == 0)
 +              return 0;
 +
 +      sec = &profile->sec;
 +      brcmf_dbg(CONN, "wpa_versions 0x%x cipher_pairwise 0x%x\n",
 +                sec->wpa_versions, sec->cipher_pairwise);
 +
 +      if (sec->wpa_versions & (NL80211_WPA_VERSION_1 | NL80211_WPA_VERSION_2))
 +              return 0;
 +
 +      if (!(sec->cipher_pairwise &
 +          (WLAN_CIPHER_SUITE_WEP40 | WLAN_CIPHER_SUITE_WEP104)))
 +              return 0;
 +
 +      memset(&key, 0, sizeof(key));
 +      key.len = (u32) sme->key_len;
 +      key.index = (u32) sme->key_idx;
 +      if (key.len > sizeof(key.data)) {
 +              brcmf_err("Too long key length (%u)\n", key.len);
 +              return -EINVAL;
 +      }
 +      memcpy(key.data, sme->key, key.len);
 +      key.flags = BRCMF_PRIMARY_KEY;
 +      switch (sec->cipher_pairwise) {
 +      case WLAN_CIPHER_SUITE_WEP40:
 +              key.algo = CRYPTO_ALGO_WEP1;
 +              break;
 +      case WLAN_CIPHER_SUITE_WEP104:
 +              key.algo = CRYPTO_ALGO_WEP128;
 +              break;
 +      default:
 +              brcmf_err("Invalid algorithm (%d)\n",
 +                        sme->crypto.ciphers_pairwise[0]);
 +              return -EINVAL;
 +      }
 +      /* Set the new key/index */
 +      brcmf_dbg(CONN, "key length (%d) key index (%d) algo (%d)\n",
 +                key.len, key.index, key.algo);
 +      brcmf_dbg(CONN, "key \"%s\"\n", key.data);
 +      err = send_key_to_dongle(ndev, &key);
 +      if (err)
 +              return err;
 +
 +      if (sec->auth_type == NL80211_AUTHTYPE_SHARED_KEY) {
 +              brcmf_dbg(CONN, "set auth_type to shared key\n");
 +              val = WL_AUTH_SHARED_KEY;       /* shared key */
 +              err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "auth", val);
 +              if (err)
 +                      brcmf_err("set auth failed (%d)\n", err);
 +      }
 +      return err;
 +}
 +
 +static
 +enum nl80211_auth_type brcmf_war_auth_type(struct brcmf_if *ifp,
 +                                         enum nl80211_auth_type type)
 +{
 +      if (type == NL80211_AUTHTYPE_AUTOMATIC &&
 +          brcmf_feat_is_quirk_enabled(ifp, BRCMF_FEAT_QUIRK_AUTO_AUTH)) {
 +              brcmf_dbg(CONN, "WAR: use OPEN instead of AUTO\n");
 +              type = NL80211_AUTHTYPE_OPEN_SYSTEM;
 +      }
 +      return type;
 +}
 +
 +static s32
 +brcmf_cfg80211_connect(struct wiphy *wiphy, struct net_device *ndev,
 +                     struct cfg80211_connect_params *sme)
 +{
 +      struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
 +      struct brcmf_if *ifp = netdev_priv(ndev);
 +      struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
 +      struct ieee80211_channel *chan = sme->channel;
 +      struct brcmf_join_params join_params;
 +      size_t join_params_size;
 +      const struct brcmf_tlv *rsn_ie;
 +      const struct brcmf_vs_tlv *wpa_ie;
 +      const void *ie;
 +      u32 ie_len;
 +      struct brcmf_ext_join_params_le *ext_join_params;
 +      u16 chanspec;
 +      s32 err = 0;
 +
 +      brcmf_dbg(TRACE, "Enter\n");
 +      if (!check_vif_up(ifp->vif))
 +              return -EIO;
 +
 +      if (!sme->ssid) {
 +              brcmf_err("Invalid ssid\n");
 +              return -EOPNOTSUPP;
 +      }
 +
 +      if (ifp->vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif) {
 +              /* A normal (non P2P) connection request setup. */
 +              ie = NULL;
 +              ie_len = 0;
 +              /* find the WPA_IE */
 +              wpa_ie = brcmf_find_wpaie((u8 *)sme->ie, sme->ie_len);
 +              if (wpa_ie) {
 +                      ie = wpa_ie;
 +                      ie_len = wpa_ie->len + TLV_HDR_LEN;
 +              } else {
 +                      /* find the RSN_IE */
 +                      rsn_ie = brcmf_parse_tlvs((const u8 *)sme->ie,
 +                                                sme->ie_len,
 +                                                WLAN_EID_RSN);
 +                      if (rsn_ie) {
 +                              ie = rsn_ie;
 +                              ie_len = rsn_ie->len + TLV_HDR_LEN;
 +                      }
 +              }
 +              brcmf_fil_iovar_data_set(ifp, "wpaie", ie, ie_len);
 +      }
 +
 +      err = brcmf_vif_set_mgmt_ie(ifp->vif, BRCMF_VNDR_IE_ASSOCREQ_FLAG,
 +                                  sme->ie, sme->ie_len);
 +      if (err)
 +              brcmf_err("Set Assoc REQ IE Failed\n");
 +      else
 +              brcmf_dbg(TRACE, "Applied Vndr IEs for Assoc request\n");
 +
 +      set_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);
 +
 +      if (chan) {
 +              cfg->channel =
 +                      ieee80211_frequency_to_channel(chan->center_freq);
 +              chanspec = channel_to_chanspec(&cfg->d11inf, chan);
 +              brcmf_dbg(CONN, "channel=%d, center_req=%d, chanspec=0x%04x\n",
 +                        cfg->channel, chan->center_freq, chanspec);
 +      } else {
 +              cfg->channel = 0;
 +              chanspec = 0;
 +      }
 +
 +      brcmf_dbg(INFO, "ie (%p), ie_len (%zd)\n", sme->ie, sme->ie_len);
 +
 +      err = brcmf_set_wpa_version(ndev, sme);
 +      if (err) {
 +              brcmf_err("wl_set_wpa_version failed (%d)\n", err);
 +              goto done;
 +      }
 +
 +      sme->auth_type = brcmf_war_auth_type(ifp, sme->auth_type);
 +      err = brcmf_set_auth_type(ndev, sme);
 +      if (err) {
 +              brcmf_err("wl_set_auth_type failed (%d)\n", err);
 +              goto done;
 +      }
 +
 +      err = brcmf_set_wsec_mode(ndev, sme, sme->mfp == NL80211_MFP_REQUIRED);
 +      if (err) {
 +              brcmf_err("wl_set_set_cipher failed (%d)\n", err);
 +              goto done;
 +      }
 +
 +      err = brcmf_set_key_mgmt(ndev, sme);
 +      if (err) {
 +              brcmf_err("wl_set_key_mgmt failed (%d)\n", err);
 +              goto done;
 +      }
 +
 +      err = brcmf_set_sharedkey(ndev, sme);
 +      if (err) {
 +              brcmf_err("brcmf_set_sharedkey failed (%d)\n", err);
 +              goto done;
 +      }
 +
 +      profile->ssid.SSID_len = min_t(u32, (u32)sizeof(profile->ssid.SSID),
 +                                     (u32)sme->ssid_len);
 +      memcpy(&profile->ssid.SSID, sme->ssid, profile->ssid.SSID_len);
 +      if (profile->ssid.SSID_len < IEEE80211_MAX_SSID_LEN) {
 +              profile->ssid.SSID[profile->ssid.SSID_len] = 0;
 +              brcmf_dbg(CONN, "SSID \"%s\", len (%d)\n", profile->ssid.SSID,
 +                        profile->ssid.SSID_len);
 +      }
 +
 +      /* Join with specific BSSID and cached SSID
 +       * If SSID is zero join based on BSSID only
 +       */
 +      join_params_size = offsetof(struct brcmf_ext_join_params_le, assoc_le) +
 +              offsetof(struct brcmf_assoc_params_le, chanspec_list);
 +      if (cfg->channel)
 +              join_params_size += sizeof(u16);
 +      ext_join_params = kzalloc(join_params_size, GFP_KERNEL);
 +      if (ext_join_params == NULL) {
 +              err = -ENOMEM;
 +              goto done;
 +      }
 +      ext_join_params->ssid_le.SSID_len = cpu_to_le32(profile->ssid.SSID_len);
 +      memcpy(&ext_join_params->ssid_le.SSID, sme->ssid,
 +             profile->ssid.SSID_len);
 +
 +      /* Set up join scan parameters */
 +      ext_join_params->scan_le.scan_type = -1;
 +      ext_join_params->scan_le.home_time = cpu_to_le32(-1);
 +
 +      if (sme->bssid)
 +              memcpy(&ext_join_params->assoc_le.bssid, sme->bssid, ETH_ALEN);
 +      else
 +              memset(&ext_join_params->assoc_le.bssid, 0xFF, ETH_ALEN);
 +
 +      if (cfg->channel) {
 +              ext_join_params->assoc_le.chanspec_num = cpu_to_le32(1);
 +
 +              ext_join_params->assoc_le.chanspec_list[0] =
 +                      cpu_to_le16(chanspec);
 +              /* Increase dwell time to receive probe response or detect
 +               * beacon from target AP at a noisy air only during connect
 +               * command.
 +               */
 +              ext_join_params->scan_le.active_time =
 +                      cpu_to_le32(BRCMF_SCAN_JOIN_ACTIVE_DWELL_TIME_MS);
 +              ext_join_params->scan_le.passive_time =
 +                      cpu_to_le32(BRCMF_SCAN_JOIN_PASSIVE_DWELL_TIME_MS);
 +              /* To sync with presence period of VSDB GO send probe request
 +               * more frequently. Probe request will be stopped when it gets
 +               * probe response from target AP/GO.
 +               */
 +              ext_join_params->scan_le.nprobes =
 +                      cpu_to_le32(BRCMF_SCAN_JOIN_ACTIVE_DWELL_TIME_MS /
 +                                  BRCMF_SCAN_JOIN_PROBE_INTERVAL_MS);
 +      } else {
 +              ext_join_params->scan_le.active_time = cpu_to_le32(-1);
 +              ext_join_params->scan_le.passive_time = cpu_to_le32(-1);
 +              ext_join_params->scan_le.nprobes = cpu_to_le32(-1);
 +      }
 +
 +      err  = brcmf_fil_bsscfg_data_set(ifp, "join", ext_join_params,
 +                                       join_params_size);
 +      kfree(ext_join_params);
 +      if (!err)
 +              /* This is it. join command worked, we are done */
 +              goto done;
 +
 +      /* join command failed, fallback to set ssid */
 +      memset(&join_params, 0, sizeof(join_params));
 +      join_params_size = sizeof(join_params.ssid_le);
 +
 +      memcpy(&join_params.ssid_le.SSID, sme->ssid, profile->ssid.SSID_len);
 +      join_params.ssid_le.SSID_len = cpu_to_le32(profile->ssid.SSID_len);
 +
 +      if (sme->bssid)
 +              memcpy(join_params.params_le.bssid, sme->bssid, ETH_ALEN);
 +      else
 +              memset(join_params.params_le.bssid, 0xFF, ETH_ALEN);
 +
 +      if (cfg->channel) {
 +              join_params.params_le.chanspec_list[0] = cpu_to_le16(chanspec);
 +              join_params.params_le.chanspec_num = cpu_to_le32(1);
 +              join_params_size += sizeof(join_params.params_le);
 +      }
 +      err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID,
 +                                   &join_params, join_params_size);
 +      if (err)
 +              brcmf_err("BRCMF_C_SET_SSID failed (%d)\n", err);
 +
 +done:
 +      if (err)
 +              clear_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);
 +      brcmf_dbg(TRACE, "Exit\n");
 +      return err;
 +}
 +
 +static s32
 +brcmf_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *ndev,
 +                     u16 reason_code)
 +{
 +      struct brcmf_if *ifp = netdev_priv(ndev);
 +      struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
 +      struct brcmf_scb_val_le scbval;
 +      s32 err = 0;
 +
 +      brcmf_dbg(TRACE, "Enter. Reason code = %d\n", reason_code);
 +      if (!check_vif_up(ifp->vif))
 +              return -EIO;
 +
 +      clear_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state);
 +      cfg80211_disconnected(ndev, reason_code, NULL, 0, GFP_KERNEL);
 +
 +      memcpy(&scbval.ea, &profile->bssid, ETH_ALEN);
 +      scbval.val = cpu_to_le32(reason_code);
 +      err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_DISASSOC,
 +                                   &scbval, sizeof(scbval));
 +      if (err)
 +              brcmf_err("error (%d)\n", err);
 +
 +      brcmf_dbg(TRACE, "Exit\n");
 +      return err;
 +}
 +
 +static s32
 +brcmf_cfg80211_set_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev,
 +                          enum nl80211_tx_power_setting type, s32 mbm)
 +{
 +
 +      struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
 +      struct net_device *ndev = cfg_to_ndev(cfg);
 +      struct brcmf_if *ifp = netdev_priv(ndev);
 +      u16 txpwrmw;
 +      s32 err = 0;
 +      s32 disable = 0;
 +      s32 dbm = MBM_TO_DBM(mbm);
 +
 +      brcmf_dbg(TRACE, "Enter\n");
 +      if (!check_vif_up(ifp->vif))
 +              return -EIO;
 +
 +      switch (type) {
 +      case NL80211_TX_POWER_AUTOMATIC:
 +              break;
 +      case NL80211_TX_POWER_LIMITED:
 +      case NL80211_TX_POWER_FIXED:
 +              if (dbm < 0) {
 +                      brcmf_err("TX_POWER_FIXED - dbm is negative\n");
 +                      err = -EINVAL;
 +                      goto done;
 +              }
 +              break;
 +      }
 +      /* Make sure radio is off or on as far as software is concerned */
 +      disable = WL_RADIO_SW_DISABLE << 16;
 +      err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_RADIO, disable);
 +      if (err)
 +              brcmf_err("WLC_SET_RADIO error (%d)\n", err);
 +
 +      if (dbm > 0xffff)
 +              txpwrmw = 0xffff;
 +      else
 +              txpwrmw = (u16) dbm;
 +      err = brcmf_fil_iovar_int_set(ifp, "qtxpower",
 +                                    (s32)brcmf_mw_to_qdbm(txpwrmw));
 +      if (err)
 +              brcmf_err("qtxpower error (%d)\n", err);
 +      cfg->conf->tx_power = dbm;
 +
 +done:
 +      brcmf_dbg(TRACE, "Exit\n");
 +      return err;
 +}
 +
 +static s32 brcmf_cfg80211_get_tx_power(struct wiphy *wiphy,
 +                                     struct wireless_dev *wdev,
 +                                     s32 *dbm)
 +{
 +      struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
 +      struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
 +      s32 txpwrdbm;
 +      u8 result;
 +      s32 err = 0;
 +
 +      brcmf_dbg(TRACE, "Enter\n");
 +      if (!check_vif_up(ifp->vif))
 +              return -EIO;
 +
 +      err = brcmf_fil_iovar_int_get(ifp, "qtxpower", &txpwrdbm);
 +      if (err) {
 +              brcmf_err("error (%d)\n", err);
 +              goto done;
 +      }
 +
 +      result = (u8) (txpwrdbm & ~WL_TXPWR_OVERRIDE);
 +      *dbm = (s32) brcmf_qdbm_to_mw(result);
 +
 +done:
 +      brcmf_dbg(TRACE, "Exit\n");
 +      return err;
 +}
 +
 +static s32
 +brcmf_cfg80211_config_default_key(struct wiphy *wiphy, struct net_device *ndev,
 +                             u8 key_idx, bool unicast, bool multicast)
 +{
 +      struct brcmf_if *ifp = netdev_priv(ndev);
 +      u32 index;
 +      u32 wsec;
 +      s32 err = 0;
 +
 +      brcmf_dbg(TRACE, "Enter\n");
 +      brcmf_dbg(CONN, "key index (%d)\n", key_idx);
 +      if (!check_vif_up(ifp->vif))
 +              return -EIO;
 +
 +      err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec);
 +      if (err) {
 +              brcmf_err("WLC_GET_WSEC error (%d)\n", err);
 +              goto done;
 +      }
 +
 +      if (wsec & WEP_ENABLED) {
 +              /* Just select a new current key */
 +              index = key_idx;
 +              err = brcmf_fil_cmd_int_set(ifp,
 +                                          BRCMF_C_SET_KEY_PRIMARY, index);
 +              if (err)
 +                      brcmf_err("error (%d)\n", err);
 +      }
 +done:
 +      brcmf_dbg(TRACE, "Exit\n");
 +      return err;
 +}
 +
 +static s32
 +brcmf_add_keyext(struct wiphy *wiphy, struct net_device *ndev,
 +            u8 key_idx, const u8 *mac_addr, struct key_params *params)
 +{
 +      struct brcmf_if *ifp = netdev_priv(ndev);
 +      struct brcmf_wsec_key key;
 +      s32 err = 0;
 +      u8 keybuf[8];
 +
 +      memset(&key, 0, sizeof(key));
 +      key.index = (u32) key_idx;
 +      /* Instead of bcast for ea address for default wep keys,
 +               driver needs it to be Null */
 +      if (!is_multicast_ether_addr(mac_addr))
 +              memcpy((char *)&key.ea, (void *)mac_addr, ETH_ALEN);
 +      key.len = (u32) params->key_len;
 +      /* check for key index change */
 +      if (key.len == 0) {
 +              /* key delete */
 +              err = send_key_to_dongle(ndev, &key);
 +              if (err)
 +                      brcmf_err("key delete error (%d)\n", err);
 +      } else {
 +              if (key.len > sizeof(key.data)) {
 +                      brcmf_err("Invalid key length (%d)\n", key.len);
 +                      return -EINVAL;
 +              }
 +
 +              brcmf_dbg(CONN, "Setting the key index %d\n", key.index);
 +              memcpy(key.data, params->key, key.len);
 +
 +              if (!brcmf_is_apmode(ifp->vif) &&
 +                  (params->cipher == WLAN_CIPHER_SUITE_TKIP)) {
 +                      brcmf_dbg(CONN, "Swapping RX/TX MIC key\n");
 +                      memcpy(keybuf, &key.data[24], sizeof(keybuf));
 +                      memcpy(&key.data[24], &key.data[16], sizeof(keybuf));
 +                      memcpy(&key.data[16], keybuf, sizeof(keybuf));
 +              }
 +
 +              /* if IW_ENCODE_EXT_RX_SEQ_VALID set */
 +              if (params->seq && params->seq_len == 6) {
 +                      /* rx iv */
 +                      u8 *ivptr;
 +                      ivptr = (u8 *) params->seq;
 +                      key.rxiv.hi = (ivptr[5] << 24) | (ivptr[4] << 16) |
 +                          (ivptr[3] << 8) | ivptr[2];
 +                      key.rxiv.lo = (ivptr[1] << 8) | ivptr[0];
 +                      key.iv_initialized = true;
 +              }
 +
 +              switch (params->cipher) {
 +              case WLAN_CIPHER_SUITE_WEP40:
 +                      key.algo = CRYPTO_ALGO_WEP1;
 +                      brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP40\n");
 +                      break;
 +              case WLAN_CIPHER_SUITE_WEP104:
 +                      key.algo = CRYPTO_ALGO_WEP128;
 +                      brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP104\n");
 +                      break;
 +              case WLAN_CIPHER_SUITE_TKIP:
 +                      key.algo = CRYPTO_ALGO_TKIP;
 +                      brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_TKIP\n");
 +                      break;
 +              case WLAN_CIPHER_SUITE_AES_CMAC:
 +                      key.algo = CRYPTO_ALGO_AES_CCM;
 +                      brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_AES_CMAC\n");
 +                      break;
 +              case WLAN_CIPHER_SUITE_CCMP:
 +                      key.algo = CRYPTO_ALGO_AES_CCM;
 +                      brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_CCMP\n");
 +                      break;
 +              default:
 +                      brcmf_err("Invalid cipher (0x%x)\n", params->cipher);
 +                      return -EINVAL;
 +              }
 +              err = send_key_to_dongle(ndev, &key);
 +              if (err)
 +                      brcmf_err("wsec_key error (%d)\n", err);
 +      }
 +      return err;
 +}
 +
 +static s32
 +brcmf_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev,
 +                  u8 key_idx, bool pairwise, const u8 *mac_addr,
 +                  struct key_params *params)
 +{
 +      struct brcmf_if *ifp = netdev_priv(ndev);
 +      struct brcmf_wsec_key key;
 +      s32 val;
 +      s32 wsec;
 +      s32 err = 0;
 +      u8 keybuf[8];
 +
 +      brcmf_dbg(TRACE, "Enter\n");
 +      brcmf_dbg(CONN, "key index (%d)\n", key_idx);
 +      if (!check_vif_up(ifp->vif))
 +              return -EIO;
 +
 +      if (mac_addr &&
 +              (params->cipher != WLAN_CIPHER_SUITE_WEP40) &&
 +              (params->cipher != WLAN_CIPHER_SUITE_WEP104)) {
 +              brcmf_dbg(TRACE, "Exit");
 +              return brcmf_add_keyext(wiphy, ndev, key_idx, mac_addr, params);
 +      }
 +      memset(&key, 0, sizeof(key));
 +
 +      key.len = (u32) params->key_len;
 +      key.index = (u32) key_idx;
 +
 +      if (key.len > sizeof(key.data)) {
 +              brcmf_err("Too long key length (%u)\n", key.len);
 +              err = -EINVAL;
 +              goto done;
 +      }
 +      memcpy(key.data, params->key, key.len);
 +
 +      key.flags = BRCMF_PRIMARY_KEY;
 +      switch (params->cipher) {
 +      case WLAN_CIPHER_SUITE_WEP40:
 +              key.algo = CRYPTO_ALGO_WEP1;
 +              val = WEP_ENABLED;
 +              brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP40\n");
 +              break;
 +      case WLAN_CIPHER_SUITE_WEP104:
 +              key.algo = CRYPTO_ALGO_WEP128;
 +              val = WEP_ENABLED;
 +              brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP104\n");
 +              break;
 +      case WLAN_CIPHER_SUITE_TKIP:
 +              if (!brcmf_is_apmode(ifp->vif)) {
 +                      brcmf_dbg(CONN, "Swapping RX/TX MIC key\n");
 +                      memcpy(keybuf, &key.data[24], sizeof(keybuf));
 +                      memcpy(&key.data[24], &key.data[16], sizeof(keybuf));
 +                      memcpy(&key.data[16], keybuf, sizeof(keybuf));
 +              }
 +              key.algo = CRYPTO_ALGO_TKIP;
 +              val = TKIP_ENABLED;
 +              brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_TKIP\n");
 +              break;
 +      case WLAN_CIPHER_SUITE_AES_CMAC:
 +              key.algo = CRYPTO_ALGO_AES_CCM;
 +              val = AES_ENABLED;
 +              brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_AES_CMAC\n");
 +              break;
 +      case WLAN_CIPHER_SUITE_CCMP:
 +              key.algo = CRYPTO_ALGO_AES_CCM;
 +              val = AES_ENABLED;
 +              brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_CCMP\n");
 +              break;
 +      default:
 +              brcmf_err("Invalid cipher (0x%x)\n", params->cipher);
 +              err = -EINVAL;
 +              goto done;
 +      }
 +
 +      err = send_key_to_dongle(ndev, &key);
 +      if (err)
 +              goto done;
 +
 +      err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec);
 +      if (err) {
 +              brcmf_err("get wsec error (%d)\n", err);
 +              goto done;
 +      }
 +      wsec |= val;
 +      err = brcmf_fil_bsscfg_int_set(ifp, "wsec", wsec);
 +      if (err) {
 +              brcmf_err("set wsec error (%d)\n", err);
 +              goto done;
 +      }
 +
 +done:
 +      brcmf_dbg(TRACE, "Exit\n");
 +      return err;
 +}
 +
 +static s32
 +brcmf_cfg80211_del_key(struct wiphy *wiphy, struct net_device *ndev,
 +                  u8 key_idx, bool pairwise, const u8 *mac_addr)
 +{
 +      struct brcmf_if *ifp = netdev_priv(ndev);
 +      struct brcmf_wsec_key key;
 +      s32 err = 0;
 +
 +      brcmf_dbg(TRACE, "Enter\n");
 +      if (!check_vif_up(ifp->vif))
 +              return -EIO;
 +
 +      if (key_idx >= DOT11_MAX_DEFAULT_KEYS) {
 +              /* we ignore this key index in this case */
 +              brcmf_err("invalid key index (%d)\n", key_idx);
 +              return -EINVAL;
 +      }
 +
 +      memset(&key, 0, sizeof(key));
 +
 +      key.index = (u32) key_idx;
 +      key.flags = BRCMF_PRIMARY_KEY;
 +      key.algo = CRYPTO_ALGO_OFF;
 +
 +      brcmf_dbg(CONN, "key index (%d)\n", key_idx);
 +
 +      /* Set the new key/index */
 +      err = send_key_to_dongle(ndev, &key);
 +
 +      brcmf_dbg(TRACE, "Exit\n");
 +      return err;
 +}
 +
 +static s32
 +brcmf_cfg80211_get_key(struct wiphy *wiphy, struct net_device *ndev,
 +                  u8 key_idx, bool pairwise, const u8 *mac_addr, void *cookie,
 +                  void (*callback) (void *cookie, struct key_params * params))
 +{
 +      struct key_params params;
 +      struct brcmf_if *ifp = netdev_priv(ndev);
 +      struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
 +      struct brcmf_cfg80211_security *sec;
 +      s32 wsec;
 +      s32 err = 0;
 +
 +      brcmf_dbg(TRACE, "Enter\n");
 +      brcmf_dbg(CONN, "key index (%d)\n", key_idx);
 +      if (!check_vif_up(ifp->vif))
 +              return -EIO;
 +
 +      memset(&params, 0, sizeof(params));
 +
 +      err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec);
 +      if (err) {
 +              brcmf_err("WLC_GET_WSEC error (%d)\n", err);
 +              /* Ignore this error, may happen during DISASSOC */
 +              err = -EAGAIN;
 +              goto done;
 +      }
 +      if (wsec & WEP_ENABLED) {
 +              sec = &profile->sec;
 +              if (sec->cipher_pairwise & WLAN_CIPHER_SUITE_WEP40) {
 +                      params.cipher = WLAN_CIPHER_SUITE_WEP40;
 +                      brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP40\n");
 +              } else if (sec->cipher_pairwise & WLAN_CIPHER_SUITE_WEP104) {
 +                      params.cipher = WLAN_CIPHER_SUITE_WEP104;
 +                      brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP104\n");
 +              }
 +      } else if (wsec & TKIP_ENABLED) {
 +              params.cipher = WLAN_CIPHER_SUITE_TKIP;
 +              brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_TKIP\n");
 +      } else if (wsec & AES_ENABLED) {
 +              params.cipher = WLAN_CIPHER_SUITE_AES_CMAC;
 +              brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_AES_CMAC\n");
 +      } else  {
 +              brcmf_err("Invalid algo (0x%x)\n", wsec);
 +              err = -EINVAL;
 +              goto done;
 +      }
 +      callback(cookie, &params);
 +
 +done:
 +      brcmf_dbg(TRACE, "Exit\n");
 +      return err;
 +}
 +
 +static s32
 +brcmf_cfg80211_config_default_mgmt_key(struct wiphy *wiphy,
 +                                  struct net_device *ndev, u8 key_idx)
 +{
 +      brcmf_dbg(INFO, "Not supported\n");
 +
 +      return -EOPNOTSUPP;
 +}
 +
 +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;
 +
 +      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);
 +              err = brcmf_fil_iovar_data_get(ifp, "sta_info",
 +                                             &sta_info_le,
 +                                             sizeof(sta_info_le));
 +              if (err < 0) {
 +                      brcmf_err("GET STA INFO failed, %d\n", err);
 +                      goto done;
 +              }
 +              sinfo->filled = STATION_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 |= STATION_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 {
 +                      sinfo->filled |= STATION_INFO_TX_BITRATE;
 +                      sinfo->txrate.legacy = rate * 5;
 +                      brcmf_dbg(CONN, "Rate %d Mbps\n", rate / 2);
 +              }
 +
 +              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 |= STATION_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 |= STATION_INFO_BSS_PARAM;
 +              }
 +      } else
 +              err = -EPERM;
 +done:
 +      brcmf_dbg(TRACE, "Exit\n");
 +      return err;
 +}
 +
 +static s32
 +brcmf_cfg80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *ndev,
 +                         bool enabled, s32 timeout)
 +{
 +      s32 pm;
 +      s32 err = 0;
 +      struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
 +      struct brcmf_if *ifp = netdev_priv(ndev);
 +
 +      brcmf_dbg(TRACE, "Enter\n");
 +
 +      /*
 +       * Powersave enable/disable request is coming from the
 +       * cfg80211 even before the interface is up. In that
 +       * scenario, driver will be storing the power save
 +       * preference in cfg struct to apply this to
 +       * FW later while initializing the dongle
 +       */
 +      cfg->pwr_save = enabled;
 +      if (!check_vif_up(ifp->vif)) {
 +
 +              brcmf_dbg(INFO, "Device is not ready, storing the value in cfg_info struct\n");
 +              goto done;
 +      }
 +
 +      pm = enabled ? PM_FAST : PM_OFF;
 +      /* Do not enable the power save after assoc if it is a p2p interface */
 +      if (ifp->vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT) {
 +              brcmf_dbg(INFO, "Do not enable power save for P2P clients\n");
 +              pm = PM_OFF;
 +      }
 +      brcmf_dbg(INFO, "power save %s\n", (pm ? "enabled" : "disabled"));
 +
 +      err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, pm);
 +      if (err) {
 +              if (err == -ENODEV)
 +                      brcmf_err("net_device is not ready yet\n");
 +              else
 +                      brcmf_err("error (%d)\n", err);
 +      }
 +done:
 +      brcmf_dbg(TRACE, "Exit\n");
 +      return err;
 +}
 +
 +static s32 brcmf_inform_single_bss(struct brcmf_cfg80211_info *cfg,
 +                                 struct brcmf_bss_info_le *bi)
 +{
 +      struct wiphy *wiphy = cfg_to_wiphy(cfg);
 +      struct ieee80211_channel *notify_channel;
 +      struct cfg80211_bss *bss;
 +      struct ieee80211_supported_band *band;
 +      struct brcmu_chan ch;
 +      u16 channel;
 +      u32 freq;
 +      u16 notify_capability;
 +      u16 notify_interval;
 +      u8 *notify_ie;
 +      size_t notify_ielen;
 +      s32 notify_signal;
 +
 +      if (le32_to_cpu(bi->length) > WL_BSS_INFO_MAX) {
 +              brcmf_err("Bss info is larger than buffer. Discarding\n");
 +              return 0;
 +      }
 +
 +      if (!bi->ctl_ch) {
 +              ch.chspec = le16_to_cpu(bi->chanspec);
 +              cfg->d11inf.decchspec(&ch);
 +              bi->ctl_ch = ch.chnum;
 +      }
 +      channel = bi->ctl_ch;
 +
 +      if (channel <= CH_MAX_2G_CHANNEL)
 +              band = wiphy->bands[IEEE80211_BAND_2GHZ];
 +      else
 +              band = wiphy->bands[IEEE80211_BAND_5GHZ];
 +
 +      freq = ieee80211_channel_to_frequency(channel, band->band);
 +      notify_channel = ieee80211_get_channel(wiphy, freq);
 +
 +      notify_capability = le16_to_cpu(bi->capability);
 +      notify_interval = le16_to_cpu(bi->beacon_period);
 +      notify_ie = (u8 *)bi + le16_to_cpu(bi->ie_offset);
 +      notify_ielen = le32_to_cpu(bi->ie_length);
 +      notify_signal = (s16)le16_to_cpu(bi->RSSI) * 100;
 +
 +      brcmf_dbg(CONN, "bssid: %pM\n", bi->BSSID);
 +      brcmf_dbg(CONN, "Channel: %d(%d)\n", channel, freq);
 +      brcmf_dbg(CONN, "Capability: %X\n", notify_capability);
 +      brcmf_dbg(CONN, "Beacon interval: %d\n", notify_interval);
 +      brcmf_dbg(CONN, "Signal: %d\n", notify_signal);
 +
 +      bss = cfg80211_inform_bss(wiphy, notify_channel,
 +                                CFG80211_BSS_FTYPE_UNKNOWN,
 +                                (const u8 *)bi->BSSID,
 +                                0, notify_capability,
 +                                notify_interval, notify_ie,
 +                                notify_ielen, notify_signal,
 +                                GFP_KERNEL);
 +
 +      if (!bss)
 +              return -ENOMEM;
 +
 +      cfg80211_put_bss(wiphy, bss);
 +
 +      return 0;
 +}
 +
 +static struct brcmf_bss_info_le *
 +next_bss_le(struct brcmf_scan_results *list, struct brcmf_bss_info_le *bss)
 +{
 +      if (bss == NULL)
 +              return list->bss_info_le;
 +      return (struct brcmf_bss_info_le *)((unsigned long)bss +
 +                                          le32_to_cpu(bss->length));
 +}
 +
 +static s32 brcmf_inform_bss(struct brcmf_cfg80211_info *cfg)
 +{
 +      struct brcmf_scan_results *bss_list;
 +      struct brcmf_bss_info_le *bi = NULL;    /* must be initialized */
 +      s32 err = 0;
 +      int i;
 +
 +      bss_list = (struct brcmf_scan_results *)cfg->escan_info.escan_buf;
 +      if (bss_list->count != 0 &&
 +          bss_list->version != BRCMF_BSS_INFO_VERSION) {
 +              brcmf_err("Version %d != WL_BSS_INFO_VERSION\n",
 +                        bss_list->version);
 +              return -EOPNOTSUPP;
 +      }
 +      brcmf_dbg(SCAN, "scanned AP count (%d)\n", bss_list->count);
 +      for (i = 0; i < bss_list->count; i++) {
 +              bi = next_bss_le(bss_list, bi);
 +              err = brcmf_inform_single_bss(cfg, bi);
 +              if (err)
 +                      break;
 +      }
 +      return err;
 +}
 +
 +static s32 wl_inform_ibss(struct brcmf_cfg80211_info *cfg,
 +                        struct net_device *ndev, const u8 *bssid)
 +{
 +      struct wiphy *wiphy = cfg_to_wiphy(cfg);
 +      struct ieee80211_channel *notify_channel;
 +      struct brcmf_bss_info_le *bi = NULL;
 +      struct ieee80211_supported_band *band;
 +      struct cfg80211_bss *bss;
 +      struct brcmu_chan ch;
 +      u8 *buf = NULL;
 +      s32 err = 0;
 +      u32 freq;
 +      u16 notify_capability;
 +      u16 notify_interval;
 +      u8 *notify_ie;
 +      size_t notify_ielen;
 +      s32 notify_signal;
 +
 +      brcmf_dbg(TRACE, "Enter\n");
 +
 +      buf = kzalloc(WL_BSS_INFO_MAX, GFP_KERNEL);
 +      if (buf == NULL) {
 +              err = -ENOMEM;
 +              goto CleanUp;
 +      }
 +
 +      *(__le32 *)buf = cpu_to_le32(WL_BSS_INFO_MAX);
 +
 +      err = brcmf_fil_cmd_data_get(netdev_priv(ndev), BRCMF_C_GET_BSS_INFO,
 +                                   buf, WL_BSS_INFO_MAX);
 +      if (err) {
 +              brcmf_err("WLC_GET_BSS_INFO failed: %d\n", err);
 +              goto CleanUp;
 +      }
 +
 +      bi = (struct brcmf_bss_info_le *)(buf + 4);
 +
 +      ch.chspec = le16_to_cpu(bi->chanspec);
 +      cfg->d11inf.decchspec(&ch);
 +
 +      if (ch.band == BRCMU_CHAN_BAND_2G)
 +              band = wiphy->bands[IEEE80211_BAND_2GHZ];
 +      else
 +              band = wiphy->bands[IEEE80211_BAND_5GHZ];
 +
 +      freq = ieee80211_channel_to_frequency(ch.chnum, band->band);
 +      notify_channel = ieee80211_get_channel(wiphy, freq);
 +
 +      notify_capability = le16_to_cpu(bi->capability);
 +      notify_interval = le16_to_cpu(bi->beacon_period);
 +      notify_ie = (u8 *)bi + le16_to_cpu(bi->ie_offset);
 +      notify_ielen = le32_to_cpu(bi->ie_length);
 +      notify_signal = (s16)le16_to_cpu(bi->RSSI) * 100;
 +
 +      brcmf_dbg(CONN, "channel: %d(%d)\n", ch.chnum, freq);
 +      brcmf_dbg(CONN, "capability: %X\n", notify_capability);
 +      brcmf_dbg(CONN, "beacon interval: %d\n", notify_interval);
 +      brcmf_dbg(CONN, "signal: %d\n", notify_signal);
 +
 +      bss = cfg80211_inform_bss(wiphy, notify_channel,
 +                                CFG80211_BSS_FTYPE_UNKNOWN, bssid, 0,
 +                                notify_capability, notify_interval,
 +                                notify_ie, notify_ielen, notify_signal,
 +                                GFP_KERNEL);
 +
 +      if (!bss) {
 +              err = -ENOMEM;
 +              goto CleanUp;
 +      }
 +
 +      cfg80211_put_bss(wiphy, bss);
 +
 +CleanUp:
 +
 +      kfree(buf);
 +
 +      brcmf_dbg(TRACE, "Exit\n");
 +
 +      return err;
 +}
 +
 +static s32 brcmf_update_bss_info(struct brcmf_cfg80211_info *cfg,
 +                               struct brcmf_if *ifp)
 +{
 +      struct brcmf_cfg80211_profile *profile = ndev_to_prof(ifp->ndev);
 +      struct brcmf_bss_info_le *bi;
 +      struct brcmf_ssid *ssid;
 +      const struct brcmf_tlv *tim;
 +      u16 beacon_interval;
 +      u8 dtim_period;
 +      size_t ie_len;
 +      u8 *ie;
 +      s32 err = 0;
 +
 +      brcmf_dbg(TRACE, "Enter\n");
 +      if (brcmf_is_ibssmode(ifp->vif))
 +              return err;
 +
 +      ssid = &profile->ssid;
 +
 +      *(__le32 *)cfg->extra_buf = cpu_to_le32(WL_EXTRA_BUF_MAX);
 +      err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BSS_INFO,
 +                                   cfg->extra_buf, WL_EXTRA_BUF_MAX);
 +      if (err) {
 +              brcmf_err("Could not get bss info %d\n", err);
 +              goto update_bss_info_out;
 +      }
 +
 +      bi = (struct brcmf_bss_info_le *)(cfg->extra_buf + 4);
 +      err = brcmf_inform_single_bss(cfg, bi);
 +      if (err)
 +              goto update_bss_info_out;
 +
 +      ie = ((u8 *)bi) + le16_to_cpu(bi->ie_offset);
 +      ie_len = le32_to_cpu(bi->ie_length);
 +      beacon_interval = le16_to_cpu(bi->beacon_period);
 +
 +      tim = brcmf_parse_tlvs(ie, ie_len, WLAN_EID_TIM);
 +      if (tim)
 +              dtim_period = tim->data[1];
 +      else {
 +              /*
 +              * active scan was done so we could not get dtim
 +              * information out of probe response.
 +              * so we speficially query dtim information to dongle.
 +              */
 +              u32 var;
 +              err = brcmf_fil_iovar_int_get(ifp, "dtim_assoc", &var);
 +              if (err) {
 +                      brcmf_err("wl dtim_assoc failed (%d)\n", err);
 +                      goto update_bss_info_out;
 +              }
 +              dtim_period = (u8)var;
 +      }
 +
 +update_bss_info_out:
 +      brcmf_dbg(TRACE, "Exit");
 +      return err;
 +}
 +
 +void brcmf_abort_scanning(struct brcmf_cfg80211_info *cfg)
 +{
 +      struct escan_info *escan = &cfg->escan_info;
 +
 +      set_bit(BRCMF_SCAN_STATUS_ABORT, &cfg->scan_status);
 +      if (cfg->scan_request) {
 +              escan->escan_state = WL_ESCAN_STATE_IDLE;
 +              brcmf_notify_escan_complete(cfg, escan->ifp, true, true);
 +      }
 +      clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
 +      clear_bit(BRCMF_SCAN_STATUS_ABORT, &cfg->scan_status);
 +}
 +
 +static void brcmf_cfg80211_escan_timeout_worker(struct work_struct *work)
 +{
 +      struct brcmf_cfg80211_info *cfg =
 +                      container_of(work, struct brcmf_cfg80211_info,
 +                                   escan_timeout_work);
 +
 +      brcmf_inform_bss(cfg);
 +      brcmf_notify_escan_complete(cfg, cfg->escan_info.ifp, true, true);
 +}
 +
 +static void brcmf_escan_timeout(unsigned long data)
 +{
 +      struct brcmf_cfg80211_info *cfg =
 +                      (struct brcmf_cfg80211_info *)data;
 +
 +      if (cfg->scan_request) {
 +              brcmf_err("timer expired\n");
 +              schedule_work(&cfg->escan_timeout_work);
 +      }
 +}
 +
 +static s32
 +brcmf_compare_update_same_bss(struct brcmf_cfg80211_info *cfg,
 +                            struct brcmf_bss_info_le *bss,
 +                            struct brcmf_bss_info_le *bss_info_le)
 +{
 +      struct brcmu_chan ch_bss, ch_bss_info_le;
 +
 +      ch_bss.chspec = le16_to_cpu(bss->chanspec);
 +      cfg->d11inf.decchspec(&ch_bss);
 +      ch_bss_info_le.chspec = le16_to_cpu(bss_info_le->chanspec);
 +      cfg->d11inf.decchspec(&ch_bss_info_le);
 +
 +      if (!memcmp(&bss_info_le->BSSID, &bss->BSSID, ETH_ALEN) &&
 +              ch_bss.band == ch_bss_info_le.band &&
 +              bss_info_le->SSID_len == bss->SSID_len &&
 +              !memcmp(bss_info_le->SSID, bss->SSID, bss_info_le->SSID_len)) {
 +              if ((bss->flags & BRCMF_BSS_RSSI_ON_CHANNEL) ==
 +                      (bss_info_le->flags & BRCMF_BSS_RSSI_ON_CHANNEL)) {
 +                      s16 bss_rssi = le16_to_cpu(bss->RSSI);
 +                      s16 bss_info_rssi = le16_to_cpu(bss_info_le->RSSI);
 +
 +                      /* preserve max RSSI if the measurements are
 +                      * both on-channel or both off-channel
 +                      */
 +                      if (bss_info_rssi > bss_rssi)
 +                              bss->RSSI = bss_info_le->RSSI;
 +              } else if ((bss->flags & BRCMF_BSS_RSSI_ON_CHANNEL) &&
 +                      (bss_info_le->flags & BRCMF_BSS_RSSI_ON_CHANNEL) == 0) {
 +                      /* preserve the on-channel rssi measurement
 +                      * if the new measurement is off channel
 +                      */
 +                      bss->RSSI = bss_info_le->RSSI;
 +                      bss->flags |= BRCMF_BSS_RSSI_ON_CHANNEL;
 +              }
 +              return 1;
 +      }
 +      return 0;
 +}
 +
 +static s32
 +brcmf_cfg80211_escan_handler(struct brcmf_if *ifp,
 +                           const struct brcmf_event_msg *e, void *data)
 +{
 +      struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
 +      s32 status;
 +      struct brcmf_escan_result_le *escan_result_le;
 +      struct brcmf_bss_info_le *bss_info_le;
 +      struct brcmf_bss_info_le *bss = NULL;
 +      u32 bi_length;
 +      struct brcmf_scan_results *list;
 +      u32 i;
 +      bool aborted;
 +
 +      status = e->status;
 +
 +      if (!test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) {
 +              brcmf_err("scan not ready, bssidx=%d\n", ifp->bssidx);
 +              return -EPERM;
 +      }
 +
 +      if (status == BRCMF_E_STATUS_PARTIAL) {
 +              brcmf_dbg(SCAN, "ESCAN Partial result\n");
 +              escan_result_le = (struct brcmf_escan_result_le *) data;
 +              if (!escan_result_le) {
 +                      brcmf_err("Invalid escan result (NULL pointer)\n");
 +                      goto exit;
 +              }
 +              if (le16_to_cpu(escan_result_le->bss_count) != 1) {
 +                      brcmf_err("Invalid bss_count %d: ignoring\n",
 +                                escan_result_le->bss_count);
 +                      goto exit;
 +              }
 +              bss_info_le = &escan_result_le->bss_info_le;
 +
 +              if (brcmf_p2p_scan_finding_common_channel(cfg, bss_info_le))
 +                      goto exit;
 +
 +              if (!cfg->scan_request) {
 +                      brcmf_dbg(SCAN, "result without cfg80211 request\n");
 +                      goto exit;
 +              }
 +
 +              bi_length = le32_to_cpu(bss_info_le->length);
 +              if (bi_length != (le32_to_cpu(escan_result_le->buflen) -
 +                                      WL_ESCAN_RESULTS_FIXED_SIZE)) {
 +                      brcmf_err("Invalid bss_info length %d: ignoring\n",
 +                                bi_length);
 +                      goto exit;
 +              }
 +
 +              if (!(cfg_to_wiphy(cfg)->interface_modes &
 +                                      BIT(NL80211_IFTYPE_ADHOC))) {
 +                      if (le16_to_cpu(bss_info_le->capability) &
 +                                              WLAN_CAPABILITY_IBSS) {
 +                              brcmf_err("Ignoring IBSS result\n");
 +                              goto exit;
 +                      }
 +              }
 +
 +              list = (struct brcmf_scan_results *)
 +                              cfg->escan_info.escan_buf;
 +              if (bi_length > WL_ESCAN_BUF_SIZE - list->buflen) {
 +                      brcmf_err("Buffer is too small: ignoring\n");
 +                      goto exit;
 +              }
 +
 +              for (i = 0; i < list->count; i++) {
 +                      bss = bss ? (struct brcmf_bss_info_le *)
 +                              ((unsigned char *)bss +
 +                              le32_to_cpu(bss->length)) : list->bss_info_le;
 +                      if (brcmf_compare_update_same_bss(cfg, bss,
 +                                                        bss_info_le))
 +                              goto exit;
 +              }
 +              memcpy(&(cfg->escan_info.escan_buf[list->buflen]),
 +                      bss_info_le, bi_length);
 +              list->version = le32_to_cpu(bss_info_le->version);
 +              list->buflen += bi_length;
 +              list->count++;
 +      } else {
 +              cfg->escan_info.escan_state = WL_ESCAN_STATE_IDLE;
 +              if (brcmf_p2p_scan_finding_common_channel(cfg, NULL))
 +                      goto exit;
 +              if (cfg->scan_request) {
 +                      brcmf_inform_bss(cfg);
 +                      aborted = status != BRCMF_E_STATUS_SUCCESS;
 +                      brcmf_notify_escan_complete(cfg, ifp, aborted, false);
 +              } else
 +                      brcmf_dbg(SCAN, "Ignored scan complete result 0x%x\n",
 +                                status);
 +      }
 +exit:
 +      return 0;
 +}
 +
 +static void brcmf_init_escan(struct brcmf_cfg80211_info *cfg)
 +{
 +      brcmf_fweh_register(cfg->pub, BRCMF_E_ESCAN_RESULT,
 +                          brcmf_cfg80211_escan_handler);
 +      cfg->escan_info.escan_state = WL_ESCAN_STATE_IDLE;
 +      /* Init scan_timeout timer */
 +      init_timer(&cfg->escan_timeout);
 +      cfg->escan_timeout.data = (unsigned long) cfg;
 +      cfg->escan_timeout.function = brcmf_escan_timeout;
 +      INIT_WORK(&cfg->escan_timeout_work,
 +                brcmf_cfg80211_escan_timeout_worker);
 +}
 +
 +static __always_inline void brcmf_delay(u32 ms)
 +{
 +      if (ms < 1000 / HZ) {
 +              cond_resched();
 +              mdelay(ms);
 +      } else {
 +              msleep(ms);
 +      }
 +}
 +
 +static s32 brcmf_config_wowl_pattern(struct brcmf_if *ifp, u8 cmd[4],
 +                                   u8 *pattern, u32 patternsize, u8 *mask,
 +                                   u32 packet_offset)
 +{
 +      struct brcmf_fil_wowl_pattern_le *filter;
 +      u32 masksize;
 +      u32 patternoffset;
 +      u8 *buf;
 +      u32 bufsize;
 +      s32 ret;
 +
 +      masksize = (patternsize + 7) / 8;
 +      patternoffset = sizeof(*filter) - sizeof(filter->cmd) + masksize;
 +
 +      bufsize = sizeof(*filter) + patternsize + masksize;
 +      buf = kzalloc(bufsize, GFP_KERNEL);
 +      if (!buf)
 +              return -ENOMEM;
 +      filter = (struct brcmf_fil_wowl_pattern_le *)buf;
 +
 +      memcpy(filter->cmd, cmd, 4);
 +      filter->masksize = cpu_to_le32(masksize);
 +      filter->offset = cpu_to_le32(packet_offset);
 +      filter->patternoffset = cpu_to_le32(patternoffset);
 +      filter->patternsize = cpu_to_le32(patternsize);
 +      filter->type = cpu_to_le32(BRCMF_WOWL_PATTERN_TYPE_BITMAP);
 +
 +      if ((mask) && (masksize))
 +              memcpy(buf + sizeof(*filter), mask, masksize);
 +      if ((pattern) && (patternsize))
 +              memcpy(buf + sizeof(*filter) + masksize, pattern, patternsize);
 +
 +      ret = brcmf_fil_iovar_data_set(ifp, "wowl_pattern", buf, bufsize);
 +
 +      kfree(buf);
 +      return ret;
 +}
 +
 +static s32 brcmf_cfg80211_resume(struct wiphy *wiphy)
 +{
 +      struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
 +      struct net_device *ndev = cfg_to_ndev(cfg);
 +      struct brcmf_if *ifp = netdev_priv(ndev);
 +
 +      brcmf_dbg(TRACE, "Enter\n");
 +
 +      if (cfg->wowl_enabled) {
 +              brcmf_configure_arp_offload(ifp, true);
 +              brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM,
 +                                    cfg->pre_wowl_pmmode);
 +              brcmf_fil_iovar_int_set(ifp, "wowl_clear", 0);
 +              brcmf_config_wowl_pattern(ifp, "clr", NULL, 0, NULL, 0);
 +              cfg->wowl_enabled = false;
 +      }
 +      return 0;
 +}
 +
 +static void brcmf_configure_wowl(struct brcmf_cfg80211_info *cfg,
 +                               struct brcmf_if *ifp,
 +                               struct cfg80211_wowlan *wowl)
 +{
 +      u32 wowl_config;
 +      u32 i;
 +
 +      brcmf_dbg(TRACE, "Suspend, wowl config.\n");
 +
 +      brcmf_configure_arp_offload(ifp, false);
 +      brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_PM, &cfg->pre_wowl_pmmode);
 +      brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, PM_MAX);
 +
 +      wowl_config = 0;
 +      if (wowl->disconnect)
 +              wowl_config = BRCMF_WOWL_DIS | BRCMF_WOWL_BCN | BRCMF_WOWL_RETR;
 +      if (wowl->magic_pkt)
 +              wowl_config |= BRCMF_WOWL_MAGIC;
 +      if ((wowl->patterns) && (wowl->n_patterns)) {
 +              wowl_config |= BRCMF_WOWL_NET;
 +              for (i = 0; i < wowl->n_patterns; i++) {
 +                      brcmf_config_wowl_pattern(ifp, "add",
 +                              (u8 *)wowl->patterns[i].pattern,
 +                              wowl->patterns[i].pattern_len,
 +                              (u8 *)wowl->patterns[i].mask,
 +                              wowl->patterns[i].pkt_offset);
 +              }
 +      }
 +      brcmf_fil_iovar_int_set(ifp, "wowl", wowl_config);
 +      brcmf_fil_iovar_int_set(ifp, "wowl_activate", 1);
 +      brcmf_bus_wowl_config(cfg->pub->bus_if, true);
 +      cfg->wowl_enabled = true;
 +}
 +
 +static s32 brcmf_cfg80211_suspend(struct wiphy *wiphy,
 +                                struct cfg80211_wowlan *wowl)
 +{
 +      struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
 +      struct net_device *ndev = cfg_to_ndev(cfg);
 +      struct brcmf_if *ifp = netdev_priv(ndev);
 +      struct brcmf_cfg80211_vif *vif;
 +
 +      brcmf_dbg(TRACE, "Enter\n");
 +
 +      /* if the primary net_device is not READY there is nothing
 +       * we can do but pray resume goes smoothly.
 +       */
 +      if (!check_vif_up(ifp->vif))
 +              goto exit;
 +
 +      /* end any scanning */
 +      if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status))
 +              brcmf_abort_scanning(cfg);
 +
 +      if (wowl == NULL) {
 +              brcmf_bus_wowl_config(cfg->pub->bus_if, false);
 +              list_for_each_entry(vif, &cfg->vif_list, list) {
 +                      if (!test_bit(BRCMF_VIF_STATUS_READY, &vif->sme_state))
 +                              continue;
 +                      /* While going to suspend if associated with AP
 +                       * disassociate from AP to save power while system is
 +                       * in suspended state
 +                       */
 +                      brcmf_link_down(vif);
 +                      /* Make sure WPA_Supplicant receives all the event
 +                       * generated due to DISASSOC call to the fw to keep
 +                       * the state fw and WPA_Supplicant state consistent
 +                       */
 +                      brcmf_delay(500);
 +              }
 +              /* Configure MPC */
 +              brcmf_set_mpc(ifp, 1);
 +
 +      } else {
 +              /* Configure WOWL paramaters */
 +              brcmf_configure_wowl(cfg, ifp, wowl);
 +      }
 +
 +exit:
 +      brcmf_dbg(TRACE, "Exit\n");
 +      /* clear any scanning activity */
 +      cfg->scan_status = 0;
 +      return 0;
 +}
 +
 +static __used s32
 +brcmf_update_pmklist(struct net_device *ndev,
 +                   struct brcmf_cfg80211_pmk_list *pmk_list, s32 err)
 +{
 +      int i, j;
 +      int pmkid_len;
 +
 +      pmkid_len = le32_to_cpu(pmk_list->pmkids.npmkid);
 +
 +      brcmf_dbg(CONN, "No of elements %d\n", pmkid_len);
 +      for (i = 0; i < pmkid_len; i++) {
 +              brcmf_dbg(CONN, "PMKID[%d]: %pM =\n", i,
 +                        &pmk_list->pmkids.pmkid[i].BSSID);
 +              for (j = 0; j < WLAN_PMKID_LEN; j++)
 +                      brcmf_dbg(CONN, "%02x\n",
 +                                pmk_list->pmkids.pmkid[i].PMKID[j]);
 +      }
 +
 +      if (!err)
 +              brcmf_fil_iovar_data_set(netdev_priv(ndev), "pmkid_info",
 +                                       (char *)pmk_list, sizeof(*pmk_list));
 +
 +      return err;
 +}
 +
 +static s32
 +brcmf_cfg80211_set_pmksa(struct wiphy *wiphy, struct net_device *ndev,
 +                       struct cfg80211_pmksa *pmksa)
 +{
 +      struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
 +      struct brcmf_if *ifp = netdev_priv(ndev);
 +      struct pmkid_list *pmkids = &cfg->pmk_list->pmkids;
 +      s32 err = 0;
 +      int i;
 +      int pmkid_len;
 +
 +      brcmf_dbg(TRACE, "Enter\n");
 +      if (!check_vif_up(ifp->vif))
 +              return -EIO;
 +
 +      pmkid_len = le32_to_cpu(pmkids->npmkid);
 +      for (i = 0; i < pmkid_len; i++)
 +              if (!memcmp(pmksa->bssid, pmkids->pmkid[i].BSSID, ETH_ALEN))
 +                      break;
 +      if (i < WL_NUM_PMKIDS_MAX) {
 +              memcpy(pmkids->pmkid[i].BSSID, pmksa->bssid, ETH_ALEN);
 +              memcpy(pmkids->pmkid[i].PMKID, pmksa->pmkid, WLAN_PMKID_LEN);
 +              if (i == pmkid_len) {
 +                      pmkid_len++;
 +                      pmkids->npmkid = cpu_to_le32(pmkid_len);
 +              }
 +      } else
 +              err = -EINVAL;
 +
 +      brcmf_dbg(CONN, "set_pmksa,IW_PMKSA_ADD - PMKID: %pM =\n",
 +                pmkids->pmkid[pmkid_len].BSSID);
 +      for (i = 0; i < WLAN_PMKID_LEN; i++)
 +              brcmf_dbg(CONN, "%02x\n", pmkids->pmkid[pmkid_len].PMKID[i]);
 +
 +      err = brcmf_update_pmklist(ndev, cfg->pmk_list, err);
 +
 +      brcmf_dbg(TRACE, "Exit\n");
 +      return err;
 +}
 +
 +static s32
 +brcmf_cfg80211_del_pmksa(struct wiphy *wiphy, struct net_device *ndev,
 +                    struct cfg80211_pmksa *pmksa)
 +{
 +      struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
 +      struct brcmf_if *ifp = netdev_priv(ndev);
 +      struct pmkid_list pmkid;
 +      s32 err = 0;
 +      int i, pmkid_len;
 +
 +      brcmf_dbg(TRACE, "Enter\n");
 +      if (!check_vif_up(ifp->vif))
 +              return -EIO;
 +
 +      memcpy(&pmkid.pmkid[0].BSSID, pmksa->bssid, ETH_ALEN);
 +      memcpy(&pmkid.pmkid[0].PMKID, pmksa->pmkid, WLAN_PMKID_LEN);
 +
 +      brcmf_dbg(CONN, "del_pmksa,IW_PMKSA_REMOVE - PMKID: %pM =\n",
 +                &pmkid.pmkid[0].BSSID);
 +      for (i = 0; i < WLAN_PMKID_LEN; i++)
 +              brcmf_dbg(CONN, "%02x\n", pmkid.pmkid[0].PMKID[i]);
 +
 +      pmkid_len = le32_to_cpu(cfg->pmk_list->pmkids.npmkid);
 +      for (i = 0; i < pmkid_len; i++)
 +              if (!memcmp
 +                  (pmksa->bssid, &cfg->pmk_list->pmkids.pmkid[i].BSSID,
 +                   ETH_ALEN))
 +                      break;
 +
 +      if ((pmkid_len > 0)
 +          && (i < pmkid_len)) {
 +              memset(&cfg->pmk_list->pmkids.pmkid[i], 0,
 +                     sizeof(struct pmkid));
 +              for (; i < (pmkid_len - 1); i++) {
 +                      memcpy(&cfg->pmk_list->pmkids.pmkid[i].BSSID,
 +                             &cfg->pmk_list->pmkids.pmkid[i + 1].BSSID,
 +                             ETH_ALEN);
 +                      memcpy(&cfg->pmk_list->pmkids.pmkid[i].PMKID,
 +                             &cfg->pmk_list->pmkids.pmkid[i + 1].PMKID,
 +                             WLAN_PMKID_LEN);
 +              }
 +              cfg->pmk_list->pmkids.npmkid = cpu_to_le32(pmkid_len - 1);
 +      } else
 +              err = -EINVAL;
 +
 +      err = brcmf_update_pmklist(ndev, cfg->pmk_list, err);
 +
 +      brcmf_dbg(TRACE, "Exit\n");
 +      return err;
 +
 +}
 +
 +static s32
 +brcmf_cfg80211_flush_pmksa(struct wiphy *wiphy, struct net_device *ndev)
 +{
 +      struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
 +      struct brcmf_if *ifp = netdev_priv(ndev);
 +      s32 err = 0;
 +
 +      brcmf_dbg(TRACE, "Enter\n");
 +      if (!check_vif_up(ifp->vif))
 +              return -EIO;
 +
 +      memset(cfg->pmk_list, 0, sizeof(*cfg->pmk_list));
 +      err = brcmf_update_pmklist(ndev, cfg->pmk_list, err);
 +
 +      brcmf_dbg(TRACE, "Exit\n");
 +      return err;
 +
 +}
 +
 +/*
 + * PFN result doesn't have all the info which are
 + * required by the supplicant
 + * (For e.g IEs) Do a target Escan so that sched scan results are reported
 + * via wl_inform_single_bss in the required format. Escan does require the
 + * scan request in the form of cfg80211_scan_request. For timebeing, create
 + * cfg80211_scan_request one out of the received PNO event.
 + */
 +static s32
 +brcmf_notify_sched_scan_results(struct brcmf_if *ifp,
 +                              const struct brcmf_event_msg *e, void *data)
 +{
 +      struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
 +      struct brcmf_pno_net_info_le *netinfo, *netinfo_start;
 +      struct cfg80211_scan_request *request = NULL;
 +      struct cfg80211_ssid *ssid = NULL;
 +      struct ieee80211_channel *channel = NULL;
 +      struct wiphy *wiphy = cfg_to_wiphy(cfg);
 +      int err = 0;
 +      int channel_req = 0;
 +      int band = 0;
 +      struct brcmf_pno_scanresults_le *pfn_result;
 +      u32 result_count;
 +      u32 status;
 +
 +      brcmf_dbg(SCAN, "Enter\n");
 +
 +      if (e->event_code == BRCMF_E_PFN_NET_LOST) {
 +              brcmf_dbg(SCAN, "PFN NET LOST event. Do Nothing\n");
 +              return 0;
 +      }
 +
 +      pfn_result = (struct brcmf_pno_scanresults_le *)data;
 +      result_count = le32_to_cpu(pfn_result->count);
 +      status = le32_to_cpu(pfn_result->status);
 +
 +      /*
 +       * PFN event is limited to fit 512 bytes so we may get
 +       * multiple NET_FOUND events. For now place a warning here.
 +       */
 +      WARN_ON(status != BRCMF_PNO_SCAN_COMPLETE);
 +      brcmf_dbg(SCAN, "PFN NET FOUND event. count: %d\n", result_count);
 +      if (result_count > 0) {
 +              int i;
 +
 +              request = kzalloc(sizeof(*request), GFP_KERNEL);
 +              ssid = kcalloc(result_count, sizeof(*ssid), GFP_KERNEL);
 +              channel = kcalloc(result_count, sizeof(*channel), GFP_KERNEL);
 +              if (!request || !ssid || !channel) {
 +                      err = -ENOMEM;
 +                      goto out_err;
 +              }
 +
 +              request->wiphy = wiphy;
 +              data += sizeof(struct brcmf_pno_scanresults_le);
 +              netinfo_start = (struct brcmf_pno_net_info_le *)data;
 +
 +              for (i = 0; i < result_count; i++) {
 +                      netinfo = &netinfo_start[i];
 +                      if (!netinfo) {
 +                              brcmf_err("Invalid netinfo ptr. index: %d\n",
 +                                        i);
 +                              err = -EINVAL;
 +                              goto out_err;
 +                      }
 +
 +                      brcmf_dbg(SCAN, "SSID:%s Channel:%d\n",
 +                                netinfo->SSID, netinfo->channel);
 +                      memcpy(ssid[i].ssid, netinfo->SSID, netinfo->SSID_len);
 +                      ssid[i].ssid_len = netinfo->SSID_len;
 +                      request->n_ssids++;
 +
 +                      channel_req = netinfo->channel;
 +                      if (channel_req <= CH_MAX_2G_CHANNEL)
 +                              band = NL80211_BAND_2GHZ;
 +                      else
 +                              band = NL80211_BAND_5GHZ;
 +                      channel[i].center_freq =
 +                              ieee80211_channel_to_frequency(channel_req,
 +                                                             band);
 +                      channel[i].band = band;
 +                      channel[i].flags |= IEEE80211_CHAN_NO_HT40;
 +                      request->channels[i] = &channel[i];
 +                      request->n_channels++;
 +              }
 +
 +              /* assign parsed ssid array */
 +              if (request->n_ssids)
 +                      request->ssids = &ssid[0];
 +
 +              if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) {
 +                      /* Abort any on-going scan */
 +                      brcmf_abort_scanning(cfg);
 +              }
 +
 +              set_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
 +              cfg->escan_info.run = brcmf_run_escan;
 +              err = brcmf_do_escan(cfg, wiphy, ifp, request);
 +              if (err) {
 +                      clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
 +                      goto out_err;
 +              }
 +              cfg->sched_escan = true;
 +              cfg->scan_request = request;
 +      } else {
 +              brcmf_err("FALSE PNO Event. (pfn_count == 0)\n");
 +              goto out_err;
 +      }
 +
 +      kfree(ssid);
 +      kfree(channel);
 +      kfree(request);
 +      return 0;
 +
 +out_err:
 +      kfree(ssid);
 +      kfree(channel);
 +      kfree(request);
 +      cfg80211_sched_scan_stopped(wiphy);
 +      return err;
 +}
 +
 +static int brcmf_dev_pno_clean(struct net_device *ndev)
 +{
 +      int ret;
 +
 +      /* Disable pfn */
 +      ret = brcmf_fil_iovar_int_set(netdev_priv(ndev), "pfn", 0);
 +      if (ret == 0) {
 +              /* clear pfn */
 +              ret = brcmf_fil_iovar_data_set(netdev_priv(ndev), "pfnclear",
 +                                             NULL, 0);
 +      }
 +      if (ret < 0)
 +              brcmf_err("failed code %d\n", ret);
 +
 +      return ret;
 +}
 +
 +static int brcmf_dev_pno_config(struct net_device *ndev)
 +{
 +      struct brcmf_pno_param_le pfn_param;
 +
 +      memset(&pfn_param, 0, sizeof(pfn_param));
 +      pfn_param.version = cpu_to_le32(BRCMF_PNO_VERSION);
 +
 +      /* set extra pno params */
 +      pfn_param.flags = cpu_to_le16(1 << BRCMF_PNO_ENABLE_ADAPTSCAN_BIT);
 +      pfn_param.repeat = BRCMF_PNO_REPEAT;
 +      pfn_param.exp = BRCMF_PNO_FREQ_EXPO_MAX;
 +
 +      /* set up pno scan fr */
 +      pfn_param.scan_freq = cpu_to_le32(BRCMF_PNO_TIME);
 +
 +      return brcmf_fil_iovar_data_set(netdev_priv(ndev), "pfn_set",
 +                                      &pfn_param, sizeof(pfn_param));
 +}
 +
 +static int
 +brcmf_cfg80211_sched_scan_start(struct wiphy *wiphy,
 +                              struct net_device *ndev,
 +                              struct cfg80211_sched_scan_request *request)
 +{
 +      struct brcmf_if *ifp = netdev_priv(ndev);
 +      struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy);
 +      struct brcmf_pno_net_param_le pfn;
 +      int i;
 +      int ret = 0;
 +
 +      brcmf_dbg(SCAN, "Enter n_match_sets:%d n_ssids:%d\n",
 +                request->n_match_sets, request->n_ssids);
 +      if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) {
 +              brcmf_err("Scanning already: status (%lu)\n", cfg->scan_status);
 +              return -EAGAIN;
 +      }
 +      if (test_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status)) {
 +              brcmf_err("Scanning suppressed: status (%lu)\n",
 +                        cfg->scan_status);
 +              return -EAGAIN;
 +      }
 +
 +      if (!request->n_ssids || !request->n_match_sets) {
 +              brcmf_dbg(SCAN, "Invalid sched scan req!! n_ssids:%d\n",
 +                        request->n_ssids);
 +              return -EINVAL;
 +      }
 +
 +      if (request->n_ssids > 0) {
 +              for (i = 0; i < request->n_ssids; i++) {
 +                      /* Active scan req for ssids */
 +                      brcmf_dbg(SCAN, ">>> Active scan req for ssid (%s)\n",
 +                                request->ssids[i].ssid);
 +
 +                      /*
 +                       * match_set ssids is a supert set of n_ssid list,
 +                       * so we need not add these set seperately.
 +                       */
 +              }
 +      }
 +
 +      if (request->n_match_sets > 0) {
 +              /* clean up everything */
 +              ret = brcmf_dev_pno_clean(ndev);
 +              if  (ret < 0) {
 +                      brcmf_err("failed error=%d\n", ret);
 +                      return ret;
 +              }
 +
 +              /* configure pno */
 +              ret = brcmf_dev_pno_config(ndev);
 +              if (ret < 0) {
 +                      brcmf_err("PNO setup failed!! ret=%d\n", ret);
 +                      return -EINVAL;
 +              }
 +
 +              /* configure each match set */
 +              for (i = 0; i < request->n_match_sets; i++) {
 +                      struct cfg80211_ssid *ssid;
 +                      u32 ssid_len;
 +
 +                      ssid = &request->match_sets[i].ssid;
 +                      ssid_len = ssid->ssid_len;
 +
 +                      if (!ssid_len) {
 +                              brcmf_err("skip broadcast ssid\n");
 +                              continue;
 +                      }
 +                      pfn.auth = cpu_to_le32(WLAN_AUTH_OPEN);
 +                      pfn.wpa_auth = cpu_to_le32(BRCMF_PNO_WPA_AUTH_ANY);
 +                      pfn.wsec = cpu_to_le32(0);
 +                      pfn.infra = cpu_to_le32(1);
 +                      pfn.flags = cpu_to_le32(1 << BRCMF_PNO_HIDDEN_BIT);
 +                      pfn.ssid.SSID_len = cpu_to_le32(ssid_len);
 +                      memcpy(pfn.ssid.SSID, ssid->ssid, ssid_len);
 +                      ret = brcmf_fil_iovar_data_set(ifp, "pfn_add", &pfn,
 +                                                     sizeof(pfn));
 +                      brcmf_dbg(SCAN, ">>> PNO filter %s for ssid (%s)\n",
 +                                ret == 0 ? "set" : "failed", ssid->ssid);
 +              }
 +              /* Enable the PNO */
 +              if (brcmf_fil_iovar_int_set(ifp, "pfn", 1) < 0) {
 +                      brcmf_err("PNO enable failed!! ret=%d\n", ret);
 +                      return -EINVAL;
 +              }
 +      } else {
 +              return -EINVAL;
 +      }
 +
 +      return 0;
 +}
 +
 +static int brcmf_cfg80211_sched_scan_stop(struct wiphy *wiphy,
 +                                        struct net_device *ndev)
 +{
 +      struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
 +
 +      brcmf_dbg(SCAN, "enter\n");
 +      brcmf_dev_pno_clean(ndev);
 +      if (cfg->sched_escan)
 +              brcmf_notify_escan_complete(cfg, netdev_priv(ndev), true, true);
 +      return 0;
 +}
 +
 +static s32 brcmf_configure_opensecurity(struct brcmf_if *ifp)
 +{
 +      s32 err;
 +
 +      /* set auth */
 +      err = brcmf_fil_bsscfg_int_set(ifp, "auth", 0);
 +      if (err < 0) {
 +              brcmf_err("auth error %d\n", err);
 +              return err;
 +      }
 +      /* set wsec */
 +      err = brcmf_fil_bsscfg_int_set(ifp, "wsec", 0);
 +      if (err < 0) {
 +              brcmf_err("wsec error %d\n", err);
 +              return err;
 +      }
 +      /* set upper-layer auth */
 +      err = brcmf_fil_bsscfg_int_set(ifp, "wpa_auth", WPA_AUTH_NONE);
 +      if (err < 0) {
 +              brcmf_err("wpa_auth error %d\n", err);
 +              return err;
 +      }
 +
 +      return 0;
 +}
 +
 +static bool brcmf_valid_wpa_oui(u8 *oui, bool is_rsn_ie)
 +{
 +      if (is_rsn_ie)
 +              return (memcmp(oui, RSN_OUI, TLV_OUI_LEN) == 0);
 +
 +      return (memcmp(oui, WPA_OUI, TLV_OUI_LEN) == 0);
 +}
 +
 +static s32
 +brcmf_configure_wpaie(struct net_device *ndev,
 +                    const struct brcmf_vs_tlv *wpa_ie,
 +                    bool is_rsn_ie)
 +{
 +      struct brcmf_if *ifp = netdev_priv(ndev);
 +      u32 auth = 0; /* d11 open authentication */
 +      u16 count;
 +      s32 err = 0;
 +      s32 len = 0;
 +      u32 i;
 +      u32 wsec;
 +      u32 pval = 0;
 +      u32 gval = 0;
 +      u32 wpa_auth = 0;
 +      u32 offset;
 +      u8 *data;
 +      u16 rsn_cap;
 +      u32 wme_bss_disable;
 +
 +      brcmf_dbg(TRACE, "Enter\n");
 +      if (wpa_ie == NULL)
 +              goto exit;
 +
 +      len = wpa_ie->len + TLV_HDR_LEN;
 +      data = (u8 *)wpa_ie;
 +      offset = TLV_HDR_LEN;
 +      if (!is_rsn_ie)
 +              offset += VS_IE_FIXED_HDR_LEN;
 +      else
 +              offset += WPA_IE_VERSION_LEN;
 +
 +      /* check for multicast cipher suite */
 +      if (offset + WPA_IE_MIN_OUI_LEN > len) {
 +              err = -EINVAL;
 +              brcmf_err("no multicast cipher suite\n");
 +              goto exit;
 +      }
 +
 +      if (!brcmf_valid_wpa_oui(&data[offset], is_rsn_ie)) {
 +              err = -EINVAL;
 +              brcmf_err("ivalid OUI\n");
 +              goto exit;
 +      }
 +      offset += TLV_OUI_LEN;
 +
 +      /* pick up multicast cipher */
 +      switch (data[offset]) {
 +      case WPA_CIPHER_NONE:
 +              gval = 0;
 +              break;
 +      case WPA_CIPHER_WEP_40:
 +      case WPA_CIPHER_WEP_104:
 +              gval = WEP_ENABLED;
 +              break;
 +      case WPA_CIPHER_TKIP:
 +              gval = TKIP_ENABLED;
 +              break;
 +      case WPA_CIPHER_AES_CCM:
 +              gval = AES_ENABLED;
 +              break;
 +      default:
 +              err = -EINVAL;
 +              brcmf_err("Invalid multi cast cipher info\n");
 +              goto exit;
 +      }
 +
 +      offset++;
 +      /* walk thru unicast cipher list and pick up what we recognize */
 +      count = data[offset] + (data[offset + 1] << 8);
 +      offset += WPA_IE_SUITE_COUNT_LEN;
 +      /* Check for unicast suite(s) */
 +      if (offset + (WPA_IE_MIN_OUI_LEN * count) > len) {
 +              err = -EINVAL;
 +              brcmf_err("no unicast cipher suite\n");
 +              goto exit;
 +      }
 +      for (i = 0; i < count; i++) {
 +              if (!brcmf_valid_wpa_oui(&data[offset], is_rsn_ie)) {
 +                      err = -EINVAL;
 +                      brcmf_err("ivalid OUI\n");
 +                      goto exit;
 +              }
 +              offset += TLV_OUI_LEN;
 +              switch (data[offset]) {
 +              case WPA_CIPHER_NONE:
 +                      break;
 +              case WPA_CIPHER_WEP_40:
 +              case WPA_CIPHER_WEP_104:
 +                      pval |= WEP_ENABLED;
 +                      break;
 +              case WPA_CIPHER_TKIP:
 +                      pval |= TKIP_ENABLED;
 +                      break;
 +              case WPA_CIPHER_AES_CCM:
 +                      pval |= AES_ENABLED;
 +                      break;
 +              default:
 +                      brcmf_err("Ivalid unicast security info\n");
 +              }
 +              offset++;
 +      }
 +      /* walk thru auth management suite list and pick up what we recognize */
 +      count = data[offset] + (data[offset + 1] << 8);
 +      offset += WPA_IE_SUITE_COUNT_LEN;
 +      /* Check for auth key management suite(s) */
 +      if (offset + (WPA_IE_MIN_OUI_LEN * count) > len) {
 +              err = -EINVAL;
 +              brcmf_err("no auth key mgmt suite\n");
 +              goto exit;
 +      }
 +      for (i = 0; i < count; i++) {
 +              if (!brcmf_valid_wpa_oui(&data[offset], is_rsn_ie)) {
 +                      err = -EINVAL;
 +                      brcmf_err("ivalid OUI\n");
 +                      goto exit;
 +              }
 +              offset += TLV_OUI_LEN;
 +              switch (data[offset]) {
 +              case RSN_AKM_NONE:
 +                      brcmf_dbg(TRACE, "RSN_AKM_NONE\n");
 +                      wpa_auth |= WPA_AUTH_NONE;
 +                      break;
 +              case RSN_AKM_UNSPECIFIED:
 +                      brcmf_dbg(TRACE, "RSN_AKM_UNSPECIFIED\n");
 +                      is_rsn_ie ? (wpa_auth |= WPA2_AUTH_UNSPECIFIED) :
 +                                  (wpa_auth |= WPA_AUTH_UNSPECIFIED);
 +                      break;
 +              case RSN_AKM_PSK:
 +                      brcmf_dbg(TRACE, "RSN_AKM_PSK\n");
 +                      is_rsn_ie ? (wpa_auth |= WPA2_AUTH_PSK) :
 +                                  (wpa_auth |= WPA_AUTH_PSK);
 +                      break;
 +              default:
 +                      brcmf_err("Ivalid key mgmt info\n");
 +              }
 +              offset++;
 +      }
 +
 +      if (is_rsn_ie) {
 +              wme_bss_disable = 1;
 +              if ((offset + RSN_CAP_LEN) <= len) {
 +                      rsn_cap = data[offset] + (data[offset + 1] << 8);
 +                      if (rsn_cap & RSN_CAP_PTK_REPLAY_CNTR_MASK)
 +                              wme_bss_disable = 0;
 +              }
 +              /* set wme_bss_disable to sync RSN Capabilities */
 +              err = brcmf_fil_bsscfg_int_set(ifp, "wme_bss_disable",
 +                                             wme_bss_disable);
 +              if (err < 0) {
 +                      brcmf_err("wme_bss_disable error %d\n", err);
 +                      goto exit;
 +              }
 +      }
 +      /* FOR WPS , set SES_OW_ENABLED */
 +      wsec = (pval | gval | SES_OW_ENABLED);
 +
 +      /* set auth */
 +      err = brcmf_fil_bsscfg_int_set(ifp, "auth", auth);
 +      if (err < 0) {
 +              brcmf_err("auth error %d\n", err);
 +              goto exit;
 +      }
 +      /* set wsec */
 +      err = brcmf_fil_bsscfg_int_set(ifp, "wsec", wsec);
 +      if (err < 0) {
 +              brcmf_err("wsec error %d\n", err);
 +              goto exit;
 +      }
 +      /* set upper-layer auth */
 +      err = brcmf_fil_bsscfg_int_set(ifp, "wpa_auth", wpa_auth);
 +      if (err < 0) {
 +              brcmf_err("wpa_auth error %d\n", err);
 +              goto exit;
 +      }
 +
 +exit:
 +      return err;
 +}
 +
 +static s32
 +brcmf_parse_vndr_ies(const u8 *vndr_ie_buf, u32 vndr_ie_len,
 +                   struct parsed_vndr_ies *vndr_ies)
 +{
 +      struct brcmf_vs_tlv *vndrie;
 +      struct brcmf_tlv *ie;
 +      struct parsed_vndr_ie_info *parsed_info;
 +      s32 remaining_len;
 +
 +      remaining_len = (s32)vndr_ie_len;
 +      memset(vndr_ies, 0, sizeof(*vndr_ies));
 +
 +      ie = (struct brcmf_tlv *)vndr_ie_buf;
 +      while (ie) {
 +              if (ie->id != WLAN_EID_VENDOR_SPECIFIC)
 +                      goto next;
 +              vndrie = (struct brcmf_vs_tlv *)ie;
 +              /* len should be bigger than OUI length + one */
 +              if (vndrie->len < (VS_IE_FIXED_HDR_LEN - TLV_HDR_LEN + 1)) {
 +                      brcmf_err("invalid vndr ie. length is too small %d\n",
 +                                vndrie->len);
 +                      goto next;
 +              }
 +              /* if wpa or wme ie, do not add ie */
 +              if (!memcmp(vndrie->oui, (u8 *)WPA_OUI, TLV_OUI_LEN) &&
 +                  ((vndrie->oui_type == WPA_OUI_TYPE) ||
 +                  (vndrie->oui_type == WME_OUI_TYPE))) {
 +                      brcmf_dbg(TRACE, "Found WPA/WME oui. Do not add it\n");
 +                      goto next;
 +              }
 +
 +              parsed_info = &vndr_ies->ie_info[vndr_ies->count];
 +
 +              /* save vndr ie information */
 +              parsed_info->ie_ptr = (char *)vndrie;
 +              parsed_info->ie_len = vndrie->len + TLV_HDR_LEN;
 +              memcpy(&parsed_info->vndrie, vndrie, sizeof(*vndrie));
 +
 +              vndr_ies->count++;
 +
 +              brcmf_dbg(TRACE, "** OUI %02x %02x %02x, type 0x%02x\n",
 +                        parsed_info->vndrie.oui[0],
 +                        parsed_info->vndrie.oui[1],
 +                        parsed_info->vndrie.oui[2],
 +                        parsed_info->vndrie.oui_type);
 +
 +              if (vndr_ies->count >= VNDR_IE_PARSE_LIMIT)
 +                      break;
 +next:
 +              remaining_len -= (ie->len + TLV_HDR_LEN);
 +              if (remaining_len <= TLV_HDR_LEN)
 +                      ie = NULL;
 +              else
 +                      ie = (struct brcmf_tlv *)(((u8 *)ie) + ie->len +
 +                              TLV_HDR_LEN);
 +      }
 +      return 0;
 +}
 +
 +static u32
 +brcmf_vndr_ie(u8 *iebuf, s32 pktflag, u8 *ie_ptr, u32 ie_len, s8 *add_del_cmd)
 +{
 +
 +      __le32 iecount_le;
 +      __le32 pktflag_le;
 +
 +      strncpy(iebuf, add_del_cmd, VNDR_IE_CMD_LEN - 1);
 +      iebuf[VNDR_IE_CMD_LEN - 1] = '\0';
 +
 +      iecount_le = cpu_to_le32(1);
 +      memcpy(&iebuf[VNDR_IE_COUNT_OFFSET], &iecount_le, sizeof(iecount_le));
 +
 +      pktflag_le = cpu_to_le32(pktflag);
 +      memcpy(&iebuf[VNDR_IE_PKTFLAG_OFFSET], &pktflag_le, sizeof(pktflag_le));
 +
 +      memcpy(&iebuf[VNDR_IE_VSIE_OFFSET], ie_ptr, ie_len);
 +
 +      return ie_len + VNDR_IE_HDR_SIZE;
 +}
 +
 +s32 brcmf_vif_set_mgmt_ie(struct brcmf_cfg80211_vif *vif, s32 pktflag,
 +                        const u8 *vndr_ie_buf, u32 vndr_ie_len)
 +{
 +      struct brcmf_if *ifp;
 +      struct vif_saved_ie *saved_ie;
 +      s32 err = 0;
 +      u8  *iovar_ie_buf;
 +      u8  *curr_ie_buf;
 +      u8  *mgmt_ie_buf = NULL;
 +      int mgmt_ie_buf_len;
 +      u32 *mgmt_ie_len;
 +      u32 del_add_ie_buf_len = 0;
 +      u32 total_ie_buf_len = 0;
 +      u32 parsed_ie_buf_len = 0;
 +      struct parsed_vndr_ies old_vndr_ies;
 +      struct parsed_vndr_ies new_vndr_ies;
 +      struct parsed_vndr_ie_info *vndrie_info;
 +      s32 i;
 +      u8 *ptr;
 +      int remained_buf_len;
 +
 +      if (!vif)
 +              return -ENODEV;
 +      ifp = vif->ifp;
 +      saved_ie = &vif->saved_ie;
 +
 +      brcmf_dbg(TRACE, "bssidx %d, pktflag : 0x%02X\n", ifp->bssidx, pktflag);
 +      iovar_ie_buf = kzalloc(WL_EXTRA_BUF_MAX, GFP_KERNEL);
 +      if (!iovar_ie_buf)
 +              return -ENOMEM;
 +      curr_ie_buf = iovar_ie_buf;
 +      switch (pktflag) {
 +      case BRCMF_VNDR_IE_PRBREQ_FLAG:
 +              mgmt_ie_buf = saved_ie->probe_req_ie;
 +              mgmt_ie_len = &saved_ie->probe_req_ie_len;
 +              mgmt_ie_buf_len = sizeof(saved_ie->probe_req_ie);
 +              break;
 +      case BRCMF_VNDR_IE_PRBRSP_FLAG:
 +              mgmt_ie_buf = saved_ie->probe_res_ie;
 +              mgmt_ie_len = &saved_ie->probe_res_ie_len;
 +              mgmt_ie_buf_len = sizeof(saved_ie->probe_res_ie);
 +              break;
 +      case BRCMF_VNDR_IE_BEACON_FLAG:
 +              mgmt_ie_buf = saved_ie->beacon_ie;
 +              mgmt_ie_len = &saved_ie->beacon_ie_len;
 +              mgmt_ie_buf_len = sizeof(saved_ie->beacon_ie);
 +              break;
 +      case BRCMF_VNDR_IE_ASSOCREQ_FLAG:
 +              mgmt_ie_buf = saved_ie->assoc_req_ie;
 +              mgmt_ie_len = &saved_ie->assoc_req_ie_len;
 +              mgmt_ie_buf_len = sizeof(saved_ie->assoc_req_ie);
 +              break;
 +      default:
 +              err = -EPERM;
 +              brcmf_err("not suitable type\n");
 +              goto exit;
 +      }
 +
 +      if (vndr_ie_len > mgmt_ie_buf_len) {
 +              err = -ENOMEM;
 +              brcmf_err("extra IE size too big\n");
 +              goto exit;
 +      }
 +
 +      /* parse and save new vndr_ie in curr_ie_buff before comparing it */
 +      if (vndr_ie_buf && vndr_ie_len && curr_ie_buf) {
 +              ptr = curr_ie_buf;
 +              brcmf_parse_vndr_ies(vndr_ie_buf, vndr_ie_len, &new_vndr_ies);
 +              for (i = 0; i < new_vndr_ies.count; i++) {
 +                      vndrie_info = &new_vndr_ies.ie_info[i];
 +                      memcpy(ptr + parsed_ie_buf_len, vndrie_info->ie_ptr,
 +                             vndrie_info->ie_len);
 +                      parsed_ie_buf_len += vndrie_info->ie_len;
 +              }
 +      }
 +
 +      if (mgmt_ie_buf && *mgmt_ie_len) {
 +              if (parsed_ie_buf_len && (parsed_ie_buf_len == *mgmt_ie_len) &&
 +                  (memcmp(mgmt_ie_buf, curr_ie_buf,
 +                          parsed_ie_buf_len) == 0)) {
 +                      brcmf_dbg(TRACE, "Previous mgmt IE equals to current IE\n");
 +                      goto exit;
 +              }
 +
 +              /* parse old vndr_ie */
 +              brcmf_parse_vndr_ies(mgmt_ie_buf, *mgmt_ie_len, &old_vndr_ies);
 +
 +              /* make a command to delete old ie */
 +              for (i = 0; i < old_vndr_ies.count; i++) {
 +                      vndrie_info = &old_vndr_ies.ie_info[i];
 +
 +                      brcmf_dbg(TRACE, "DEL ID : %d, Len: %d , OUI:%02x:%02x:%02x\n",
 +                                vndrie_info->vndrie.id,
 +                                vndrie_info->vndrie.len,
 +                                vndrie_info->vndrie.oui[0],
 +                                vndrie_info->vndrie.oui[1],
 +                                vndrie_info->vndrie.oui[2]);
 +
 +                      del_add_ie_buf_len = brcmf_vndr_ie(curr_ie_buf, pktflag,
 +                                                         vndrie_info->ie_ptr,
 +                                                         vndrie_info->ie_len,
 +                                                         "del");
 +                      curr_ie_buf += del_add_ie_buf_len;
 +                      total_ie_buf_len += del_add_ie_buf_len;
 +              }
 +      }
 +
 +      *mgmt_ie_len = 0;
 +      /* Add if there is any extra IE */
 +      if (mgmt_ie_buf && parsed_ie_buf_len) {
 +              ptr = mgmt_ie_buf;
 +
 +              remained_buf_len = mgmt_ie_buf_len;
 +
 +              /* make a command to add new ie */
 +              for (i = 0; i < new_vndr_ies.count; i++) {
 +                      vndrie_info = &new_vndr_ies.ie_info[i];
 +
 +                      /* verify remained buf size before copy data */
 +                      if (remained_buf_len < (vndrie_info->vndrie.len +
 +                                                      VNDR_IE_VSIE_OFFSET)) {
 +                              brcmf_err("no space in mgmt_ie_buf: len left %d",
 +                                        remained_buf_len);
 +                              break;
 +                      }
 +                      remained_buf_len -= (vndrie_info->ie_len +
 +                                           VNDR_IE_VSIE_OFFSET);
 +
 +                      brcmf_dbg(TRACE, "ADDED ID : %d, Len: %d, OUI:%02x:%02x:%02x\n",
 +                                vndrie_info->vndrie.id,
 +                                vndrie_info->vndrie.len,
 +                                vndrie_info->vndrie.oui[0],
 +                                vndrie_info->vndrie.oui[1],
 +                                vndrie_info->vndrie.oui[2]);
 +
 +                      del_add_ie_buf_len = brcmf_vndr_ie(curr_ie_buf, pktflag,
 +                                                         vndrie_info->ie_ptr,
 +                                                         vndrie_info->ie_len,
 +                                                         "add");
 +
 +                      /* save the parsed IE in wl struct */
 +                      memcpy(ptr + (*mgmt_ie_len), vndrie_info->ie_ptr,
 +                             vndrie_info->ie_len);
 +                      *mgmt_ie_len += vndrie_info->ie_len;
 +
 +                      curr_ie_buf += del_add_ie_buf_len;
 +                      total_ie_buf_len += del_add_ie_buf_len;
 +              }
 +      }
 +      if (total_ie_buf_len) {
 +              err  = brcmf_fil_bsscfg_data_set(ifp, "vndr_ie", iovar_ie_buf,
 +                                               total_ie_buf_len);
 +              if (err)
 +                      brcmf_err("vndr ie set error : %d\n", err);
 +      }
 +
 +exit:
 +      kfree(iovar_ie_buf);
 +      return err;
 +}
 +
 +s32 brcmf_vif_clear_mgmt_ies(struct brcmf_cfg80211_vif *vif)
 +{
 +      s32 pktflags[] = {
 +              BRCMF_VNDR_IE_PRBREQ_FLAG,
 +              BRCMF_VNDR_IE_PRBRSP_FLAG,
 +              BRCMF_VNDR_IE_BEACON_FLAG
 +      };
 +      int i;
 +
 +      for (i = 0; i < ARRAY_SIZE(pktflags); i++)
 +              brcmf_vif_set_mgmt_ie(vif, pktflags[i], NULL, 0);
 +
 +      memset(&vif->saved_ie, 0, sizeof(vif->saved_ie));
 +      return 0;
 +}
 +
 +static s32
 +brcmf_config_ap_mgmt_ie(struct brcmf_cfg80211_vif *vif,
 +                      struct cfg80211_beacon_data *beacon)
 +{
 +      s32 err;
 +
 +      /* Set Beacon IEs to FW */
 +      err = brcmf_vif_set_mgmt_ie(vif, BRCMF_VNDR_IE_BEACON_FLAG,
 +                                  beacon->tail, beacon->tail_len);
 +      if (err) {
 +              brcmf_err("Set Beacon IE Failed\n");
 +              return err;
 +      }
 +      brcmf_dbg(TRACE, "Applied Vndr IEs for Beacon\n");
 +
 +      /* Set Probe Response IEs to FW */
 +      err = brcmf_vif_set_mgmt_ie(vif, BRCMF_VNDR_IE_PRBRSP_FLAG,
 +                                  beacon->proberesp_ies,
 +                                  beacon->proberesp_ies_len);
 +      if (err)
 +              brcmf_err("Set Probe Resp IE Failed\n");
 +      else
 +              brcmf_dbg(TRACE, "Applied Vndr IEs for Probe Resp\n");
 +
 +      return err;
 +}
 +
 +static s32
 +brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev,
 +                      struct cfg80211_ap_settings *settings)
 +{
 +      s32 ie_offset;
 +      struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
 +      struct brcmf_if *ifp = netdev_priv(ndev);
 +      const struct brcmf_tlv *ssid_ie;
 +      struct brcmf_ssid_le ssid_le;
 +      s32 err = -EPERM;
 +      const struct brcmf_tlv *rsn_ie;
 +      const struct brcmf_vs_tlv *wpa_ie;
 +      struct brcmf_join_params join_params;
 +      enum nl80211_iftype dev_role;
 +      struct brcmf_fil_bss_enable_le bss_enable;
 +      u16 chanspec;
 +
 +      brcmf_dbg(TRACE, "ctrlchn=%d, center=%d, bw=%d, beacon_interval=%d, dtim_period=%d,\n",
 +                settings->chandef.chan->hw_value,
 +                settings->chandef.center_freq1, settings->chandef.width,
 +                settings->beacon_interval, settings->dtim_period);
 +      brcmf_dbg(TRACE, "ssid=%s(%zu), auth_type=%d, inactivity_timeout=%d\n",
 +                settings->ssid, settings->ssid_len, settings->auth_type,
 +                settings->inactivity_timeout);
 +
 +      dev_role = ifp->vif->wdev.iftype;
 +
 +      memset(&ssid_le, 0, sizeof(ssid_le));
 +      if (settings->ssid == NULL || settings->ssid_len == 0) {
 +              ie_offset = DOT11_MGMT_HDR_LEN + DOT11_BCN_PRB_FIXED_LEN;
 +              ssid_ie = brcmf_parse_tlvs(
 +                              (u8 *)&settings->beacon.head[ie_offset],
 +                              settings->beacon.head_len - ie_offset,
 +                              WLAN_EID_SSID);
 +              if (!ssid_ie)
 +                      return -EINVAL;
 +
 +              memcpy(ssid_le.SSID, ssid_ie->data, ssid_ie->len);
 +              ssid_le.SSID_len = cpu_to_le32(ssid_ie->len);
 +              brcmf_dbg(TRACE, "SSID is (%s) in Head\n", ssid_le.SSID);
 +      } else {
 +              memcpy(ssid_le.SSID, settings->ssid, settings->ssid_len);
 +              ssid_le.SSID_len = cpu_to_le32((u32)settings->ssid_len);
 +      }
 +
 +      brcmf_set_mpc(ifp, 0);
 +      brcmf_configure_arp_offload(ifp, false);
 +
 +      /* find the RSN_IE */
 +      rsn_ie = brcmf_parse_tlvs((u8 *)settings->beacon.tail,
 +                                settings->beacon.tail_len, WLAN_EID_RSN);
 +
 +      /* find the WPA_IE */
 +      wpa_ie = brcmf_find_wpaie((u8 *)settings->beacon.tail,
 +                                settings->beacon.tail_len);
 +
 +      if ((wpa_ie != NULL || rsn_ie != NULL)) {
 +              brcmf_dbg(TRACE, "WPA(2) IE is found\n");
 +              if (wpa_ie != NULL) {
 +                      /* WPA IE */
 +                      err = brcmf_configure_wpaie(ndev, wpa_ie, false);
 +                      if (err < 0)
 +                              goto exit;
 +              } else {
 +                      /* RSN IE */
 +                      err = brcmf_configure_wpaie(ndev,
 +                              (struct brcmf_vs_tlv *)rsn_ie, true);
 +                      if (err < 0)
 +                              goto exit;
 +              }
 +      } else {
 +              brcmf_dbg(TRACE, "No WPA(2) IEs found\n");
 +              brcmf_configure_opensecurity(ifp);
 +      }
 +
 +      brcmf_config_ap_mgmt_ie(ifp->vif, &settings->beacon);
 +
 +      chanspec = chandef_to_chanspec(&cfg->d11inf, &settings->chandef);
 +      err = brcmf_fil_iovar_int_set(ifp, "chanspec", chanspec);
 +      if (err < 0) {
 +              brcmf_err("Set Channel failed: chspec=%d, %d\n", chanspec, err);
 +              goto exit;
 +      }
 +
 +      if (settings->beacon_interval) {
 +              err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_BCNPRD,
 +                                          settings->beacon_interval);
 +              if (err < 0) {
 +                      brcmf_err("Beacon Interval Set Error, %d\n", err);
 +                      goto exit;
 +              }
 +      }
 +      if (settings->dtim_period) {
 +              err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_DTIMPRD,
 +                                          settings->dtim_period);
 +              if (err < 0) {
 +                      brcmf_err("DTIM Interval Set Error, %d\n", err);
 +                      goto exit;
 +              }
 +      }
 +
 +      if (dev_role == NL80211_IFTYPE_AP) {
 +              err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_DOWN, 1);
 +              if (err < 0) {
 +                      brcmf_err("BRCMF_C_DOWN error %d\n", err);
 +                      goto exit;
 +              }
 +              brcmf_fil_iovar_int_set(ifp, "apsta", 0);
 +      }
 +
 +      err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_INFRA, 1);
 +      if (err < 0) {
 +              brcmf_err("SET INFRA error %d\n", err);
 +              goto exit;
 +      }
 +      if (dev_role == NL80211_IFTYPE_AP) {
 +              err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_AP, 1);
 +              if (err < 0) {
 +                      brcmf_err("setting AP mode failed %d\n", err);
 +                      goto exit;
 +              }
 +              err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 1);
 +              if (err < 0) {
 +                      brcmf_err("BRCMF_C_UP error (%d)\n", err);
 +                      goto exit;
 +              }
 +
 +              memset(&join_params, 0, sizeof(join_params));
 +              /* join parameters starts with ssid */
 +              memcpy(&join_params.ssid_le, &ssid_le, sizeof(ssid_le));
 +              /* create softap */
 +              err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID,
 +                                           &join_params, sizeof(join_params));
 +              if (err < 0) {
 +                      brcmf_err("SET SSID error (%d)\n", err);
 +                      goto exit;
 +              }
 +              brcmf_dbg(TRACE, "AP mode configuration complete\n");
 +      } else {
 +              err = brcmf_fil_bsscfg_data_set(ifp, "ssid", &ssid_le,
 +                                              sizeof(ssid_le));
 +              if (err < 0) {
 +                      brcmf_err("setting ssid failed %d\n", err);
 +                      goto exit;
 +              }
 +              bss_enable.bsscfg_idx = cpu_to_le32(ifp->bssidx);
 +              bss_enable.enable = cpu_to_le32(1);
 +              err = brcmf_fil_iovar_data_set(ifp, "bss", &bss_enable,
 +                                             sizeof(bss_enable));
 +              if (err < 0) {
 +                      brcmf_err("bss_enable config failed %d\n", err);
 +                      goto exit;
 +              }
 +
 +              brcmf_dbg(TRACE, "GO mode configuration complete\n");
 +      }
 +      clear_bit(BRCMF_VIF_STATUS_AP_CREATING, &ifp->vif->sme_state);
 +      set_bit(BRCMF_VIF_STATUS_AP_CREATED, &ifp->vif->sme_state);
 +
 +exit:
 +      if (err) {
 +              brcmf_set_mpc(ifp, 1);
 +              brcmf_configure_arp_offload(ifp, true);
 +      }
 +      return err;
 +}
 +
 +static int brcmf_cfg80211_stop_ap(struct wiphy *wiphy, struct net_device *ndev)
 +{
 +      struct brcmf_if *ifp = netdev_priv(ndev);
 +      s32 err;
 +      struct brcmf_fil_bss_enable_le bss_enable;
 +      struct brcmf_join_params join_params;
 +
 +      brcmf_dbg(TRACE, "Enter\n");
 +
 +      if (ifp->vif->wdev.iftype == NL80211_IFTYPE_AP) {
 +              /* Due to most likely deauths outstanding we sleep */
 +              /* first to make sure they get processed by fw. */
 +              msleep(400);
 +
 +              memset(&join_params, 0, sizeof(join_params));
 +              err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID,
 +                                           &join_params, sizeof(join_params));
 +              if (err < 0)
 +                      brcmf_err("SET SSID error (%d)\n", err);
 +              err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 0);
 +              if (err < 0)
 +                      brcmf_err("BRCMF_C_UP error %d\n", err);
 +              err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_AP, 0);
 +              if (err < 0)
 +                      brcmf_err("setting AP mode failed %d\n", err);
 +              err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_INFRA, 0);
 +              if (err < 0)
 +                      brcmf_err("setting INFRA mode failed %d\n", err);
 +      } else {
 +              bss_enable.bsscfg_idx = cpu_to_le32(ifp->bssidx);
 +              bss_enable.enable = cpu_to_le32(0);
 +              err = brcmf_fil_iovar_data_set(ifp, "bss", &bss_enable,
 +                                             sizeof(bss_enable));
 +              if (err < 0)
 +                      brcmf_err("bss_enable config failed %d\n", err);
 +      }
 +      brcmf_set_mpc(ifp, 1);
 +      brcmf_configure_arp_offload(ifp, true);
 +      set_bit(BRCMF_VIF_STATUS_AP_CREATING, &ifp->vif->sme_state);
 +      clear_bit(BRCMF_VIF_STATUS_AP_CREATED, &ifp->vif->sme_state);
 +
 +      return err;
 +}
 +
 +static s32
 +brcmf_cfg80211_change_beacon(struct wiphy *wiphy, struct net_device *ndev,
 +                           struct cfg80211_beacon_data *info)
 +{
 +      struct brcmf_if *ifp = netdev_priv(ndev);
 +      s32 err;
 +
 +      brcmf_dbg(TRACE, "Enter\n");
 +
 +      err = brcmf_config_ap_mgmt_ie(ifp->vif, info);
 +
 +      return err;
 +}
 +
 +static int
 +brcmf_cfg80211_del_station(struct wiphy *wiphy, struct net_device *ndev,
 +                         struct station_del_parameters *params)
 +{
 +      struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
 +      struct brcmf_scb_val_le scbval;
 +      struct brcmf_if *ifp = netdev_priv(ndev);
 +      s32 err;
 +
 +      if (!params->mac)
 +              return -EFAULT;
 +
 +      brcmf_dbg(TRACE, "Enter %pM\n", params->mac);
 +
 +      if (ifp->vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif)
 +              ifp = cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif->ifp;
 +      if (!check_vif_up(ifp->vif))
 +              return -EIO;
 +
 +      memcpy(&scbval.ea, params->mac, ETH_ALEN);
 +      scbval.val = cpu_to_le32(WLAN_REASON_DEAUTH_LEAVING);
 +      err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SCB_DEAUTHENTICATE_FOR_REASON,
 +                                   &scbval, sizeof(scbval));
 +      if (err)
 +              brcmf_err("SCB_DEAUTHENTICATE_FOR_REASON failed %d\n", err);
 +
 +      brcmf_dbg(TRACE, "Exit\n");
 +      return err;
 +}
 +
 +
 +static void
 +brcmf_cfg80211_mgmt_frame_register(struct wiphy *wiphy,
 +                                 struct wireless_dev *wdev,
 +                                 u16 frame_type, bool reg)
 +{
 +      struct brcmf_cfg80211_vif *vif;
 +      u16 mgmt_type;
 +
 +      brcmf_dbg(TRACE, "Enter, frame_type %04x, reg=%d\n", frame_type, reg);
 +
 +      mgmt_type = (frame_type & IEEE80211_FCTL_STYPE) >> 4;
 +      vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
 +      if (reg)
 +              vif->mgmt_rx_reg |= BIT(mgmt_type);
 +      else
 +              vif->mgmt_rx_reg &= ~BIT(mgmt_type);
 +}
 +
 +
 +static int
 +brcmf_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
 +                     struct cfg80211_mgmt_tx_params *params, u64 *cookie)
 +{
 +      struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
 +      struct ieee80211_channel *chan = params->chan;
 +      const u8 *buf = params->buf;
 +      size_t len = params->len;
 +      const struct ieee80211_mgmt *mgmt;
 +      struct brcmf_cfg80211_vif *vif;
 +      s32 err = 0;
 +      s32 ie_offset;
 +      s32 ie_len;
 +      struct brcmf_fil_action_frame_le *action_frame;
 +      struct brcmf_fil_af_params_le *af_params;
 +      bool ack;
 +      s32 chan_nr;
 +      u32 freq;
 +
 +      brcmf_dbg(TRACE, "Enter\n");
 +
 +      *cookie = 0;
 +
 +      mgmt = (const struct ieee80211_mgmt *)buf;
 +
 +      if (!ieee80211_is_mgmt(mgmt->frame_control)) {
 +              brcmf_err("Driver only allows MGMT packet type\n");
 +              return -EPERM;
 +      }
 +
 +      vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
 +
 +      if (ieee80211_is_probe_resp(mgmt->frame_control)) {
 +              /* Right now the only reason to get a probe response */
 +              /* is for p2p listen response or for p2p GO from     */
 +              /* wpa_supplicant. Unfortunately the probe is send   */
 +              /* on primary ndev, while dongle wants it on the p2p */
 +              /* vif. Since this is only reason for a probe        */
 +              /* response to be sent, the vif is taken from cfg.   */
 +              /* If ever desired to send proberesp for non p2p     */
 +              /* response then data should be checked for          */
 +              /* "DIRECT-". Note in future supplicant will take    */
 +              /* dedicated p2p wdev to do this and then this 'hack'*/
 +              /* is not needed anymore.                            */
 +              ie_offset =  DOT11_MGMT_HDR_LEN +
 +                           DOT11_BCN_PRB_FIXED_LEN;
 +              ie_len = len - ie_offset;
 +              if (vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif)
 +                      vif = cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif;
 +              err = brcmf_vif_set_mgmt_ie(vif,
 +                                          BRCMF_VNDR_IE_PRBRSP_FLAG,
 +                                          &buf[ie_offset],
 +                                          ie_len);
 +              cfg80211_mgmt_tx_status(wdev, *cookie, buf, len, true,
 +                                      GFP_KERNEL);
 +      } else if (ieee80211_is_action(mgmt->frame_control)) {
 +              af_params = kzalloc(sizeof(*af_params), GFP_KERNEL);
 +              if (af_params == NULL) {
 +                      brcmf_err("unable to allocate frame\n");
 +                      err = -ENOMEM;
 +                      goto exit;
 +              }
 +              action_frame = &af_params->action_frame;
 +              /* Add the packet Id */
 +              action_frame->packet_id = cpu_to_le32(*cookie);
 +              /* Add BSSID */
 +              memcpy(&action_frame->da[0], &mgmt->da[0], ETH_ALEN);
 +              memcpy(&af_params->bssid[0], &mgmt->bssid[0], ETH_ALEN);
 +              /* Add the length exepted for 802.11 header  */
 +              action_frame->len = cpu_to_le16(len - DOT11_MGMT_HDR_LEN);
 +              /* Add the channel. Use the one specified as parameter if any or
 +               * the current one (got from the firmware) otherwise
 +               */
 +              if (chan)
 +                      freq = chan->center_freq;
 +              else
 +                      brcmf_fil_cmd_int_get(vif->ifp, BRCMF_C_GET_CHANNEL,
 +                                            &freq);
 +              chan_nr = ieee80211_frequency_to_channel(freq);
 +              af_params->channel = cpu_to_le32(chan_nr);
 +
 +              memcpy(action_frame->data, &buf[DOT11_MGMT_HDR_LEN],
 +                     le16_to_cpu(action_frame->len));
 +
 +              brcmf_dbg(TRACE, "Action frame, cookie=%lld, len=%d, freq=%d\n",
 +                        *cookie, le16_to_cpu(action_frame->len), freq);
 +
 +              ack = brcmf_p2p_send_action_frame(cfg, cfg_to_ndev(cfg),
 +                                                af_params);
 +
 +              cfg80211_mgmt_tx_status(wdev, *cookie, buf, len, ack,
 +                                      GFP_KERNEL);
 +              kfree(af_params);
 +      } else {
 +              brcmf_dbg(TRACE, "Unhandled, fc=%04x!!\n", mgmt->frame_control);
 +              brcmf_dbg_hex_dump(true, buf, len, "payload, len=%Zu\n", len);
 +      }
 +
 +exit:
 +      return err;
 +}
 +
 +
 +static int
 +brcmf_cfg80211_cancel_remain_on_channel(struct wiphy *wiphy,
 +                                      struct wireless_dev *wdev,
 +                                      u64 cookie)
 +{
 +      struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
 +      struct brcmf_cfg80211_vif *vif;
 +      int err = 0;
 +
 +      brcmf_dbg(TRACE, "Enter p2p listen cancel\n");
 +
 +      vif = cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif;
 +      if (vif == NULL) {
 +              brcmf_err("No p2p device available for probe response\n");
 +              err = -ENODEV;
 +              goto exit;
 +      }
 +      brcmf_p2p_cancel_remain_on_channel(vif->ifp);
 +exit:
 +      return err;
 +}
 +
 +static int brcmf_cfg80211_crit_proto_start(struct wiphy *wiphy,
 +                                         struct wireless_dev *wdev,
 +                                         enum nl80211_crit_proto_id proto,
 +                                         u16 duration)
 +{
 +      struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
 +      struct brcmf_cfg80211_vif *vif;
 +
 +      vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
 +
 +      /* only DHCP support for now */
 +      if (proto != NL80211_CRIT_PROTO_DHCP)
 +              return -EINVAL;
 +
 +      /* suppress and abort scanning */
 +      set_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status);
 +      brcmf_abort_scanning(cfg);
 +
 +      return brcmf_btcoex_set_mode(vif, BRCMF_BTCOEX_DISABLED, duration);
 +}
 +
 +static void brcmf_cfg80211_crit_proto_stop(struct wiphy *wiphy,
 +                                         struct wireless_dev *wdev)
 +{
 +      struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
 +      struct brcmf_cfg80211_vif *vif;
 +
 +      vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
 +
 +      brcmf_btcoex_set_mode(vif, BRCMF_BTCOEX_ENABLED, 0);
 +      clear_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status);
 +}
 +
 +static s32
 +brcmf_notify_tdls_peer_event(struct brcmf_if *ifp,
 +                           const struct brcmf_event_msg *e, void *data)
 +{
 +      switch (e->reason) {
 +      case BRCMF_E_REASON_TDLS_PEER_DISCOVERED:
 +              brcmf_dbg(TRACE, "TDLS Peer Discovered\n");
 +              break;
 +      case BRCMF_E_REASON_TDLS_PEER_CONNECTED:
 +              brcmf_dbg(TRACE, "TDLS Peer Connected\n");
 +              brcmf_proto_add_tdls_peer(ifp->drvr, ifp->ifidx, (u8 *)e->addr);
 +              break;
 +      case BRCMF_E_REASON_TDLS_PEER_DISCONNECTED:
 +              brcmf_dbg(TRACE, "TDLS Peer Disconnected\n");
 +              brcmf_proto_delete_peer(ifp->drvr, ifp->ifidx, (u8 *)e->addr);
 +              break;
 +      }
 +
 +      return 0;
 +}
 +
 +static int brcmf_convert_nl80211_tdls_oper(enum nl80211_tdls_operation oper)
 +{
 +      int ret;
 +
 +      switch (oper) {
 +      case NL80211_TDLS_DISCOVERY_REQ:
 +              ret = BRCMF_TDLS_MANUAL_EP_DISCOVERY;
 +              break;
 +      case NL80211_TDLS_SETUP:
 +              ret = BRCMF_TDLS_MANUAL_EP_CREATE;
 +              break;
 +      case NL80211_TDLS_TEARDOWN:
 +              ret = BRCMF_TDLS_MANUAL_EP_DELETE;
 +              break;
 +      default:
 +              brcmf_err("unsupported operation: %d\n", oper);
 +              ret = -EOPNOTSUPP;
 +      }
 +      return ret;
 +}
 +
 +static int brcmf_cfg80211_tdls_oper(struct wiphy *wiphy,
 +                                  struct net_device *ndev, const u8 *peer,
 +                                  enum nl80211_tdls_operation oper)
 +{
 +      struct brcmf_if *ifp;
 +      struct brcmf_tdls_iovar_le info;
 +      int ret = 0;
 +
 +      ret = brcmf_convert_nl80211_tdls_oper(oper);
 +      if (ret < 0)
 +              return ret;
 +
 +      ifp = netdev_priv(ndev);
 +      memset(&info, 0, sizeof(info));
 +      info.mode = (u8)ret;
 +      if (peer)
 +              memcpy(info.ea, peer, ETH_ALEN);
 +
 +      ret = brcmf_fil_iovar_data_set(ifp, "tdls_endpoint",
 +                                     &info, sizeof(info));
 +      if (ret < 0)
 +              brcmf_err("tdls_endpoint iovar failed: ret=%d\n", ret);
 +
 +      return ret;
 +}
 +
 +static struct cfg80211_ops wl_cfg80211_ops = {
 +      .add_virtual_intf = brcmf_cfg80211_add_iface,
 +      .del_virtual_intf = brcmf_cfg80211_del_iface,
 +      .change_virtual_intf = brcmf_cfg80211_change_iface,
 +      .scan = brcmf_cfg80211_scan,
 +      .set_wiphy_params = brcmf_cfg80211_set_wiphy_params,
 +      .join_ibss = brcmf_cfg80211_join_ibss,
 +      .leave_ibss = brcmf_cfg80211_leave_ibss,
 +      .get_station = brcmf_cfg80211_get_station,
 +      .set_tx_power = brcmf_cfg80211_set_tx_power,
 +      .get_tx_power = brcmf_cfg80211_get_tx_power,
 +      .add_key = brcmf_cfg80211_add_key,
 +      .del_key = brcmf_cfg80211_del_key,
 +      .get_key = brcmf_cfg80211_get_key,
 +      .set_default_key = brcmf_cfg80211_config_default_key,
 +      .set_default_mgmt_key = brcmf_cfg80211_config_default_mgmt_key,
 +      .set_power_mgmt = brcmf_cfg80211_set_power_mgmt,
 +      .connect = brcmf_cfg80211_connect,
 +      .disconnect = brcmf_cfg80211_disconnect,
 +      .suspend = brcmf_cfg80211_suspend,
 +      .resume = brcmf_cfg80211_resume,
 +      .set_pmksa = brcmf_cfg80211_set_pmksa,
 +      .del_pmksa = brcmf_cfg80211_del_pmksa,
 +      .flush_pmksa = brcmf_cfg80211_flush_pmksa,
 +      .start_ap = brcmf_cfg80211_start_ap,
 +      .stop_ap = brcmf_cfg80211_stop_ap,
 +      .change_beacon = brcmf_cfg80211_change_beacon,
 +      .del_station = brcmf_cfg80211_del_station,
 +      .sched_scan_start = brcmf_cfg80211_sched_scan_start,
 +      .sched_scan_stop = brcmf_cfg80211_sched_scan_stop,
 +      .mgmt_frame_register = brcmf_cfg80211_mgmt_frame_register,
 +      .mgmt_tx = brcmf_cfg80211_mgmt_tx,
 +      .remain_on_channel = brcmf_p2p_remain_on_channel,
 +      .cancel_remain_on_channel = brcmf_cfg80211_cancel_remain_on_channel,
 +      .start_p2p_device = brcmf_p2p_start_device,
 +      .stop_p2p_device = brcmf_p2p_stop_device,
 +      .crit_proto_start = brcmf_cfg80211_crit_proto_start,
 +      .crit_proto_stop = brcmf_cfg80211_crit_proto_stop,
 +      .tdls_oper = brcmf_cfg80211_tdls_oper,
 +};
 +
 +struct brcmf_cfg80211_vif *brcmf_alloc_vif(struct brcmf_cfg80211_info *cfg,
 +                                         enum nl80211_iftype type,
 +                                         bool pm_block)
 +{
 +      struct brcmf_cfg80211_vif *vif;
 +
 +      brcmf_dbg(TRACE, "allocating virtual interface (size=%zu)\n",
 +                sizeof(*vif));
 +      vif = kzalloc(sizeof(*vif), GFP_KERNEL);
 +      if (!vif)
 +              return ERR_PTR(-ENOMEM);
 +
 +      vif->wdev.wiphy = cfg->wiphy;
 +      vif->wdev.iftype = type;
 +
 +      vif->pm_block = pm_block;
 +      vif->roam_off = -1;
 +
 +      brcmf_init_prof(&vif->profile);
 +
 +      list_add_tail(&vif->list, &cfg->vif_list);
 +      return vif;
 +}
 +
 +void brcmf_free_vif(struct brcmf_cfg80211_vif *vif)
 +{
 +      list_del(&vif->list);
 +      kfree(vif);
 +}
 +
 +void brcmf_cfg80211_free_netdev(struct net_device *ndev)
 +{
 +      struct brcmf_cfg80211_vif *vif;
 +      struct brcmf_if *ifp;
 +
 +      ifp = netdev_priv(ndev);
 +      vif = ifp->vif;
 +
 +      brcmf_free_vif(vif);
 +      free_netdev(ndev);
 +}
 +
 +static bool brcmf_is_linkup(const struct brcmf_event_msg *e)
 +{
 +      u32 event = e->event_code;
 +      u32 status = e->status;
 +
 +      if (event == BRCMF_E_SET_SSID && status == BRCMF_E_STATUS_SUCCESS) {
 +              brcmf_dbg(CONN, "Processing set ssid\n");
 +              return true;
 +      }
 +
 +      return false;
 +}
 +
 +static bool brcmf_is_linkdown(const struct brcmf_event_msg *e)
 +{
 +      u32 event = e->event_code;
 +      u16 flags = e->flags;
 +
 +      if ((event == BRCMF_E_DEAUTH) || (event == BRCMF_E_DEAUTH_IND) ||
 +          (event == BRCMF_E_DISASSOC_IND) ||
 +          ((event == BRCMF_E_LINK) && (!(flags & BRCMF_EVENT_MSG_LINK)))) {
 +              brcmf_dbg(CONN, "Processing link down\n");
 +              return true;
 +      }
 +      return false;
 +}
 +
 +static bool brcmf_is_nonetwork(struct brcmf_cfg80211_info *cfg,
 +                             const struct brcmf_event_msg *e)
 +{
 +      u32 event = e->event_code;
 +      u32 status = e->status;
 +
 +      if (event == BRCMF_E_LINK && status == BRCMF_E_STATUS_NO_NETWORKS) {
 +              brcmf_dbg(CONN, "Processing Link %s & no network found\n",
 +                        e->flags & BRCMF_EVENT_MSG_LINK ? "up" : "down");
 +              return true;
 +      }
 +
 +      if (event == BRCMF_E_SET_SSID && status != BRCMF_E_STATUS_SUCCESS) {
 +              brcmf_dbg(CONN, "Processing connecting & no network found\n");
 +              return true;
 +      }
 +
 +      return false;
 +}
 +
 +static void brcmf_clear_assoc_ies(struct brcmf_cfg80211_info *cfg)
 +{
 +      struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
 +
 +      kfree(conn_info->req_ie);
 +      conn_info->req_ie = NULL;
 +      conn_info->req_ie_len = 0;
 +      kfree(conn_info->resp_ie);
 +      conn_info->resp_ie = NULL;
 +      conn_info->resp_ie_len = 0;
 +}
 +
 +static s32 brcmf_get_assoc_ies(struct brcmf_cfg80211_info *cfg,
 +                             struct brcmf_if *ifp)
 +{
 +      struct brcmf_cfg80211_assoc_ielen_le *assoc_info;
 +      struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
 +      u32 req_len;
 +      u32 resp_len;
 +      s32 err = 0;
 +
 +      brcmf_clear_assoc_ies(cfg);
 +
 +      err = brcmf_fil_iovar_data_get(ifp, "assoc_info",
 +                                     cfg->extra_buf, WL_ASSOC_INFO_MAX);
 +      if (err) {
 +              brcmf_err("could not get assoc info (%d)\n", err);
 +              return err;
 +      }
 +      assoc_info =
 +              (struct brcmf_cfg80211_assoc_ielen_le *)cfg->extra_buf;
 +      req_len = le32_to_cpu(assoc_info->req_len);
 +      resp_len = le32_to_cpu(assoc_info->resp_len);
 +      if (req_len) {
 +              err = brcmf_fil_iovar_data_get(ifp, "assoc_req_ies",
 +                                             cfg->extra_buf,
 +                                             WL_ASSOC_INFO_MAX);
 +              if (err) {
 +                      brcmf_err("could not get assoc req (%d)\n", err);
 +                      return err;
 +              }
 +              conn_info->req_ie_len = req_len;
 +              conn_info->req_ie =
 +                  kmemdup(cfg->extra_buf, conn_info->req_ie_len,
 +                          GFP_KERNEL);
 +      } else {
 +              conn_info->req_ie_len = 0;
 +              conn_info->req_ie = NULL;
 +      }
 +      if (resp_len) {
 +              err = brcmf_fil_iovar_data_get(ifp, "assoc_resp_ies",
 +                                             cfg->extra_buf,
 +                                             WL_ASSOC_INFO_MAX);
 +              if (err) {
 +                      brcmf_err("could not get assoc resp (%d)\n", err);
 +                      return err;
 +              }
 +              conn_info->resp_ie_len = resp_len;
 +              conn_info->resp_ie =
 +                  kmemdup(cfg->extra_buf, conn_info->resp_ie_len,
 +                          GFP_KERNEL);
 +      } else {
 +              conn_info->resp_ie_len = 0;
 +              conn_info->resp_ie = NULL;
 +      }
 +      brcmf_dbg(CONN, "req len (%d) resp len (%d)\n",
 +                conn_info->req_ie_len, conn_info->resp_ie_len);
 +
 +      return err;
 +}
 +
 +static s32
 +brcmf_bss_roaming_done(struct brcmf_cfg80211_info *cfg,
 +                     struct net_device *ndev,
 +                     const struct brcmf_event_msg *e)
 +{
 +      struct brcmf_if *ifp = netdev_priv(ndev);
 +      struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
 +      struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
 +      struct wiphy *wiphy = cfg_to_wiphy(cfg);
 +      struct ieee80211_channel *notify_channel = NULL;
 +      struct ieee80211_supported_band *band;
 +      struct brcmf_bss_info_le *bi;
 +      struct brcmu_chan ch;
 +      u32 freq;
 +      s32 err = 0;
 +      u8 *buf;
 +
 +      brcmf_dbg(TRACE, "Enter\n");
 +
 +      brcmf_get_assoc_ies(cfg, ifp);
 +      memcpy(profile->bssid, e->addr, ETH_ALEN);
 +      brcmf_update_bss_info(cfg, ifp);
 +
 +      buf = kzalloc(WL_BSS_INFO_MAX, GFP_KERNEL);
 +      if (buf == NULL) {
 +              err = -ENOMEM;
 +              goto done;
 +      }
 +
 +      /* data sent to dongle has to be little endian */
 +      *(__le32 *)buf = 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)
 +              goto done;
 +
 +      bi = (struct brcmf_bss_info_le *)(buf + 4);
 +      ch.chspec = le16_to_cpu(bi->chanspec);
 +      cfg->d11inf.decchspec(&ch);
 +
 +      if (ch.band == BRCMU_CHAN_BAND_2G)
 +              band = wiphy->bands[IEEE80211_BAND_2GHZ];
 +      else
 +              band = wiphy->bands[IEEE80211_BAND_5GHZ];
 +
 +      freq = ieee80211_channel_to_frequency(ch.chnum, band->band);
 +      notify_channel = ieee80211_get_channel(wiphy, freq);
 +
 +done:
 +      kfree(buf);
 +      cfg80211_roamed(ndev, notify_channel, (u8 *)profile->bssid,
 +                      conn_info->req_ie, conn_info->req_ie_len,
 +                      conn_info->resp_ie, conn_info->resp_ie_len, GFP_KERNEL);
 +      brcmf_dbg(CONN, "Report roaming result\n");
 +
 +      set_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state);
 +      brcmf_dbg(TRACE, "Exit\n");
 +      return err;
 +}
 +
 +static s32
 +brcmf_bss_connect_done(struct brcmf_cfg80211_info *cfg,
 +                     struct net_device *ndev, const struct brcmf_event_msg *e,
 +                     bool completed)
 +{
 +      struct brcmf_if *ifp = netdev_priv(ndev);
 +      struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
 +      struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
 +
 +      brcmf_dbg(TRACE, "Enter\n");
 +
 +      if (test_and_clear_bit(BRCMF_VIF_STATUS_CONNECTING,
 +                             &ifp->vif->sme_state)) {
 +              if (completed) {
 +                      brcmf_get_assoc_ies(cfg, ifp);
 +                      memcpy(profile->bssid, e->addr, ETH_ALEN);
 +                      brcmf_update_bss_info(cfg, ifp);
 +                      set_bit(BRCMF_VIF_STATUS_CONNECTED,
 +                              &ifp->vif->sme_state);
 +              }
 +              cfg80211_connect_result(ndev,
 +                                      (u8 *)profile->bssid,
 +                                      conn_info->req_ie,
 +                                      conn_info->req_ie_len,
 +                                      conn_info->resp_ie,
 +                                      conn_info->resp_ie_len,
 +                                      completed ? WLAN_STATUS_SUCCESS :
 +                                                  WLAN_STATUS_AUTH_TIMEOUT,
 +                                      GFP_KERNEL);
 +              brcmf_dbg(CONN, "Report connect result - connection %s\n",
 +                        completed ? "succeeded" : "failed");
 +      }
 +      brcmf_dbg(TRACE, "Exit\n");
 +      return 0;
 +}
 +
 +static s32
 +brcmf_notify_connect_status_ap(struct brcmf_cfg80211_info *cfg,
 +                             struct net_device *ndev,
 +                             const struct brcmf_event_msg *e, void *data)
 +{
 +      static int generation;
 +      u32 event = e->event_code;
 +      u32 reason = e->reason;
 +      struct station_info sinfo;
 +
 +      brcmf_dbg(CONN, "event %d, reason %d\n", event, reason);
 +      if (event == BRCMF_E_LINK && reason == BRCMF_E_REASON_LINK_BSSCFG_DIS &&
 +          ndev != cfg_to_ndev(cfg)) {
 +              brcmf_dbg(CONN, "AP mode link down\n");
 +              complete(&cfg->vif_disabled);
 +              return 0;
 +      }
 +
 +      if (((event == BRCMF_E_ASSOC_IND) || (event == BRCMF_E_REASSOC_IND)) &&
 +          (reason == BRCMF_E_STATUS_SUCCESS)) {
 +              memset(&sinfo, 0, sizeof(sinfo));
 +              sinfo.filled = STATION_INFO_ASSOC_REQ_IES;
 +              if (!data) {
 +                      brcmf_err("No IEs present in ASSOC/REASSOC_IND");
 +                      return -EINVAL;
 +              }
 +              sinfo.assoc_req_ies = data;
 +              sinfo.assoc_req_ies_len = e->datalen;
 +              generation++;
 +              sinfo.generation = generation;
 +              cfg80211_new_sta(ndev, e->addr, &sinfo, GFP_KERNEL);
 +      } else if ((event == BRCMF_E_DISASSOC_IND) ||
 +                 (event == BRCMF_E_DEAUTH_IND) ||
 +                 (event == BRCMF_E_DEAUTH)) {
 +              cfg80211_del_sta(ndev, e->addr, GFP_KERNEL);
 +      }
 +      return 0;
 +}
 +
 +static s32
 +brcmf_notify_connect_status(struct brcmf_if *ifp,
 +                          const struct brcmf_event_msg *e, void *data)
 +{
 +      struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
 +      struct net_device *ndev = ifp->ndev;
 +      struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
 +      struct ieee80211_channel *chan;
 +      s32 err = 0;
 +
 +      if ((e->event_code == BRCMF_E_DEAUTH) ||
 +          (e->event_code == BRCMF_E_DEAUTH_IND) ||
 +          (e->event_code == BRCMF_E_DISASSOC_IND) ||
 +          ((e->event_code == BRCMF_E_LINK) && (!e->flags))) {
 +              brcmf_proto_delete_peer(ifp->drvr, ifp->ifidx, (u8 *)e->addr);
 +      }
 +
 +      if (brcmf_is_apmode(ifp->vif)) {
 +              err = brcmf_notify_connect_status_ap(cfg, ndev, e, data);
 +      } else if (brcmf_is_linkup(e)) {
 +              brcmf_dbg(CONN, "Linkup\n");
 +              if (brcmf_is_ibssmode(ifp->vif)) {
 +                      chan = ieee80211_get_channel(cfg->wiphy, cfg->channel);
 +                      memcpy(profile->bssid, e->addr, ETH_ALEN);
 +                      wl_inform_ibss(cfg, ndev, e->addr);
 +                      cfg80211_ibss_joined(ndev, e->addr, chan, GFP_KERNEL);
 +                      clear_bit(BRCMF_VIF_STATUS_CONNECTING,
 +                                &ifp->vif->sme_state);
 +                      set_bit(BRCMF_VIF_STATUS_CONNECTED,
 +                              &ifp->vif->sme_state);
 +              } else
 +                      brcmf_bss_connect_done(cfg, ndev, e, true);
 +      } else if (brcmf_is_linkdown(e)) {
 +              brcmf_dbg(CONN, "Linkdown\n");
 +              if (!brcmf_is_ibssmode(ifp->vif)) {
 +                      brcmf_bss_connect_done(cfg, ndev, e, false);
 +              }
 +              brcmf_link_down(ifp->vif);
 +              brcmf_init_prof(ndev_to_prof(ndev));
 +              if (ndev != cfg_to_ndev(cfg))
 +                      complete(&cfg->vif_disabled);
 +      } else if (brcmf_is_nonetwork(cfg, e)) {
 +              if (brcmf_is_ibssmode(ifp->vif))
 +                      clear_bit(BRCMF_VIF_STATUS_CONNECTING,
 +                                &ifp->vif->sme_state);
 +              else
 +                      brcmf_bss_connect_done(cfg, ndev, e, false);
 +      }
 +
 +      return err;
 +}
 +
 +static s32
 +brcmf_notify_roaming_status(struct brcmf_if *ifp,
 +                          const struct brcmf_event_msg *e, void *data)
 +{
 +      struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
 +      u32 event = e->event_code;
 +      u32 status = e->status;
 +
 +      if (event == BRCMF_E_ROAM && status == BRCMF_E_STATUS_SUCCESS) {
 +              if (test_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state))
 +                      brcmf_bss_roaming_done(cfg, ifp->ndev, e);
 +              else
 +                      brcmf_bss_connect_done(cfg, ifp->ndev, e, true);
 +      }
 +
 +      return 0;
 +}
 +
 +static s32
 +brcmf_notify_mic_status(struct brcmf_if *ifp,
 +                      const struct brcmf_event_msg *e, void *data)
 +{
 +      u16 flags = e->flags;
 +      enum nl80211_key_type key_type;
 +
 +      if (flags & BRCMF_EVENT_MSG_GROUP)
 +              key_type = NL80211_KEYTYPE_GROUP;
 +      else
 +              key_type = NL80211_KEYTYPE_PAIRWISE;
 +
 +      cfg80211_michael_mic_failure(ifp->ndev, (u8 *)&e->addr, key_type, -1,
 +                                   NULL, GFP_KERNEL);
 +
 +      return 0;
 +}
 +
 +static s32 brcmf_notify_vif_event(struct brcmf_if *ifp,
 +                                const struct brcmf_event_msg *e, void *data)
 +{
 +      struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
 +      struct brcmf_if_event *ifevent = (struct brcmf_if_event *)data;
 +      struct brcmf_cfg80211_vif_event *event = &cfg->vif_event;
 +      struct brcmf_cfg80211_vif *vif;
 +
 +      brcmf_dbg(TRACE, "Enter: action %u flags %u ifidx %u bsscfg %u\n",
 +                ifevent->action, ifevent->flags, ifevent->ifidx,
 +                ifevent->bssidx);
 +
 +      mutex_lock(&event->vif_event_lock);
 +      event->action = ifevent->action;
 +      vif = event->vif;
 +
 +      switch (ifevent->action) {
 +      case BRCMF_E_IF_ADD:
 +              /* waiting process may have timed out */
 +              if (!cfg->vif_event.vif) {
 +                      mutex_unlock(&event->vif_event_lock);
 +                      return -EBADF;
 +              }
 +
 +              ifp->vif = vif;
 +              vif->ifp = ifp;
 +              if (ifp->ndev) {
 +                      vif->wdev.netdev = ifp->ndev;
 +                      ifp->ndev->ieee80211_ptr = &vif->wdev;
 +                      SET_NETDEV_DEV(ifp->ndev, wiphy_dev(cfg->wiphy));
 +              }
 +              mutex_unlock(&event->vif_event_lock);
 +              wake_up(&event->vif_wq);
 +              return 0;
 +
 +      case BRCMF_E_IF_DEL:
 +              mutex_unlock(&event->vif_event_lock);
 +              /* event may not be upon user request */
 +              if (brcmf_cfg80211_vif_event_armed(cfg))
 +                      wake_up(&event->vif_wq);
 +              return 0;
 +
 +      case BRCMF_E_IF_CHANGE:
 +              mutex_unlock(&event->vif_event_lock);
 +              wake_up(&event->vif_wq);
 +              return 0;
 +
 +      default:
 +              mutex_unlock(&event->vif_event_lock);
 +              break;
 +      }
 +      return -EINVAL;
 +}
 +
 +static void brcmf_init_conf(struct brcmf_cfg80211_conf *conf)
 +{
 +      conf->frag_threshold = (u32)-1;
 +      conf->rts_threshold = (u32)-1;
 +      conf->retry_short = (u32)-1;
 +      conf->retry_long = (u32)-1;
 +      conf->tx_power = -1;
 +}
 +
 +static void brcmf_register_event_handlers(struct brcmf_cfg80211_info *cfg)
 +{
 +      brcmf_fweh_register(cfg->pub, BRCMF_E_LINK,
 +                          brcmf_notify_connect_status);
 +      brcmf_fweh_register(cfg->pub, BRCMF_E_DEAUTH_IND,
 +                          brcmf_notify_connect_status);
 +      brcmf_fweh_register(cfg->pub, BRCMF_E_DEAUTH,
 +                          brcmf_notify_connect_status);
 +      brcmf_fweh_register(cfg->pub, BRCMF_E_DISASSOC_IND,
 +                          brcmf_notify_connect_status);
 +      brcmf_fweh_register(cfg->pub, BRCMF_E_ASSOC_IND,
 +                          brcmf_notify_connect_status);
 +      brcmf_fweh_register(cfg->pub, BRCMF_E_REASSOC_IND,
 +                          brcmf_notify_connect_status);
 +      brcmf_fweh_register(cfg->pub, BRCMF_E_ROAM,
 +                          brcmf_notify_roaming_status);
 +      brcmf_fweh_register(cfg->pub, BRCMF_E_MIC_ERROR,
 +                          brcmf_notify_mic_status);
 +      brcmf_fweh_register(cfg->pub, BRCMF_E_SET_SSID,
 +                          brcmf_notify_connect_status);
 +      brcmf_fweh_register(cfg->pub, BRCMF_E_PFN_NET_FOUND,
 +                          brcmf_notify_sched_scan_results);
 +      brcmf_fweh_register(cfg->pub, BRCMF_E_IF,
 +                          brcmf_notify_vif_event);
 +      brcmf_fweh_register(cfg->pub, BRCMF_E_P2P_PROBEREQ_MSG,
 +                          brcmf_p2p_notify_rx_mgmt_p2p_probereq);
 +      brcmf_fweh_register(cfg->pub, BRCMF_E_P2P_DISC_LISTEN_COMPLETE,
 +                          brcmf_p2p_notify_listen_complete);
 +      brcmf_fweh_register(cfg->pub, BRCMF_E_ACTION_FRAME_RX,
 +                          brcmf_p2p_notify_action_frame_rx);
 +      brcmf_fweh_register(cfg->pub, BRCMF_E_ACTION_FRAME_COMPLETE,
 +                          brcmf_p2p_notify_action_tx_complete);
 +      brcmf_fweh_register(cfg->pub, BRCMF_E_ACTION_FRAME_OFF_CHAN_COMPLETE,
 +                          brcmf_p2p_notify_action_tx_complete);
 +}
 +
 +static void brcmf_deinit_priv_mem(struct brcmf_cfg80211_info *cfg)
 +{
 +      kfree(cfg->conf);
 +      cfg->conf = NULL;
 +      kfree(cfg->escan_ioctl_buf);
 +      cfg->escan_ioctl_buf = NULL;
 +      kfree(cfg->extra_buf);
 +      cfg->extra_buf = NULL;
 +      kfree(cfg->pmk_list);
 +      cfg->pmk_list = NULL;
 +}
 +
 +static s32 brcmf_init_priv_mem(struct brcmf_cfg80211_info *cfg)
 +{
 +      cfg->conf = kzalloc(sizeof(*cfg->conf), GFP_KERNEL);
 +      if (!cfg->conf)
 +              goto init_priv_mem_out;
 +      cfg->escan_ioctl_buf = kzalloc(BRCMF_DCMD_MEDLEN, GFP_KERNEL);
 +      if (!cfg->escan_ioctl_buf)
 +              goto init_priv_mem_out;
 +      cfg->extra_buf = kzalloc(WL_EXTRA_BUF_MAX, GFP_KERNEL);
 +      if (!cfg->extra_buf)
 +              goto init_priv_mem_out;
 +      cfg->pmk_list = kzalloc(sizeof(*cfg->pmk_list), GFP_KERNEL);
 +      if (!cfg->pmk_list)
 +              goto init_priv_mem_out;
 +
 +      return 0;
 +
 +init_priv_mem_out:
 +      brcmf_deinit_priv_mem(cfg);
 +
 +      return -ENOMEM;
 +}
 +
 +static s32 wl_init_priv(struct brcmf_cfg80211_info *cfg)
 +{
 +      s32 err = 0;
 +
 +      cfg->scan_request = NULL;
 +      cfg->pwr_save = true;
 +      cfg->active_scan = true;        /* we do active scan per default */
 +      cfg->dongle_up = false;         /* dongle is not up yet */
 +      err = brcmf_init_priv_mem(cfg);
 +      if (err)
 +              return err;
 +      brcmf_register_event_handlers(cfg);
 +      mutex_init(&cfg->usr_sync);
 +      brcmf_init_escan(cfg);
 +      brcmf_init_conf(cfg->conf);
 +      init_completion(&cfg->vif_disabled);
 +      return err;
 +}
 +
 +static void wl_deinit_priv(struct brcmf_cfg80211_info *cfg)
 +{
 +      cfg->dongle_up = false; /* dongle down */
 +      brcmf_abort_scanning(cfg);
 +      brcmf_deinit_priv_mem(cfg);
 +}
 +
 +static void init_vif_event(struct brcmf_cfg80211_vif_event *event)
 +{
 +      init_waitqueue_head(&event->vif_wq);
 +      mutex_init(&event->vif_event_lock);
 +}
 +
 +static s32
 +brcmf_dongle_roam(struct brcmf_if *ifp, u32 bcn_timeout)
 +{
 +      s32 err = 0;
 +      __le32 roamtrigger[2];
 +      __le32 roam_delta[2];
 +
 +      /*
 +       * Setup timeout if Beacons are lost and roam is
 +       * off to report link down
 +       */
 +      if (brcmf_roamoff) {
 +              err = brcmf_fil_iovar_int_set(ifp, "bcn_timeout", bcn_timeout);
 +              if (err) {
 +                      brcmf_err("bcn_timeout error (%d)\n", err);
 +                      goto dongle_rom_out;
 +              }
 +      }
 +
 +      /*
 +       * Enable/Disable built-in roaming to allow supplicant
 +       * to take care of roaming
 +       */
 +      brcmf_dbg(INFO, "Internal Roaming = %s\n",
 +                brcmf_roamoff ? "Off" : "On");
 +      err = brcmf_fil_iovar_int_set(ifp, "roam_off", !!(brcmf_roamoff));
 +      if (err) {
 +              brcmf_err("roam_off error (%d)\n", err);
 +              goto dongle_rom_out;
 +      }
 +
 +      roamtrigger[0] = cpu_to_le32(WL_ROAM_TRIGGER_LEVEL);
 +      roamtrigger[1] = cpu_to_le32(BRCM_BAND_ALL);
 +      err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_ROAM_TRIGGER,
 +                                   (void *)roamtrigger, sizeof(roamtrigger));
 +      if (err) {
 +              brcmf_err("WLC_SET_ROAM_TRIGGER error (%d)\n", err);
 +              goto dongle_rom_out;
 +      }
 +
 +      roam_delta[0] = cpu_to_le32(WL_ROAM_DELTA);
 +      roam_delta[1] = cpu_to_le32(BRCM_BAND_ALL);
 +      err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_ROAM_DELTA,
 +                                   (void *)roam_delta, sizeof(roam_delta));
 +      if (err) {
 +              brcmf_err("WLC_SET_ROAM_DELTA error (%d)\n", err);
 +              goto dongle_rom_out;
 +      }
 +
 +dongle_rom_out:
 +      return err;
 +}
 +
 +static s32
 +brcmf_dongle_scantime(struct brcmf_if *ifp, s32 scan_assoc_time,
 +                    s32 scan_unassoc_time, s32 scan_passive_time)
 +{
 +      s32 err = 0;
 +
 +      err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_CHANNEL_TIME,
 +                                  scan_assoc_time);
 +      if (err) {
 +              if (err == -EOPNOTSUPP)
 +                      brcmf_dbg(INFO, "Scan assoc time is not supported\n");
 +              else
 +                      brcmf_err("Scan assoc time error (%d)\n", err);
 +              goto dongle_scantime_out;
 +      }
 +      err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_UNASSOC_TIME,
 +                                  scan_unassoc_time);
 +      if (err) {
 +              if (err == -EOPNOTSUPP)
 +                      brcmf_dbg(INFO, "Scan unassoc time is not supported\n");
 +              else
 +                      brcmf_err("Scan unassoc time error (%d)\n", err);
 +              goto dongle_scantime_out;
 +      }
 +
 +      err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_PASSIVE_TIME,
 +                                  scan_passive_time);
 +      if (err) {
 +              if (err == -EOPNOTSUPP)
 +                      brcmf_dbg(INFO, "Scan passive time is not supported\n");
 +              else
 +                      brcmf_err("Scan passive time error (%d)\n", err);
 +              goto dongle_scantime_out;
 +      }
 +
 +dongle_scantime_out:
 +      return err;
 +}
 +
 +/* Filter the list of channels received from firmware counting only
 + * the 20MHz channels. The wiphy band data only needs those which get
 + * flagged to indicate if they can take part in higher bandwidth.
 + */
 +static void brcmf_count_20mhz_channels(struct brcmf_cfg80211_info *cfg,
 +                                     struct brcmf_chanspec_list *chlist,
 +                                     u32 chcnt[])
 +{
 +      u32 total = le32_to_cpu(chlist->count);
 +      struct brcmu_chan ch;
 +      int i;
 +
 +      for (i = 0; i < total; i++) {
 +              ch.chspec = (u16)le32_to_cpu(chlist->element[i]);
 +              cfg->d11inf.decchspec(&ch);
 +
 +              /* Firmware gives a ordered list. We skip non-20MHz
 +               * channels is 2G. For 5G we can abort upon reaching
 +               * a non-20MHz channel in the list.
 +               */
 +              if (ch.bw != BRCMU_CHAN_BW_20) {
 +                      if (ch.band == BRCMU_CHAN_BAND_5G)
 +                              break;
 +                      else
 +                              continue;
 +              }
 +
 +              if (ch.band == BRCMU_CHAN_BAND_2G)
 +                      chcnt[0] += 1;
 +              else if (ch.band == BRCMU_CHAN_BAND_5G)
 +                      chcnt[1] += 1;
 +      }
 +}
 +
 +static void brcmf_update_bw40_channel_flag(struct ieee80211_channel *channel,
 +                                         struct brcmu_chan *ch)
 +{
 +      u32 ht40_flag;
 +
 +      ht40_flag = channel->flags & IEEE80211_CHAN_NO_HT40;
 +      if (ch->sb == BRCMU_CHAN_SB_U) {
 +              if (ht40_flag == IEEE80211_CHAN_NO_HT40)
 +                      channel->flags &= ~IEEE80211_CHAN_NO_HT40;
 +              channel->flags |= IEEE80211_CHAN_NO_HT40PLUS;
 +      } else {
 +              /* It should be one of
 +               * IEEE80211_CHAN_NO_HT40 or
 +               * IEEE80211_CHAN_NO_HT40PLUS
 +               */
 +              channel->flags &= ~IEEE80211_CHAN_NO_HT40;
 +              if (ht40_flag == IEEE80211_CHAN_NO_HT40)
 +                      channel->flags |= IEEE80211_CHAN_NO_HT40MINUS;
 +      }
 +}
 +
 +static int brcmf_construct_chaninfo(struct brcmf_cfg80211_info *cfg,
 +                                  u32 bw_cap[])
 +{
 +      struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
 +      struct ieee80211_supported_band *band;
 +      struct ieee80211_channel *channel;
 +      struct wiphy *wiphy;
 +      struct brcmf_chanspec_list *list;
 +      struct brcmu_chan ch;
 +      int err;
 +      u8 *pbuf;
 +      u32 i, j;
 +      u32 total;
 +      u32 chaninfo;
 +      u32 chcnt[2] = { 0, 0 };
 +      u32 index;
 +
 +      pbuf = kzalloc(BRCMF_DCMD_MEDLEN, GFP_KERNEL);
 +
 +      if (pbuf == NULL)
 +              return -ENOMEM;
 +
 +      list = (struct brcmf_chanspec_list *)pbuf;
 +
 +      err = brcmf_fil_iovar_data_get(ifp, "chanspecs", pbuf,
 +                                     BRCMF_DCMD_MEDLEN);
 +      if (err) {
 +              brcmf_err("get chanspecs error (%d)\n", err);
 +              goto fail_pbuf;
 +      }
 +
 +      brcmf_count_20mhz_channels(cfg, list, chcnt);
 +      wiphy = cfg_to_wiphy(cfg);
 +      if (chcnt[0]) {
 +              band = kmemdup(&__wl_band_2ghz, sizeof(__wl_band_2ghz),
 +                             GFP_KERNEL);
 +              if (band == NULL) {
 +                      err = -ENOMEM;
 +                      goto fail_pbuf;
 +              }
 +              band->channels = kcalloc(chcnt[0], sizeof(*channel),
 +                                       GFP_KERNEL);
 +              if (band->channels == NULL) {
 +                      kfree(band);
 +                      err = -ENOMEM;
 +                      goto fail_pbuf;
 +              }
 +              band->n_channels = 0;
 +              wiphy->bands[IEEE80211_BAND_2GHZ] = band;
 +      }
 +      if (chcnt[1]) {
 +              band = kmemdup(&__wl_band_5ghz_a, sizeof(__wl_band_5ghz_a),
 +                             GFP_KERNEL);
 +              if (band == NULL) {
 +                      err = -ENOMEM;
 +                      goto fail_band2g;
 +              }
 +              band->channels = kcalloc(chcnt[1], sizeof(*channel),
 +                                       GFP_KERNEL);
 +              if (band->channels == NULL) {
 +                      kfree(band);
 +                      err = -ENOMEM;
 +                      goto fail_band2g;
 +              }
 +              band->n_channels = 0;
 +              wiphy->bands[IEEE80211_BAND_5GHZ] = band;
 +      }
 +
 +      total = le32_to_cpu(list->count);
 +      for (i = 0; i < total; i++) {
 +              ch.chspec = (u16)le32_to_cpu(list->element[i]);
 +              cfg->d11inf.decchspec(&ch);
 +
 +              if (ch.band == BRCMU_CHAN_BAND_2G) {
 +                      band = wiphy->bands[IEEE80211_BAND_2GHZ];
 +              } else if (ch.band == BRCMU_CHAN_BAND_5G) {
 +                      band = wiphy->bands[IEEE80211_BAND_5GHZ];
 +              } else {
 +                      brcmf_err("Invalid channel Spec. 0x%x.\n", ch.chspec);
 +                      continue;
 +              }
 +              if (!(bw_cap[band->band] & WLC_BW_40MHZ_BIT) &&
 +                  ch.bw == BRCMU_CHAN_BW_40)
 +                      continue;
 +              if (!(bw_cap[band->band] & WLC_BW_80MHZ_BIT) &&
 +                  ch.bw == BRCMU_CHAN_BW_80)
 +                      continue;
 +
 +              channel = band->channels;
 +              index = band->n_channels;
 +              for (j = 0; j < band->n_channels; j++) {
 +                      if (channel[j].hw_value == ch.chnum) {
 +                              index = j;
 +                              break;
 +                      }
 +              }
 +              channel[index].center_freq =
 +                      ieee80211_channel_to_frequency(ch.chnum, band->band);
 +              channel[index].hw_value = ch.chnum;
 +
 +              /* assuming the chanspecs order is HT20,
 +               * HT40 upper, HT40 lower, and VHT80.
 +               */
 +              if (ch.bw == BRCMU_CHAN_BW_80) {
 +                      channel[index].flags &= ~IEEE80211_CHAN_NO_80MHZ;
 +              } else if (ch.bw == BRCMU_CHAN_BW_40) {
 +                      brcmf_update_bw40_channel_flag(&channel[index], &ch);
 +              } else {
 +                      /* disable other bandwidths for now as mentioned
 +                       * order assure they are enabled for subsequent
 +                       * chanspecs.
 +                       */
 +                      channel[index].flags = IEEE80211_CHAN_NO_HT40 |
 +                                             IEEE80211_CHAN_NO_80MHZ;
 +                      ch.bw = BRCMU_CHAN_BW_20;
 +                      cfg->d11inf.encchspec(&ch);
 +                      chaninfo = ch.chspec;
 +                      err = brcmf_fil_bsscfg_int_get(ifp, "per_chan_info",
 +                                                     &chaninfo);
 +                      if (!err) {
 +                              if (chaninfo & WL_CHAN_RADAR)
 +                                      channel[index].flags |=
 +                                              (IEEE80211_CHAN_RADAR |
 +                                               IEEE80211_CHAN_NO_IR);
 +                              if (chaninfo & WL_CHAN_PASSIVE)
 +                                      channel[index].flags |=
 +                                              IEEE80211_CHAN_NO_IR;
 +                      }
 +              }
 +              if (index == band->n_channels)
 +                      band->n_channels++;
 +      }
 +      kfree(pbuf);
 +      return 0;
 +
 +fail_band2g:
 +      kfree(wiphy->bands[IEEE80211_BAND_2GHZ]->channels);
 +      kfree(wiphy->bands[IEEE80211_BAND_2GHZ]);
 +      wiphy->bands[IEEE80211_BAND_2GHZ] = NULL;
 +fail_pbuf:
 +      kfree(pbuf);
 +      return err;
 +}
 +
 +static int brcmf_enable_bw40_2g(struct brcmf_cfg80211_info *cfg)
 +{
 +      struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
 +      struct ieee80211_supported_band *band;
 +      struct brcmf_fil_bwcap_le band_bwcap;
 +      struct brcmf_chanspec_list *list;
 +      u8 *pbuf;
 +      u32 val;
 +      int err;
 +      struct brcmu_chan ch;
 +      u32 num_chan;
 +      int i, j;
 +
 +      /* verify support for bw_cap command */
 +      val = WLC_BAND_5G;
 +      err = brcmf_fil_iovar_int_get(ifp, "bw_cap", &val);
 +
 +      if (!err) {
 +              /* only set 2G bandwidth using bw_cap command */
 +              band_bwcap.band = cpu_to_le32(WLC_BAND_2G);
 +              band_bwcap.bw_cap = cpu_to_le32(WLC_BW_CAP_40MHZ);
 +              err = brcmf_fil_iovar_data_set(ifp, "bw_cap", &band_bwcap,
 +                                             sizeof(band_bwcap));
 +      } else {
 +              brcmf_dbg(INFO, "fallback to mimo_bw_cap\n");
 +              val = WLC_N_BW_40ALL;
 +              err = brcmf_fil_iovar_int_set(ifp, "mimo_bw_cap", val);
 +      }
 +
 +      if (!err) {
 +              /* update channel info in 2G band */
 +              pbuf = kzalloc(BRCMF_DCMD_MEDLEN, GFP_KERNEL);
 +
 +              if (pbuf == NULL)
 +                      return -ENOMEM;
 +
 +              ch.band = BRCMU_CHAN_BAND_2G;
 +              ch.bw = BRCMU_CHAN_BW_40;
 +              ch.sb = BRCMU_CHAN_SB_NONE;
 +              ch.chnum = 0;
 +              cfg->d11inf.encchspec(&ch);
 +
 +              /* pass encoded chanspec in query */
 +              *(__le16 *)pbuf = cpu_to_le16(ch.chspec);
 +
 +              err = brcmf_fil_iovar_data_get(ifp, "chanspecs", pbuf,
 +                                             BRCMF_DCMD_MEDLEN);
 +              if (err) {
 +                      brcmf_err("get chanspecs error (%d)\n", err);
 +                      kfree(pbuf);
 +                      return err;
 +              }
 +
 +              band = cfg_to_wiphy(cfg)->bands[IEEE80211_BAND_2GHZ];
 +              list = (struct brcmf_chanspec_list *)pbuf;
 +              num_chan = le32_to_cpu(list->count);
 +              for (i = 0; i < num_chan; i++) {
 +                      ch.chspec = (u16)le32_to_cpu(list->element[i]);
 +                      cfg->d11inf.decchspec(&ch);
 +                      if (WARN_ON(ch.band != BRCMU_CHAN_BAND_2G))
 +                              continue;
 +                      if (WARN_ON(ch.bw != BRCMU_CHAN_BW_40))
 +                              continue;
 +                      for (j = 0; j < band->n_channels; j++) {
 +                              if (band->channels[j].hw_value == ch.chnum)
 +                                      break;
 +                      }
 +                      if (WARN_ON(j == band->n_channels))
 +                              continue;
 +
 +                      brcmf_update_bw40_channel_flag(&band->channels[j], &ch);
 +              }
 +              kfree(pbuf);
 +      }
 +      return err;
 +}
 +
 +static void brcmf_get_bwcap(struct brcmf_if *ifp, u32 bw_cap[])
 +{
 +      u32 band, mimo_bwcap;
 +      int err;
 +
 +      band = WLC_BAND_2G;
 +      err = brcmf_fil_iovar_int_get(ifp, "bw_cap", &band);
 +      if (!err) {
 +              bw_cap[IEEE80211_BAND_2GHZ] = band;
 +              band = WLC_BAND_5G;
 +              err = brcmf_fil_iovar_int_get(ifp, "bw_cap", &band);
 +              if (!err) {
 +                      bw_cap[IEEE80211_BAND_5GHZ] = band;
 +                      return;
 +              }
 +              WARN_ON(1);
 +              return;
 +      }
 +      brcmf_dbg(INFO, "fallback to mimo_bw_cap info\n");
 +      mimo_bwcap = 0;
 +      err = brcmf_fil_iovar_int_get(ifp, "mimo_bw_cap", &mimo_bwcap);
 +      if (err)
 +              /* assume 20MHz if firmware does not give a clue */
 +              mimo_bwcap = WLC_N_BW_20ALL;
 +
 +      switch (mimo_bwcap) {
 +      case WLC_N_BW_40ALL:
 +              bw_cap[IEEE80211_BAND_2GHZ] |= WLC_BW_40MHZ_BIT;
 +              /* fall-thru */
 +      case WLC_N_BW_20IN2G_40IN5G:
 +              bw_cap[IEEE80211_BAND_5GHZ] |= WLC_BW_40MHZ_BIT;
 +              /* fall-thru */
 +      case WLC_N_BW_20ALL:
 +              bw_cap[IEEE80211_BAND_2GHZ] |= WLC_BW_20MHZ_BIT;
 +              bw_cap[IEEE80211_BAND_5GHZ] |= WLC_BW_20MHZ_BIT;
 +              break;
 +      default:
 +              brcmf_err("invalid mimo_bw_cap value\n");
 +      }
 +}
 +
 +static void brcmf_update_ht_cap(struct ieee80211_supported_band *band,
 +                              u32 bw_cap[2], u32 nchain)
 +{
 +      band->ht_cap.ht_supported = true;
 +      if (bw_cap[band->band] & WLC_BW_40MHZ_BIT) {
 +              band->ht_cap.cap |= IEEE80211_HT_CAP_SGI_40;
 +              band->ht_cap.cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40;
 +      }
 +      band->ht_cap.cap |= IEEE80211_HT_CAP_SGI_20;
 +      band->ht_cap.cap |= IEEE80211_HT_CAP_DSSSCCK40;
 +      band->ht_cap.ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K;
 +      band->ht_cap.ampdu_density = IEEE80211_HT_MPDU_DENSITY_16;
 +      memset(band->ht_cap.mcs.rx_mask, 0xff, nchain);
 +      band->ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED;
 +}
 +
 +static __le16 brcmf_get_mcs_map(u32 nchain, enum ieee80211_vht_mcs_support supp)
 +{
 +      u16 mcs_map;
 +      int i;
 +
 +      for (i = 0, mcs_map = 0xFFFF; i < nchain; i++)
 +              mcs_map = (mcs_map << 2) | supp;
 +
 +      return cpu_to_le16(mcs_map);
 +}
 +
 +static void brcmf_update_vht_cap(struct ieee80211_supported_band *band,
 +                               u32 bw_cap[2], u32 nchain)
 +{
 +      __le16 mcs_map;
 +
 +      /* not allowed in 2.4G band */
 +      if (band->band == IEEE80211_BAND_2GHZ)
 +              return;
 +
 +      band->vht_cap.vht_supported = true;
 +      /* 80MHz is mandatory */
 +      band->vht_cap.cap |= IEEE80211_VHT_CAP_SHORT_GI_80;
 +      if (bw_cap[band->band] & WLC_BW_160MHZ_BIT) {
 +              band->vht_cap.cap |= IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ;
 +              band->vht_cap.cap |= IEEE80211_VHT_CAP_SHORT_GI_160;
 +      }
 +      /* all support 256-QAM */
 +      mcs_map = brcmf_get_mcs_map(nchain, IEEE80211_VHT_MCS_SUPPORT_0_9);
 +      band->vht_cap.vht_mcs.rx_mcs_map = mcs_map;
 +      band->vht_cap.vht_mcs.tx_mcs_map = mcs_map;
 +}
 +
 +static int brcmf_setup_wiphybands(struct wiphy *wiphy)
 +{
 +      struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy);
 +      struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
 +      u32 nmode = 0;
 +      u32 vhtmode = 0;
 +      u32 bw_cap[2] = { WLC_BW_20MHZ_BIT, WLC_BW_20MHZ_BIT };
 +      u32 rxchain;
 +      u32 nchain;
 +      int err;
 +      s32 i;
 +      struct ieee80211_supported_band *band;
 +
 +      (void)brcmf_fil_iovar_int_get(ifp, "vhtmode", &vhtmode);
 +      err = brcmf_fil_iovar_int_get(ifp, "nmode", &nmode);
 +      if (err) {
 +              brcmf_err("nmode error (%d)\n", err);
 +      } else {
 +              brcmf_get_bwcap(ifp, bw_cap);
 +      }
 +      brcmf_dbg(INFO, "nmode=%d, vhtmode=%d, bw_cap=(%d, %d)\n",
 +                nmode, vhtmode, bw_cap[IEEE80211_BAND_2GHZ],
 +                bw_cap[IEEE80211_BAND_5GHZ]);
 +
 +      err = brcmf_fil_iovar_int_get(ifp, "rxchain", &rxchain);
 +      if (err) {
 +              brcmf_err("rxchain error (%d)\n", err);
 +              nchain = 1;
 +      } else {
 +              for (nchain = 0; rxchain; nchain++)
 +                      rxchain = rxchain & (rxchain - 1);
 +      }
 +      brcmf_dbg(INFO, "nchain=%d\n", nchain);
 +
 +      err = brcmf_construct_chaninfo(cfg, bw_cap);
 +      if (err) {
 +              brcmf_err("brcmf_construct_chaninfo failed (%d)\n", err);
 +              return err;
 +      }
 +
 +      wiphy = cfg_to_wiphy(cfg);
 +      for (i = 0; i < ARRAY_SIZE(wiphy->bands); i++) {
 +              band = wiphy->bands[i];
 +              if (band == NULL)
 +                      continue;
 +
 +              if (nmode)
 +                      brcmf_update_ht_cap(band, bw_cap, nchain);
 +              if (vhtmode)
 +                      brcmf_update_vht_cap(band, bw_cap, nchain);
 +      }
 +
 +      return 0;
 +}
 +
 +static const struct ieee80211_iface_limit brcmf_iface_limits[] = {
 +      {
 +              .max = 2,
 +              .types = BIT(NL80211_IFTYPE_STATION) |
 +                       BIT(NL80211_IFTYPE_ADHOC) |
 +                       BIT(NL80211_IFTYPE_AP)
 +      },
 +      {
 +              .max = 1,
 +              .types = BIT(NL80211_IFTYPE_P2P_CLIENT) |
 +                       BIT(NL80211_IFTYPE_P2P_GO)
 +      },
 +      {
 +              .max = 1,
 +              .types = BIT(NL80211_IFTYPE_P2P_DEVICE)
 +      }
 +};
 +static struct ieee80211_iface_combination brcmf_iface_combos[] = {
 +      {
 +               .max_interfaces = BRCMF_IFACE_MAX_CNT,
 +               .num_different_channels = 1,
 +               .n_limits = ARRAY_SIZE(brcmf_iface_limits),
 +               .limits = brcmf_iface_limits
 +      }
 +};
 +
 +static const struct ieee80211_txrx_stypes
 +brcmf_txrx_stypes[NUM_NL80211_IFTYPES] = {
 +      [NL80211_IFTYPE_STATION] = {
 +              .tx = 0xffff,
 +              .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
 +                    BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
 +      },
 +      [NL80211_IFTYPE_P2P_CLIENT] = {
 +              .tx = 0xffff,
 +              .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
 +                    BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
 +      },
 +      [NL80211_IFTYPE_P2P_GO] = {
 +              .tx = 0xffff,
 +              .rx = BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) |
 +                    BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) |
 +                    BIT(IEEE80211_STYPE_PROBE_REQ >> 4) |
 +                    BIT(IEEE80211_STYPE_DISASSOC >> 4) |
 +                    BIT(IEEE80211_STYPE_AUTH >> 4) |
 +                    BIT(IEEE80211_STYPE_DEAUTH >> 4) |
 +                    BIT(IEEE80211_STYPE_ACTION >> 4)
 +      },
 +      [NL80211_IFTYPE_P2P_DEVICE] = {
 +              .tx = 0xffff,
 +              .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
 +                    BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
 +      }
 +};
 +
 +static void brcmf_wiphy_pno_params(struct wiphy *wiphy)
 +{
 +      /* scheduled scan settings */
 +      wiphy->max_sched_scan_ssids = BRCMF_PNO_MAX_PFN_COUNT;
 +      wiphy->max_match_sets = BRCMF_PNO_MAX_PFN_COUNT;
 +      wiphy->max_sched_scan_ie_len = BRCMF_SCAN_IE_LEN_MAX;
 +      wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN;
 +}
 +
 +#ifdef CONFIG_PM
 +static const struct wiphy_wowlan_support brcmf_wowlan_support = {
 +      .flags = WIPHY_WOWLAN_MAGIC_PKT | WIPHY_WOWLAN_DISCONNECT,
 +      .n_patterns = BRCMF_WOWL_MAXPATTERNS,
 +      .pattern_max_len = BRCMF_WOWL_MAXPATTERNSIZE,
 +      .pattern_min_len = 1,
 +      .max_pkt_offset = 1500,
 +};
 +#endif
 +
 +static void brcmf_wiphy_wowl_params(struct wiphy *wiphy)
 +{
 +#ifdef CONFIG_PM
 +      /* wowl settings */
 +      wiphy->wowlan = &brcmf_wowlan_support;
 +#endif
 +}
 +
 +static int brcmf_setup_wiphy(struct wiphy *wiphy, struct brcmf_if *ifp)
 +{
 +      struct ieee80211_iface_combination ifc_combo;
 +      wiphy->max_scan_ssids = WL_NUM_SCAN_MAX;
 +      wiphy->max_scan_ie_len = BRCMF_SCAN_IE_LEN_MAX;
 +      wiphy->max_num_pmkids = WL_NUM_PMKIDS_MAX;
 +      wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
 +                               BIT(NL80211_IFTYPE_ADHOC) |
 +                               BIT(NL80211_IFTYPE_AP) |
 +                               BIT(NL80211_IFTYPE_P2P_CLIENT) |
 +                               BIT(NL80211_IFTYPE_P2P_GO) |
 +                               BIT(NL80211_IFTYPE_P2P_DEVICE);
 +      /* need VSDB firmware feature for concurrent channels */
 +      ifc_combo = brcmf_iface_combos[0];
 +      if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MCHAN))
 +              ifc_combo.num_different_channels = 2;
 +      wiphy->iface_combinations = kmemdup(&ifc_combo,
 +                                          sizeof(ifc_combo),
 +                                          GFP_KERNEL);
 +      wiphy->n_iface_combinations = ARRAY_SIZE(brcmf_iface_combos);
 +      wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
 +      wiphy->cipher_suites = __wl_cipher_suites;
 +      wiphy->n_cipher_suites = ARRAY_SIZE(__wl_cipher_suites);
 +      wiphy->flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT |
 +                      WIPHY_FLAG_OFFCHAN_TX |
 +                      WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL |
 +                      WIPHY_FLAG_SUPPORTS_TDLS;
 +      if (!brcmf_roamoff)
 +              wiphy->flags |= WIPHY_FLAG_SUPPORTS_FW_ROAM;
 +      wiphy->mgmt_stypes = brcmf_txrx_stypes;
 +      wiphy->max_remain_on_channel_duration = 5000;
 +      brcmf_wiphy_pno_params(wiphy);
 +
 +      /* vendor commands/events support */
 +      wiphy->vendor_commands = brcmf_vendor_cmds;
 +      wiphy->n_vendor_commands = BRCMF_VNDR_CMDS_LAST - 1;
 +
 +      if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL))
 +              brcmf_wiphy_wowl_params(wiphy);
 +
 +      return brcmf_setup_wiphybands(wiphy);
 +}
 +
 +static s32 brcmf_config_dongle(struct brcmf_cfg80211_info *cfg)
 +{
 +      struct net_device *ndev;
 +      struct wireless_dev *wdev;
 +      struct brcmf_if *ifp;
 +      s32 power_mode;
 +      s32 err = 0;
 +
 +      if (cfg->dongle_up)
 +              return err;
 +
 +      ndev = cfg_to_ndev(cfg);
 +      wdev = ndev->ieee80211_ptr;
 +      ifp = netdev_priv(ndev);
 +
 +      /* make sure RF is ready for work */
 +      brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 0);
 +
 +      brcmf_dongle_scantime(ifp, WL_SCAN_CHANNEL_TIME,
 +                            WL_SCAN_UNASSOC_TIME, WL_SCAN_PASSIVE_TIME);
 +
 +      power_mode = cfg->pwr_save ? PM_FAST : PM_OFF;
 +      err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, power_mode);
 +      if (err)
 +              goto default_conf_out;
 +      brcmf_dbg(INFO, "power save set to %s\n",
 +                (power_mode ? "enabled" : "disabled"));
 +
 +      err = brcmf_dongle_roam(ifp, WL_BEACON_TIMEOUT);
 +      if (err)
 +              goto default_conf_out;
 +      err = brcmf_cfg80211_change_iface(wdev->wiphy, ndev, wdev->iftype,
 +                                        NULL, NULL);
 +      if (err)
 +              goto default_conf_out;
 +
 +      brcmf_configure_arp_offload(ifp, true);
 +
 +      cfg->dongle_up = true;
 +default_conf_out:
 +
 +      return err;
 +
 +}
 +
 +static s32 __brcmf_cfg80211_up(struct brcmf_if *ifp)
 +{
 +      set_bit(BRCMF_VIF_STATUS_READY, &ifp->vif->sme_state);
 +
 +      return brcmf_config_dongle(ifp->drvr->config);
 +}
 +
 +static s32 __brcmf_cfg80211_down(struct brcmf_if *ifp)
 +{
 +      struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
 +
 +      /*
 +       * While going down, if associated with AP disassociate
 +       * from AP to save power
 +       */
 +      if (check_vif_up(ifp->vif)) {
 +              brcmf_link_down(ifp->vif);
 +
 +              /* Make sure WPA_Supplicant receives all the event
 +                 generated due to DISASSOC call to the fw to keep
 +                 the state fw and WPA_Supplicant state consistent
 +               */
 +              brcmf_delay(500);
 +      }
 +
 +      brcmf_abort_scanning(cfg);
 +      clear_bit(BRCMF_VIF_STATUS_READY, &ifp->vif->sme_state);
 +
 +      return 0;
 +}
 +
 +s32 brcmf_cfg80211_up(struct net_device *ndev)
 +{
 +      struct brcmf_if *ifp = netdev_priv(ndev);
 +      struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
 +      s32 err = 0;
 +
 +      mutex_lock(&cfg->usr_sync);
 +      err = __brcmf_cfg80211_up(ifp);
 +      mutex_unlock(&cfg->usr_sync);
 +
 +      return err;
 +}
 +
 +s32 brcmf_cfg80211_down(struct net_device *ndev)
 +{
 +      struct brcmf_if *ifp = netdev_priv(ndev);
 +      struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
 +      s32 err = 0;
 +
 +      mutex_lock(&cfg->usr_sync);
 +      err = __brcmf_cfg80211_down(ifp);
 +      mutex_unlock(&cfg->usr_sync);
 +
 +      return err;
 +}
 +
 +enum nl80211_iftype brcmf_cfg80211_get_iftype(struct brcmf_if *ifp)
 +{
 +      struct wireless_dev *wdev = &ifp->vif->wdev;
 +
 +      return wdev->iftype;
 +}
 +
 +bool brcmf_get_vif_state_any(struct brcmf_cfg80211_info *cfg,
 +                           unsigned long state)
 +{
 +      struct brcmf_cfg80211_vif *vif;
 +
 +      list_for_each_entry(vif, &cfg->vif_list, list) {
 +              if (test_bit(state, &vif->sme_state))
 +                      return true;
 +      }
 +      return false;
 +}
 +
 +static inline bool vif_event_equals(struct brcmf_cfg80211_vif_event *event,
 +                                  u8 action)
 +{
 +      u8 evt_action;
 +
 +      mutex_lock(&event->vif_event_lock);
 +      evt_action = event->action;
 +      mutex_unlock(&event->vif_event_lock);
 +      return evt_action == action;
 +}
 +
 +void brcmf_cfg80211_arm_vif_event(struct brcmf_cfg80211_info *cfg,
 +                                struct brcmf_cfg80211_vif *vif)
 +{
 +      struct brcmf_cfg80211_vif_event *event = &cfg->vif_event;
 +
 +      mutex_lock(&event->vif_event_lock);
 +      event->vif = vif;
 +      event->action = 0;
 +      mutex_unlock(&event->vif_event_lock);
 +}
 +
 +bool brcmf_cfg80211_vif_event_armed(struct brcmf_cfg80211_info *cfg)
 +{
 +      struct brcmf_cfg80211_vif_event *event = &cfg->vif_event;
 +      bool armed;
 +
 +      mutex_lock(&event->vif_event_lock);
 +      armed = event->vif != NULL;
 +      mutex_unlock(&event->vif_event_lock);
 +
 +      return armed;
 +}
 +int brcmf_cfg80211_wait_vif_event_timeout(struct brcmf_cfg80211_info *cfg,
 +                                        u8 action, ulong timeout)
 +{
 +      struct brcmf_cfg80211_vif_event *event = &cfg->vif_event;
 +
 +      return wait_event_timeout(event->vif_wq,
 +                                vif_event_equals(event, action), timeout);
 +}
 +
 +static void brcmf_free_wiphy(struct wiphy *wiphy)
 +{
 +      kfree(wiphy->iface_combinations);
 +      if (wiphy->bands[IEEE80211_BAND_2GHZ]) {
 +              kfree(wiphy->bands[IEEE80211_BAND_2GHZ]->channels);
 +              kfree(wiphy->bands[IEEE80211_BAND_2GHZ]);
 +      }
 +      if (wiphy->bands[IEEE80211_BAND_5GHZ]) {
 +              kfree(wiphy->bands[IEEE80211_BAND_5GHZ]->channels);
 +              kfree(wiphy->bands[IEEE80211_BAND_5GHZ]);
 +      }
 +      wiphy_free(wiphy);
 +}
 +
 +struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr,
 +                                                struct device *busdev)
 +{
 +      struct net_device *ndev = drvr->iflist[0]->ndev;
 +      struct brcmf_cfg80211_info *cfg;
 +      struct wiphy *wiphy;
 +      struct brcmf_cfg80211_vif *vif;
 +      struct brcmf_if *ifp;
 +      s32 err = 0;
 +      s32 io_type;
 +      u16 *cap = NULL;
 +
 +      if (!ndev) {
 +              brcmf_err("ndev is invalid\n");
 +              return NULL;
 +      }
 +
 +      ifp = netdev_priv(ndev);
 +      wiphy = wiphy_new(&wl_cfg80211_ops, sizeof(struct brcmf_cfg80211_info));
 +      if (!wiphy) {
 +              brcmf_err("Could not allocate wiphy device\n");
 +              return NULL;
 +      }
 +      set_wiphy_dev(wiphy, busdev);
 +
 +      cfg = wiphy_priv(wiphy);
 +      cfg->wiphy = wiphy;
 +      cfg->pub = drvr;
 +      init_vif_event(&cfg->vif_event);
 +      INIT_LIST_HEAD(&cfg->vif_list);
 +
 +      vif = brcmf_alloc_vif(cfg, NL80211_IFTYPE_STATION, false);
 +      if (IS_ERR(vif))
 +              goto wiphy_out;
 +
 +      vif->ifp = ifp;
 +      vif->wdev.netdev = ndev;
 +      ndev->ieee80211_ptr = &vif->wdev;
 +      SET_NETDEV_DEV(ndev, wiphy_dev(cfg->wiphy));
 +
 +      err = wl_init_priv(cfg);
 +      if (err) {
 +              brcmf_err("Failed to init iwm_priv (%d)\n", err);
 +              brcmf_free_vif(vif);
 +              goto wiphy_out;
 +      }
 +      ifp->vif = vif;
 +
 +      /* determine d11 io type before wiphy setup */
 +      err = brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_VERSION, &io_type);
 +      if (err) {
 +              brcmf_err("Failed to get D11 version (%d)\n", err);
 +              goto priv_out;
 +      }
 +      cfg->d11inf.io_type = (u8)io_type;
 +      brcmu_d11_attach(&cfg->d11inf);
 +
 +      err = brcmf_setup_wiphy(wiphy, ifp);
 +      if (err < 0)
 +              goto priv_out;
 +
 +      brcmf_dbg(INFO, "Registering custom regulatory\n");
 +      wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG;
 +      wiphy_apply_custom_regulatory(wiphy, &brcmf_regdom);
 +
 +      /* firmware defaults to 40MHz disabled in 2G band. We signal
 +       * cfg80211 here that we do and have it decide we can enable
 +       * it. But first check if device does support 2G operation.
 +       */
 +      if (wiphy->bands[IEEE80211_BAND_2GHZ]) {
 +              cap = &wiphy->bands[IEEE80211_BAND_2GHZ]->ht_cap.cap;
 +              *cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40;
 +      }
 +      err = wiphy_register(wiphy);
 +      if (err < 0) {
 +              brcmf_err("Could not register wiphy device (%d)\n", err);
 +              goto priv_out;
 +      }
 +
 +      /* If cfg80211 didn't disable 40MHz HT CAP in wiphy_register(),
 +       * setup 40MHz in 2GHz band and enable OBSS scanning.
 +       */
 +      if (cap && (*cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40)) {
 +              err = brcmf_enable_bw40_2g(cfg);
 +              if (!err)
 +                      err = brcmf_fil_iovar_int_set(ifp, "obss_coex",
 +                                                    BRCMF_OBSS_COEX_AUTO);
 +              else
 +                      *cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
 +      }
 +
 +      err = brcmf_p2p_attach(cfg);
 +      if (err) {
 +              brcmf_err("P2P initilisation failed (%d)\n", err);
 +              goto wiphy_unreg_out;
 +      }
 +      err = brcmf_btcoex_attach(cfg);
 +      if (err) {
 +              brcmf_err("BT-coex initialisation failed (%d)\n", err);
 +              brcmf_p2p_detach(&cfg->p2p);
 +              goto wiphy_unreg_out;
 +      }
 +
 +      err = brcmf_fil_iovar_int_set(ifp, "tdls_enable", 1);
 +      if (err) {
 +              brcmf_dbg(INFO, "TDLS not enabled (%d)\n", err);
 +              wiphy->flags &= ~WIPHY_FLAG_SUPPORTS_TDLS;
 +      } else {
 +              brcmf_fweh_register(cfg->pub, BRCMF_E_TDLS_PEER_EVENT,
 +                                  brcmf_notify_tdls_peer_event);
 +      }
 +
 +      return cfg;
 +
 +wiphy_unreg_out:
 +      wiphy_unregister(cfg->wiphy);
 +priv_out:
 +      wl_deinit_priv(cfg);
 +      brcmf_free_vif(vif);
 +wiphy_out:
 +      brcmf_free_wiphy(wiphy);
 +      return NULL;
 +}
 +
 +void brcmf_cfg80211_detach(struct brcmf_cfg80211_info *cfg)
 +{
 +      if (!cfg)
 +              return;
 +
 +      WARN_ON(!list_empty(&cfg->vif_list));
 +      wiphy_unregister(cfg->wiphy);
 +      brcmf_btcoex_detach(cfg);
 +      brcmf_p2p_detach(&cfg->p2p);
 +      wl_deinit_priv(cfg);
 +      brcmf_free_wiphy(cfg->wiphy);
 +}
index 72e87b51f9993c0f1d896bdb9f06644cfafd7678,0000000000000000000000000000000000000000..0b0d51a61060cbc42f8f755ba9b2e543b08b9426
mode 100644,000000..100644
--- /dev/null
@@@ -1,4318 -1,0 +1,4317 @@@
-       uint fw_len, nv_len;
 +/*
 + * Copyright (c) 2010 Broadcom Corporation
 + *
 + * Permission to use, copy, modify, and/or distribute this software for any
 + * purpose with or without fee is hereby granted, provided that the above
 + * copyright notice and this permission notice appear in all copies.
 + *
 + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
 + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
 + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
 + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 + */
 +
 +#include <linux/types.h>
 +#include <linux/kernel.h>
 +#include <linux/kthread.h>
 +#include <linux/printk.h>
 +#include <linux/pci_ids.h>
 +#include <linux/netdevice.h>
 +#include <linux/interrupt.h>
 +#include <linux/sched.h>
 +#include <linux/mmc/sdio.h>
 +#include <linux/mmc/sdio_ids.h>
 +#include <linux/mmc/sdio_func.h>
 +#include <linux/mmc/card.h>
 +#include <linux/semaphore.h>
 +#include <linux/firmware.h>
 +#include <linux/module.h>
 +#include <linux/bcma/bcma.h>
 +#include <linux/debugfs.h>
 +#include <linux/vmalloc.h>
 +#include <linux/platform_data/brcmfmac-sdio.h>
 +#include <linux/moduleparam.h>
 +#include <asm/unaligned.h>
 +#include <defs.h>
 +#include <brcmu_wifi.h>
 +#include <brcmu_utils.h>
 +#include <brcm_hw_ids.h>
 +#include <soc.h>
 +#include "sdio.h"
 +#include "chip.h"
 +#include "firmware.h"
 +
 +#define DCMD_RESP_TIMEOUT  2000       /* In milli second */
 +
 +#ifdef DEBUG
 +
 +#define BRCMF_TRAP_INFO_SIZE  80
 +
 +#define CBUF_LEN      (128)
 +
 +/* Device console log buffer state */
 +#define CONSOLE_BUFFER_MAX    2024
 +
 +struct rte_log_le {
 +      __le32 buf;             /* Can't be pointer on (64-bit) hosts */
 +      __le32 buf_size;
 +      __le32 idx;
 +      char *_buf_compat;      /* Redundant pointer for backward compat. */
 +};
 +
 +struct rte_console {
 +      /* Virtual UART
 +       * When there is no UART (e.g. Quickturn),
 +       * the host should write a complete
 +       * input line directly into cbuf and then write
 +       * the length into vcons_in.
 +       * This may also be used when there is a real UART
 +       * (at risk of conflicting with
 +       * the real UART).  vcons_out is currently unused.
 +       */
 +      uint vcons_in;
 +      uint vcons_out;
 +
 +      /* Output (logging) buffer
 +       * Console output is written to a ring buffer log_buf at index log_idx.
 +       * The host may read the output when it sees log_idx advance.
 +       * Output will be lost if the output wraps around faster than the host
 +       * polls.
 +       */
 +      struct rte_log_le log_le;
 +
 +      /* Console input line buffer
 +       * Characters are read one at a time into cbuf
 +       * until <CR> is received, then
 +       * the buffer is processed as a command line.
 +       * Also used for virtual UART.
 +       */
 +      uint cbuf_idx;
 +      char cbuf[CBUF_LEN];
 +};
 +
 +#endif                                /* DEBUG */
 +#include <chipcommon.h>
 +
 +#include "bus.h"
 +#include "debug.h"
 +#include "tracepoint.h"
 +
 +#define TXQLEN                2048    /* bulk tx queue length */
 +#define TXHI          (TXQLEN - 256)  /* turn on flow control above TXHI */
 +#define TXLOW         (TXHI - 256)    /* turn off flow control below TXLOW */
 +#define PRIOMASK      7
 +
 +#define TXRETRIES     2       /* # of retries for tx frames */
 +
 +#define BRCMF_RXBOUND 50      /* Default for max rx frames in
 +                               one scheduling */
 +
 +#define BRCMF_TXBOUND 20      /* Default for max tx frames in
 +                               one scheduling */
 +
 +#define BRCMF_TXMINMAX        1       /* Max tx frames if rx still pending */
 +
 +#define MEMBLOCK      2048    /* Block size used for downloading
 +                               of dongle image */
 +#define MAX_DATA_BUF  (32 * 1024)     /* Must be large enough to hold
 +                               biggest possible glom */
 +
 +#define BRCMF_FIRSTREAD       (1 << 6)
 +
 +
 +/* SBSDIO_DEVICE_CTL */
 +
 +/* 1: device will assert busy signal when receiving CMD53 */
 +#define SBSDIO_DEVCTL_SETBUSY         0x01
 +/* 1: assertion of sdio interrupt is synchronous to the sdio clock */
 +#define SBSDIO_DEVCTL_SPI_INTR_SYNC   0x02
 +/* 1: mask all interrupts to host except the chipActive (rev 8) */
 +#define SBSDIO_DEVCTL_CA_INT_ONLY     0x04
 +/* 1: isolate internal sdio signals, put external pads in tri-state; requires
 + * sdio bus power cycle to clear (rev 9) */
 +#define SBSDIO_DEVCTL_PADS_ISO                0x08
 +/* Force SD->SB reset mapping (rev 11) */
 +#define SBSDIO_DEVCTL_SB_RST_CTL      0x30
 +/*   Determined by CoreControl bit */
 +#define SBSDIO_DEVCTL_RST_CORECTL     0x00
 +/*   Force backplane reset */
 +#define SBSDIO_DEVCTL_RST_BPRESET     0x10
 +/*   Force no backplane reset */
 +#define SBSDIO_DEVCTL_RST_NOBPRESET   0x20
 +
 +/* direct(mapped) cis space */
 +
 +/* MAPPED common CIS address */
 +#define SBSDIO_CIS_BASE_COMMON                0x1000
 +/* maximum bytes in one CIS */
 +#define SBSDIO_CIS_SIZE_LIMIT         0x200
 +/* cis offset addr is < 17 bits */
 +#define SBSDIO_CIS_OFT_ADDR_MASK      0x1FFFF
 +
 +/* manfid tuple length, include tuple, link bytes */
 +#define SBSDIO_CIS_MANFID_TUPLE_LEN   6
 +
 +#define CORE_BUS_REG(base, field) \
 +              (base + offsetof(struct sdpcmd_regs, field))
 +
 +/* SDIO function 1 register CHIPCLKCSR */
 +/* Force ALP request to backplane */
 +#define SBSDIO_FORCE_ALP              0x01
 +/* Force HT request to backplane */
 +#define SBSDIO_FORCE_HT                       0x02
 +/* Force ILP request to backplane */
 +#define SBSDIO_FORCE_ILP              0x04
 +/* Make ALP ready (power up xtal) */
 +#define SBSDIO_ALP_AVAIL_REQ          0x08
 +/* Make HT ready (power up PLL) */
 +#define SBSDIO_HT_AVAIL_REQ           0x10
 +/* Squelch clock requests from HW */
 +#define SBSDIO_FORCE_HW_CLKREQ_OFF    0x20
 +/* Status: ALP is ready */
 +#define SBSDIO_ALP_AVAIL              0x40
 +/* Status: HT is ready */
 +#define SBSDIO_HT_AVAIL                       0x80
 +#define SBSDIO_CSR_MASK                       0x1F
 +#define SBSDIO_AVBITS         (SBSDIO_HT_AVAIL | SBSDIO_ALP_AVAIL)
 +#define SBSDIO_ALPAV(regval)  ((regval) & SBSDIO_AVBITS)
 +#define SBSDIO_HTAV(regval)   (((regval) & SBSDIO_AVBITS) == SBSDIO_AVBITS)
 +#define SBSDIO_ALPONLY(regval)        (SBSDIO_ALPAV(regval) && !SBSDIO_HTAV(regval))
 +#define SBSDIO_CLKAV(regval, alponly) \
 +      (SBSDIO_ALPAV(regval) && (alponly ? 1 : SBSDIO_HTAV(regval)))
 +
 +/* intstatus */
 +#define I_SMB_SW0     (1 << 0)        /* To SB Mail S/W interrupt 0 */
 +#define I_SMB_SW1     (1 << 1)        /* To SB Mail S/W interrupt 1 */
 +#define I_SMB_SW2     (1 << 2)        /* To SB Mail S/W interrupt 2 */
 +#define I_SMB_SW3     (1 << 3)        /* To SB Mail S/W interrupt 3 */
 +#define I_SMB_SW_MASK 0x0000000f      /* To SB Mail S/W interrupts mask */
 +#define I_SMB_SW_SHIFT        0       /* To SB Mail S/W interrupts shift */
 +#define I_HMB_SW0     (1 << 4)        /* To Host Mail S/W interrupt 0 */
 +#define I_HMB_SW1     (1 << 5)        /* To Host Mail S/W interrupt 1 */
 +#define I_HMB_SW2     (1 << 6)        /* To Host Mail S/W interrupt 2 */
 +#define I_HMB_SW3     (1 << 7)        /* To Host Mail S/W interrupt 3 */
 +#define I_HMB_SW_MASK 0x000000f0      /* To Host Mail S/W interrupts mask */
 +#define I_HMB_SW_SHIFT        4       /* To Host Mail S/W interrupts shift */
 +#define I_WR_OOSYNC   (1 << 8)        /* Write Frame Out Of Sync */
 +#define I_RD_OOSYNC   (1 << 9)        /* Read Frame Out Of Sync */
 +#define       I_PC            (1 << 10)       /* descriptor error */
 +#define       I_PD            (1 << 11)       /* data error */
 +#define       I_DE            (1 << 12)       /* Descriptor protocol Error */
 +#define       I_RU            (1 << 13)       /* Receive descriptor Underflow */
 +#define       I_RO            (1 << 14)       /* Receive fifo Overflow */
 +#define       I_XU            (1 << 15)       /* Transmit fifo Underflow */
 +#define       I_RI            (1 << 16)       /* Receive Interrupt */
 +#define I_BUSPWR      (1 << 17)       /* SDIO Bus Power Change (rev 9) */
 +#define I_XMTDATA_AVAIL (1 << 23)     /* bits in fifo */
 +#define       I_XI            (1 << 24)       /* Transmit Interrupt */
 +#define I_RF_TERM     (1 << 25)       /* Read Frame Terminate */
 +#define I_WF_TERM     (1 << 26)       /* Write Frame Terminate */
 +#define I_PCMCIA_XU   (1 << 27)       /* PCMCIA Transmit FIFO Underflow */
 +#define I_SBINT               (1 << 28)       /* sbintstatus Interrupt */
 +#define I_CHIPACTIVE  (1 << 29)       /* chip from doze to active state */
 +#define I_SRESET      (1 << 30)       /* CCCR RES interrupt */
 +#define I_IOE2                (1U << 31)      /* CCCR IOE2 Bit Changed */
 +#define       I_ERRORS        (I_PC | I_PD | I_DE | I_RU | I_RO | I_XU)
 +#define I_DMA         (I_RI | I_XI | I_ERRORS)
 +
 +/* corecontrol */
 +#define CC_CISRDY             (1 << 0)        /* CIS Ready */
 +#define CC_BPRESEN            (1 << 1)        /* CCCR RES signal */
 +#define CC_F2RDY              (1 << 2)        /* set CCCR IOR2 bit */
 +#define CC_CLRPADSISO         (1 << 3)        /* clear SDIO pads isolation */
 +#define CC_XMTDATAAVAIL_MODE  (1 << 4)
 +#define CC_XMTDATAAVAIL_CTRL  (1 << 5)
 +
 +/* SDA_FRAMECTRL */
 +#define SFC_RF_TERM   (1 << 0)        /* Read Frame Terminate */
 +#define SFC_WF_TERM   (1 << 1)        /* Write Frame Terminate */
 +#define SFC_CRC4WOOS  (1 << 2)        /* CRC error for write out of sync */
 +#define SFC_ABORTALL  (1 << 3)        /* Abort all in-progress frames */
 +
 +/*
 + * Software allocation of To SB Mailbox resources
 + */
 +
 +/* tosbmailbox bits corresponding to intstatus bits */
 +#define SMB_NAK               (1 << 0)        /* Frame NAK */
 +#define SMB_INT_ACK   (1 << 1)        /* Host Interrupt ACK */
 +#define SMB_USE_OOB   (1 << 2)        /* Use OOB Wakeup */
 +#define SMB_DEV_INT   (1 << 3)        /* Miscellaneous Interrupt */
 +
 +/* tosbmailboxdata */
 +#define SMB_DATA_VERSION_SHIFT        16      /* host protocol version */
 +
 +/*
 + * Software allocation of To Host Mailbox resources
 + */
 +
 +/* intstatus bits */
 +#define I_HMB_FC_STATE        I_HMB_SW0       /* Flow Control State */
 +#define I_HMB_FC_CHANGE       I_HMB_SW1       /* Flow Control State Changed */
 +#define I_HMB_FRAME_IND       I_HMB_SW2       /* Frame Indication */
 +#define I_HMB_HOST_INT        I_HMB_SW3       /* Miscellaneous Interrupt */
 +
 +/* tohostmailboxdata */
 +#define HMB_DATA_NAKHANDLED   1       /* retransmit NAK'd frame */
 +#define HMB_DATA_DEVREADY     2       /* talk to host after enable */
 +#define HMB_DATA_FC           4       /* per prio flowcontrol update flag */
 +#define HMB_DATA_FWREADY      8       /* fw ready for protocol activity */
 +
 +#define HMB_DATA_FCDATA_MASK  0xff000000
 +#define HMB_DATA_FCDATA_SHIFT 24
 +
 +#define HMB_DATA_VERSION_MASK 0x00ff0000
 +#define HMB_DATA_VERSION_SHIFT        16
 +
 +/*
 + * Software-defined protocol header
 + */
 +
 +/* Current protocol version */
 +#define SDPCM_PROT_VERSION    4
 +
 +/*
 + * Shared structure between dongle and the host.
 + * The structure contains pointers to trap or assert information.
 + */
 +#define SDPCM_SHARED_VERSION       0x0003
 +#define SDPCM_SHARED_VERSION_MASK  0x00FF
 +#define SDPCM_SHARED_ASSERT_BUILT  0x0100
 +#define SDPCM_SHARED_ASSERT        0x0200
 +#define SDPCM_SHARED_TRAP          0x0400
 +
 +/* Space for header read, limit for data packets */
 +#define MAX_HDR_READ  (1 << 6)
 +#define MAX_RX_DATASZ 2048
 +
 +/* Bump up limit on waiting for HT to account for first startup;
 + * if the image is doing a CRC calculation before programming the PMU
 + * for HT availability, it could take a couple hundred ms more, so
 + * max out at a 1 second (1000000us).
 + */
 +#undef PMU_MAX_TRANSITION_DLY
 +#define PMU_MAX_TRANSITION_DLY 1000000
 +
 +/* Value for ChipClockCSR during initial setup */
 +#define BRCMF_INIT_CLKCTL1    (SBSDIO_FORCE_HW_CLKREQ_OFF |   \
 +                                      SBSDIO_ALP_AVAIL_REQ)
 +
 +/* Flags for SDH calls */
 +#define F2SYNC        (SDIO_REQ_4BYTE | SDIO_REQ_FIXED)
 +
 +#define BRCMF_IDLE_ACTIVE     0       /* Do not request any SD clock change
 +                                       * when idle
 +                                       */
 +#define BRCMF_IDLE_INTERVAL   1
 +
 +#define KSO_WAIT_US 50
 +#define MAX_KSO_ATTEMPTS (PMU_MAX_TRANSITION_DLY/KSO_WAIT_US)
 +
 +/*
 + * Conversion of 802.1D priority to precedence level
 + */
 +static uint prio2prec(u32 prio)
 +{
 +      return (prio == PRIO_8021D_NONE || prio == PRIO_8021D_BE) ?
 +             (prio^2) : prio;
 +}
 +
 +#ifdef DEBUG
 +/* Device console log buffer state */
 +struct brcmf_console {
 +      uint count;             /* Poll interval msec counter */
 +      uint log_addr;          /* Log struct address (fixed) */
 +      struct rte_log_le log_le;       /* Log struct (host copy) */
 +      uint bufsize;           /* Size of log buffer */
 +      u8 *buf;                /* Log buffer (host copy) */
 +      uint last;              /* Last buffer read index */
 +};
 +
 +struct brcmf_trap_info {
 +      __le32          type;
 +      __le32          epc;
 +      __le32          cpsr;
 +      __le32          spsr;
 +      __le32          r0;     /* a1 */
 +      __le32          r1;     /* a2 */
 +      __le32          r2;     /* a3 */
 +      __le32          r3;     /* a4 */
 +      __le32          r4;     /* v1 */
 +      __le32          r5;     /* v2 */
 +      __le32          r6;     /* v3 */
 +      __le32          r7;     /* v4 */
 +      __le32          r8;     /* v5 */
 +      __le32          r9;     /* sb/v6 */
 +      __le32          r10;    /* sl/v7 */
 +      __le32          r11;    /* fp/v8 */
 +      __le32          r12;    /* ip */
 +      __le32          r13;    /* sp */
 +      __le32          r14;    /* lr */
 +      __le32          pc;     /* r15 */
 +};
 +#endif                                /* DEBUG */
 +
 +struct sdpcm_shared {
 +      u32 flags;
 +      u32 trap_addr;
 +      u32 assert_exp_addr;
 +      u32 assert_file_addr;
 +      u32 assert_line;
 +      u32 console_addr;       /* Address of struct rte_console */
 +      u32 msgtrace_addr;
 +      u8 tag[32];
 +      u32 brpt_addr;
 +};
 +
 +struct sdpcm_shared_le {
 +      __le32 flags;
 +      __le32 trap_addr;
 +      __le32 assert_exp_addr;
 +      __le32 assert_file_addr;
 +      __le32 assert_line;
 +      __le32 console_addr;    /* Address of struct rte_console */
 +      __le32 msgtrace_addr;
 +      u8 tag[32];
 +      __le32 brpt_addr;
 +};
 +
 +/* dongle SDIO bus specific header info */
 +struct brcmf_sdio_hdrinfo {
 +      u8 seq_num;
 +      u8 channel;
 +      u16 len;
 +      u16 len_left;
 +      u16 len_nxtfrm;
 +      u8 dat_offset;
 +      bool lastfrm;
 +      u16 tail_pad;
 +};
 +
 +/*
 + * hold counter variables
 + */
 +struct brcmf_sdio_count {
 +      uint intrcount;         /* Count of device interrupt callbacks */
 +      uint lastintrs;         /* Count as of last watchdog timer */
 +      uint pollcnt;           /* Count of active polls */
 +      uint regfails;          /* Count of R_REG failures */
 +      uint tx_sderrs;         /* Count of tx attempts with sd errors */
 +      uint fcqueued;          /* Tx packets that got queued */
 +      uint rxrtx;             /* Count of rtx requests (NAK to dongle) */
 +      uint rx_toolong;        /* Receive frames too long to receive */
 +      uint rxc_errors;        /* SDIO errors when reading control frames */
 +      uint rx_hdrfail;        /* SDIO errors on header reads */
 +      uint rx_badhdr;         /* Bad received headers (roosync?) */
 +      uint rx_badseq;         /* Mismatched rx sequence number */
 +      uint fc_rcvd;           /* Number of flow-control events received */
 +      uint fc_xoff;           /* Number which turned on flow-control */
 +      uint fc_xon;            /* Number which turned off flow-control */
 +      uint rxglomfail;        /* Failed deglom attempts */
 +      uint rxglomframes;      /* Number of glom frames (superframes) */
 +      uint rxglompkts;        /* Number of packets from glom frames */
 +      uint f2rxhdrs;          /* Number of header reads */
 +      uint f2rxdata;          /* Number of frame data reads */
 +      uint f2txdata;          /* Number of f2 frame writes */
 +      uint f1regdata;         /* Number of f1 register accesses */
 +      uint tickcnt;           /* Number of watchdog been schedule */
 +      ulong tx_ctlerrs;       /* Err of sending ctrl frames */
 +      ulong tx_ctlpkts;       /* Ctrl frames sent to dongle */
 +      ulong rx_ctlerrs;       /* Err of processing rx ctrl frames */
 +      ulong rx_ctlpkts;       /* Ctrl frames processed from dongle */
 +      ulong rx_readahead_cnt; /* packets where header read-ahead was used */
 +};
 +
 +/* misc chip info needed by some of the routines */
 +/* Private data for SDIO bus interaction */
 +struct brcmf_sdio {
 +      struct brcmf_sdio_dev *sdiodev; /* sdio device handler */
 +      struct brcmf_chip *ci;  /* Chip info struct */
 +
 +      u32 ramsize;            /* Size of RAM in SOCRAM (bytes) */
 +
 +      u32 hostintmask;        /* Copy of Host Interrupt Mask */
 +      atomic_t intstatus;     /* Intstatus bits (events) pending */
 +      atomic_t fcstate;       /* State of dongle flow-control */
 +
 +      uint blocksize;         /* Block size of SDIO transfers */
 +      uint roundup;           /* Max roundup limit */
 +
 +      struct pktq txq;        /* Queue length used for flow-control */
 +      u8 flowcontrol; /* per prio flow control bitmask */
 +      u8 tx_seq;              /* Transmit sequence number (next) */
 +      u8 tx_max;              /* Maximum transmit sequence allowed */
 +
 +      u8 *hdrbuf;             /* buffer for handling rx frame */
 +      u8 *rxhdr;              /* Header of current rx frame (in hdrbuf) */
 +      u8 rx_seq;              /* Receive sequence number (expected) */
 +      struct brcmf_sdio_hdrinfo cur_read;
 +                              /* info of current read frame */
 +      bool rxskip;            /* Skip receive (awaiting NAK ACK) */
 +      bool rxpending;         /* Data frame pending in dongle */
 +
 +      uint rxbound;           /* Rx frames to read before resched */
 +      uint txbound;           /* Tx frames to send before resched */
 +      uint txminmax;
 +
 +      struct sk_buff *glomd;  /* Packet containing glomming descriptor */
 +      struct sk_buff_head glom; /* Packet list for glommed superframe */
 +      uint glomerr;           /* Glom packet read errors */
 +
 +      u8 *rxbuf;              /* Buffer for receiving control packets */
 +      uint rxblen;            /* Allocated length of rxbuf */
 +      u8 *rxctl;              /* Aligned pointer into rxbuf */
 +      u8 *rxctl_orig;         /* pointer for freeing rxctl */
 +      uint rxlen;             /* Length of valid data in buffer */
 +      spinlock_t rxctl_lock;  /* protection lock for ctrl frame resources */
 +
 +      u8 sdpcm_ver;   /* Bus protocol reported by dongle */
 +
 +      bool intr;              /* Use interrupts */
 +      bool poll;              /* Use polling */
 +      atomic_t ipend;         /* Device interrupt is pending */
 +      uint spurious;          /* Count of spurious interrupts */
 +      uint pollrate;          /* Ticks between device polls */
 +      uint polltick;          /* Tick counter */
 +
 +#ifdef DEBUG
 +      uint console_interval;
 +      struct brcmf_console console;   /* Console output polling support */
 +      uint console_addr;      /* Console address from shared struct */
 +#endif                                /* DEBUG */
 +
 +      uint clkstate;          /* State of sd and backplane clock(s) */
 +      bool activity;          /* Activity flag for clock down */
 +      s32 idletime;           /* Control for activity timeout */
 +      s32 idlecount;  /* Activity timeout counter */
 +      s32 idleclock;  /* How to set bus driver when idle */
 +      bool rxflow_mode;       /* Rx flow control mode */
 +      bool rxflow;            /* Is rx flow control on */
 +      bool alp_only;          /* Don't use HT clock (ALP only) */
 +
 +      u8 *ctrl_frame_buf;
 +      u16 ctrl_frame_len;
 +      bool ctrl_frame_stat;
 +
 +      spinlock_t txq_lock;            /* protect bus->txq */
 +      struct semaphore tx_seq_lock;   /* protect bus->tx_seq */
 +      wait_queue_head_t ctrl_wait;
 +      wait_queue_head_t dcmd_resp_wait;
 +
 +      struct timer_list timer;
 +      struct completion watchdog_wait;
 +      struct task_struct *watchdog_tsk;
 +      bool wd_timer_valid;
 +      uint save_ms;
 +
 +      struct workqueue_struct *brcmf_wq;
 +      struct work_struct datawork;
 +      atomic_t dpc_tskcnt;
 +
 +      bool txoff;             /* Transmit flow-controlled */
 +      struct brcmf_sdio_count sdcnt;
 +      bool sr_enabled; /* SaveRestore enabled */
 +      bool sleeping; /* SDIO bus sleeping */
 +
 +      u8 tx_hdrlen;           /* sdio bus header length for tx packet */
 +      bool txglom;            /* host tx glomming enable flag */
 +      u16 head_align;         /* buffer pointer alignment */
 +      u16 sgentry_align;      /* scatter-gather buffer alignment */
 +};
 +
 +/* clkstate */
 +#define CLK_NONE      0
 +#define CLK_SDONLY    1
 +#define CLK_PENDING   2
 +#define CLK_AVAIL     3
 +
 +#ifdef DEBUG
 +static int qcount[NUMPRIO];
 +#endif                                /* DEBUG */
 +
 +#define DEFAULT_SDIO_DRIVE_STRENGTH   6       /* in milliamps */
 +
 +#define RETRYCHAN(chan) ((chan) == SDPCM_EVENT_CHANNEL)
 +
 +/* Retry count for register access failures */
 +static const uint retry_limit = 2;
 +
 +/* Limit on rounding up frames */
 +static const uint max_roundup = 512;
 +
 +#define ALIGNMENT  4
 +
 +enum brcmf_sdio_frmtype {
 +      BRCMF_SDIO_FT_NORMAL,
 +      BRCMF_SDIO_FT_SUPER,
 +      BRCMF_SDIO_FT_SUB,
 +};
 +
 +#define SDIOD_DRVSTR_KEY(chip, pmu)     (((chip) << 16) | (pmu))
 +
 +/* SDIO Pad drive strength to select value mappings */
 +struct sdiod_drive_str {
 +      u8 strength;    /* Pad Drive Strength in mA */
 +      u8 sel;         /* Chip-specific select value */
 +};
 +
 +/* SDIO Drive Strength to sel value table for PMU Rev 11 (1.8V) */
 +static const struct sdiod_drive_str sdiod_drvstr_tab1_1v8[] = {
 +      {32, 0x6},
 +      {26, 0x7},
 +      {22, 0x4},
 +      {16, 0x5},
 +      {12, 0x2},
 +      {8, 0x3},
 +      {4, 0x0},
 +      {0, 0x1}
 +};
 +
 +/* SDIO Drive Strength to sel value table for PMU Rev 13 (1.8v) */
 +static const struct sdiod_drive_str sdiod_drive_strength_tab5_1v8[] = {
 +      {6, 0x7},
 +      {5, 0x6},
 +      {4, 0x5},
 +      {3, 0x4},
 +      {2, 0x2},
 +      {1, 0x1},
 +      {0, 0x0}
 +};
 +
 +/* SDIO Drive Strength to sel value table for PMU Rev 17 (1.8v) */
 +static const struct sdiod_drive_str sdiod_drvstr_tab6_1v8[] = {
 +      {3, 0x3},
 +      {2, 0x2},
 +      {1, 0x1},
 +      {0, 0x0} };
 +
 +/* SDIO Drive Strength to sel value table for 43143 PMU Rev 17 (3.3V) */
 +static const struct sdiod_drive_str sdiod_drvstr_tab2_3v3[] = {
 +      {16, 0x7},
 +      {12, 0x5},
 +      {8,  0x3},
 +      {4,  0x1}
 +};
 +
 +#define BCM43143_FIRMWARE_NAME                "brcm/brcmfmac43143-sdio.bin"
 +#define BCM43143_NVRAM_NAME           "brcm/brcmfmac43143-sdio.txt"
 +#define BCM43241B0_FIRMWARE_NAME      "brcm/brcmfmac43241b0-sdio.bin"
 +#define BCM43241B0_NVRAM_NAME         "brcm/brcmfmac43241b0-sdio.txt"
 +#define BCM43241B4_FIRMWARE_NAME      "brcm/brcmfmac43241b4-sdio.bin"
 +#define BCM43241B4_NVRAM_NAME         "brcm/brcmfmac43241b4-sdio.txt"
 +#define BCM4329_FIRMWARE_NAME         "brcm/brcmfmac4329-sdio.bin"
 +#define BCM4329_NVRAM_NAME            "brcm/brcmfmac4329-sdio.txt"
 +#define BCM4330_FIRMWARE_NAME         "brcm/brcmfmac4330-sdio.bin"
 +#define BCM4330_NVRAM_NAME            "brcm/brcmfmac4330-sdio.txt"
 +#define BCM4334_FIRMWARE_NAME         "brcm/brcmfmac4334-sdio.bin"
 +#define BCM4334_NVRAM_NAME            "brcm/brcmfmac4334-sdio.txt"
 +#define BCM4335_FIRMWARE_NAME         "brcm/brcmfmac4335-sdio.bin"
 +#define BCM4335_NVRAM_NAME            "brcm/brcmfmac4335-sdio.txt"
 +#define BCM43362_FIRMWARE_NAME                "brcm/brcmfmac43362-sdio.bin"
 +#define BCM43362_NVRAM_NAME           "brcm/brcmfmac43362-sdio.txt"
 +#define BCM4339_FIRMWARE_NAME         "brcm/brcmfmac4339-sdio.bin"
 +#define BCM4339_NVRAM_NAME            "brcm/brcmfmac4339-sdio.txt"
 +#define BCM4354_FIRMWARE_NAME         "brcm/brcmfmac4354-sdio.bin"
 +#define BCM4354_NVRAM_NAME            "brcm/brcmfmac4354-sdio.txt"
 +
 +MODULE_FIRMWARE(BCM43143_FIRMWARE_NAME);
 +MODULE_FIRMWARE(BCM43143_NVRAM_NAME);
 +MODULE_FIRMWARE(BCM43241B0_FIRMWARE_NAME);
 +MODULE_FIRMWARE(BCM43241B0_NVRAM_NAME);
 +MODULE_FIRMWARE(BCM43241B4_FIRMWARE_NAME);
 +MODULE_FIRMWARE(BCM43241B4_NVRAM_NAME);
 +MODULE_FIRMWARE(BCM4329_FIRMWARE_NAME);
 +MODULE_FIRMWARE(BCM4329_NVRAM_NAME);
 +MODULE_FIRMWARE(BCM4330_FIRMWARE_NAME);
 +MODULE_FIRMWARE(BCM4330_NVRAM_NAME);
 +MODULE_FIRMWARE(BCM4334_FIRMWARE_NAME);
 +MODULE_FIRMWARE(BCM4334_NVRAM_NAME);
 +MODULE_FIRMWARE(BCM4335_FIRMWARE_NAME);
 +MODULE_FIRMWARE(BCM4335_NVRAM_NAME);
 +MODULE_FIRMWARE(BCM43362_FIRMWARE_NAME);
 +MODULE_FIRMWARE(BCM43362_NVRAM_NAME);
 +MODULE_FIRMWARE(BCM4339_FIRMWARE_NAME);
 +MODULE_FIRMWARE(BCM4339_NVRAM_NAME);
 +MODULE_FIRMWARE(BCM4354_FIRMWARE_NAME);
 +MODULE_FIRMWARE(BCM4354_NVRAM_NAME);
 +
 +struct brcmf_firmware_names {
 +      u32 chipid;
 +      u32 revmsk;
 +      const char *bin;
 +      const char *nv;
 +};
 +
 +enum brcmf_firmware_type {
 +      BRCMF_FIRMWARE_BIN,
 +      BRCMF_FIRMWARE_NVRAM
 +};
 +
 +#define BRCMF_FIRMWARE_NVRAM(name) \
 +      name ## _FIRMWARE_NAME, name ## _NVRAM_NAME
 +
 +static const struct brcmf_firmware_names brcmf_fwname_data[] = {
 +      { BRCM_CC_43143_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM43143) },
 +      { BRCM_CC_43241_CHIP_ID, 0x0000001F, BRCMF_FIRMWARE_NVRAM(BCM43241B0) },
 +      { BRCM_CC_43241_CHIP_ID, 0xFFFFFFE0, BRCMF_FIRMWARE_NVRAM(BCM43241B4) },
 +      { BRCM_CC_4329_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4329) },
 +      { BRCM_CC_4330_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4330) },
 +      { BRCM_CC_4334_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4334) },
 +      { BRCM_CC_4335_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4335) },
 +      { BRCM_CC_43362_CHIP_ID, 0xFFFFFFFE, BRCMF_FIRMWARE_NVRAM(BCM43362) },
 +      { BRCM_CC_4339_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4339) },
 +      { BRCM_CC_4354_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4354) }
 +};
 +
 +static int brcmf_sdio_get_fwnames(struct brcmf_chip *ci,
 +                                struct brcmf_sdio_dev *sdiodev)
 +{
 +      int i;
-       fw_len = sizeof(sdiodev->fw_name) - 1;
-       nv_len = sizeof(sdiodev->nvram_name) - 1;
 +      char end;
 +
 +      for (i = 0; i < ARRAY_SIZE(brcmf_fwname_data); i++) {
 +              if (brcmf_fwname_data[i].chipid == ci->chip &&
 +                  brcmf_fwname_data[i].revmsk & BIT(ci->chiprev))
 +                      break;
 +      }
 +
 +      if (i == ARRAY_SIZE(brcmf_fwname_data)) {
 +              brcmf_err("Unknown chipid %d [%d]\n", ci->chip, ci->chiprev);
 +              return -ENODEV;
 +      }
 +
-               strncpy(sdiodev->fw_name, brcmf_firmware_path, fw_len);
-               strncpy(sdiodev->nvram_name, brcmf_firmware_path, nv_len);
-               fw_len -= strlen(sdiodev->fw_name);
-               nv_len -= strlen(sdiodev->nvram_name);
 +      /* check if firmware path is provided by module parameter */
 +      if (brcmf_firmware_path[0] != '\0') {
-                       strncat(sdiodev->fw_name, "/", fw_len);
-                       strncat(sdiodev->nvram_name, "/", nv_len);
-                       fw_len--;
-                       nv_len--;
++              strlcpy(sdiodev->fw_name, brcmf_firmware_path,
++                      sizeof(sdiodev->fw_name));
++              strlcpy(sdiodev->nvram_name, brcmf_firmware_path,
++                      sizeof(sdiodev->nvram_name));
 +
 +              end = brcmf_firmware_path[strlen(brcmf_firmware_path) - 1];
 +              if (end != '/') {
-       strncat(sdiodev->fw_name, brcmf_fwname_data[i].bin, fw_len);
-       strncat(sdiodev->nvram_name, brcmf_fwname_data[i].nv, nv_len);
++                      strlcat(sdiodev->fw_name, "/",
++                              sizeof(sdiodev->fw_name));
++                      strlcat(sdiodev->nvram_name, "/",
++                              sizeof(sdiodev->nvram_name));
 +              }
 +      }
++      strlcat(sdiodev->fw_name, brcmf_fwname_data[i].bin,
++              sizeof(sdiodev->fw_name));
++      strlcat(sdiodev->nvram_name, brcmf_fwname_data[i].nv,
++              sizeof(sdiodev->nvram_name));
 +
 +      return 0;
 +}
 +
 +static void pkt_align(struct sk_buff *p, int len, int align)
 +{
 +      uint datalign;
 +      datalign = (unsigned long)(p->data);
 +      datalign = roundup(datalign, (align)) - datalign;
 +      if (datalign)
 +              skb_pull(p, datalign);
 +      __skb_trim(p, len);
 +}
 +
 +/* To check if there's window offered */
 +static bool data_ok(struct brcmf_sdio *bus)
 +{
 +      return (u8)(bus->tx_max - bus->tx_seq) != 0 &&
 +             ((u8)(bus->tx_max - bus->tx_seq) & 0x80) == 0;
 +}
 +
 +/*
 + * Reads a register in the SDIO hardware block. This block occupies a series of
 + * adresses on the 32 bit backplane bus.
 + */
 +static int r_sdreg32(struct brcmf_sdio *bus, u32 *regvar, u32 offset)
 +{
 +      struct brcmf_core *core;
 +      int ret;
 +
 +      core = brcmf_chip_get_core(bus->ci, BCMA_CORE_SDIO_DEV);
 +      *regvar = brcmf_sdiod_regrl(bus->sdiodev, core->base + offset, &ret);
 +
 +      return ret;
 +}
 +
 +static int w_sdreg32(struct brcmf_sdio *bus, u32 regval, u32 reg_offset)
 +{
 +      struct brcmf_core *core;
 +      int ret;
 +
 +      core = brcmf_chip_get_core(bus->ci, BCMA_CORE_SDIO_DEV);
 +      brcmf_sdiod_regwl(bus->sdiodev, core->base + reg_offset, regval, &ret);
 +
 +      return ret;
 +}
 +
 +static int
 +brcmf_sdio_kso_control(struct brcmf_sdio *bus, bool on)
 +{
 +      u8 wr_val = 0, rd_val, cmp_val, bmask;
 +      int err = 0;
 +      int try_cnt = 0;
 +
 +      brcmf_dbg(TRACE, "Enter: on=%d\n", on);
 +
 +      wr_val = (on << SBSDIO_FUNC1_SLEEPCSR_KSO_SHIFT);
 +      /* 1st KSO write goes to AOS wake up core if device is asleep  */
 +      brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR,
 +                        wr_val, &err);
 +
 +      if (on) {
 +              /* device WAKEUP through KSO:
 +               * write bit 0 & read back until
 +               * both bits 0 (kso bit) & 1 (dev on status) are set
 +               */
 +              cmp_val = SBSDIO_FUNC1_SLEEPCSR_KSO_MASK |
 +                        SBSDIO_FUNC1_SLEEPCSR_DEVON_MASK;
 +              bmask = cmp_val;
 +              usleep_range(2000, 3000);
 +      } else {
 +              /* Put device to sleep, turn off KSO */
 +              cmp_val = 0;
 +              /* only check for bit0, bit1(dev on status) may not
 +               * get cleared right away
 +               */
 +              bmask = SBSDIO_FUNC1_SLEEPCSR_KSO_MASK;
 +      }
 +
 +      do {
 +              /* reliable KSO bit set/clr:
 +               * the sdiod sleep write access is synced to PMU 32khz clk
 +               * just one write attempt may fail,
 +               * read it back until it matches written value
 +               */
 +              rd_val = brcmf_sdiod_regrb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR,
 +                                         &err);
 +              if (((rd_val & bmask) == cmp_val) && !err)
 +                      break;
 +
 +              udelay(KSO_WAIT_US);
 +              brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR,
 +                                wr_val, &err);
 +      } while (try_cnt++ < MAX_KSO_ATTEMPTS);
 +
 +      if (try_cnt > 2)
 +              brcmf_dbg(SDIO, "try_cnt=%d rd_val=0x%x err=%d\n", try_cnt,
 +                        rd_val, err);
 +
 +      if (try_cnt > MAX_KSO_ATTEMPTS)
 +              brcmf_err("max tries: rd_val=0x%x err=%d\n", rd_val, err);
 +
 +      return err;
 +}
 +
 +#define HOSTINTMASK           (I_HMB_SW_MASK | I_CHIPACTIVE)
 +
 +/* Turn backplane clock on or off */
 +static int brcmf_sdio_htclk(struct brcmf_sdio *bus, bool on, bool pendok)
 +{
 +      int err;
 +      u8 clkctl, clkreq, devctl;
 +      unsigned long timeout;
 +
 +      brcmf_dbg(SDIO, "Enter\n");
 +
 +      clkctl = 0;
 +
 +      if (bus->sr_enabled) {
 +              bus->clkstate = (on ? CLK_AVAIL : CLK_SDONLY);
 +              return 0;
 +      }
 +
 +      if (on) {
 +              /* Request HT Avail */
 +              clkreq =
 +                  bus->alp_only ? SBSDIO_ALP_AVAIL_REQ : SBSDIO_HT_AVAIL_REQ;
 +
 +              brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR,
 +                                clkreq, &err);
 +              if (err) {
 +                      brcmf_err("HT Avail request error: %d\n", err);
 +                      return -EBADE;
 +              }
 +
 +              /* Check current status */
 +              clkctl = brcmf_sdiod_regrb(bus->sdiodev,
 +                                         SBSDIO_FUNC1_CHIPCLKCSR, &err);
 +              if (err) {
 +                      brcmf_err("HT Avail read error: %d\n", err);
 +                      return -EBADE;
 +              }
 +
 +              /* Go to pending and await interrupt if appropriate */
 +              if (!SBSDIO_CLKAV(clkctl, bus->alp_only) && pendok) {
 +                      /* Allow only clock-available interrupt */
 +                      devctl = brcmf_sdiod_regrb(bus->sdiodev,
 +                                                 SBSDIO_DEVICE_CTL, &err);
 +                      if (err) {
 +                              brcmf_err("Devctl error setting CA: %d\n",
 +                                        err);
 +                              return -EBADE;
 +                      }
 +
 +                      devctl |= SBSDIO_DEVCTL_CA_INT_ONLY;
 +                      brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_DEVICE_CTL,
 +                                        devctl, &err);
 +                      brcmf_dbg(SDIO, "CLKCTL: set PENDING\n");
 +                      bus->clkstate = CLK_PENDING;
 +
 +                      return 0;
 +              } else if (bus->clkstate == CLK_PENDING) {
 +                      /* Cancel CA-only interrupt filter */
 +                      devctl = brcmf_sdiod_regrb(bus->sdiodev,
 +                                                 SBSDIO_DEVICE_CTL, &err);
 +                      devctl &= ~SBSDIO_DEVCTL_CA_INT_ONLY;
 +                      brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_DEVICE_CTL,
 +                                        devctl, &err);
 +              }
 +
 +              /* Otherwise, wait here (polling) for HT Avail */
 +              timeout = jiffies +
 +                        msecs_to_jiffies(PMU_MAX_TRANSITION_DLY/1000);
 +              while (!SBSDIO_CLKAV(clkctl, bus->alp_only)) {
 +                      clkctl = brcmf_sdiod_regrb(bus->sdiodev,
 +                                                 SBSDIO_FUNC1_CHIPCLKCSR,
 +                                                 &err);
 +                      if (time_after(jiffies, timeout))
 +                              break;
 +                      else
 +                              usleep_range(5000, 10000);
 +              }
 +              if (err) {
 +                      brcmf_err("HT Avail request error: %d\n", err);
 +                      return -EBADE;
 +              }
 +              if (!SBSDIO_CLKAV(clkctl, bus->alp_only)) {
 +                      brcmf_err("HT Avail timeout (%d): clkctl 0x%02x\n",
 +                                PMU_MAX_TRANSITION_DLY, clkctl);
 +                      return -EBADE;
 +              }
 +
 +              /* Mark clock available */
 +              bus->clkstate = CLK_AVAIL;
 +              brcmf_dbg(SDIO, "CLKCTL: turned ON\n");
 +
 +#if defined(DEBUG)
 +              if (!bus->alp_only) {
 +                      if (SBSDIO_ALPONLY(clkctl))
 +                              brcmf_err("HT Clock should be on\n");
 +              }
 +#endif                                /* defined (DEBUG) */
 +
 +      } else {
 +              clkreq = 0;
 +
 +              if (bus->clkstate == CLK_PENDING) {
 +                      /* Cancel CA-only interrupt filter */
 +                      devctl = brcmf_sdiod_regrb(bus->sdiodev,
 +                                                 SBSDIO_DEVICE_CTL, &err);
 +                      devctl &= ~SBSDIO_DEVCTL_CA_INT_ONLY;
 +                      brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_DEVICE_CTL,
 +                                        devctl, &err);
 +              }
 +
 +              bus->clkstate = CLK_SDONLY;
 +              brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR,
 +                                clkreq, &err);
 +              brcmf_dbg(SDIO, "CLKCTL: turned OFF\n");
 +              if (err) {
 +                      brcmf_err("Failed access turning clock off: %d\n",
 +                                err);
 +                      return -EBADE;
 +              }
 +      }
 +      return 0;
 +}
 +
 +/* Change idle/active SD state */
 +static int brcmf_sdio_sdclk(struct brcmf_sdio *bus, bool on)
 +{
 +      brcmf_dbg(SDIO, "Enter\n");
 +
 +      if (on)
 +              bus->clkstate = CLK_SDONLY;
 +      else
 +              bus->clkstate = CLK_NONE;
 +
 +      return 0;
 +}
 +
 +/* Transition SD and backplane clock readiness */
 +static int brcmf_sdio_clkctl(struct brcmf_sdio *bus, uint target, bool pendok)
 +{
 +#ifdef DEBUG
 +      uint oldstate = bus->clkstate;
 +#endif                                /* DEBUG */
 +
 +      brcmf_dbg(SDIO, "Enter\n");
 +
 +      /* Early exit if we're already there */
 +      if (bus->clkstate == target) {
 +              if (target == CLK_AVAIL) {
 +                      brcmf_sdio_wd_timer(bus, BRCMF_WD_POLL_MS);
 +                      bus->activity = true;
 +              }
 +              return 0;
 +      }
 +
 +      switch (target) {
 +      case CLK_AVAIL:
 +              /* Make sure SD clock is available */
 +              if (bus->clkstate == CLK_NONE)
 +                      brcmf_sdio_sdclk(bus, true);
 +              /* Now request HT Avail on the backplane */
 +              brcmf_sdio_htclk(bus, true, pendok);
 +              brcmf_sdio_wd_timer(bus, BRCMF_WD_POLL_MS);
 +              bus->activity = true;
 +              break;
 +
 +      case CLK_SDONLY:
 +              /* Remove HT request, or bring up SD clock */
 +              if (bus->clkstate == CLK_NONE)
 +                      brcmf_sdio_sdclk(bus, true);
 +              else if (bus->clkstate == CLK_AVAIL)
 +                      brcmf_sdio_htclk(bus, false, false);
 +              else
 +                      brcmf_err("request for %d -> %d\n",
 +                                bus->clkstate, target);
 +              brcmf_sdio_wd_timer(bus, BRCMF_WD_POLL_MS);
 +              break;
 +
 +      case CLK_NONE:
 +              /* Make sure to remove HT request */
 +              if (bus->clkstate == CLK_AVAIL)
 +                      brcmf_sdio_htclk(bus, false, false);
 +              /* Now remove the SD clock */
 +              brcmf_sdio_sdclk(bus, false);
 +              brcmf_sdio_wd_timer(bus, 0);
 +              break;
 +      }
 +#ifdef DEBUG
 +      brcmf_dbg(SDIO, "%d -> %d\n", oldstate, bus->clkstate);
 +#endif                                /* DEBUG */
 +
 +      return 0;
 +}
 +
 +static int
 +brcmf_sdio_bus_sleep(struct brcmf_sdio *bus, bool sleep, bool pendok)
 +{
 +      int err = 0;
 +      u8 clkcsr;
 +
 +      brcmf_dbg(SDIO, "Enter: request %s currently %s\n",
 +                (sleep ? "SLEEP" : "WAKE"),
 +                (bus->sleeping ? "SLEEP" : "WAKE"));
 +
 +      /* If SR is enabled control bus state with KSO */
 +      if (bus->sr_enabled) {
 +              /* Done if we're already in the requested state */
 +              if (sleep == bus->sleeping)
 +                      goto end;
 +
 +              /* Going to sleep */
 +              if (sleep) {
 +                      /* Don't sleep if something is pending */
 +                      if (atomic_read(&bus->intstatus) ||
 +                          atomic_read(&bus->ipend) > 0 ||
 +                          (!atomic_read(&bus->fcstate) &&
 +                          brcmu_pktq_mlen(&bus->txq, ~bus->flowcontrol) &&
 +                          data_ok(bus))) {
 +                               err = -EBUSY;
 +                               goto done;
 +                      }
 +
 +                      clkcsr = brcmf_sdiod_regrb(bus->sdiodev,
 +                                                 SBSDIO_FUNC1_CHIPCLKCSR,
 +                                                 &err);
 +                      if ((clkcsr & SBSDIO_CSR_MASK) == 0) {
 +                              brcmf_dbg(SDIO, "no clock, set ALP\n");
 +                              brcmf_sdiod_regwb(bus->sdiodev,
 +                                                SBSDIO_FUNC1_CHIPCLKCSR,
 +                                                SBSDIO_ALP_AVAIL_REQ, &err);
 +                      }
 +                      err = brcmf_sdio_kso_control(bus, false);
 +                      /* disable watchdog */
 +                      if (!err)
 +                              brcmf_sdio_wd_timer(bus, 0);
 +              } else {
 +                      bus->idlecount = 0;
 +                      err = brcmf_sdio_kso_control(bus, true);
 +              }
 +              if (!err) {
 +                      /* Change state */
 +                      bus->sleeping = sleep;
 +                      brcmf_dbg(SDIO, "new state %s\n",
 +                                (sleep ? "SLEEP" : "WAKE"));
 +              } else {
 +                      brcmf_err("error while changing bus sleep state %d\n",
 +                                err);
 +                      goto done;
 +              }
 +      }
 +
 +end:
 +      /* control clocks */
 +      if (sleep) {
 +              if (!bus->sr_enabled)
 +                      brcmf_sdio_clkctl(bus, CLK_NONE, pendok);
 +      } else {
 +              brcmf_sdio_clkctl(bus, CLK_AVAIL, pendok);
 +      }
 +done:
 +      brcmf_dbg(SDIO, "Exit: err=%d\n", err);
 +      return err;
 +
 +}
 +
 +#ifdef DEBUG
 +static inline bool brcmf_sdio_valid_shared_address(u32 addr)
 +{
 +      return !(addr == 0 || ((~addr >> 16) & 0xffff) == (addr & 0xffff));
 +}
 +
 +static int brcmf_sdio_readshared(struct brcmf_sdio *bus,
 +                               struct sdpcm_shared *sh)
 +{
 +      u32 addr;
 +      int rv;
 +      u32 shaddr = 0;
 +      struct sdpcm_shared_le sh_le;
 +      __le32 addr_le;
 +
 +      shaddr = bus->ci->rambase + bus->ramsize - 4;
 +
 +      /*
 +       * Read last word in socram to determine
 +       * address of sdpcm_shared structure
 +       */
 +      sdio_claim_host(bus->sdiodev->func[1]);
 +      brcmf_sdio_bus_sleep(bus, false, false);
 +      rv = brcmf_sdiod_ramrw(bus->sdiodev, false, shaddr, (u8 *)&addr_le, 4);
 +      sdio_release_host(bus->sdiodev->func[1]);
 +      if (rv < 0)
 +              return rv;
 +
 +      addr = le32_to_cpu(addr_le);
 +
 +      brcmf_dbg(SDIO, "sdpcm_shared address 0x%08X\n", addr);
 +
 +      /*
 +       * Check if addr is valid.
 +       * NVRAM length at the end of memory should have been overwritten.
 +       */
 +      if (!brcmf_sdio_valid_shared_address(addr)) {
 +                      brcmf_err("invalid sdpcm_shared address 0x%08X\n",
 +                                addr);
 +                      return -EINVAL;
 +      }
 +
 +      /* Read hndrte_shared structure */
 +      rv = brcmf_sdiod_ramrw(bus->sdiodev, false, addr, (u8 *)&sh_le,
 +                             sizeof(struct sdpcm_shared_le));
 +      if (rv < 0)
 +              return rv;
 +
 +      /* Endianness */
 +      sh->flags = le32_to_cpu(sh_le.flags);
 +      sh->trap_addr = le32_to_cpu(sh_le.trap_addr);
 +      sh->assert_exp_addr = le32_to_cpu(sh_le.assert_exp_addr);
 +      sh->assert_file_addr = le32_to_cpu(sh_le.assert_file_addr);
 +      sh->assert_line = le32_to_cpu(sh_le.assert_line);
 +      sh->console_addr = le32_to_cpu(sh_le.console_addr);
 +      sh->msgtrace_addr = le32_to_cpu(sh_le.msgtrace_addr);
 +
 +      if ((sh->flags & SDPCM_SHARED_VERSION_MASK) > SDPCM_SHARED_VERSION) {
 +              brcmf_err("sdpcm shared version unsupported: dhd %d dongle %d\n",
 +                        SDPCM_SHARED_VERSION,
 +                        sh->flags & SDPCM_SHARED_VERSION_MASK);
 +              return -EPROTO;
 +      }
 +
 +      return 0;
 +}
 +
 +static void brcmf_sdio_get_console_addr(struct brcmf_sdio *bus)
 +{
 +      struct sdpcm_shared sh;
 +
 +      if (brcmf_sdio_readshared(bus, &sh) == 0)
 +              bus->console_addr = sh.console_addr;
 +}
 +#else
 +static void brcmf_sdio_get_console_addr(struct brcmf_sdio *bus)
 +{
 +}
 +#endif /* DEBUG */
 +
 +static u32 brcmf_sdio_hostmail(struct brcmf_sdio *bus)
 +{
 +      u32 intstatus = 0;
 +      u32 hmb_data;
 +      u8 fcbits;
 +      int ret;
 +
 +      brcmf_dbg(SDIO, "Enter\n");
 +
 +      /* Read mailbox data and ack that we did so */
 +      ret = r_sdreg32(bus, &hmb_data,
 +                      offsetof(struct sdpcmd_regs, tohostmailboxdata));
 +
 +      if (ret == 0)
 +              w_sdreg32(bus, SMB_INT_ACK,
 +                        offsetof(struct sdpcmd_regs, tosbmailbox));
 +      bus->sdcnt.f1regdata += 2;
 +
 +      /* Dongle recomposed rx frames, accept them again */
 +      if (hmb_data & HMB_DATA_NAKHANDLED) {
 +              brcmf_dbg(SDIO, "Dongle reports NAK handled, expect rtx of %d\n",
 +                        bus->rx_seq);
 +              if (!bus->rxskip)
 +                      brcmf_err("unexpected NAKHANDLED!\n");
 +
 +              bus->rxskip = false;
 +              intstatus |= I_HMB_FRAME_IND;
 +      }
 +
 +      /*
 +       * DEVREADY does not occur with gSPI.
 +       */
 +      if (hmb_data & (HMB_DATA_DEVREADY | HMB_DATA_FWREADY)) {
 +              bus->sdpcm_ver =
 +                  (hmb_data & HMB_DATA_VERSION_MASK) >>
 +                  HMB_DATA_VERSION_SHIFT;
 +              if (bus->sdpcm_ver != SDPCM_PROT_VERSION)
 +                      brcmf_err("Version mismatch, dongle reports %d, "
 +                                "expecting %d\n",
 +                                bus->sdpcm_ver, SDPCM_PROT_VERSION);
 +              else
 +                      brcmf_dbg(SDIO, "Dongle ready, protocol version %d\n",
 +                                bus->sdpcm_ver);
 +
 +              /*
 +               * Retrieve console state address now that firmware should have
 +               * updated it.
 +               */
 +              brcmf_sdio_get_console_addr(bus);
 +      }
 +
 +      /*
 +       * Flow Control has been moved into the RX headers and this out of band
 +       * method isn't used any more.
 +       * remaining backward compatible with older dongles.
 +       */
 +      if (hmb_data & HMB_DATA_FC) {
 +              fcbits = (hmb_data & HMB_DATA_FCDATA_MASK) >>
 +                                                      HMB_DATA_FCDATA_SHIFT;
 +
 +              if (fcbits & ~bus->flowcontrol)
 +                      bus->sdcnt.fc_xoff++;
 +
 +              if (bus->flowcontrol & ~fcbits)
 +                      bus->sdcnt.fc_xon++;
 +
 +              bus->sdcnt.fc_rcvd++;
 +              bus->flowcontrol = fcbits;
 +      }
 +
 +      /* Shouldn't be any others */
 +      if (hmb_data & ~(HMB_DATA_DEVREADY |
 +                       HMB_DATA_NAKHANDLED |
 +                       HMB_DATA_FC |
 +                       HMB_DATA_FWREADY |
 +                       HMB_DATA_FCDATA_MASK | HMB_DATA_VERSION_MASK))
 +              brcmf_err("Unknown mailbox data content: 0x%02x\n",
 +                        hmb_data);
 +
 +      return intstatus;
 +}
 +
 +static void brcmf_sdio_rxfail(struct brcmf_sdio *bus, bool abort, bool rtx)
 +{
 +      uint retries = 0;
 +      u16 lastrbc;
 +      u8 hi, lo;
 +      int err;
 +
 +      brcmf_err("%sterminate frame%s\n",
 +                abort ? "abort command, " : "",
 +                rtx ? ", send NAK" : "");
 +
 +      if (abort)
 +              brcmf_sdiod_abort(bus->sdiodev, SDIO_FUNC_2);
 +
 +      brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_FRAMECTRL,
 +                        SFC_RF_TERM, &err);
 +      bus->sdcnt.f1regdata++;
 +
 +      /* Wait until the packet has been flushed (device/FIFO stable) */
 +      for (lastrbc = retries = 0xffff; retries > 0; retries--) {
 +              hi = brcmf_sdiod_regrb(bus->sdiodev,
 +                                     SBSDIO_FUNC1_RFRAMEBCHI, &err);
 +              lo = brcmf_sdiod_regrb(bus->sdiodev,
 +                                     SBSDIO_FUNC1_RFRAMEBCLO, &err);
 +              bus->sdcnt.f1regdata += 2;
 +
 +              if ((hi == 0) && (lo == 0))
 +                      break;
 +
 +              if ((hi > (lastrbc >> 8)) && (lo > (lastrbc & 0x00ff))) {
 +                      brcmf_err("count growing: last 0x%04x now 0x%04x\n",
 +                                lastrbc, (hi << 8) + lo);
 +              }
 +              lastrbc = (hi << 8) + lo;
 +      }
 +
 +      if (!retries)
 +              brcmf_err("count never zeroed: last 0x%04x\n", lastrbc);
 +      else
 +              brcmf_dbg(SDIO, "flush took %d iterations\n", 0xffff - retries);
 +
 +      if (rtx) {
 +              bus->sdcnt.rxrtx++;
 +              err = w_sdreg32(bus, SMB_NAK,
 +                              offsetof(struct sdpcmd_regs, tosbmailbox));
 +
 +              bus->sdcnt.f1regdata++;
 +              if (err == 0)
 +                      bus->rxskip = true;
 +      }
 +
 +      /* Clear partial in any case */
 +      bus->cur_read.len = 0;
 +}
 +
 +static void brcmf_sdio_txfail(struct brcmf_sdio *bus)
 +{
 +      struct brcmf_sdio_dev *sdiodev = bus->sdiodev;
 +      u8 i, hi, lo;
 +
 +      /* On failure, abort the command and terminate the frame */
 +      brcmf_err("sdio error, abort command and terminate frame\n");
 +      bus->sdcnt.tx_sderrs++;
 +
 +      brcmf_sdiod_abort(sdiodev, SDIO_FUNC_2);
 +      brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_FRAMECTRL, SFC_WF_TERM, NULL);
 +      bus->sdcnt.f1regdata++;
 +
 +      for (i = 0; i < 3; i++) {
 +              hi = brcmf_sdiod_regrb(sdiodev, SBSDIO_FUNC1_WFRAMEBCHI, NULL);
 +              lo = brcmf_sdiod_regrb(sdiodev, SBSDIO_FUNC1_WFRAMEBCLO, NULL);
 +              bus->sdcnt.f1regdata += 2;
 +              if ((hi == 0) && (lo == 0))
 +                      break;
 +      }
 +}
 +
 +/* return total length of buffer chain */
 +static uint brcmf_sdio_glom_len(struct brcmf_sdio *bus)
 +{
 +      struct sk_buff *p;
 +      uint total;
 +
 +      total = 0;
 +      skb_queue_walk(&bus->glom, p)
 +              total += p->len;
 +      return total;
 +}
 +
 +static void brcmf_sdio_free_glom(struct brcmf_sdio *bus)
 +{
 +      struct sk_buff *cur, *next;
 +
 +      skb_queue_walk_safe(&bus->glom, cur, next) {
 +              skb_unlink(cur, &bus->glom);
 +              brcmu_pkt_buf_free_skb(cur);
 +      }
 +}
 +
 +/**
 + * brcmfmac sdio bus specific header
 + * This is the lowest layer header wrapped on the packets transmitted between
 + * host and WiFi dongle which contains information needed for SDIO core and
 + * firmware
 + *
 + * It consists of 3 parts: hardware header, hardware extension header and
 + * software header
 + * hardware header (frame tag) - 4 bytes
 + * Byte 0~1: Frame length
 + * Byte 2~3: Checksum, bit-wise inverse of frame length
 + * hardware extension header - 8 bytes
 + * Tx glom mode only, N/A for Rx or normal Tx
 + * Byte 0~1: Packet length excluding hw frame tag
 + * Byte 2: Reserved
 + * Byte 3: Frame flags, bit 0: last frame indication
 + * Byte 4~5: Reserved
 + * Byte 6~7: Tail padding length
 + * software header - 8 bytes
 + * Byte 0: Rx/Tx sequence number
 + * Byte 1: 4 MSB Channel number, 4 LSB arbitrary flag
 + * Byte 2: Length of next data frame, reserved for Tx
 + * Byte 3: Data offset
 + * Byte 4: Flow control bits, reserved for Tx
 + * Byte 5: Maximum Sequence number allowed by firmware for Tx, N/A for Tx packet
 + * Byte 6~7: Reserved
 + */
 +#define SDPCM_HWHDR_LEN                       4
 +#define SDPCM_HWEXT_LEN                       8
 +#define SDPCM_SWHDR_LEN                       8
 +#define SDPCM_HDRLEN                  (SDPCM_HWHDR_LEN + SDPCM_SWHDR_LEN)
 +/* software header */
 +#define SDPCM_SEQ_MASK                        0x000000ff
 +#define SDPCM_SEQ_WRAP                        256
 +#define SDPCM_CHANNEL_MASK            0x00000f00
 +#define SDPCM_CHANNEL_SHIFT           8
 +#define SDPCM_CONTROL_CHANNEL         0       /* Control */
 +#define SDPCM_EVENT_CHANNEL           1       /* Asyc Event Indication */
 +#define SDPCM_DATA_CHANNEL            2       /* Data Xmit/Recv */
 +#define SDPCM_GLOM_CHANNEL            3       /* Coalesced packets */
 +#define SDPCM_TEST_CHANNEL            15      /* Test/debug packets */
 +#define SDPCM_GLOMDESC(p)             (((u8 *)p)[1] & 0x80)
 +#define SDPCM_NEXTLEN_MASK            0x00ff0000
 +#define SDPCM_NEXTLEN_SHIFT           16
 +#define SDPCM_DOFFSET_MASK            0xff000000
 +#define SDPCM_DOFFSET_SHIFT           24
 +#define SDPCM_FCMASK_MASK             0x000000ff
 +#define SDPCM_WINDOW_MASK             0x0000ff00
 +#define SDPCM_WINDOW_SHIFT            8
 +
 +static inline u8 brcmf_sdio_getdatoffset(u8 *swheader)
 +{
 +      u32 hdrvalue;
 +      hdrvalue = *(u32 *)swheader;
 +      return (u8)((hdrvalue & SDPCM_DOFFSET_MASK) >> SDPCM_DOFFSET_SHIFT);
 +}
 +
 +static int brcmf_sdio_hdparse(struct brcmf_sdio *bus, u8 *header,
 +                            struct brcmf_sdio_hdrinfo *rd,
 +                            enum brcmf_sdio_frmtype type)
 +{
 +      u16 len, checksum;
 +      u8 rx_seq, fc, tx_seq_max;
 +      u32 swheader;
 +
 +      trace_brcmf_sdpcm_hdr(SDPCM_RX, header);
 +
 +      /* hw header */
 +      len = get_unaligned_le16(header);
 +      checksum = get_unaligned_le16(header + sizeof(u16));
 +      /* All zero means no more to read */
 +      if (!(len | checksum)) {
 +              bus->rxpending = false;
 +              return -ENODATA;
 +      }
 +      if ((u16)(~(len ^ checksum))) {
 +              brcmf_err("HW header checksum error\n");
 +              bus->sdcnt.rx_badhdr++;
 +              brcmf_sdio_rxfail(bus, false, false);
 +              return -EIO;
 +      }
 +      if (len < SDPCM_HDRLEN) {
 +              brcmf_err("HW header length error\n");
 +              return -EPROTO;
 +      }
 +      if (type == BRCMF_SDIO_FT_SUPER &&
 +          (roundup(len, bus->blocksize) != rd->len)) {
 +              brcmf_err("HW superframe header length error\n");
 +              return -EPROTO;
 +      }
 +      if (type == BRCMF_SDIO_FT_SUB && len > rd->len) {
 +              brcmf_err("HW subframe header length error\n");
 +              return -EPROTO;
 +      }
 +      rd->len = len;
 +
 +      /* software header */
 +      header += SDPCM_HWHDR_LEN;
 +      swheader = le32_to_cpu(*(__le32 *)header);
 +      if (type == BRCMF_SDIO_FT_SUPER && SDPCM_GLOMDESC(header)) {
 +              brcmf_err("Glom descriptor found in superframe head\n");
 +              rd->len = 0;
 +              return -EINVAL;
 +      }
 +      rx_seq = (u8)(swheader & SDPCM_SEQ_MASK);
 +      rd->channel = (swheader & SDPCM_CHANNEL_MASK) >> SDPCM_CHANNEL_SHIFT;
 +      if (len > MAX_RX_DATASZ && rd->channel != SDPCM_CONTROL_CHANNEL &&
 +          type != BRCMF_SDIO_FT_SUPER) {
 +              brcmf_err("HW header length too long\n");
 +              bus->sdcnt.rx_toolong++;
 +              brcmf_sdio_rxfail(bus, false, false);
 +              rd->len = 0;
 +              return -EPROTO;
 +      }
 +      if (type == BRCMF_SDIO_FT_SUPER && rd->channel != SDPCM_GLOM_CHANNEL) {
 +              brcmf_err("Wrong channel for superframe\n");
 +              rd->len = 0;
 +              return -EINVAL;
 +      }
 +      if (type == BRCMF_SDIO_FT_SUB && rd->channel != SDPCM_DATA_CHANNEL &&
 +          rd->channel != SDPCM_EVENT_CHANNEL) {
 +              brcmf_err("Wrong channel for subframe\n");
 +              rd->len = 0;
 +              return -EINVAL;
 +      }
 +      rd->dat_offset = brcmf_sdio_getdatoffset(header);
 +      if (rd->dat_offset < SDPCM_HDRLEN || rd->dat_offset > rd->len) {
 +              brcmf_err("seq %d: bad data offset\n", rx_seq);
 +              bus->sdcnt.rx_badhdr++;
 +              brcmf_sdio_rxfail(bus, false, false);
 +              rd->len = 0;
 +              return -ENXIO;
 +      }
 +      if (rd->seq_num != rx_seq) {
 +              brcmf_err("seq %d: sequence number error, expect %d\n",
 +                        rx_seq, rd->seq_num);
 +              bus->sdcnt.rx_badseq++;
 +              rd->seq_num = rx_seq;
 +      }
 +      /* no need to check the reset for subframe */
 +      if (type == BRCMF_SDIO_FT_SUB)
 +              return 0;
 +      rd->len_nxtfrm = (swheader & SDPCM_NEXTLEN_MASK) >> SDPCM_NEXTLEN_SHIFT;
 +      if (rd->len_nxtfrm << 4 > MAX_RX_DATASZ) {
 +              /* only warm for NON glom packet */
 +              if (rd->channel != SDPCM_GLOM_CHANNEL)
 +                      brcmf_err("seq %d: next length error\n", rx_seq);
 +              rd->len_nxtfrm = 0;
 +      }
 +      swheader = le32_to_cpu(*(__le32 *)(header + 4));
 +      fc = swheader & SDPCM_FCMASK_MASK;
 +      if (bus->flowcontrol != fc) {
 +              if (~bus->flowcontrol & fc)
 +                      bus->sdcnt.fc_xoff++;
 +              if (bus->flowcontrol & ~fc)
 +                      bus->sdcnt.fc_xon++;
 +              bus->sdcnt.fc_rcvd++;
 +              bus->flowcontrol = fc;
 +      }
 +      tx_seq_max = (swheader & SDPCM_WINDOW_MASK) >> SDPCM_WINDOW_SHIFT;
 +      if ((u8)(tx_seq_max - bus->tx_seq) > 0x40) {
 +              brcmf_err("seq %d: max tx seq number error\n", rx_seq);
 +              tx_seq_max = bus->tx_seq + 2;
 +      }
 +      bus->tx_max = tx_seq_max;
 +
 +      return 0;
 +}
 +
 +static inline void brcmf_sdio_update_hwhdr(u8 *header, u16 frm_length)
 +{
 +      *(__le16 *)header = cpu_to_le16(frm_length);
 +      *(((__le16 *)header) + 1) = cpu_to_le16(~frm_length);
 +}
 +
 +static void brcmf_sdio_hdpack(struct brcmf_sdio *bus, u8 *header,
 +                            struct brcmf_sdio_hdrinfo *hd_info)
 +{
 +      u32 hdrval;
 +      u8 hdr_offset;
 +
 +      brcmf_sdio_update_hwhdr(header, hd_info->len);
 +      hdr_offset = SDPCM_HWHDR_LEN;
 +
 +      if (bus->txglom) {
 +              hdrval = (hd_info->len - hdr_offset) | (hd_info->lastfrm << 24);
 +              *((__le32 *)(header + hdr_offset)) = cpu_to_le32(hdrval);
 +              hdrval = (u16)hd_info->tail_pad << 16;
 +              *(((__le32 *)(header + hdr_offset)) + 1) = cpu_to_le32(hdrval);
 +              hdr_offset += SDPCM_HWEXT_LEN;
 +      }
 +
 +      hdrval = hd_info->seq_num;
 +      hdrval |= (hd_info->channel << SDPCM_CHANNEL_SHIFT) &
 +                SDPCM_CHANNEL_MASK;
 +      hdrval |= (hd_info->dat_offset << SDPCM_DOFFSET_SHIFT) &
 +                SDPCM_DOFFSET_MASK;
 +      *((__le32 *)(header + hdr_offset)) = cpu_to_le32(hdrval);
 +      *(((__le32 *)(header + hdr_offset)) + 1) = 0;
 +      trace_brcmf_sdpcm_hdr(SDPCM_TX + !!(bus->txglom), header);
 +}
 +
 +static u8 brcmf_sdio_rxglom(struct brcmf_sdio *bus, u8 rxseq)
 +{
 +      u16 dlen, totlen;
 +      u8 *dptr, num = 0;
 +      u16 sublen;
 +      struct sk_buff *pfirst, *pnext;
 +
 +      int errcode;
 +      u8 doff, sfdoff;
 +
 +      struct brcmf_sdio_hdrinfo rd_new;
 +
 +      /* If packets, issue read(s) and send up packet chain */
 +      /* Return sequence numbers consumed? */
 +
 +      brcmf_dbg(SDIO, "start: glomd %p glom %p\n",
 +                bus->glomd, skb_peek(&bus->glom));
 +
 +      /* If there's a descriptor, generate the packet chain */
 +      if (bus->glomd) {
 +              pfirst = pnext = NULL;
 +              dlen = (u16) (bus->glomd->len);
 +              dptr = bus->glomd->data;
 +              if (!dlen || (dlen & 1)) {
 +                      brcmf_err("bad glomd len(%d), ignore descriptor\n",
 +                                dlen);
 +                      dlen = 0;
 +              }
 +
 +              for (totlen = num = 0; dlen; num++) {
 +                      /* Get (and move past) next length */
 +                      sublen = get_unaligned_le16(dptr);
 +                      dlen -= sizeof(u16);
 +                      dptr += sizeof(u16);
 +                      if ((sublen < SDPCM_HDRLEN) ||
 +                          ((num == 0) && (sublen < (2 * SDPCM_HDRLEN)))) {
 +                              brcmf_err("descriptor len %d bad: %d\n",
 +                                        num, sublen);
 +                              pnext = NULL;
 +                              break;
 +                      }
 +                      if (sublen % bus->sgentry_align) {
 +                              brcmf_err("sublen %d not multiple of %d\n",
 +                                        sublen, bus->sgentry_align);
 +                      }
 +                      totlen += sublen;
 +
 +                      /* For last frame, adjust read len so total
 +                               is a block multiple */
 +                      if (!dlen) {
 +                              sublen +=
 +                                  (roundup(totlen, bus->blocksize) - totlen);
 +                              totlen = roundup(totlen, bus->blocksize);
 +                      }
 +
 +                      /* Allocate/chain packet for next subframe */
 +                      pnext = brcmu_pkt_buf_get_skb(sublen + bus->sgentry_align);
 +                      if (pnext == NULL) {
 +                              brcmf_err("bcm_pkt_buf_get_skb failed, num %d len %d\n",
 +                                        num, sublen);
 +                              break;
 +                      }
 +                      skb_queue_tail(&bus->glom, pnext);
 +
 +                      /* Adhere to start alignment requirements */
 +                      pkt_align(pnext, sublen, bus->sgentry_align);
 +              }
 +
 +              /* If all allocations succeeded, save packet chain
 +                       in bus structure */
 +              if (pnext) {
 +                      brcmf_dbg(GLOM, "allocated %d-byte packet chain for %d subframes\n",
 +                                totlen, num);
 +                      if (BRCMF_GLOM_ON() && bus->cur_read.len &&
 +                          totlen != bus->cur_read.len) {
 +                              brcmf_dbg(GLOM, "glomdesc mismatch: nextlen %d glomdesc %d rxseq %d\n",
 +                                        bus->cur_read.len, totlen, rxseq);
 +                      }
 +                      pfirst = pnext = NULL;
 +              } else {
 +                      brcmf_sdio_free_glom(bus);
 +                      num = 0;
 +              }
 +
 +              /* Done with descriptor packet */
 +              brcmu_pkt_buf_free_skb(bus->glomd);
 +              bus->glomd = NULL;
 +              bus->cur_read.len = 0;
 +      }
 +
 +      /* Ok -- either we just generated a packet chain,
 +               or had one from before */
 +      if (!skb_queue_empty(&bus->glom)) {
 +              if (BRCMF_GLOM_ON()) {
 +                      brcmf_dbg(GLOM, "try superframe read, packet chain:\n");
 +                      skb_queue_walk(&bus->glom, pnext) {
 +                              brcmf_dbg(GLOM, "    %p: %p len 0x%04x (%d)\n",
 +                                        pnext, (u8 *) (pnext->data),
 +                                        pnext->len, pnext->len);
 +                      }
 +              }
 +
 +              pfirst = skb_peek(&bus->glom);
 +              dlen = (u16) brcmf_sdio_glom_len(bus);
 +
 +              /* Do an SDIO read for the superframe.  Configurable iovar to
 +               * read directly into the chained packet, or allocate a large
 +               * packet and and copy into the chain.
 +               */
 +              sdio_claim_host(bus->sdiodev->func[1]);
 +              errcode = brcmf_sdiod_recv_chain(bus->sdiodev,
 +                                               &bus->glom, dlen);
 +              sdio_release_host(bus->sdiodev->func[1]);
 +              bus->sdcnt.f2rxdata++;
 +
 +              /* On failure, kill the superframe, allow a couple retries */
 +              if (errcode < 0) {
 +                      brcmf_err("glom read of %d bytes failed: %d\n",
 +                                dlen, errcode);
 +
 +                      sdio_claim_host(bus->sdiodev->func[1]);
 +                      if (bus->glomerr++ < 3) {
 +                              brcmf_sdio_rxfail(bus, true, true);
 +                      } else {
 +                              bus->glomerr = 0;
 +                              brcmf_sdio_rxfail(bus, true, false);
 +                              bus->sdcnt.rxglomfail++;
 +                              brcmf_sdio_free_glom(bus);
 +                      }
 +                      sdio_release_host(bus->sdiodev->func[1]);
 +                      return 0;
 +              }
 +
 +              brcmf_dbg_hex_dump(BRCMF_GLOM_ON(),
 +                                 pfirst->data, min_t(int, pfirst->len, 48),
 +                                 "SUPERFRAME:\n");
 +
 +              rd_new.seq_num = rxseq;
 +              rd_new.len = dlen;
 +              sdio_claim_host(bus->sdiodev->func[1]);
 +              errcode = brcmf_sdio_hdparse(bus, pfirst->data, &rd_new,
 +                                           BRCMF_SDIO_FT_SUPER);
 +              sdio_release_host(bus->sdiodev->func[1]);
 +              bus->cur_read.len = rd_new.len_nxtfrm << 4;
 +
 +              /* Remove superframe header, remember offset */
 +              skb_pull(pfirst, rd_new.dat_offset);
 +              sfdoff = rd_new.dat_offset;
 +              num = 0;
 +
 +              /* Validate all the subframe headers */
 +              skb_queue_walk(&bus->glom, pnext) {
 +                      /* leave when invalid subframe is found */
 +                      if (errcode)
 +                              break;
 +
 +                      rd_new.len = pnext->len;
 +                      rd_new.seq_num = rxseq++;
 +                      sdio_claim_host(bus->sdiodev->func[1]);
 +                      errcode = brcmf_sdio_hdparse(bus, pnext->data, &rd_new,
 +                                                   BRCMF_SDIO_FT_SUB);
 +                      sdio_release_host(bus->sdiodev->func[1]);
 +                      brcmf_dbg_hex_dump(BRCMF_GLOM_ON(),
 +                                         pnext->data, 32, "subframe:\n");
 +
 +                      num++;
 +              }
 +
 +              if (errcode) {
 +                      /* Terminate frame on error, request
 +                               a couple retries */
 +                      sdio_claim_host(bus->sdiodev->func[1]);
 +                      if (bus->glomerr++ < 3) {
 +                              /* Restore superframe header space */
 +                              skb_push(pfirst, sfdoff);
 +                              brcmf_sdio_rxfail(bus, true, true);
 +                      } else {
 +                              bus->glomerr = 0;
 +                              brcmf_sdio_rxfail(bus, true, false);
 +                              bus->sdcnt.rxglomfail++;
 +                              brcmf_sdio_free_glom(bus);
 +                      }
 +                      sdio_release_host(bus->sdiodev->func[1]);
 +                      bus->cur_read.len = 0;
 +                      return 0;
 +              }
 +
 +              /* Basic SD framing looks ok - process each packet (header) */
 +
 +              skb_queue_walk_safe(&bus->glom, pfirst, pnext) {
 +                      dptr = (u8 *) (pfirst->data);
 +                      sublen = get_unaligned_le16(dptr);
 +                      doff = brcmf_sdio_getdatoffset(&dptr[SDPCM_HWHDR_LEN]);
 +
 +                      brcmf_dbg_hex_dump(BRCMF_BYTES_ON() && BRCMF_DATA_ON(),
 +                                         dptr, pfirst->len,
 +                                         "Rx Subframe Data:\n");
 +
 +                      __skb_trim(pfirst, sublen);
 +                      skb_pull(pfirst, doff);
 +
 +                      if (pfirst->len == 0) {
 +                              skb_unlink(pfirst, &bus->glom);
 +                              brcmu_pkt_buf_free_skb(pfirst);
 +                              continue;
 +                      }
 +
 +                      brcmf_dbg_hex_dump(BRCMF_GLOM_ON(),
 +                                         pfirst->data,
 +                                         min_t(int, pfirst->len, 32),
 +                                         "subframe %d to stack, %p (%p/%d) nxt/lnk %p/%p\n",
 +                                         bus->glom.qlen, pfirst, pfirst->data,
 +                                         pfirst->len, pfirst->next,
 +                                         pfirst->prev);
 +                      skb_unlink(pfirst, &bus->glom);
 +                      brcmf_rx_frame(bus->sdiodev->dev, pfirst);
 +                      bus->sdcnt.rxglompkts++;
 +              }
 +
 +              bus->sdcnt.rxglomframes++;
 +      }
 +      return num;
 +}
 +
 +static int brcmf_sdio_dcmd_resp_wait(struct brcmf_sdio *bus, uint *condition,
 +                                   bool *pending)
 +{
 +      DECLARE_WAITQUEUE(wait, current);
 +      int timeout = msecs_to_jiffies(DCMD_RESP_TIMEOUT);
 +
 +      /* Wait until control frame is available */
 +      add_wait_queue(&bus->dcmd_resp_wait, &wait);
 +      set_current_state(TASK_INTERRUPTIBLE);
 +
 +      while (!(*condition) && (!signal_pending(current) && timeout))
 +              timeout = schedule_timeout(timeout);
 +
 +      if (signal_pending(current))
 +              *pending = true;
 +
 +      set_current_state(TASK_RUNNING);
 +      remove_wait_queue(&bus->dcmd_resp_wait, &wait);
 +
 +      return timeout;
 +}
 +
 +static int brcmf_sdio_dcmd_resp_wake(struct brcmf_sdio *bus)
 +{
 +      if (waitqueue_active(&bus->dcmd_resp_wait))
 +              wake_up_interruptible(&bus->dcmd_resp_wait);
 +
 +      return 0;
 +}
 +static void
 +brcmf_sdio_read_control(struct brcmf_sdio *bus, u8 *hdr, uint len, uint doff)
 +{
 +      uint rdlen, pad;
 +      u8 *buf = NULL, *rbuf;
 +      int sdret;
 +
 +      brcmf_dbg(TRACE, "Enter\n");
 +
 +      if (bus->rxblen)
 +              buf = vzalloc(bus->rxblen);
 +      if (!buf)
 +              goto done;
 +
 +      rbuf = bus->rxbuf;
 +      pad = ((unsigned long)rbuf % bus->head_align);
 +      if (pad)
 +              rbuf += (bus->head_align - pad);
 +
 +      /* Copy the already-read portion over */
 +      memcpy(buf, hdr, BRCMF_FIRSTREAD);
 +      if (len <= BRCMF_FIRSTREAD)
 +              goto gotpkt;
 +
 +      /* Raise rdlen to next SDIO block to avoid tail command */
 +      rdlen = len - BRCMF_FIRSTREAD;
 +      if (bus->roundup && bus->blocksize && (rdlen > bus->blocksize)) {
 +              pad = bus->blocksize - (rdlen % bus->blocksize);
 +              if ((pad <= bus->roundup) && (pad < bus->blocksize) &&
 +                  ((len + pad) < bus->sdiodev->bus_if->maxctl))
 +                      rdlen += pad;
 +      } else if (rdlen % bus->head_align) {
 +              rdlen += bus->head_align - (rdlen % bus->head_align);
 +      }
 +
 +      /* Drop if the read is too big or it exceeds our maximum */
 +      if ((rdlen + BRCMF_FIRSTREAD) > bus->sdiodev->bus_if->maxctl) {
 +              brcmf_err("%d-byte control read exceeds %d-byte buffer\n",
 +                        rdlen, bus->sdiodev->bus_if->maxctl);
 +              brcmf_sdio_rxfail(bus, false, false);
 +              goto done;
 +      }
 +
 +      if ((len - doff) > bus->sdiodev->bus_if->maxctl) {
 +              brcmf_err("%d-byte ctl frame (%d-byte ctl data) exceeds %d-byte limit\n",
 +                        len, len - doff, bus->sdiodev->bus_if->maxctl);
 +              bus->sdcnt.rx_toolong++;
 +              brcmf_sdio_rxfail(bus, false, false);
 +              goto done;
 +      }
 +
 +      /* Read remain of frame body */
 +      sdret = brcmf_sdiod_recv_buf(bus->sdiodev, rbuf, rdlen);
 +      bus->sdcnt.f2rxdata++;
 +
 +      /* Control frame failures need retransmission */
 +      if (sdret < 0) {
 +              brcmf_err("read %d control bytes failed: %d\n",
 +                        rdlen, sdret);
 +              bus->sdcnt.rxc_errors++;
 +              brcmf_sdio_rxfail(bus, true, true);
 +              goto done;
 +      } else
 +              memcpy(buf + BRCMF_FIRSTREAD, rbuf, rdlen);
 +
 +gotpkt:
 +
 +      brcmf_dbg_hex_dump(BRCMF_BYTES_ON() && BRCMF_CTL_ON(),
 +                         buf, len, "RxCtrl:\n");
 +
 +      /* Point to valid data and indicate its length */
 +      spin_lock_bh(&bus->rxctl_lock);
 +      if (bus->rxctl) {
 +              brcmf_err("last control frame is being processed.\n");
 +              spin_unlock_bh(&bus->rxctl_lock);
 +              vfree(buf);
 +              goto done;
 +      }
 +      bus->rxctl = buf + doff;
 +      bus->rxctl_orig = buf;
 +      bus->rxlen = len - doff;
 +      spin_unlock_bh(&bus->rxctl_lock);
 +
 +done:
 +      /* Awake any waiters */
 +      brcmf_sdio_dcmd_resp_wake(bus);
 +}
 +
 +/* Pad read to blocksize for efficiency */
 +static void brcmf_sdio_pad(struct brcmf_sdio *bus, u16 *pad, u16 *rdlen)
 +{
 +      if (bus->roundup && bus->blocksize && *rdlen > bus->blocksize) {
 +              *pad = bus->blocksize - (*rdlen % bus->blocksize);
 +              if (*pad <= bus->roundup && *pad < bus->blocksize &&
 +                  *rdlen + *pad + BRCMF_FIRSTREAD < MAX_RX_DATASZ)
 +                      *rdlen += *pad;
 +      } else if (*rdlen % bus->head_align) {
 +              *rdlen += bus->head_align - (*rdlen % bus->head_align);
 +      }
 +}
 +
 +static uint brcmf_sdio_readframes(struct brcmf_sdio *bus, uint maxframes)
 +{
 +      struct sk_buff *pkt;            /* Packet for event or data frames */
 +      u16 pad;                /* Number of pad bytes to read */
 +      uint rxleft = 0;        /* Remaining number of frames allowed */
 +      int ret;                /* Return code from calls */
 +      uint rxcount = 0;       /* Total frames read */
 +      struct brcmf_sdio_hdrinfo *rd = &bus->cur_read, rd_new;
 +      u8 head_read = 0;
 +
 +      brcmf_dbg(TRACE, "Enter\n");
 +
 +      /* Not finished unless we encounter no more frames indication */
 +      bus->rxpending = true;
 +
 +      for (rd->seq_num = bus->rx_seq, rxleft = maxframes;
 +           !bus->rxskip && rxleft && brcmf_bus_ready(bus->sdiodev->bus_if);
 +           rd->seq_num++, rxleft--) {
 +
 +              /* Handle glomming separately */
 +              if (bus->glomd || !skb_queue_empty(&bus->glom)) {
 +                      u8 cnt;
 +                      brcmf_dbg(GLOM, "calling rxglom: glomd %p, glom %p\n",
 +                                bus->glomd, skb_peek(&bus->glom));
 +                      cnt = brcmf_sdio_rxglom(bus, rd->seq_num);
 +                      brcmf_dbg(GLOM, "rxglom returned %d\n", cnt);
 +                      rd->seq_num += cnt - 1;
 +                      rxleft = (rxleft > cnt) ? (rxleft - cnt) : 1;
 +                      continue;
 +              }
 +
 +              rd->len_left = rd->len;
 +              /* read header first for unknow frame length */
 +              sdio_claim_host(bus->sdiodev->func[1]);
 +              if (!rd->len) {
 +                      ret = brcmf_sdiod_recv_buf(bus->sdiodev,
 +                                                 bus->rxhdr, BRCMF_FIRSTREAD);
 +                      bus->sdcnt.f2rxhdrs++;
 +                      if (ret < 0) {
 +                              brcmf_err("RXHEADER FAILED: %d\n",
 +                                        ret);
 +                              bus->sdcnt.rx_hdrfail++;
 +                              brcmf_sdio_rxfail(bus, true, true);
 +                              sdio_release_host(bus->sdiodev->func[1]);
 +                              continue;
 +                      }
 +
 +                      brcmf_dbg_hex_dump(BRCMF_BYTES_ON() || BRCMF_HDRS_ON(),
 +                                         bus->rxhdr, SDPCM_HDRLEN,
 +                                         "RxHdr:\n");
 +
 +                      if (brcmf_sdio_hdparse(bus, bus->rxhdr, rd,
 +                                             BRCMF_SDIO_FT_NORMAL)) {
 +                              sdio_release_host(bus->sdiodev->func[1]);
 +                              if (!bus->rxpending)
 +                                      break;
 +                              else
 +                                      continue;
 +                      }
 +
 +                      if (rd->channel == SDPCM_CONTROL_CHANNEL) {
 +                              brcmf_sdio_read_control(bus, bus->rxhdr,
 +                                                      rd->len,
 +                                                      rd->dat_offset);
 +                              /* prepare the descriptor for the next read */
 +                              rd->len = rd->len_nxtfrm << 4;
 +                              rd->len_nxtfrm = 0;
 +                              /* treat all packet as event if we don't know */
 +                              rd->channel = SDPCM_EVENT_CHANNEL;
 +                              sdio_release_host(bus->sdiodev->func[1]);
 +                              continue;
 +                      }
 +                      rd->len_left = rd->len > BRCMF_FIRSTREAD ?
 +                                     rd->len - BRCMF_FIRSTREAD : 0;
 +                      head_read = BRCMF_FIRSTREAD;
 +              }
 +
 +              brcmf_sdio_pad(bus, &pad, &rd->len_left);
 +
 +              pkt = brcmu_pkt_buf_get_skb(rd->len_left + head_read +
 +                                          bus->head_align);
 +              if (!pkt) {
 +                      /* Give up on data, request rtx of events */
 +                      brcmf_err("brcmu_pkt_buf_get_skb failed\n");
 +                      brcmf_sdio_rxfail(bus, false,
 +                                          RETRYCHAN(rd->channel));
 +                      sdio_release_host(bus->sdiodev->func[1]);
 +                      continue;
 +              }
 +              skb_pull(pkt, head_read);
 +              pkt_align(pkt, rd->len_left, bus->head_align);
 +
 +              ret = brcmf_sdiod_recv_pkt(bus->sdiodev, pkt);
 +              bus->sdcnt.f2rxdata++;
 +              sdio_release_host(bus->sdiodev->func[1]);
 +
 +              if (ret < 0) {
 +                      brcmf_err("read %d bytes from channel %d failed: %d\n",
 +                                rd->len, rd->channel, ret);
 +                      brcmu_pkt_buf_free_skb(pkt);
 +                      sdio_claim_host(bus->sdiodev->func[1]);
 +                      brcmf_sdio_rxfail(bus, true,
 +                                          RETRYCHAN(rd->channel));
 +                      sdio_release_host(bus->sdiodev->func[1]);
 +                      continue;
 +              }
 +
 +              if (head_read) {
 +                      skb_push(pkt, head_read);
 +                      memcpy(pkt->data, bus->rxhdr, head_read);
 +                      head_read = 0;
 +              } else {
 +                      memcpy(bus->rxhdr, pkt->data, SDPCM_HDRLEN);
 +                      rd_new.seq_num = rd->seq_num;
 +                      sdio_claim_host(bus->sdiodev->func[1]);
 +                      if (brcmf_sdio_hdparse(bus, bus->rxhdr, &rd_new,
 +                                             BRCMF_SDIO_FT_NORMAL)) {
 +                              rd->len = 0;
 +                              brcmu_pkt_buf_free_skb(pkt);
 +                      }
 +                      bus->sdcnt.rx_readahead_cnt++;
 +                      if (rd->len != roundup(rd_new.len, 16)) {
 +                              brcmf_err("frame length mismatch:read %d, should be %d\n",
 +                                        rd->len,
 +                                        roundup(rd_new.len, 16) >> 4);
 +                              rd->len = 0;
 +                              brcmf_sdio_rxfail(bus, true, true);
 +                              sdio_release_host(bus->sdiodev->func[1]);
 +                              brcmu_pkt_buf_free_skb(pkt);
 +                              continue;
 +                      }
 +                      sdio_release_host(bus->sdiodev->func[1]);
 +                      rd->len_nxtfrm = rd_new.len_nxtfrm;
 +                      rd->channel = rd_new.channel;
 +                      rd->dat_offset = rd_new.dat_offset;
 +
 +                      brcmf_dbg_hex_dump(!(BRCMF_BYTES_ON() &&
 +                                           BRCMF_DATA_ON()) &&
 +                                         BRCMF_HDRS_ON(),
 +                                         bus->rxhdr, SDPCM_HDRLEN,
 +                                         "RxHdr:\n");
 +
 +                      if (rd_new.channel == SDPCM_CONTROL_CHANNEL) {
 +                              brcmf_err("readahead on control packet %d?\n",
 +                                        rd_new.seq_num);
 +                              /* Force retry w/normal header read */
 +                              rd->len = 0;
 +                              sdio_claim_host(bus->sdiodev->func[1]);
 +                              brcmf_sdio_rxfail(bus, false, true);
 +                              sdio_release_host(bus->sdiodev->func[1]);
 +                              brcmu_pkt_buf_free_skb(pkt);
 +                              continue;
 +                      }
 +              }
 +
 +              brcmf_dbg_hex_dump(BRCMF_BYTES_ON() && BRCMF_DATA_ON(),
 +                                 pkt->data, rd->len, "Rx Data:\n");
 +
 +              /* Save superframe descriptor and allocate packet frame */
 +              if (rd->channel == SDPCM_GLOM_CHANNEL) {
 +                      if (SDPCM_GLOMDESC(&bus->rxhdr[SDPCM_HWHDR_LEN])) {
 +                              brcmf_dbg(GLOM, "glom descriptor, %d bytes:\n",
 +                                        rd->len);
 +                              brcmf_dbg_hex_dump(BRCMF_GLOM_ON(),
 +                                                 pkt->data, rd->len,
 +                                                 "Glom Data:\n");
 +                              __skb_trim(pkt, rd->len);
 +                              skb_pull(pkt, SDPCM_HDRLEN);
 +                              bus->glomd = pkt;
 +                      } else {
 +                              brcmf_err("%s: glom superframe w/o "
 +                                        "descriptor!\n", __func__);
 +                              sdio_claim_host(bus->sdiodev->func[1]);
 +                              brcmf_sdio_rxfail(bus, false, false);
 +                              sdio_release_host(bus->sdiodev->func[1]);
 +                      }
 +                      /* prepare the descriptor for the next read */
 +                      rd->len = rd->len_nxtfrm << 4;
 +                      rd->len_nxtfrm = 0;
 +                      /* treat all packet as event if we don't know */
 +                      rd->channel = SDPCM_EVENT_CHANNEL;
 +                      continue;
 +              }
 +
 +              /* Fill in packet len and prio, deliver upward */
 +              __skb_trim(pkt, rd->len);
 +              skb_pull(pkt, rd->dat_offset);
 +
 +              /* prepare the descriptor for the next read */
 +              rd->len = rd->len_nxtfrm << 4;
 +              rd->len_nxtfrm = 0;
 +              /* treat all packet as event if we don't know */
 +              rd->channel = SDPCM_EVENT_CHANNEL;
 +
 +              if (pkt->len == 0) {
 +                      brcmu_pkt_buf_free_skb(pkt);
 +                      continue;
 +              }
 +
 +              brcmf_rx_frame(bus->sdiodev->dev, pkt);
 +      }
 +
 +      rxcount = maxframes - rxleft;
 +      /* Message if we hit the limit */
 +      if (!rxleft)
 +              brcmf_dbg(DATA, "hit rx limit of %d frames\n", maxframes);
 +      else
 +              brcmf_dbg(DATA, "processed %d frames\n", rxcount);
 +      /* Back off rxseq if awaiting rtx, update rx_seq */
 +      if (bus->rxskip)
 +              rd->seq_num--;
 +      bus->rx_seq = rd->seq_num;
 +
 +      return rxcount;
 +}
 +
 +static void
 +brcmf_sdio_wait_event_wakeup(struct brcmf_sdio *bus)
 +{
 +      if (waitqueue_active(&bus->ctrl_wait))
 +              wake_up_interruptible(&bus->ctrl_wait);
 +      return;
 +}
 +
 +static int brcmf_sdio_txpkt_hdalign(struct brcmf_sdio *bus, struct sk_buff *pkt)
 +{
 +      u16 head_pad;
 +      u8 *dat_buf;
 +
 +      dat_buf = (u8 *)(pkt->data);
 +
 +      /* Check head padding */
 +      head_pad = ((unsigned long)dat_buf % bus->head_align);
 +      if (head_pad) {
 +              if (skb_headroom(pkt) < head_pad) {
 +                      bus->sdiodev->bus_if->tx_realloc++;
 +                      head_pad = 0;
 +                      if (skb_cow(pkt, head_pad))
 +                              return -ENOMEM;
 +              }
 +              skb_push(pkt, head_pad);
 +              dat_buf = (u8 *)(pkt->data);
 +              memset(dat_buf, 0, head_pad + bus->tx_hdrlen);
 +      }
 +      return head_pad;
 +}
 +
 +/**
 + * struct brcmf_skbuff_cb reserves first two bytes in sk_buff::cb for
 + * bus layer usage.
 + */
 +/* flag marking a dummy skb added for DMA alignment requirement */
 +#define ALIGN_SKB_FLAG                0x8000
 +/* bit mask of data length chopped from the previous packet */
 +#define ALIGN_SKB_CHOP_LEN_MASK       0x7fff
 +
 +static int brcmf_sdio_txpkt_prep_sg(struct brcmf_sdio *bus,
 +                                  struct sk_buff_head *pktq,
 +                                  struct sk_buff *pkt, u16 total_len)
 +{
 +      struct brcmf_sdio_dev *sdiodev;
 +      struct sk_buff *pkt_pad;
 +      u16 tail_pad, tail_chop, chain_pad;
 +      unsigned int blksize;
 +      bool lastfrm;
 +      int ntail, ret;
 +
 +      sdiodev = bus->sdiodev;
 +      blksize = sdiodev->func[SDIO_FUNC_2]->cur_blksize;
 +      /* sg entry alignment should be a divisor of block size */
 +      WARN_ON(blksize % bus->sgentry_align);
 +
 +      /* Check tail padding */
 +      lastfrm = skb_queue_is_last(pktq, pkt);
 +      tail_pad = 0;
 +      tail_chop = pkt->len % bus->sgentry_align;
 +      if (tail_chop)
 +              tail_pad = bus->sgentry_align - tail_chop;
 +      chain_pad = (total_len + tail_pad) % blksize;
 +      if (lastfrm && chain_pad)
 +              tail_pad += blksize - chain_pad;
 +      if (skb_tailroom(pkt) < tail_pad && pkt->len > blksize) {
 +              pkt_pad = brcmu_pkt_buf_get_skb(tail_pad + tail_chop +
 +                                              bus->head_align);
 +              if (pkt_pad == NULL)
 +                      return -ENOMEM;
 +              ret = brcmf_sdio_txpkt_hdalign(bus, pkt_pad);
 +              if (unlikely(ret < 0)) {
 +                      kfree_skb(pkt_pad);
 +                      return ret;
 +              }
 +              memcpy(pkt_pad->data,
 +                     pkt->data + pkt->len - tail_chop,
 +                     tail_chop);
 +              *(u16 *)(pkt_pad->cb) = ALIGN_SKB_FLAG + tail_chop;
 +              skb_trim(pkt, pkt->len - tail_chop);
 +              skb_trim(pkt_pad, tail_pad + tail_chop);
 +              __skb_queue_after(pktq, pkt, pkt_pad);
 +      } else {
 +              ntail = pkt->data_len + tail_pad -
 +                      (pkt->end - pkt->tail);
 +              if (skb_cloned(pkt) || ntail > 0)
 +                      if (pskb_expand_head(pkt, 0, ntail, GFP_ATOMIC))
 +                              return -ENOMEM;
 +              if (skb_linearize(pkt))
 +                      return -ENOMEM;
 +              __skb_put(pkt, tail_pad);
 +      }
 +
 +      return tail_pad;
 +}
 +
 +/**
 + * brcmf_sdio_txpkt_prep - packet preparation for transmit
 + * @bus: brcmf_sdio structure pointer
 + * @pktq: packet list pointer
 + * @chan: virtual channel to transmit the packet
 + *
 + * Processes to be applied to the packet
 + *    - Align data buffer pointer
 + *    - Align data buffer length
 + *    - Prepare header
 + * Return: negative value if there is error
 + */
 +static int
 +brcmf_sdio_txpkt_prep(struct brcmf_sdio *bus, struct sk_buff_head *pktq,
 +                    uint chan)
 +{
 +      u16 head_pad, total_len;
 +      struct sk_buff *pkt_next;
 +      u8 txseq;
 +      int ret;
 +      struct brcmf_sdio_hdrinfo hd_info = {0};
 +
 +      txseq = bus->tx_seq;
 +      total_len = 0;
 +      skb_queue_walk(pktq, pkt_next) {
 +              /* alignment packet inserted in previous
 +               * loop cycle can be skipped as it is
 +               * already properly aligned and does not
 +               * need an sdpcm header.
 +               */
 +              if (*(u16 *)(pkt_next->cb) & ALIGN_SKB_FLAG)
 +                      continue;
 +
 +              /* align packet data pointer */
 +              ret = brcmf_sdio_txpkt_hdalign(bus, pkt_next);
 +              if (ret < 0)
 +                      return ret;
 +              head_pad = (u16)ret;
 +              if (head_pad)
 +                      memset(pkt_next->data + bus->tx_hdrlen, 0, head_pad);
 +
 +              total_len += pkt_next->len;
 +
 +              hd_info.len = pkt_next->len;
 +              hd_info.lastfrm = skb_queue_is_last(pktq, pkt_next);
 +              if (bus->txglom && pktq->qlen > 1) {
 +                      ret = brcmf_sdio_txpkt_prep_sg(bus, pktq,
 +                                                     pkt_next, total_len);
 +                      if (ret < 0)
 +                              return ret;
 +                      hd_info.tail_pad = (u16)ret;
 +                      total_len += (u16)ret;
 +              }
 +
 +              hd_info.channel = chan;
 +              hd_info.dat_offset = head_pad + bus->tx_hdrlen;
 +              hd_info.seq_num = txseq++;
 +
 +              /* Now fill the header */
 +              brcmf_sdio_hdpack(bus, pkt_next->data, &hd_info);
 +
 +              if (BRCMF_BYTES_ON() &&
 +                  ((BRCMF_CTL_ON() && chan == SDPCM_CONTROL_CHANNEL) ||
 +                   (BRCMF_DATA_ON() && chan != SDPCM_CONTROL_CHANNEL)))
 +                      brcmf_dbg_hex_dump(true, pkt_next->data, hd_info.len,
 +                                         "Tx Frame:\n");
 +              else if (BRCMF_HDRS_ON())
 +                      brcmf_dbg_hex_dump(true, pkt_next->data,
 +                                         head_pad + bus->tx_hdrlen,
 +                                         "Tx Header:\n");
 +      }
 +      /* Hardware length tag of the first packet should be total
 +       * length of the chain (including padding)
 +       */
 +      if (bus->txglom)
 +              brcmf_sdio_update_hwhdr(pktq->next->data, total_len);
 +      return 0;
 +}
 +
 +/**
 + * brcmf_sdio_txpkt_postp - packet post processing for transmit
 + * @bus: brcmf_sdio structure pointer
 + * @pktq: packet list pointer
 + *
 + * Processes to be applied to the packet
 + *    - Remove head padding
 + *    - Remove tail padding
 + */
 +static void
 +brcmf_sdio_txpkt_postp(struct brcmf_sdio *bus, struct sk_buff_head *pktq)
 +{
 +      u8 *hdr;
 +      u32 dat_offset;
 +      u16 tail_pad;
 +      u16 dummy_flags, chop_len;
 +      struct sk_buff *pkt_next, *tmp, *pkt_prev;
 +
 +      skb_queue_walk_safe(pktq, pkt_next, tmp) {
 +              dummy_flags = *(u16 *)(pkt_next->cb);
 +              if (dummy_flags & ALIGN_SKB_FLAG) {
 +                      chop_len = dummy_flags & ALIGN_SKB_CHOP_LEN_MASK;
 +                      if (chop_len) {
 +                              pkt_prev = pkt_next->prev;
 +                              skb_put(pkt_prev, chop_len);
 +                      }
 +                      __skb_unlink(pkt_next, pktq);
 +                      brcmu_pkt_buf_free_skb(pkt_next);
 +              } else {
 +                      hdr = pkt_next->data + bus->tx_hdrlen - SDPCM_SWHDR_LEN;
 +                      dat_offset = le32_to_cpu(*(__le32 *)hdr);
 +                      dat_offset = (dat_offset & SDPCM_DOFFSET_MASK) >>
 +                                   SDPCM_DOFFSET_SHIFT;
 +                      skb_pull(pkt_next, dat_offset);
 +                      if (bus->txglom) {
 +                              tail_pad = le16_to_cpu(*(__le16 *)(hdr - 2));
 +                              skb_trim(pkt_next, pkt_next->len - tail_pad);
 +                      }
 +              }
 +      }
 +}
 +
 +/* Writes a HW/SW header into the packet and sends it. */
 +/* Assumes: (a) header space already there, (b) caller holds lock */
 +static int brcmf_sdio_txpkt(struct brcmf_sdio *bus, struct sk_buff_head *pktq,
 +                          uint chan)
 +{
 +      int ret;
 +      struct sk_buff *pkt_next, *tmp;
 +
 +      brcmf_dbg(TRACE, "Enter\n");
 +
 +      ret = brcmf_sdio_txpkt_prep(bus, pktq, chan);
 +      if (ret)
 +              goto done;
 +
 +      sdio_claim_host(bus->sdiodev->func[1]);
 +      ret = brcmf_sdiod_send_pkt(bus->sdiodev, pktq);
 +      bus->sdcnt.f2txdata++;
 +
 +      if (ret < 0)
 +              brcmf_sdio_txfail(bus);
 +
 +      sdio_release_host(bus->sdiodev->func[1]);
 +
 +done:
 +      brcmf_sdio_txpkt_postp(bus, pktq);
 +      if (ret == 0)
 +              bus->tx_seq = (bus->tx_seq + pktq->qlen) % SDPCM_SEQ_WRAP;
 +      skb_queue_walk_safe(pktq, pkt_next, tmp) {
 +              __skb_unlink(pkt_next, pktq);
 +              brcmf_txcomplete(bus->sdiodev->dev, pkt_next, ret == 0);
 +      }
 +      return ret;
 +}
 +
 +static uint brcmf_sdio_sendfromq(struct brcmf_sdio *bus, uint maxframes)
 +{
 +      struct sk_buff *pkt;
 +      struct sk_buff_head pktq;
 +      u32 intstatus = 0;
 +      int ret = 0, prec_out, i;
 +      uint cnt = 0;
 +      u8 tx_prec_map, pkt_num;
 +
 +      brcmf_dbg(TRACE, "Enter\n");
 +
 +      tx_prec_map = ~bus->flowcontrol;
 +
 +      /* Send frames until the limit or some other event */
 +      for (cnt = 0; (cnt < maxframes) && data_ok(bus);) {
 +              pkt_num = 1;
 +              if (down_interruptible(&bus->tx_seq_lock))
 +                      return cnt;
 +              if (bus->txglom)
 +                      pkt_num = min_t(u8, bus->tx_max - bus->tx_seq,
 +                                      bus->sdiodev->txglomsz);
 +              pkt_num = min_t(u32, pkt_num,
 +                              brcmu_pktq_mlen(&bus->txq, ~bus->flowcontrol));
 +              __skb_queue_head_init(&pktq);
 +              spin_lock_bh(&bus->txq_lock);
 +              for (i = 0; i < pkt_num; i++) {
 +                      pkt = brcmu_pktq_mdeq(&bus->txq, tx_prec_map,
 +                                            &prec_out);
 +                      if (pkt == NULL)
 +                              break;
 +                      __skb_queue_tail(&pktq, pkt);
 +              }
 +              spin_unlock_bh(&bus->txq_lock);
 +              if (i == 0) {
 +                      up(&bus->tx_seq_lock);
 +                      break;
 +              }
 +
 +              ret = brcmf_sdio_txpkt(bus, &pktq, SDPCM_DATA_CHANNEL);
 +              up(&bus->tx_seq_lock);
 +
 +              cnt += i;
 +
 +              /* In poll mode, need to check for other events */
 +              if (!bus->intr) {
 +                      /* Check device status, signal pending interrupt */
 +                      sdio_claim_host(bus->sdiodev->func[1]);
 +                      ret = r_sdreg32(bus, &intstatus,
 +                                      offsetof(struct sdpcmd_regs,
 +                                               intstatus));
 +                      sdio_release_host(bus->sdiodev->func[1]);
 +                      bus->sdcnt.f2txdata++;
 +                      if (ret != 0)
 +                              break;
 +                      if (intstatus & bus->hostintmask)
 +                              atomic_set(&bus->ipend, 1);
 +              }
 +      }
 +
 +      /* Deflow-control stack if needed */
 +      if ((bus->sdiodev->bus_if->state == BRCMF_BUS_DATA) &&
 +          bus->txoff && (pktq_len(&bus->txq) < TXLOW)) {
 +              bus->txoff = false;
 +              brcmf_txflowblock(bus->sdiodev->dev, false);
 +      }
 +
 +      return cnt;
 +}
 +
 +static int brcmf_sdio_tx_ctrlframe(struct brcmf_sdio *bus, u8 *frame, u16 len)
 +{
 +      u8 doff;
 +      u16 pad;
 +      uint retries = 0;
 +      struct brcmf_sdio_hdrinfo hd_info = {0};
 +      int ret;
 +
 +      brcmf_dbg(TRACE, "Enter\n");
 +
 +      /* Back the pointer to make room for bus header */
 +      frame -= bus->tx_hdrlen;
 +      len += bus->tx_hdrlen;
 +
 +      /* Add alignment padding (optional for ctl frames) */
 +      doff = ((unsigned long)frame % bus->head_align);
 +      if (doff) {
 +              frame -= doff;
 +              len += doff;
 +              memset(frame + bus->tx_hdrlen, 0, doff);
 +      }
 +
 +      /* Round send length to next SDIO block */
 +      pad = 0;
 +      if (bus->roundup && bus->blocksize && (len > bus->blocksize)) {
 +              pad = bus->blocksize - (len % bus->blocksize);
 +              if ((pad > bus->roundup) || (pad >= bus->blocksize))
 +                      pad = 0;
 +      } else if (len % bus->head_align) {
 +              pad = bus->head_align - (len % bus->head_align);
 +      }
 +      len += pad;
 +
 +      hd_info.len = len - pad;
 +      hd_info.channel = SDPCM_CONTROL_CHANNEL;
 +      hd_info.dat_offset = doff + bus->tx_hdrlen;
 +      hd_info.seq_num = bus->tx_seq;
 +      hd_info.lastfrm = true;
 +      hd_info.tail_pad = pad;
 +      brcmf_sdio_hdpack(bus, frame, &hd_info);
 +
 +      if (bus->txglom)
 +              brcmf_sdio_update_hwhdr(frame, len);
 +
 +      brcmf_dbg_hex_dump(BRCMF_BYTES_ON() && BRCMF_CTL_ON(),
 +                         frame, len, "Tx Frame:\n");
 +      brcmf_dbg_hex_dump(!(BRCMF_BYTES_ON() && BRCMF_CTL_ON()) &&
 +                         BRCMF_HDRS_ON(),
 +                         frame, min_t(u16, len, 16), "TxHdr:\n");
 +
 +      do {
 +              ret = brcmf_sdiod_send_buf(bus->sdiodev, frame, len);
 +
 +              if (ret < 0)
 +                      brcmf_sdio_txfail(bus);
 +              else
 +                      bus->tx_seq = (bus->tx_seq + 1) % SDPCM_SEQ_WRAP;
 +      } while (ret < 0 && retries++ < TXRETRIES);
 +
 +      return ret;
 +}
 +
 +static void brcmf_sdio_bus_stop(struct device *dev)
 +{
 +      u32 local_hostintmask;
 +      u8 saveclk;
 +      int err;
 +      struct brcmf_bus *bus_if = dev_get_drvdata(dev);
 +      struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
 +      struct brcmf_sdio *bus = sdiodev->bus;
 +
 +      brcmf_dbg(TRACE, "Enter\n");
 +
 +      if (bus->watchdog_tsk) {
 +              send_sig(SIGTERM, bus->watchdog_tsk, 1);
 +              kthread_stop(bus->watchdog_tsk);
 +              bus->watchdog_tsk = NULL;
 +      }
 +
 +      if (bus_if->state == BRCMF_BUS_DOWN) {
 +              sdio_claim_host(sdiodev->func[1]);
 +
 +              /* Enable clock for device interrupts */
 +              brcmf_sdio_bus_sleep(bus, false, false);
 +
 +              /* Disable and clear interrupts at the chip level also */
 +              w_sdreg32(bus, 0, offsetof(struct sdpcmd_regs, hostintmask));
 +              local_hostintmask = bus->hostintmask;
 +              bus->hostintmask = 0;
 +
 +              /* Force backplane clocks to assure F2 interrupt propagates */
 +              saveclk = brcmf_sdiod_regrb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR,
 +                                          &err);
 +              if (!err)
 +                      brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR,
 +                                        (saveclk | SBSDIO_FORCE_HT), &err);
 +              if (err)
 +                      brcmf_err("Failed to force clock for F2: err %d\n",
 +                                err);
 +
 +              /* Turn off the bus (F2), free any pending packets */
 +              brcmf_dbg(INTR, "disable SDIO interrupts\n");
 +              sdio_disable_func(sdiodev->func[SDIO_FUNC_2]);
 +
 +              /* Clear any pending interrupts now that F2 is disabled */
 +              w_sdreg32(bus, local_hostintmask,
 +                        offsetof(struct sdpcmd_regs, intstatus));
 +
 +              sdio_release_host(sdiodev->func[1]);
 +      }
 +      /* Clear the data packet queues */
 +      brcmu_pktq_flush(&bus->txq, true, NULL, NULL);
 +
 +      /* Clear any held glomming stuff */
 +      if (bus->glomd)
 +              brcmu_pkt_buf_free_skb(bus->glomd);
 +      brcmf_sdio_free_glom(bus);
 +
 +      /* Clear rx control and wake any waiters */
 +      spin_lock_bh(&bus->rxctl_lock);
 +      bus->rxlen = 0;
 +      spin_unlock_bh(&bus->rxctl_lock);
 +      brcmf_sdio_dcmd_resp_wake(bus);
 +
 +      /* Reset some F2 state stuff */
 +      bus->rxskip = false;
 +      bus->tx_seq = bus->rx_seq = 0;
 +}
 +
 +static inline void brcmf_sdio_clrintr(struct brcmf_sdio *bus)
 +{
 +      unsigned long flags;
 +
 +      if (bus->sdiodev->oob_irq_requested) {
 +              spin_lock_irqsave(&bus->sdiodev->irq_en_lock, flags);
 +              if (!bus->sdiodev->irq_en && !atomic_read(&bus->ipend)) {
 +                      enable_irq(bus->sdiodev->pdata->oob_irq_nr);
 +                      bus->sdiodev->irq_en = true;
 +              }
 +              spin_unlock_irqrestore(&bus->sdiodev->irq_en_lock, flags);
 +      }
 +}
 +
 +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;
 +      u32 addr;
 +      unsigned long val;
 +      int ret;
 +
 +      buscore = brcmf_chip_get_core(bus->ci, BCMA_CORE_SDIO_DEV);
 +      addr = buscore->base + offsetof(struct sdpcmd_regs, intstatus);
 +
 +      val = brcmf_sdiod_regrl(bus->sdiodev, addr, &ret);
 +      bus->sdcnt.f1regdata++;
 +      if (ret != 0)
 +              return ret;
 +
 +      val &= bus->hostintmask;
 +      atomic_set(&bus->fcstate, !!(val & I_HMB_FC_STATE));
 +
 +      /* Clear interrupts */
 +      if (val) {
 +              brcmf_sdiod_regwl(bus->sdiodev, addr, val, &ret);
 +              bus->sdcnt.f1regdata++;
 +              atomic_orr(val, &bus->intstatus);
 +      }
 +
 +      return ret;
 +}
 +
 +static void brcmf_sdio_dpc(struct brcmf_sdio *bus)
 +{
 +      u32 newstatus = 0;
 +      unsigned long intstatus;
 +      uint txlimit = bus->txbound;    /* Tx frames to send before resched */
 +      uint framecnt;                  /* Temporary counter of tx/rx frames */
 +      int err = 0;
 +
 +      brcmf_dbg(TRACE, "Enter\n");
 +
 +      sdio_claim_host(bus->sdiodev->func[1]);
 +
 +      /* If waiting for HTAVAIL, check status */
 +      if (!bus->sr_enabled && bus->clkstate == CLK_PENDING) {
 +              u8 clkctl, devctl = 0;
 +
 +#ifdef DEBUG
 +              /* Check for inconsistent device control */
 +              devctl = brcmf_sdiod_regrb(bus->sdiodev,
 +                                         SBSDIO_DEVICE_CTL, &err);
 +#endif                                /* DEBUG */
 +
 +              /* Read CSR, if clock on switch to AVAIL, else ignore */
 +              clkctl = brcmf_sdiod_regrb(bus->sdiodev,
 +                                         SBSDIO_FUNC1_CHIPCLKCSR, &err);
 +
 +              brcmf_dbg(SDIO, "DPC: PENDING, devctl 0x%02x clkctl 0x%02x\n",
 +                        devctl, clkctl);
 +
 +              if (SBSDIO_HTAV(clkctl)) {
 +                      devctl = brcmf_sdiod_regrb(bus->sdiodev,
 +                                                 SBSDIO_DEVICE_CTL, &err);
 +                      devctl &= ~SBSDIO_DEVCTL_CA_INT_ONLY;
 +                      brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_DEVICE_CTL,
 +                                        devctl, &err);
 +                      bus->clkstate = CLK_AVAIL;
 +              }
 +      }
 +
 +      /* Make sure backplane clock is on */
 +      brcmf_sdio_bus_sleep(bus, false, true);
 +
 +      /* Pending interrupt indicates new device status */
 +      if (atomic_read(&bus->ipend) > 0) {
 +              atomic_set(&bus->ipend, 0);
 +              err = brcmf_sdio_intr_rstatus(bus);
 +      }
 +
 +      /* Start with leftover status bits */
 +      intstatus = atomic_xchg(&bus->intstatus, 0);
 +
 +      /* Handle flow-control change: read new state in case our ack
 +       * crossed another change interrupt.  If change still set, assume
 +       * FC ON for safety, let next loop through do the debounce.
 +       */
 +      if (intstatus & I_HMB_FC_CHANGE) {
 +              intstatus &= ~I_HMB_FC_CHANGE;
 +              err = w_sdreg32(bus, I_HMB_FC_CHANGE,
 +                              offsetof(struct sdpcmd_regs, intstatus));
 +
 +              err = r_sdreg32(bus, &newstatus,
 +                              offsetof(struct sdpcmd_regs, intstatus));
 +              bus->sdcnt.f1regdata += 2;
 +              atomic_set(&bus->fcstate,
 +                         !!(newstatus & (I_HMB_FC_STATE | I_HMB_FC_CHANGE)));
 +              intstatus |= (newstatus & bus->hostintmask);
 +      }
 +
 +      /* Handle host mailbox indication */
 +      if (intstatus & I_HMB_HOST_INT) {
 +              intstatus &= ~I_HMB_HOST_INT;
 +              intstatus |= brcmf_sdio_hostmail(bus);
 +      }
 +
 +      sdio_release_host(bus->sdiodev->func[1]);
 +
 +      /* Generally don't ask for these, can get CRC errors... */
 +      if (intstatus & I_WR_OOSYNC) {
 +              brcmf_err("Dongle reports WR_OOSYNC\n");
 +              intstatus &= ~I_WR_OOSYNC;
 +      }
 +
 +      if (intstatus & I_RD_OOSYNC) {
 +              brcmf_err("Dongle reports RD_OOSYNC\n");
 +              intstatus &= ~I_RD_OOSYNC;
 +      }
 +
 +      if (intstatus & I_SBINT) {
 +              brcmf_err("Dongle reports SBINT\n");
 +              intstatus &= ~I_SBINT;
 +      }
 +
 +      /* Would be active due to wake-wlan in gSPI */
 +      if (intstatus & I_CHIPACTIVE) {
 +              brcmf_dbg(INFO, "Dongle reports CHIPACTIVE\n");
 +              intstatus &= ~I_CHIPACTIVE;
 +      }
 +
 +      /* Ignore frame indications if rxskip is set */
 +      if (bus->rxskip)
 +              intstatus &= ~I_HMB_FRAME_IND;
 +
 +      /* On frame indication, read available frames */
 +      if ((intstatus & I_HMB_FRAME_IND) && (bus->clkstate == CLK_AVAIL)) {
 +              brcmf_sdio_readframes(bus, bus->rxbound);
 +              if (!bus->rxpending)
 +                      intstatus &= ~I_HMB_FRAME_IND;
 +      }
 +
 +      /* Keep still-pending events for next scheduling */
 +      if (intstatus)
 +              atomic_orr(intstatus, &bus->intstatus);
 +
 +      brcmf_sdio_clrintr(bus);
 +
 +      if (bus->ctrl_frame_stat && (bus->clkstate == CLK_AVAIL) &&
 +          (down_interruptible(&bus->tx_seq_lock) == 0)) {
 +              if (data_ok(bus)) {
 +                      sdio_claim_host(bus->sdiodev->func[1]);
 +                      err = brcmf_sdio_tx_ctrlframe(bus,  bus->ctrl_frame_buf,
 +                                                    bus->ctrl_frame_len);
 +                      sdio_release_host(bus->sdiodev->func[1]);
 +
 +                      bus->ctrl_frame_stat = false;
 +                      brcmf_sdio_wait_event_wakeup(bus);
 +              }
 +              up(&bus->tx_seq_lock);
 +      }
 +      /* Send queued frames (limit 1 if rx may still be pending) */
 +      if ((bus->clkstate == CLK_AVAIL) && !atomic_read(&bus->fcstate) &&
 +          brcmu_pktq_mlen(&bus->txq, ~bus->flowcontrol) && txlimit &&
 +          data_ok(bus)) {
 +              framecnt = bus->rxpending ? min(txlimit, bus->txminmax) :
 +                                          txlimit;
 +              brcmf_sdio_sendfromq(bus, framecnt);
 +      }
 +
 +      if (!brcmf_bus_ready(bus->sdiodev->bus_if) || (err != 0)) {
 +              brcmf_err("failed backplane access over SDIO, halting operation\n");
 +              atomic_set(&bus->intstatus, 0);
 +      } else if (atomic_read(&bus->intstatus) ||
 +                 atomic_read(&bus->ipend) > 0 ||
 +                 (!atomic_read(&bus->fcstate) &&
 +                  brcmu_pktq_mlen(&bus->txq, ~bus->flowcontrol) &&
 +                  data_ok(bus))) {
 +              atomic_inc(&bus->dpc_tskcnt);
 +      }
 +}
 +
 +static struct pktq *brcmf_sdio_bus_gettxq(struct device *dev)
 +{
 +      struct brcmf_bus *bus_if = dev_get_drvdata(dev);
 +      struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
 +      struct brcmf_sdio *bus = sdiodev->bus;
 +
 +      return &bus->txq;
 +}
 +
 +static bool brcmf_sdio_prec_enq(struct pktq *q, struct sk_buff *pkt, int prec)
 +{
 +      struct sk_buff *p;
 +      int eprec = -1;         /* precedence to evict from */
 +
 +      /* Fast case, precedence queue is not full and we are also not
 +       * exceeding total queue length
 +       */
 +      if (!pktq_pfull(q, prec) && !pktq_full(q)) {
 +              brcmu_pktq_penq(q, prec, pkt);
 +              return true;
 +      }
 +
 +      /* Determine precedence from which to evict packet, if any */
 +      if (pktq_pfull(q, prec)) {
 +              eprec = prec;
 +      } else if (pktq_full(q)) {
 +              p = brcmu_pktq_peek_tail(q, &eprec);
 +              if (eprec > prec)
 +                      return false;
 +      }
 +
 +      /* Evict if needed */
 +      if (eprec >= 0) {
 +              /* Detect queueing to unconfigured precedence */
 +              if (eprec == prec)
 +                      return false;   /* refuse newer (incoming) packet */
 +              /* Evict packet according to discard policy */
 +              p = brcmu_pktq_pdeq_tail(q, eprec);
 +              if (p == NULL)
 +                      brcmf_err("brcmu_pktq_pdeq_tail() failed\n");
 +              brcmu_pkt_buf_free_skb(p);
 +      }
 +
 +      /* Enqueue */
 +      p = brcmu_pktq_penq(q, prec, pkt);
 +      if (p == NULL)
 +              brcmf_err("brcmu_pktq_penq() failed\n");
 +
 +      return p != NULL;
 +}
 +
 +static int brcmf_sdio_bus_txdata(struct device *dev, struct sk_buff *pkt)
 +{
 +      int ret = -EBADE;
 +      uint prec;
 +      struct brcmf_bus *bus_if = dev_get_drvdata(dev);
 +      struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
 +      struct brcmf_sdio *bus = sdiodev->bus;
 +
 +      brcmf_dbg(TRACE, "Enter: pkt: data %p len %d\n", pkt->data, pkt->len);
 +
 +      /* Add space for the header */
 +      skb_push(pkt, bus->tx_hdrlen);
 +      /* precondition: IS_ALIGNED((unsigned long)(pkt->data), 2) */
 +
 +      prec = prio2prec((pkt->priority & PRIOMASK));
 +
 +      /* Check for existing queue, current flow-control,
 +                       pending event, or pending clock */
 +      brcmf_dbg(TRACE, "deferring pktq len %d\n", pktq_len(&bus->txq));
 +      bus->sdcnt.fcqueued++;
 +
 +      /* Priority based enq */
 +      spin_lock_bh(&bus->txq_lock);
 +      /* reset bus_flags in packet cb */
 +      *(u16 *)(pkt->cb) = 0;
 +      if (!brcmf_sdio_prec_enq(&bus->txq, pkt, prec)) {
 +              skb_pull(pkt, bus->tx_hdrlen);
 +              brcmf_err("out of bus->txq !!!\n");
 +              ret = -ENOSR;
 +      } else {
 +              ret = 0;
 +      }
 +
 +      if (pktq_len(&bus->txq) >= TXHI) {
 +              bus->txoff = true;
 +              brcmf_txflowblock(dev, true);
 +      }
 +      spin_unlock_bh(&bus->txq_lock);
 +
 +#ifdef DEBUG
 +      if (pktq_plen(&bus->txq, prec) > qcount[prec])
 +              qcount[prec] = pktq_plen(&bus->txq, prec);
 +#endif
 +
 +      if (atomic_read(&bus->dpc_tskcnt) == 0) {
 +              atomic_inc(&bus->dpc_tskcnt);
 +              queue_work(bus->brcmf_wq, &bus->datawork);
 +      }
 +
 +      return ret;
 +}
 +
 +#ifdef DEBUG
 +#define CONSOLE_LINE_MAX      192
 +
 +static int brcmf_sdio_readconsole(struct brcmf_sdio *bus)
 +{
 +      struct brcmf_console *c = &bus->console;
 +      u8 line[CONSOLE_LINE_MAX], ch;
 +      u32 n, idx, addr;
 +      int rv;
 +
 +      /* Don't do anything until FWREADY updates console address */
 +      if (bus->console_addr == 0)
 +              return 0;
 +
 +      /* Read console log struct */
 +      addr = bus->console_addr + offsetof(struct rte_console, log_le);
 +      rv = brcmf_sdiod_ramrw(bus->sdiodev, false, addr, (u8 *)&c->log_le,
 +                             sizeof(c->log_le));
 +      if (rv < 0)
 +              return rv;
 +
 +      /* Allocate console buffer (one time only) */
 +      if (c->buf == NULL) {
 +              c->bufsize = le32_to_cpu(c->log_le.buf_size);
 +              c->buf = kmalloc(c->bufsize, GFP_ATOMIC);
 +              if (c->buf == NULL)
 +                      return -ENOMEM;
 +      }
 +
 +      idx = le32_to_cpu(c->log_le.idx);
 +
 +      /* Protect against corrupt value */
 +      if (idx > c->bufsize)
 +              return -EBADE;
 +
 +      /* Skip reading the console buffer if the index pointer
 +       has not moved */
 +      if (idx == c->last)
 +              return 0;
 +
 +      /* Read the console buffer */
 +      addr = le32_to_cpu(c->log_le.buf);
 +      rv = brcmf_sdiod_ramrw(bus->sdiodev, false, addr, c->buf, c->bufsize);
 +      if (rv < 0)
 +              return rv;
 +
 +      while (c->last != idx) {
 +              for (n = 0; n < CONSOLE_LINE_MAX - 2; n++) {
 +                      if (c->last == idx) {
 +                              /* This would output a partial line.
 +                               * Instead, back up
 +                               * the buffer pointer and output this
 +                               * line next time around.
 +                               */
 +                              if (c->last >= n)
 +                                      c->last -= n;
 +                              else
 +                                      c->last = c->bufsize - n;
 +                              goto break2;
 +                      }
 +                      ch = c->buf[c->last];
 +                      c->last = (c->last + 1) % c->bufsize;
 +                      if (ch == '\n')
 +                              break;
 +                      line[n] = ch;
 +              }
 +
 +              if (n > 0) {
 +                      if (line[n - 1] == '\r')
 +                              n--;
 +                      line[n] = 0;
 +                      pr_debug("CONSOLE: %s\n", line);
 +              }
 +      }
 +break2:
 +
 +      return 0;
 +}
 +#endif                                /* DEBUG */
 +
 +static int
 +brcmf_sdio_bus_txctl(struct device *dev, unsigned char *msg, uint msglen)
 +{
 +      struct brcmf_bus *bus_if = dev_get_drvdata(dev);
 +      struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
 +      struct brcmf_sdio *bus = sdiodev->bus;
 +      int ret = -1;
 +
 +      brcmf_dbg(TRACE, "Enter\n");
 +
 +      if (down_interruptible(&bus->tx_seq_lock))
 +              return -EINTR;
 +
 +      if (!data_ok(bus)) {
 +              brcmf_dbg(INFO, "No bus credit bus->tx_max %d, bus->tx_seq %d\n",
 +                        bus->tx_max, bus->tx_seq);
 +              up(&bus->tx_seq_lock);
 +              /* Send from dpc */
 +              bus->ctrl_frame_buf = msg;
 +              bus->ctrl_frame_len = msglen;
 +              bus->ctrl_frame_stat = true;
 +
 +              wait_event_interruptible_timeout(bus->ctrl_wait,
 +                                               !bus->ctrl_frame_stat,
 +                                               msecs_to_jiffies(2000));
 +
 +              if (!bus->ctrl_frame_stat) {
 +                      brcmf_dbg(SDIO, "ctrl_frame_stat == false\n");
 +                      ret = 0;
 +              } else {
 +                      brcmf_dbg(SDIO, "ctrl_frame_stat == true\n");
 +                      bus->ctrl_frame_stat = false;
 +                      if (down_interruptible(&bus->tx_seq_lock))
 +                              return -EINTR;
 +                      ret = -1;
 +              }
 +      }
 +      if (ret == -1) {
 +              sdio_claim_host(bus->sdiodev->func[1]);
 +              brcmf_sdio_bus_sleep(bus, false, false);
 +              ret = brcmf_sdio_tx_ctrlframe(bus, msg, msglen);
 +              sdio_release_host(bus->sdiodev->func[1]);
 +              up(&bus->tx_seq_lock);
 +      }
 +
 +      if (ret)
 +              bus->sdcnt.tx_ctlerrs++;
 +      else
 +              bus->sdcnt.tx_ctlpkts++;
 +
 +      return ret ? -EIO : 0;
 +}
 +
 +#ifdef DEBUG
 +static int brcmf_sdio_dump_console(struct seq_file *seq, struct brcmf_sdio *bus,
 +                                 struct sdpcm_shared *sh)
 +{
 +      u32 addr, console_ptr, console_size, console_index;
 +      char *conbuf = NULL;
 +      __le32 sh_val;
 +      int rv;
 +
 +      /* obtain console information from device memory */
 +      addr = sh->console_addr + offsetof(struct rte_console, log_le);
 +      rv = brcmf_sdiod_ramrw(bus->sdiodev, false, addr,
 +                             (u8 *)&sh_val, sizeof(u32));
 +      if (rv < 0)
 +              return rv;
 +      console_ptr = le32_to_cpu(sh_val);
 +
 +      addr = sh->console_addr + offsetof(struct rte_console, log_le.buf_size);
 +      rv = brcmf_sdiod_ramrw(bus->sdiodev, false, addr,
 +                             (u8 *)&sh_val, sizeof(u32));
 +      if (rv < 0)
 +              return rv;
 +      console_size = le32_to_cpu(sh_val);
 +
 +      addr = sh->console_addr + offsetof(struct rte_console, log_le.idx);
 +      rv = brcmf_sdiod_ramrw(bus->sdiodev, false, addr,
 +                             (u8 *)&sh_val, sizeof(u32));
 +      if (rv < 0)
 +              return rv;
 +      console_index = le32_to_cpu(sh_val);
 +
 +      /* allocate buffer for console data */
 +      if (console_size <= CONSOLE_BUFFER_MAX)
 +              conbuf = vzalloc(console_size+1);
 +
 +      if (!conbuf)
 +              return -ENOMEM;
 +
 +      /* obtain the console data from device */
 +      conbuf[console_size] = '\0';
 +      rv = brcmf_sdiod_ramrw(bus->sdiodev, false, console_ptr, (u8 *)conbuf,
 +                             console_size);
 +      if (rv < 0)
 +              goto done;
 +
 +      rv = seq_write(seq, conbuf + console_index,
 +                     console_size - console_index);
 +      if (rv < 0)
 +              goto done;
 +
 +      if (console_index > 0)
 +              rv = seq_write(seq, conbuf, console_index - 1);
 +
 +done:
 +      vfree(conbuf);
 +      return rv;
 +}
 +
 +static int brcmf_sdio_trap_info(struct seq_file *seq, struct brcmf_sdio *bus,
 +                              struct sdpcm_shared *sh)
 +{
 +      int error;
 +      struct brcmf_trap_info tr;
 +
 +      if ((sh->flags & SDPCM_SHARED_TRAP) == 0) {
 +              brcmf_dbg(INFO, "no trap in firmware\n");
 +              return 0;
 +      }
 +
 +      error = brcmf_sdiod_ramrw(bus->sdiodev, false, sh->trap_addr, (u8 *)&tr,
 +                                sizeof(struct brcmf_trap_info));
 +      if (error < 0)
 +              return error;
 +
 +      seq_printf(seq,
 +                 "dongle trap info: type 0x%x @ epc 0x%08x\n"
 +                 "  cpsr 0x%08x spsr 0x%08x sp 0x%08x\n"
 +                 "  lr   0x%08x pc   0x%08x offset 0x%x\n"
 +                 "  r0   0x%08x r1   0x%08x r2 0x%08x r3 0x%08x\n"
 +                 "  r4   0x%08x r5   0x%08x r6 0x%08x r7 0x%08x\n",
 +                 le32_to_cpu(tr.type), le32_to_cpu(tr.epc),
 +                 le32_to_cpu(tr.cpsr), le32_to_cpu(tr.spsr),
 +                 le32_to_cpu(tr.r13), le32_to_cpu(tr.r14),
 +                 le32_to_cpu(tr.pc), sh->trap_addr,
 +                 le32_to_cpu(tr.r0), le32_to_cpu(tr.r1),
 +                 le32_to_cpu(tr.r2), le32_to_cpu(tr.r3),
 +                 le32_to_cpu(tr.r4), le32_to_cpu(tr.r5),
 +                 le32_to_cpu(tr.r6), le32_to_cpu(tr.r7));
 +
 +      return 0;
 +}
 +
 +static int brcmf_sdio_assert_info(struct seq_file *seq, struct brcmf_sdio *bus,
 +                                struct sdpcm_shared *sh)
 +{
 +      int error = 0;
 +      char file[80] = "?";
 +      char expr[80] = "<???>";
 +
 +      if ((sh->flags & SDPCM_SHARED_ASSERT_BUILT) == 0) {
 +              brcmf_dbg(INFO, "firmware not built with -assert\n");
 +              return 0;
 +      } else if ((sh->flags & SDPCM_SHARED_ASSERT) == 0) {
 +              brcmf_dbg(INFO, "no assert in dongle\n");
 +              return 0;
 +      }
 +
 +      sdio_claim_host(bus->sdiodev->func[1]);
 +      if (sh->assert_file_addr != 0) {
 +              error = brcmf_sdiod_ramrw(bus->sdiodev, false,
 +                                        sh->assert_file_addr, (u8 *)file, 80);
 +              if (error < 0)
 +                      return error;
 +      }
 +      if (sh->assert_exp_addr != 0) {
 +              error = brcmf_sdiod_ramrw(bus->sdiodev, false,
 +                                        sh->assert_exp_addr, (u8 *)expr, 80);
 +              if (error < 0)
 +                      return error;
 +      }
 +      sdio_release_host(bus->sdiodev->func[1]);
 +
 +      seq_printf(seq, "dongle assert: %s:%d: assert(%s)\n",
 +                 file, sh->assert_line, expr);
 +      return 0;
 +}
 +
 +static int brcmf_sdio_checkdied(struct brcmf_sdio *bus)
 +{
 +      int error;
 +      struct sdpcm_shared sh;
 +
 +      error = brcmf_sdio_readshared(bus, &sh);
 +
 +      if (error < 0)
 +              return error;
 +
 +      if ((sh.flags & SDPCM_SHARED_ASSERT_BUILT) == 0)
 +              brcmf_dbg(INFO, "firmware not built with -assert\n");
 +      else if (sh.flags & SDPCM_SHARED_ASSERT)
 +              brcmf_err("assertion in dongle\n");
 +
 +      if (sh.flags & SDPCM_SHARED_TRAP)
 +              brcmf_err("firmware trap in dongle\n");
 +
 +      return 0;
 +}
 +
 +static int brcmf_sdio_died_dump(struct seq_file *seq, struct brcmf_sdio *bus)
 +{
 +      int error = 0;
 +      struct sdpcm_shared sh;
 +
 +      error = brcmf_sdio_readshared(bus, &sh);
 +      if (error < 0)
 +              goto done;
 +
 +      error = brcmf_sdio_assert_info(seq, bus, &sh);
 +      if (error < 0)
 +              goto done;
 +
 +      error = brcmf_sdio_trap_info(seq, bus, &sh);
 +      if (error < 0)
 +              goto done;
 +
 +      error = brcmf_sdio_dump_console(seq, bus, &sh);
 +
 +done:
 +      return error;
 +}
 +
 +static int brcmf_sdio_forensic_read(struct seq_file *seq, void *data)
 +{
 +      struct brcmf_bus *bus_if = dev_get_drvdata(seq->private);
 +      struct brcmf_sdio *bus = bus_if->bus_priv.sdio->bus;
 +
 +      return brcmf_sdio_died_dump(seq, bus);
 +}
 +
 +static int brcmf_debugfs_sdio_count_read(struct seq_file *seq, void *data)
 +{
 +      struct brcmf_bus *bus_if = dev_get_drvdata(seq->private);
 +      struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
 +      struct brcmf_sdio_count *sdcnt = &sdiodev->bus->sdcnt;
 +
 +      seq_printf(seq,
 +                 "intrcount:    %u\nlastintrs:    %u\n"
 +                 "pollcnt:      %u\nregfails:     %u\n"
 +                 "tx_sderrs:    %u\nfcqueued:     %u\n"
 +                 "rxrtx:        %u\nrx_toolong:   %u\n"
 +                 "rxc_errors:   %u\nrx_hdrfail:   %u\n"
 +                 "rx_badhdr:    %u\nrx_badseq:    %u\n"
 +                 "fc_rcvd:      %u\nfc_xoff:      %u\n"
 +                 "fc_xon:       %u\nrxglomfail:   %u\n"
 +                 "rxglomframes: %u\nrxglompkts:   %u\n"
 +                 "f2rxhdrs:     %u\nf2rxdata:     %u\n"
 +                 "f2txdata:     %u\nf1regdata:    %u\n"
 +                 "tickcnt:      %u\ntx_ctlerrs:   %lu\n"
 +                 "tx_ctlpkts:   %lu\nrx_ctlerrs:   %lu\n"
 +                 "rx_ctlpkts:   %lu\nrx_readahead: %lu\n",
 +                 sdcnt->intrcount, sdcnt->lastintrs,
 +                 sdcnt->pollcnt, sdcnt->regfails,
 +                 sdcnt->tx_sderrs, sdcnt->fcqueued,
 +                 sdcnt->rxrtx, sdcnt->rx_toolong,
 +                 sdcnt->rxc_errors, sdcnt->rx_hdrfail,
 +                 sdcnt->rx_badhdr, sdcnt->rx_badseq,
 +                 sdcnt->fc_rcvd, sdcnt->fc_xoff,
 +                 sdcnt->fc_xon, sdcnt->rxglomfail,
 +                 sdcnt->rxglomframes, sdcnt->rxglompkts,
 +                 sdcnt->f2rxhdrs, sdcnt->f2rxdata,
 +                 sdcnt->f2txdata, sdcnt->f1regdata,
 +                 sdcnt->tickcnt, sdcnt->tx_ctlerrs,
 +                 sdcnt->tx_ctlpkts, sdcnt->rx_ctlerrs,
 +                 sdcnt->rx_ctlpkts, sdcnt->rx_readahead_cnt);
 +
 +      return 0;
 +}
 +
 +static void brcmf_sdio_debugfs_create(struct brcmf_sdio *bus)
 +{
 +      struct brcmf_pub *drvr = bus->sdiodev->bus_if->drvr;
 +      struct dentry *dentry = brcmf_debugfs_get_devdir(drvr);
 +
 +      if (IS_ERR_OR_NULL(dentry))
 +              return;
 +
 +      brcmf_debugfs_add_entry(drvr, "forensics", brcmf_sdio_forensic_read);
 +      brcmf_debugfs_add_entry(drvr, "counters",
 +                              brcmf_debugfs_sdio_count_read);
 +      debugfs_create_u32("console_interval", 0644, dentry,
 +                         &bus->console_interval);
 +}
 +#else
 +static int brcmf_sdio_checkdied(struct brcmf_sdio *bus)
 +{
 +      return 0;
 +}
 +
 +static void brcmf_sdio_debugfs_create(struct brcmf_sdio *bus)
 +{
 +}
 +#endif /* DEBUG */
 +
 +static int
 +brcmf_sdio_bus_rxctl(struct device *dev, unsigned char *msg, uint msglen)
 +{
 +      int timeleft;
 +      uint rxlen = 0;
 +      bool pending;
 +      u8 *buf;
 +      struct brcmf_bus *bus_if = dev_get_drvdata(dev);
 +      struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
 +      struct brcmf_sdio *bus = sdiodev->bus;
 +
 +      brcmf_dbg(TRACE, "Enter\n");
 +
 +      /* Wait until control frame is available */
 +      timeleft = brcmf_sdio_dcmd_resp_wait(bus, &bus->rxlen, &pending);
 +
 +      spin_lock_bh(&bus->rxctl_lock);
 +      rxlen = bus->rxlen;
 +      memcpy(msg, bus->rxctl, min(msglen, rxlen));
 +      bus->rxctl = NULL;
 +      buf = bus->rxctl_orig;
 +      bus->rxctl_orig = NULL;
 +      bus->rxlen = 0;
 +      spin_unlock_bh(&bus->rxctl_lock);
 +      vfree(buf);
 +
 +      if (rxlen) {
 +              brcmf_dbg(CTL, "resumed on rxctl frame, got %d expected %d\n",
 +                        rxlen, msglen);
 +      } else if (timeleft == 0) {
 +              brcmf_err("resumed on timeout\n");
 +              brcmf_sdio_checkdied(bus);
 +      } else if (pending) {
 +              brcmf_dbg(CTL, "cancelled\n");
 +              return -ERESTARTSYS;
 +      } else {
 +              brcmf_dbg(CTL, "resumed for unknown reason?\n");
 +              brcmf_sdio_checkdied(bus);
 +      }
 +
 +      if (rxlen)
 +              bus->sdcnt.rx_ctlpkts++;
 +      else
 +              bus->sdcnt.rx_ctlerrs++;
 +
 +      return rxlen ? (int)rxlen : -ETIMEDOUT;
 +}
 +
 +#ifdef DEBUG
 +static bool
 +brcmf_sdio_verifymemory(struct brcmf_sdio_dev *sdiodev, u32 ram_addr,
 +                      u8 *ram_data, uint ram_sz)
 +{
 +      char *ram_cmp;
 +      int err;
 +      bool ret = true;
 +      int address;
 +      int offset;
 +      int len;
 +
 +      /* read back and verify */
 +      brcmf_dbg(INFO, "Compare RAM dl & ul at 0x%08x; size=%d\n", ram_addr,
 +                ram_sz);
 +      ram_cmp = kmalloc(MEMBLOCK, GFP_KERNEL);
 +      /* do not proceed while no memory but  */
 +      if (!ram_cmp)
 +              return true;
 +
 +      address = ram_addr;
 +      offset = 0;
 +      while (offset < ram_sz) {
 +              len = ((offset + MEMBLOCK) < ram_sz) ? MEMBLOCK :
 +                    ram_sz - offset;
 +              err = brcmf_sdiod_ramrw(sdiodev, false, address, ram_cmp, len);
 +              if (err) {
 +                      brcmf_err("error %d on reading %d membytes at 0x%08x\n",
 +                                err, len, address);
 +                      ret = false;
 +                      break;
 +              } else if (memcmp(ram_cmp, &ram_data[offset], len)) {
 +                      brcmf_err("Downloaded RAM image is corrupted, block offset is %d, len is %d\n",
 +                                offset, len);
 +                      ret = false;
 +                      break;
 +              }
 +              offset += len;
 +              address += len;
 +      }
 +
 +      kfree(ram_cmp);
 +
 +      return ret;
 +}
 +#else /* DEBUG */
 +static bool
 +brcmf_sdio_verifymemory(struct brcmf_sdio_dev *sdiodev, u32 ram_addr,
 +                      u8 *ram_data, uint ram_sz)
 +{
 +      return true;
 +}
 +#endif        /* DEBUG */
 +
 +static int brcmf_sdio_download_code_file(struct brcmf_sdio *bus,
 +                                       const struct firmware *fw)
 +{
 +      int err;
 +
 +      brcmf_dbg(TRACE, "Enter\n");
 +
 +      err = brcmf_sdiod_ramrw(bus->sdiodev, true, bus->ci->rambase,
 +                              (u8 *)fw->data, fw->size);
 +      if (err)
 +              brcmf_err("error %d on writing %d membytes at 0x%08x\n",
 +                        err, (int)fw->size, bus->ci->rambase);
 +      else if (!brcmf_sdio_verifymemory(bus->sdiodev, bus->ci->rambase,
 +                                        (u8 *)fw->data, fw->size))
 +              err = -EIO;
 +
 +      return err;
 +}
 +
 +static int brcmf_sdio_download_nvram(struct brcmf_sdio *bus,
 +                                   void *vars, u32 varsz)
 +{
 +      int address;
 +      int err;
 +
 +      brcmf_dbg(TRACE, "Enter\n");
 +
 +      address = bus->ci->ramsize - varsz + bus->ci->rambase;
 +      err = brcmf_sdiod_ramrw(bus->sdiodev, true, address, vars, varsz);
 +      if (err)
 +              brcmf_err("error %d on writing %d nvram bytes at 0x%08x\n",
 +                        err, varsz, address);
 +      else if (!brcmf_sdio_verifymemory(bus->sdiodev, address, vars, varsz))
 +              err = -EIO;
 +
 +      return err;
 +}
 +
 +static int brcmf_sdio_download_firmware(struct brcmf_sdio *bus,
 +                                      const struct firmware *fw,
 +                                      void *nvram, u32 nvlen)
 +{
 +      int bcmerror = -EFAULT;
 +      u32 rstvec;
 +
 +      sdio_claim_host(bus->sdiodev->func[1]);
 +      brcmf_sdio_clkctl(bus, CLK_AVAIL, false);
 +
 +      /* Keep arm in reset */
 +      brcmf_chip_enter_download(bus->ci);
 +
 +      rstvec = get_unaligned_le32(fw->data);
 +      brcmf_dbg(SDIO, "firmware rstvec: %x\n", rstvec);
 +
 +      bcmerror = brcmf_sdio_download_code_file(bus, fw);
 +      release_firmware(fw);
 +      if (bcmerror) {
 +              brcmf_err("dongle image file download failed\n");
 +              brcmf_fw_nvram_free(nvram);
 +              goto err;
 +      }
 +
 +      bcmerror = brcmf_sdio_download_nvram(bus, nvram, nvlen);
 +      brcmf_fw_nvram_free(nvram);
 +      if (bcmerror) {
 +              brcmf_err("dongle nvram file download failed\n");
 +              goto err;
 +      }
 +
 +      /* Take arm out of reset */
 +      if (!brcmf_chip_exit_download(bus->ci, rstvec)) {
 +              brcmf_err("error getting out of ARM core reset\n");
 +              goto err;
 +      }
 +
 +      /* Allow HT Clock now that the ARM is running. */
 +      brcmf_bus_change_state(bus->sdiodev->bus_if, BRCMF_BUS_LOAD);
 +      bcmerror = 0;
 +
 +err:
 +      brcmf_sdio_clkctl(bus, CLK_SDONLY, false);
 +      sdio_release_host(bus->sdiodev->func[1]);
 +      return bcmerror;
 +}
 +
 +static void brcmf_sdio_sr_init(struct brcmf_sdio *bus)
 +{
 +      int err = 0;
 +      u8 val;
 +
 +      brcmf_dbg(TRACE, "Enter\n");
 +
 +      val = brcmf_sdiod_regrb(bus->sdiodev, SBSDIO_FUNC1_WAKEUPCTRL, &err);
 +      if (err) {
 +              brcmf_err("error reading SBSDIO_FUNC1_WAKEUPCTRL\n");
 +              return;
 +      }
 +
 +      val |= 1 << SBSDIO_FUNC1_WCTRL_HTWAIT_SHIFT;
 +      brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_WAKEUPCTRL, val, &err);
 +      if (err) {
 +              brcmf_err("error writing SBSDIO_FUNC1_WAKEUPCTRL\n");
 +              return;
 +      }
 +
 +      /* Add CMD14 Support */
 +      brcmf_sdiod_regwb(bus->sdiodev, SDIO_CCCR_BRCM_CARDCAP,
 +                        (SDIO_CCCR_BRCM_CARDCAP_CMD14_SUPPORT |
 +                         SDIO_CCCR_BRCM_CARDCAP_CMD14_EXT),
 +                        &err);
 +      if (err) {
 +              brcmf_err("error writing SDIO_CCCR_BRCM_CARDCAP\n");
 +              return;
 +      }
 +
 +      brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR,
 +                        SBSDIO_FORCE_HT, &err);
 +      if (err) {
 +              brcmf_err("error writing SBSDIO_FUNC1_CHIPCLKCSR\n");
 +              return;
 +      }
 +
 +      /* set flag */
 +      bus->sr_enabled = true;
 +      brcmf_dbg(INFO, "SR enabled\n");
 +}
 +
 +/* enable KSO bit */
 +static int brcmf_sdio_kso_init(struct brcmf_sdio *bus)
 +{
 +      u8 val;
 +      int err = 0;
 +
 +      brcmf_dbg(TRACE, "Enter\n");
 +
 +      /* KSO bit added in SDIO core rev 12 */
 +      if (brcmf_chip_get_core(bus->ci, BCMA_CORE_SDIO_DEV)->rev < 12)
 +              return 0;
 +
 +      val = brcmf_sdiod_regrb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR, &err);
 +      if (err) {
 +              brcmf_err("error reading SBSDIO_FUNC1_SLEEPCSR\n");
 +              return err;
 +      }
 +
 +      if (!(val & SBSDIO_FUNC1_SLEEPCSR_KSO_MASK)) {
 +              val |= (SBSDIO_FUNC1_SLEEPCSR_KSO_EN <<
 +                      SBSDIO_FUNC1_SLEEPCSR_KSO_SHIFT);
 +              brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR,
 +                                val, &err);
 +              if (err) {
 +                      brcmf_err("error writing SBSDIO_FUNC1_SLEEPCSR\n");
 +                      return err;
 +              }
 +      }
 +
 +      return 0;
 +}
 +
 +
 +static int brcmf_sdio_bus_preinit(struct device *dev)
 +{
 +      struct brcmf_bus *bus_if = dev_get_drvdata(dev);
 +      struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
 +      struct brcmf_sdio *bus = sdiodev->bus;
 +      uint pad_size;
 +      u32 value;
 +      int err;
 +
 +      /* the commands below use the terms tx and rx from
 +       * a device perspective, ie. bus:txglom affects the
 +       * bus transfers from device to host.
 +       */
 +      if (brcmf_chip_get_core(bus->ci, BCMA_CORE_SDIO_DEV)->rev < 12) {
 +              /* for sdio core rev < 12, disable txgloming */
 +              value = 0;
 +              err = brcmf_iovar_data_set(dev, "bus:txglom", &value,
 +                                         sizeof(u32));
 +      } else {
 +              /* otherwise, set txglomalign */
 +              value = 4;
 +              if (sdiodev->pdata)
 +                      value = sdiodev->pdata->sd_sgentry_align;
 +              /* SDIO ADMA requires at least 32 bit alignment */
 +              value = max_t(u32, value, 4);
 +              err = brcmf_iovar_data_set(dev, "bus:txglomalign", &value,
 +                                         sizeof(u32));
 +      }
 +
 +      if (err < 0)
 +              goto done;
 +
 +      bus->tx_hdrlen = SDPCM_HWHDR_LEN + SDPCM_SWHDR_LEN;
 +      if (sdiodev->sg_support) {
 +              bus->txglom = false;
 +              value = 1;
 +              pad_size = bus->sdiodev->func[2]->cur_blksize << 1;
 +              err = brcmf_iovar_data_set(bus->sdiodev->dev, "bus:rxglom",
 +                                         &value, sizeof(u32));
 +              if (err < 0) {
 +                      /* bus:rxglom is allowed to fail */
 +                      err = 0;
 +              } else {
 +                      bus->txglom = true;
 +                      bus->tx_hdrlen += SDPCM_HWEXT_LEN;
 +              }
 +      }
 +      brcmf_bus_add_txhdrlen(bus->sdiodev->dev, bus->tx_hdrlen);
 +
 +done:
 +      return err;
 +}
 +
 +void brcmf_sdio_isr(struct brcmf_sdio *bus)
 +{
 +      brcmf_dbg(TRACE, "Enter\n");
 +
 +      if (!bus) {
 +              brcmf_err("bus is null pointer, exiting\n");
 +              return;
 +      }
 +
 +      if (!brcmf_bus_ready(bus->sdiodev->bus_if)) {
 +              brcmf_err("bus is down. we have nothing to do\n");
 +              return;
 +      }
 +      /* Count the interrupt call */
 +      bus->sdcnt.intrcount++;
 +      if (in_interrupt())
 +              atomic_set(&bus->ipend, 1);
 +      else
 +              if (brcmf_sdio_intr_rstatus(bus)) {
 +                      brcmf_err("failed backplane access\n");
 +              }
 +
 +      /* Disable additional interrupts (is this needed now)? */
 +      if (!bus->intr)
 +              brcmf_err("isr w/o interrupt configured!\n");
 +
 +      atomic_inc(&bus->dpc_tskcnt);
 +      queue_work(bus->brcmf_wq, &bus->datawork);
 +}
 +
 +static bool brcmf_sdio_bus_watchdog(struct brcmf_sdio *bus)
 +{
 +#ifdef DEBUG
 +      struct brcmf_bus *bus_if = dev_get_drvdata(bus->sdiodev->dev);
 +#endif        /* DEBUG */
 +
 +      brcmf_dbg(TIMER, "Enter\n");
 +
 +      /* Poll period: check device if appropriate. */
 +      if (!bus->sr_enabled &&
 +          bus->poll && (++bus->polltick >= bus->pollrate)) {
 +              u32 intstatus = 0;
 +
 +              /* Reset poll tick */
 +              bus->polltick = 0;
 +
 +              /* Check device if no interrupts */
 +              if (!bus->intr ||
 +                  (bus->sdcnt.intrcount == bus->sdcnt.lastintrs)) {
 +
 +                      if (atomic_read(&bus->dpc_tskcnt) == 0) {
 +                              u8 devpend;
 +
 +                              sdio_claim_host(bus->sdiodev->func[1]);
 +                              devpend = brcmf_sdiod_regrb(bus->sdiodev,
 +                                                          SDIO_CCCR_INTx,
 +                                                          NULL);
 +                              sdio_release_host(bus->sdiodev->func[1]);
 +                              intstatus =
 +                                  devpend & (INTR_STATUS_FUNC1 |
 +                                             INTR_STATUS_FUNC2);
 +                      }
 +
 +                      /* If there is something, make like the ISR and
 +                               schedule the DPC */
 +                      if (intstatus) {
 +                              bus->sdcnt.pollcnt++;
 +                              atomic_set(&bus->ipend, 1);
 +
 +                              atomic_inc(&bus->dpc_tskcnt);
 +                              queue_work(bus->brcmf_wq, &bus->datawork);
 +                      }
 +              }
 +
 +              /* Update interrupt tracking */
 +              bus->sdcnt.lastintrs = bus->sdcnt.intrcount;
 +      }
 +#ifdef DEBUG
 +      /* Poll for console output periodically */
 +      if (bus_if && bus_if->state == BRCMF_BUS_DATA &&
 +          bus->console_interval != 0) {
 +              bus->console.count += BRCMF_WD_POLL_MS;
 +              if (bus->console.count >= bus->console_interval) {
 +                      bus->console.count -= bus->console_interval;
 +                      sdio_claim_host(bus->sdiodev->func[1]);
 +                      /* Make sure backplane clock is on */
 +                      brcmf_sdio_bus_sleep(bus, false, false);
 +                      if (brcmf_sdio_readconsole(bus) < 0)
 +                              /* stop on error */
 +                              bus->console_interval = 0;
 +                      sdio_release_host(bus->sdiodev->func[1]);
 +              }
 +      }
 +#endif                                /* DEBUG */
 +
 +      /* On idle timeout clear activity flag and/or turn off clock */
 +      if ((bus->idletime > 0) && (bus->clkstate == CLK_AVAIL)) {
 +              if (++bus->idlecount >= bus->idletime) {
 +                      bus->idlecount = 0;
 +                      if (bus->activity) {
 +                              bus->activity = false;
 +                              brcmf_sdio_wd_timer(bus, BRCMF_WD_POLL_MS);
 +                      } else {
 +                              brcmf_dbg(SDIO, "idle\n");
 +                              sdio_claim_host(bus->sdiodev->func[1]);
 +                              brcmf_sdio_bus_sleep(bus, true, false);
 +                              sdio_release_host(bus->sdiodev->func[1]);
 +                      }
 +              }
 +      }
 +
 +      return (atomic_read(&bus->ipend) > 0);
 +}
 +
 +static void brcmf_sdio_dataworker(struct work_struct *work)
 +{
 +      struct brcmf_sdio *bus = container_of(work, struct brcmf_sdio,
 +                                            datawork);
 +
 +      while (atomic_read(&bus->dpc_tskcnt)) {
 +              atomic_set(&bus->dpc_tskcnt, 0);
 +              brcmf_sdio_dpc(bus);
 +      }
 +}
 +
 +static void
 +brcmf_sdio_drivestrengthinit(struct brcmf_sdio_dev *sdiodev,
 +                           struct brcmf_chip *ci, u32 drivestrength)
 +{
 +      const struct sdiod_drive_str *str_tab = NULL;
 +      u32 str_mask;
 +      u32 str_shift;
 +      u32 base;
 +      u32 i;
 +      u32 drivestrength_sel = 0;
 +      u32 cc_data_temp;
 +      u32 addr;
 +
 +      if (!(ci->cc_caps & CC_CAP_PMU))
 +              return;
 +
 +      switch (SDIOD_DRVSTR_KEY(ci->chip, ci->pmurev)) {
 +      case SDIOD_DRVSTR_KEY(BRCM_CC_4330_CHIP_ID, 12):
 +              str_tab = sdiod_drvstr_tab1_1v8;
 +              str_mask = 0x00003800;
 +              str_shift = 11;
 +              break;
 +      case SDIOD_DRVSTR_KEY(BRCM_CC_4334_CHIP_ID, 17):
 +              str_tab = sdiod_drvstr_tab6_1v8;
 +              str_mask = 0x00001800;
 +              str_shift = 11;
 +              break;
 +      case SDIOD_DRVSTR_KEY(BRCM_CC_43143_CHIP_ID, 17):
 +              /* note: 43143 does not support tristate */
 +              i = ARRAY_SIZE(sdiod_drvstr_tab2_3v3) - 1;
 +              if (drivestrength >= sdiod_drvstr_tab2_3v3[i].strength) {
 +                      str_tab = sdiod_drvstr_tab2_3v3;
 +                      str_mask = 0x00000007;
 +                      str_shift = 0;
 +              } else
 +                      brcmf_err("Invalid SDIO Drive strength for chip %s, strength=%d\n",
 +                                ci->name, drivestrength);
 +              break;
 +      case SDIOD_DRVSTR_KEY(BRCM_CC_43362_CHIP_ID, 13):
 +              str_tab = sdiod_drive_strength_tab5_1v8;
 +              str_mask = 0x00003800;
 +              str_shift = 11;
 +              break;
 +      default:
 +              brcmf_err("No SDIO Drive strength init done for chip %s rev %d pmurev %d\n",
 +                        ci->name, ci->chiprev, ci->pmurev);
 +              break;
 +      }
 +
 +      if (str_tab != NULL) {
 +              for (i = 0; str_tab[i].strength != 0; i++) {
 +                      if (drivestrength >= str_tab[i].strength) {
 +                              drivestrength_sel = str_tab[i].sel;
 +                              break;
 +                      }
 +              }
 +              base = brcmf_chip_get_chipcommon(ci)->base;
 +              addr = CORE_CC_REG(base, chipcontrol_addr);
 +              brcmf_sdiod_regwl(sdiodev, addr, 1, NULL);
 +              cc_data_temp = brcmf_sdiod_regrl(sdiodev, addr, NULL);
 +              cc_data_temp &= ~str_mask;
 +              drivestrength_sel <<= str_shift;
 +              cc_data_temp |= drivestrength_sel;
 +              brcmf_sdiod_regwl(sdiodev, addr, cc_data_temp, NULL);
 +
 +              brcmf_dbg(INFO, "SDIO: %d mA (req=%d mA) drive strength selected, set to 0x%08x\n",
 +                        str_tab[i].strength, drivestrength, cc_data_temp);
 +      }
 +}
 +
 +static int brcmf_sdio_buscoreprep(void *ctx)
 +{
 +      struct brcmf_sdio_dev *sdiodev = ctx;
 +      int err = 0;
 +      u8 clkval, clkset;
 +
 +      /* Try forcing SDIO core to do ALPAvail request only */
 +      clkset = SBSDIO_FORCE_HW_CLKREQ_OFF | SBSDIO_ALP_AVAIL_REQ;
 +      brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, clkset, &err);
 +      if (err) {
 +              brcmf_err("error writing for HT off\n");
 +              return err;
 +      }
 +
 +      /* If register supported, wait for ALPAvail and then force ALP */
 +      /* This may take up to 15 milliseconds */
 +      clkval = brcmf_sdiod_regrb(sdiodev,
 +                                 SBSDIO_FUNC1_CHIPCLKCSR, NULL);
 +
 +      if ((clkval & ~SBSDIO_AVBITS) != clkset) {
 +              brcmf_err("ChipClkCSR access: wrote 0x%02x read 0x%02x\n",
 +                        clkset, clkval);
 +              return -EACCES;
 +      }
 +
 +      SPINWAIT(((clkval = brcmf_sdiod_regrb(sdiodev,
 +                                            SBSDIO_FUNC1_CHIPCLKCSR, NULL)),
 +                      !SBSDIO_ALPAV(clkval)),
 +                      PMU_MAX_TRANSITION_DLY);
 +      if (!SBSDIO_ALPAV(clkval)) {
 +              brcmf_err("timeout on ALPAV wait, clkval 0x%02x\n",
 +                        clkval);
 +              return -EBUSY;
 +      }
 +
 +      clkset = SBSDIO_FORCE_HW_CLKREQ_OFF | SBSDIO_FORCE_ALP;
 +      brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, clkset, &err);
 +      udelay(65);
 +
 +      /* Also, disable the extra SDIO pull-ups */
 +      brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_SDIOPULLUP, 0, NULL);
 +
 +      return 0;
 +}
 +
 +static void brcmf_sdio_buscore_exitdl(void *ctx, struct brcmf_chip *chip,
 +                                    u32 rstvec)
 +{
 +      struct brcmf_sdio_dev *sdiodev = ctx;
 +      struct brcmf_core *core;
 +      u32 reg_addr;
 +
 +      /* clear all interrupts */
 +      core = brcmf_chip_get_core(chip, BCMA_CORE_SDIO_DEV);
 +      reg_addr = core->base + offsetof(struct sdpcmd_regs, intstatus);
 +      brcmf_sdiod_regwl(sdiodev, reg_addr, 0xFFFFFFFF, NULL);
 +
 +      if (rstvec)
 +              /* Write reset vector to address 0 */
 +              brcmf_sdiod_ramrw(sdiodev, true, 0, (void *)&rstvec,
 +                                sizeof(rstvec));
 +}
 +
 +static u32 brcmf_sdio_buscore_read32(void *ctx, u32 addr)
 +{
 +      struct brcmf_sdio_dev *sdiodev = ctx;
 +      u32 val, rev;
 +
 +      val = brcmf_sdiod_regrl(sdiodev, addr, NULL);
 +      if (sdiodev->func[0]->device == BRCM_SDIO_4335_4339_DEVICE_ID &&
 +          addr == CORE_CC_REG(SI_ENUM_BASE, chipid)) {
 +              rev = (val & CID_REV_MASK) >> CID_REV_SHIFT;
 +              if (rev >= 2) {
 +                      val &= ~CID_ID_MASK;
 +                      val |= BRCM_CC_4339_CHIP_ID;
 +              }
 +      }
 +      return val;
 +}
 +
 +static void brcmf_sdio_buscore_write32(void *ctx, u32 addr, u32 val)
 +{
 +      struct brcmf_sdio_dev *sdiodev = ctx;
 +
 +      brcmf_sdiod_regwl(sdiodev, addr, val, NULL);
 +}
 +
 +static const struct brcmf_buscore_ops brcmf_sdio_buscore_ops = {
 +      .prepare = brcmf_sdio_buscoreprep,
 +      .exit_dl = brcmf_sdio_buscore_exitdl,
 +      .read32 = brcmf_sdio_buscore_read32,
 +      .write32 = brcmf_sdio_buscore_write32,
 +};
 +
 +static bool
 +brcmf_sdio_probe_attach(struct brcmf_sdio *bus)
 +{
 +      u8 clkctl = 0;
 +      int err = 0;
 +      int reg_addr;
 +      u32 reg_val;
 +      u32 drivestrength;
 +
 +      sdio_claim_host(bus->sdiodev->func[1]);
 +
 +      pr_debug("F1 signature read @0x18000000=0x%4x\n",
 +               brcmf_sdiod_regrl(bus->sdiodev, SI_ENUM_BASE, NULL));
 +
 +      /*
 +       * Force PLL off until brcmf_chip_attach()
 +       * programs PLL control regs
 +       */
 +
 +      brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR,
 +                        BRCMF_INIT_CLKCTL1, &err);
 +      if (!err)
 +              clkctl = brcmf_sdiod_regrb(bus->sdiodev,
 +                                         SBSDIO_FUNC1_CHIPCLKCSR, &err);
 +
 +      if (err || ((clkctl & ~SBSDIO_AVBITS) != BRCMF_INIT_CLKCTL1)) {
 +              brcmf_err("ChipClkCSR access: err %d wrote 0x%02x read 0x%02x\n",
 +                        err, BRCMF_INIT_CLKCTL1, clkctl);
 +              goto fail;
 +      }
 +
 +      /* SDIO register access works so moving
 +       * state from UNKNOWN to DOWN.
 +       */
 +      brcmf_bus_change_state(bus->sdiodev->bus_if, BRCMF_BUS_DOWN);
 +
 +      bus->ci = brcmf_chip_attach(bus->sdiodev, &brcmf_sdio_buscore_ops);
 +      if (IS_ERR(bus->ci)) {
 +              brcmf_err("brcmf_chip_attach failed!\n");
 +              bus->ci = NULL;
 +              goto fail;
 +      }
 +
 +      if (brcmf_sdio_kso_init(bus)) {
 +              brcmf_err("error enabling KSO\n");
 +              goto fail;
 +      }
 +
 +      if ((bus->sdiodev->pdata) && (bus->sdiodev->pdata->drive_strength))
 +              drivestrength = bus->sdiodev->pdata->drive_strength;
 +      else
 +              drivestrength = DEFAULT_SDIO_DRIVE_STRENGTH;
 +      brcmf_sdio_drivestrengthinit(bus->sdiodev, bus->ci, drivestrength);
 +
 +      /* Get info on the SOCRAM cores... */
 +      bus->ramsize = bus->ci->ramsize;
 +      if (!(bus->ramsize)) {
 +              brcmf_err("failed to find SOCRAM memory!\n");
 +              goto fail;
 +      }
 +
 +      /* Set card control so an SDIO card reset does a WLAN backplane reset */
 +      reg_val = brcmf_sdiod_regrb(bus->sdiodev,
 +                                  SDIO_CCCR_BRCM_CARDCTRL, &err);
 +      if (err)
 +              goto fail;
 +
 +      reg_val |= SDIO_CCCR_BRCM_CARDCTRL_WLANRESET;
 +
 +      brcmf_sdiod_regwb(bus->sdiodev,
 +                        SDIO_CCCR_BRCM_CARDCTRL, reg_val, &err);
 +      if (err)
 +              goto fail;
 +
 +      /* set PMUControl so a backplane reset does PMU state reload */
 +      reg_addr = CORE_CC_REG(brcmf_chip_get_chipcommon(bus->ci)->base,
 +                             pmucontrol);
 +      reg_val = brcmf_sdiod_regrl(bus->sdiodev, reg_addr, &err);
 +      if (err)
 +              goto fail;
 +
 +      reg_val |= (BCMA_CC_PMU_CTL_RES_RELOAD << BCMA_CC_PMU_CTL_RES_SHIFT);
 +
 +      brcmf_sdiod_regwl(bus->sdiodev, reg_addr, reg_val, &err);
 +      if (err)
 +              goto fail;
 +
 +      sdio_release_host(bus->sdiodev->func[1]);
 +
 +      brcmu_pktq_init(&bus->txq, (PRIOMASK + 1), TXQLEN);
 +
 +      /* allocate header buffer */
 +      bus->hdrbuf = kzalloc(MAX_HDR_READ + bus->head_align, GFP_KERNEL);
 +      if (!bus->hdrbuf)
 +              return false;
 +      /* Locate an appropriately-aligned portion of hdrbuf */
 +      bus->rxhdr = (u8 *) roundup((unsigned long)&bus->hdrbuf[0],
 +                                  bus->head_align);
 +
 +      /* Set the poll and/or interrupt flags */
 +      bus->intr = true;
 +      bus->poll = false;
 +      if (bus->poll)
 +              bus->pollrate = 1;
 +
 +      return true;
 +
 +fail:
 +      sdio_release_host(bus->sdiodev->func[1]);
 +      return false;
 +}
 +
 +static int
 +brcmf_sdio_watchdog_thread(void *data)
 +{
 +      struct brcmf_sdio *bus = (struct brcmf_sdio *)data;
 +
 +      allow_signal(SIGTERM);
 +      /* Run until signal received */
 +      while (1) {
 +              if (kthread_should_stop())
 +                      break;
 +              if (!wait_for_completion_interruptible(&bus->watchdog_wait)) {
 +                      brcmf_sdio_bus_watchdog(bus);
 +                      /* Count the tick for reference */
 +                      bus->sdcnt.tickcnt++;
 +                      reinit_completion(&bus->watchdog_wait);
 +              } else
 +                      break;
 +      }
 +      return 0;
 +}
 +
 +static void
 +brcmf_sdio_watchdog(unsigned long data)
 +{
 +      struct brcmf_sdio *bus = (struct brcmf_sdio *)data;
 +
 +      if (bus->watchdog_tsk) {
 +              complete(&bus->watchdog_wait);
 +              /* Reschedule the watchdog */
 +              if (bus->wd_timer_valid)
 +                      mod_timer(&bus->timer,
 +                                jiffies + BRCMF_WD_POLL_MS * HZ / 1000);
 +      }
 +}
 +
 +static struct brcmf_bus_ops brcmf_sdio_bus_ops = {
 +      .stop = brcmf_sdio_bus_stop,
 +      .preinit = brcmf_sdio_bus_preinit,
 +      .txdata = brcmf_sdio_bus_txdata,
 +      .txctl = brcmf_sdio_bus_txctl,
 +      .rxctl = brcmf_sdio_bus_rxctl,
 +      .gettxq = brcmf_sdio_bus_gettxq,
 +      .wowl_config = brcmf_sdio_wowl_config
 +};
 +
 +static void brcmf_sdio_firmware_callback(struct device *dev,
 +                                       const struct firmware *code,
 +                                       void *nvram, u32 nvram_len)
 +{
 +      struct brcmf_bus *bus_if = dev_get_drvdata(dev);
 +      struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
 +      struct brcmf_sdio *bus = sdiodev->bus;
 +      int err = 0;
 +      u8 saveclk;
 +
 +      brcmf_dbg(TRACE, "Enter: dev=%s\n", dev_name(dev));
 +
 +      /* try to download image and nvram to the dongle */
 +      if (bus_if->state == BRCMF_BUS_DOWN) {
 +              bus->alp_only = true;
 +              err = brcmf_sdio_download_firmware(bus, code, nvram, nvram_len);
 +              if (err)
 +                      goto fail;
 +              bus->alp_only = false;
 +      }
 +
 +      if (!bus_if->drvr)
 +              return;
 +
 +      /* Start the watchdog timer */
 +      bus->sdcnt.tickcnt = 0;
 +      brcmf_sdio_wd_timer(bus, BRCMF_WD_POLL_MS);
 +
 +      sdio_claim_host(sdiodev->func[1]);
 +
 +      /* Make sure backplane clock is on, needed to generate F2 interrupt */
 +      brcmf_sdio_clkctl(bus, CLK_AVAIL, false);
 +      if (bus->clkstate != CLK_AVAIL)
 +              goto release;
 +
 +      /* Force clocks on backplane to be sure F2 interrupt propagates */
 +      saveclk = brcmf_sdiod_regrb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, &err);
 +      if (!err) {
 +              brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR,
 +                                (saveclk | SBSDIO_FORCE_HT), &err);
 +      }
 +      if (err) {
 +              brcmf_err("Failed to force clock for F2: err %d\n", err);
 +              goto release;
 +      }
 +
 +      /* Enable function 2 (frame transfers) */
 +      w_sdreg32(bus, SDPCM_PROT_VERSION << SMB_DATA_VERSION_SHIFT,
 +                offsetof(struct sdpcmd_regs, tosbmailboxdata));
 +      err = sdio_enable_func(sdiodev->func[SDIO_FUNC_2]);
 +
 +
 +      brcmf_dbg(INFO, "enable F2: err=%d\n", err);
 +
 +      /* If F2 successfully enabled, set core and enable interrupts */
 +      if (!err) {
 +              /* Set up the interrupt mask and enable interrupts */
 +              bus->hostintmask = HOSTINTMASK;
 +              w_sdreg32(bus, bus->hostintmask,
 +                        offsetof(struct sdpcmd_regs, hostintmask));
 +
 +              brcmf_sdiod_regwb(sdiodev, SBSDIO_WATERMARK, 8, &err);
 +      } else {
 +              /* Disable F2 again */
 +              sdio_disable_func(sdiodev->func[SDIO_FUNC_2]);
 +              goto release;
 +      }
 +
 +      if (brcmf_chip_sr_capable(bus->ci)) {
 +              brcmf_sdio_sr_init(bus);
 +      } else {
 +              /* Restore previous clock setting */
 +              brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR,
 +                                saveclk, &err);
 +      }
 +
 +      if (err == 0) {
 +              err = brcmf_sdiod_intr_register(sdiodev);
 +              if (err != 0)
 +                      brcmf_err("intr register failed:%d\n", err);
 +      }
 +
 +      /* If we didn't come up, turn off backplane clock */
 +      if (err != 0)
 +              brcmf_sdio_clkctl(bus, CLK_NONE, false);
 +
 +      sdio_release_host(sdiodev->func[1]);
 +
 +      err = brcmf_bus_start(dev);
 +      if (err != 0) {
 +              brcmf_err("dongle is not responding\n");
 +              goto fail;
 +      }
 +      return;
 +
 +release:
 +      sdio_release_host(sdiodev->func[1]);
 +fail:
 +      brcmf_dbg(TRACE, "failed: dev=%s, err=%d\n", dev_name(dev), err);
 +      device_release_driver(dev);
 +}
 +
 +struct brcmf_sdio *brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev)
 +{
 +      int ret;
 +      struct brcmf_sdio *bus;
 +
 +      brcmf_dbg(TRACE, "Enter\n");
 +
 +      /* Allocate private bus interface state */
 +      bus = kzalloc(sizeof(struct brcmf_sdio), GFP_ATOMIC);
 +      if (!bus)
 +              goto fail;
 +
 +      bus->sdiodev = sdiodev;
 +      sdiodev->bus = bus;
 +      skb_queue_head_init(&bus->glom);
 +      bus->txbound = BRCMF_TXBOUND;
 +      bus->rxbound = BRCMF_RXBOUND;
 +      bus->txminmax = BRCMF_TXMINMAX;
 +      bus->tx_seq = SDPCM_SEQ_WRAP - 1;
 +
 +      /* platform specific configuration:
 +       *   alignments must be at least 4 bytes for ADMA
 +       */
 +      bus->head_align = ALIGNMENT;
 +      bus->sgentry_align = ALIGNMENT;
 +      if (sdiodev->pdata) {
 +              if (sdiodev->pdata->sd_head_align > ALIGNMENT)
 +                      bus->head_align = sdiodev->pdata->sd_head_align;
 +              if (sdiodev->pdata->sd_sgentry_align > ALIGNMENT)
 +                      bus->sgentry_align = sdiodev->pdata->sd_sgentry_align;
 +      }
 +
 +      INIT_WORK(&bus->datawork, brcmf_sdio_dataworker);
 +      bus->brcmf_wq = create_singlethread_workqueue("brcmf_wq");
 +      if (bus->brcmf_wq == NULL) {
 +              brcmf_err("insufficient memory to create txworkqueue\n");
 +              goto fail;
 +      }
 +
 +      /* attempt to attach to the dongle */
 +      if (!(brcmf_sdio_probe_attach(bus))) {
 +              brcmf_err("brcmf_sdio_probe_attach failed\n");
 +              goto fail;
 +      }
 +
 +      spin_lock_init(&bus->rxctl_lock);
 +      spin_lock_init(&bus->txq_lock);
 +      sema_init(&bus->tx_seq_lock, 1);
 +      init_waitqueue_head(&bus->ctrl_wait);
 +      init_waitqueue_head(&bus->dcmd_resp_wait);
 +
 +      /* Set up the watchdog timer */
 +      init_timer(&bus->timer);
 +      bus->timer.data = (unsigned long)bus;
 +      bus->timer.function = brcmf_sdio_watchdog;
 +
 +      /* Initialize watchdog thread */
 +      init_completion(&bus->watchdog_wait);
 +      bus->watchdog_tsk = kthread_run(brcmf_sdio_watchdog_thread,
 +                                      bus, "brcmf_watchdog");
 +      if (IS_ERR(bus->watchdog_tsk)) {
 +              pr_warn("brcmf_watchdog thread failed to start\n");
 +              bus->watchdog_tsk = NULL;
 +      }
 +      /* Initialize DPC thread */
 +      atomic_set(&bus->dpc_tskcnt, 0);
 +
 +      /* Assign bus interface call back */
 +      bus->sdiodev->bus_if->dev = bus->sdiodev->dev;
 +      bus->sdiodev->bus_if->ops = &brcmf_sdio_bus_ops;
 +      bus->sdiodev->bus_if->chip = bus->ci->chip;
 +      bus->sdiodev->bus_if->chiprev = bus->ci->chiprev;
 +
 +      /* default sdio bus header length for tx packet */
 +      bus->tx_hdrlen = SDPCM_HWHDR_LEN + SDPCM_SWHDR_LEN;
 +
 +      /* Attach to the common layer, reserve hdr space */
 +      ret = brcmf_attach(bus->sdiodev->dev);
 +      if (ret != 0) {
 +              brcmf_err("brcmf_attach failed\n");
 +              goto fail;
 +      }
 +
 +      /* Query the F2 block size, set roundup accordingly */
 +      bus->blocksize = bus->sdiodev->func[2]->cur_blksize;
 +      bus->roundup = min(max_roundup, bus->blocksize);
 +
 +      /* Allocate buffers */
 +      if (bus->sdiodev->bus_if->maxctl) {
 +              bus->sdiodev->bus_if->maxctl += bus->roundup;
 +              bus->rxblen =
 +                  roundup((bus->sdiodev->bus_if->maxctl + SDPCM_HDRLEN),
 +                          ALIGNMENT) + bus->head_align;
 +              bus->rxbuf = kmalloc(bus->rxblen, GFP_ATOMIC);
 +              if (!(bus->rxbuf)) {
 +                      brcmf_err("rxbuf allocation failed\n");
 +                      goto fail;
 +              }
 +      }
 +
 +      sdio_claim_host(bus->sdiodev->func[1]);
 +
 +      /* Disable F2 to clear any intermediate frame state on the dongle */
 +      sdio_disable_func(bus->sdiodev->func[SDIO_FUNC_2]);
 +
 +      bus->rxflow = false;
 +
 +      /* Done with backplane-dependent accesses, can drop clock... */
 +      brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, 0, NULL);
 +
 +      sdio_release_host(bus->sdiodev->func[1]);
 +
 +      /* ...and initialize clock/power states */
 +      bus->clkstate = CLK_SDONLY;
 +      bus->idletime = BRCMF_IDLE_INTERVAL;
 +      bus->idleclock = BRCMF_IDLE_ACTIVE;
 +
 +      /* SR state */
 +      bus->sleeping = false;
 +      bus->sr_enabled = false;
 +
 +      brcmf_sdio_debugfs_create(bus);
 +      brcmf_dbg(INFO, "completed!!\n");
 +
 +      ret = brcmf_sdio_get_fwnames(bus->ci, sdiodev);
 +      if (ret)
 +              goto fail;
 +
 +      ret = brcmf_fw_get_firmwares(sdiodev->dev, BRCMF_FW_REQUEST_NVRAM,
 +                                   sdiodev->fw_name, sdiodev->nvram_name,
 +                                   brcmf_sdio_firmware_callback);
 +      if (ret != 0) {
 +              brcmf_err("async firmware request failed: %d\n", ret);
 +              goto fail;
 +      }
 +
 +      return bus;
 +
 +fail:
 +      brcmf_sdio_remove(bus);
 +      return NULL;
 +}
 +
 +/* Detach and free everything */
 +void brcmf_sdio_remove(struct brcmf_sdio *bus)
 +{
 +      brcmf_dbg(TRACE, "Enter\n");
 +
 +      if (bus) {
 +              /* De-register interrupt handler */
 +              brcmf_sdiod_intr_unregister(bus->sdiodev);
 +
 +              brcmf_detach(bus->sdiodev->dev);
 +
 +              cancel_work_sync(&bus->datawork);
 +              if (bus->brcmf_wq)
 +                      destroy_workqueue(bus->brcmf_wq);
 +
 +              if (bus->ci) {
 +                      if (bus->sdiodev->bus_if->state == BRCMF_BUS_DOWN) {
 +                              sdio_claim_host(bus->sdiodev->func[1]);
 +                              brcmf_sdio_clkctl(bus, CLK_AVAIL, false);
 +                              /* Leave the device in state where it is
 +                               * 'quiet'. This is done by putting it in
 +                               * download_state which essentially resets
 +                               * all necessary cores.
 +                               */
 +                              msleep(20);
 +                              brcmf_chip_enter_download(bus->ci);
 +                              brcmf_sdio_clkctl(bus, CLK_NONE, false);
 +                              sdio_release_host(bus->sdiodev->func[1]);
 +                      }
 +                      brcmf_chip_detach(bus->ci);
 +              }
 +
 +              kfree(bus->rxbuf);
 +              kfree(bus->hdrbuf);
 +              kfree(bus);
 +      }
 +
 +      brcmf_dbg(TRACE, "Disconnected\n");
 +}
 +
 +void brcmf_sdio_wd_timer(struct brcmf_sdio *bus, uint wdtick)
 +{
 +      /* Totally stop the timer */
 +      if (!wdtick && bus->wd_timer_valid) {
 +              del_timer_sync(&bus->timer);
 +              bus->wd_timer_valid = false;
 +              bus->save_ms = wdtick;
 +              return;
 +      }
 +
 +      /* don't start the wd until fw is loaded */
 +      if (bus->sdiodev->bus_if->state != BRCMF_BUS_DATA)
 +              return;
 +
 +      if (wdtick) {
 +              if (bus->save_ms != BRCMF_WD_POLL_MS) {
 +                      if (bus->wd_timer_valid)
 +                              /* Stop timer and restart at new value */
 +                              del_timer_sync(&bus->timer);
 +
 +                      /* Create timer again when watchdog period is
 +                         dynamically changed or in the first instance
 +                       */
 +                      bus->timer.expires =
 +                              jiffies + BRCMF_WD_POLL_MS * HZ / 1000;
 +                      add_timer(&bus->timer);
 +
 +              } else {
 +                      /* Re arm the timer, at last watchdog period */
 +                      mod_timer(&bus->timer,
 +                              jiffies + BRCMF_WD_POLL_MS * HZ / 1000);
 +              }
 +
 +              bus->wd_timer_valid = true;
 +              bus->save_ms = wdtick;
 +      }
 +}
index f308e52781f6f060e61d21608b313505b145ac48,b62405865b25cd185c560731d54ab36304c54812..57325589ee5bcba07d315983e517b3e73ff82499
@@@ -69,7 -69,6 +69,7 @@@
  #include <linux/etherdevice.h>
  #include <linux/ip.h>
  #include <linux/if_arp.h>
 +#include <linux/devcoredump.h>
  #include <net/mac80211.h>
  #include <net/ieee80211_radiotap.h>
  #include <net/tcp.h>
@@@ -680,51 -679,10 +680,51 @@@ static void iwl_mvm_cleanup_iterator(vo
        memset(&mvmvif->bf_data, 0, sizeof(mvmvif->bf_data));
  }
  
 -#ifdef CONFIG_IWLWIFI_DEBUGFS
 +static ssize_t iwl_mvm_read_coredump(char *buffer, loff_t offset, size_t count,
 +                                   const void *data, size_t datalen)
 +{
 +      const struct iwl_mvm_dump_ptrs *dump_ptrs = data;
 +      ssize_t bytes_read;
 +      ssize_t bytes_read_trans;
 +
 +      if (offset < dump_ptrs->op_mode_len) {
 +              bytes_read = min_t(ssize_t, count,
 +                                 dump_ptrs->op_mode_len - offset);
 +              memcpy(buffer, (u8 *)dump_ptrs->op_mode_ptr + offset,
 +                     bytes_read);
 +              offset += bytes_read;
 +              count -= bytes_read;
 +
 +              if (count == 0)
 +                      return bytes_read;
 +      } else {
 +              bytes_read = 0;
 +      }
 +
 +      if (!dump_ptrs->trans_ptr)
 +              return bytes_read;
 +
 +      offset -= dump_ptrs->op_mode_len;
 +      bytes_read_trans = min_t(ssize_t, count,
 +                               dump_ptrs->trans_ptr->len - offset);
 +      memcpy(buffer + bytes_read,
 +             (u8 *)dump_ptrs->trans_ptr->data + offset,
 +             bytes_read_trans);
 +
 +      return bytes_read + bytes_read_trans;
 +}
 +
 +static void iwl_mvm_free_coredump(const void *data)
 +{
 +      const struct iwl_mvm_dump_ptrs *fw_error_dump = data;
 +
 +      vfree(fw_error_dump->op_mode_ptr);
 +      vfree(fw_error_dump->trans_ptr);
 +      kfree(fw_error_dump);
 +}
 +
  void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm)
  {
 -      static char *env[] = { "DRIVER=iwlwifi", "EVENT=error_dump", NULL };
        struct iwl_fw_error_dump_file *dump_file;
        struct iwl_fw_error_dump_data *dump_data;
        struct iwl_fw_error_dump_info *dump_info;
  
        lockdep_assert_held(&mvm->mutex);
  
 -      if (mvm->fw_error_dump)
 -              return;
 -
 -      fw_error_dump = kzalloc(sizeof(*mvm->fw_error_dump), GFP_KERNEL);
 +      fw_error_dump = kzalloc(sizeof(*fw_error_dump), GFP_KERNEL);
        if (!fw_error_dump)
                return;
  
        if (fw_error_dump->trans_ptr)
                file_len += fw_error_dump->trans_ptr->len;
        dump_file->file_len = cpu_to_le32(file_len);
 -      mvm->fw_error_dump = fw_error_dump;
  
 -      /* notify the userspace about the error we had */
 -      kobject_uevent_env(&mvm->hw->wiphy->dev.kobj, KOBJ_CHANGE, env);
 +      dev_coredumpm(mvm->trans->dev, THIS_MODULE, fw_error_dump, 0,
 +                    GFP_KERNEL, iwl_mvm_read_coredump, iwl_mvm_free_coredump);
  }
 -#endif
  
  static void iwl_mvm_restart_cleanup(struct iwl_mvm *mvm)
  {
  
        mvm->scan_status = IWL_MVM_SCAN_NONE;
        mvm->ps_disabled = false;
+       mvm->calibrating = false;
  
        /* just in case one was running */
        ieee80211_remain_on_channel_expired(mvm->hw);
@@@ -895,8 -859,9 +896,8 @@@ static int iwl_mvm_mac_start(struct iee
        return ret;
  }
  
 -static void iwl_mvm_mac_restart_complete(struct ieee80211_hw *hw)
 +static void iwl_mvm_restart_complete(struct iwl_mvm *mvm)
  {
 -      struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
        int ret;
  
        mutex_lock(&mvm->mutex);
        mutex_unlock(&mvm->mutex);
  }
  
 +static void
 +iwl_mvm_mac_reconfig_complete(struct ieee80211_hw *hw,
 +                            enum ieee80211_reconfig_type reconfig_type)
 +{
 +      struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
 +
 +      switch (reconfig_type) {
 +      case IEEE80211_RECONFIG_TYPE_RESTART:
 +              iwl_mvm_restart_complete(mvm);
 +              break;
 +      case IEEE80211_RECONFIG_TYPE_SUSPEND:
 +              break;
 +      }
 +}
 +
  void __iwl_mvm_mac_stop(struct iwl_mvm *mvm)
  {
        lockdep_assert_held(&mvm->mutex);
@@@ -1137,7 -1087,7 +1138,7 @@@ static int iwl_mvm_mac_add_interface(st
  static void iwl_mvm_prepare_mac_removal(struct iwl_mvm *mvm,
                                        struct ieee80211_vif *vif)
  {
 -      u32 tfd_msk = iwl_mvm_mac_get_queues_mask(mvm, vif);
 +      u32 tfd_msk = iwl_mvm_mac_get_queues_mask(vif);
  
        if (tfd_msk) {
                mutex_lock(&mvm->mutex);
@@@ -1433,9 -1383,6 +1434,9 @@@ bool iwl_mvm_bcast_filter_build_cmd(str
                .cmd = cmd,
        };
  
 +      if (IWL_MVM_FW_BCAST_FILTER_PASS_ALL)
 +              return false;
 +
        memset(cmd, 0, sizeof(*cmd));
        cmd->max_bcast_filters = ARRAY_SIZE(cmd->filters);
        cmd->max_macs = ARRAY_SIZE(cmd->macs);
@@@ -2224,9 -2171,25 +2225,9 @@@ static int iwl_mvm_mac_sched_scan_start
  
        mvm->scan_status = IWL_MVM_SCAN_SCHED;
  
 -      if (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN)) {
 -              ret = iwl_mvm_config_sched_scan(mvm, vif, req, ies);
 -              if (ret)
 -                      goto err;
 -      }
 -
 -      ret = iwl_mvm_config_sched_scan_profiles(mvm, req);
 +      ret = iwl_mvm_scan_offload_start(mvm, vif, req, ies);
        if (ret)
 -              goto err;
 -
 -      if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN)
 -              ret = iwl_mvm_unified_sched_scan_lmac(mvm, vif, req, ies);
 -      else
 -              ret = iwl_mvm_sched_scan_start(mvm, req);
 -
 -      if (!ret)
 -              goto out;
 -err:
 -      mvm->scan_status = IWL_MVM_SCAN_NONE;
 +              mvm->scan_status = IWL_MVM_SCAN_NONE;
  out:
        mutex_unlock(&mvm->mutex);
        return ret;
@@@ -3048,31 -3011,25 +3049,31 @@@ static void iwl_mvm_mac_flush(struct ie
        mvmvif = iwl_mvm_vif_from_mac80211(vif);
        mvmsta = iwl_mvm_sta_from_staid_protected(mvm, mvmvif->ap_sta_id);
  
 -      if (WARN_ON_ONCE(!mvmsta))
 -              goto done;
 +      if (WARN_ON_ONCE(!mvmsta)) {
 +              mutex_unlock(&mvm->mutex);
 +              return;
 +      }
  
        if (drop) {
                if (iwl_mvm_flush_tx_path(mvm, mvmsta->tfd_queue_msk, true))
                        IWL_ERR(mvm, "flush request fail\n");
 +              mutex_unlock(&mvm->mutex);
        } else {
 -              iwl_trans_wait_tx_queue_empty(mvm->trans,
 -                                            mvmsta->tfd_queue_msk);
 +              u32 tfd_queue_msk = mvmsta->tfd_queue_msk;
 +              mutex_unlock(&mvm->mutex);
 +
 +              /* this can take a while, and we may need/want other operations
 +               * to succeed while doing this, so do it without the mutex held
 +               */
 +              iwl_trans_wait_tx_queue_empty(mvm->trans, tfd_queue_msk);
        }
 -done:
 -      mutex_unlock(&mvm->mutex);
  }
  
  const struct ieee80211_ops iwl_mvm_hw_ops = {
        .tx = iwl_mvm_mac_tx,
        .ampdu_action = iwl_mvm_mac_ampdu_action,
        .start = iwl_mvm_mac_start,
 -      .restart_complete = iwl_mvm_mac_restart_complete,
 +      .reconfig_complete = iwl_mvm_mac_reconfig_complete,
        .stop = iwl_mvm_mac_stop,
        .add_interface = iwl_mvm_mac_add_interface,
        .remove_interface = iwl_mvm_mac_remove_interface,
index 256765accbc659f9d431899d110ced3afa26c8f0,845429c88cf403fdaffee73951fa99251b619137..d015fac06a62ae2fc144b38ffbebe59d060b5617
@@@ -548,6 -548,7 +548,7 @@@ struct iwl_mvm 
        enum iwl_ucode_type cur_ucode;
        bool ucode_loaded;
        bool init_ucode_complete;
+       bool calibrating;
        u32 error_event_table;
        u32 log_event_table;
        u32 umac_error_event_table;
        /* -1 for always, 0 for never, >0 for that many times */
        s8 restart_fw;
        struct work_struct fw_error_dump_wk;
 -      struct iwl_mvm_dump_ptrs *fw_error_dump;
  
  #ifdef CONFIG_IWLWIFI_LEDS
        struct led_classdev led;
  #ifdef CONFIG_PM_SLEEP
        struct wiphy_wowlan_support wowlan;
        int gtk_ivlen, gtk_icvlen, ptk_ivlen, ptk_icvlen;
 +
 +      /* sched scan settings for net detect */
 +      struct cfg80211_sched_scan_request *nd_config;
 +      struct ieee80211_scan_ies *nd_ies;
  #ifdef CONFIG_IWLWIFI_DEBUGFS
        u32 d3_wake_sysassert; /* must be u32 for debugfs_create_bool */
        bool d3_test_active;
@@@ -908,7 -906,8 +909,7 @@@ int iwl_mvm_mac_ctxt_add(struct iwl_mv
  int iwl_mvm_mac_ctxt_changed(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
                             bool force_assoc_off, const u8 *bssid_override);
  int iwl_mvm_mac_ctxt_remove(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
 -u32 iwl_mvm_mac_get_queues_mask(struct iwl_mvm *mvm,
 -                              struct ieee80211_vif *vif);
 +u32 iwl_mvm_mac_get_queues_mask(struct ieee80211_vif *vif);
  int iwl_mvm_mac_ctxt_beacon_changed(struct iwl_mvm *mvm,
                                    struct ieee80211_vif *vif);
  int iwl_mvm_rx_beacon_notif(struct iwl_mvm *mvm,
@@@ -951,10 -950,6 +952,10 @@@ int iwl_mvm_config_sched_scan_profiles(
                                       struct cfg80211_sched_scan_request *req);
  int iwl_mvm_sched_scan_start(struct iwl_mvm *mvm,
                             struct cfg80211_sched_scan_request *req);
 +int iwl_mvm_scan_offload_start(struct iwl_mvm *mvm,
 +                             struct ieee80211_vif *vif,
 +                             struct cfg80211_sched_scan_request *req,
 +                             struct ieee80211_scan_ies *ies);
  int iwl_mvm_scan_offload_stop(struct iwl_mvm *mvm, bool notify);
  int iwl_mvm_rx_scan_offload_results(struct iwl_mvm *mvm,
                                    struct iwl_rx_cmd_buffer *rxb,
@@@ -1212,9 -1207,11 +1213,9 @@@ void iwl_mvm_recalc_tdls_state(struct i
  void iwl_mvm_mac_mgd_protect_tdls_discover(struct ieee80211_hw *hw,
                                           struct ieee80211_vif *vif);
  
 +struct ieee80211_vif *iwl_mvm_get_bss_vif(struct iwl_mvm *mvm);
 +
  void iwl_mvm_nic_restart(struct iwl_mvm *mvm, bool fw_error);
 -#ifdef CONFIG_IWLWIFI_DEBUGFS
  void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm);
 -#else
 -static inline void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm) {}
 -#endif
  
  #endif /* __IWL_MVM_H__ */
index bd52ecfabedba1b3d6d36c416e42a6a011e06d08,5b719ee8e789075e6e7517512ca311cb6f9b6121..7a9578567f4f0853a0e560fb05c7ebb6377492c1
@@@ -403,9 -403,6 +403,9 @@@ iwl_op_mode_mvm_start(struct iwl_trans 
        if (cfg->max_rx_agg_size)
                hw->max_rx_aggregation_subframes = cfg->max_rx_agg_size;
  
 +      if (cfg->max_tx_agg_size)
 +              hw->max_tx_aggregation_subframes = cfg->max_tx_agg_size;
 +
        op_mode = hw->priv;
        op_mode->ops = &iwl_mvm_ops;
  
        }
        mvm->sf_state = SF_UNINIT;
        mvm->low_latency_agg_frame_limit = 6;
+       mvm->cur_ucode = IWL_UCODE_INIT;
  
        mutex_init(&mvm->mutex);
        mutex_init(&mvm->d0i3_suspend_mutex);
@@@ -587,18 -585,16 +588,18 @@@ static void iwl_op_mode_mvm_stop(struc
        ieee80211_unregister_hw(mvm->hw);
  
        kfree(mvm->scan_cmd);
 -      if (mvm->fw_error_dump) {
 -              vfree(mvm->fw_error_dump->op_mode_ptr);
 -              vfree(mvm->fw_error_dump->trans_ptr);
 -              kfree(mvm->fw_error_dump);
 -      }
        kfree(mvm->mcast_filter_cmd);
        mvm->mcast_filter_cmd = NULL;
  
  #if defined(CONFIG_PM_SLEEP) && defined(CONFIG_IWLWIFI_DEBUGFS)
        kfree(mvm->d3_resume_sram);
 +      if (mvm->nd_config) {
 +              kfree(mvm->nd_config->match_sets);
 +              kfree(mvm->nd_config);
 +              mvm->nd_config = NULL;
 +              kfree(mvm->nd_ies);
 +              mvm->nd_ies = NULL;
 +      }
  #endif
  
        iwl_trans_op_mode_leave(mvm->trans);
@@@ -757,6 -753,7 +758,7 @@@ void iwl_mvm_set_hw_ctkill_state(struc
  static bool iwl_mvm_set_hw_rfkill_state(struct iwl_op_mode *op_mode, bool state)
  {
        struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
+       bool calibrating = ACCESS_ONCE(mvm->calibrating);
  
        if (state)
                set_bit(IWL_MVM_STATUS_HW_RFKILL, &mvm->status);
  
        wiphy_rfkill_set_hw_state(mvm->hw->wiphy, iwl_mvm_is_radio_killed(mvm));
  
-       return state && mvm->cur_ucode != IWL_UCODE_INIT;
+       /* iwl_run_init_mvm_ucode is waiting for results, abort it */
+       if (calibrating)
+               iwl_abort_notification_waits(&mvm->notif_wait);
+       /*
+        * Stop the device if we run OPERATIONAL firmware or if we are in the
+        * middle of the calibrations.
+        */
+       return state && (mvm->cur_ucode != IWL_UCODE_INIT || calibrating);
  }
  
  static void iwl_mvm_free_skb(struct iwl_op_mode *op_mode, struct sk_buff *skb)
index 5cd59a43e1da51acb1fd4debb21ce8e9f38be1c0,7554f705383063fa3c7c3610595e9e39f0cc9af8..fb2a8628b8fca3ef75184471bf40faf89f7801d4
@@@ -270,8 -270,7 +270,8 @@@ static void iwl_mvm_scan_condition_iter
        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
        bool *global_bound = data;
  
 -      if (mvmvif->phy_ctxt && mvmvif->phy_ctxt->id < MAX_PHYS)
 +      if (vif->type != NL80211_IFTYPE_P2P_DEVICE && mvmvif->phy_ctxt &&
 +          mvmvif->phy_ctxt->id < MAX_PHYS)
                *global_bound = true;
  }
  
@@@ -603,16 -602,6 +603,6 @@@ static int iwl_mvm_cancel_regular_scan(
                                               SCAN_COMPLETE_NOTIFICATION };
        int ret;
  
-       if (mvm->scan_status == IWL_MVM_SCAN_NONE)
-               return 0;
-       if (iwl_mvm_is_radio_killed(mvm)) {
-               ieee80211_scan_completed(mvm->hw, true);
-               iwl_mvm_unref(mvm, IWL_MVM_REF_SCAN);
-               mvm->scan_status = IWL_MVM_SCAN_NONE;
-               return 0;
-       }
        iwl_init_notification_wait(&mvm->notif_wait, &wait_scan_abort,
                                   scan_abort_notif,
                                   ARRAY_SIZE(scan_abort_notif),
@@@ -673,7 -662,6 +663,7 @@@ int iwl_mvm_rx_scan_offload_complete_no
                mvm->scan_status = IWL_MVM_SCAN_NONE;
                ieee80211_scan_completed(mvm->hw,
                                         status == IWL_SCAN_OFFLOAD_ABORTED);
 +              iwl_mvm_unref(mvm, IWL_MVM_REF_SCAN);
        }
  
        mvm->last_ebs_successful = !ebs_status;
@@@ -1009,31 -997,6 +999,31 @@@ int iwl_mvm_sched_scan_start(struct iwl
                                    sizeof(scan_req), &scan_req);
  }
  
 +int iwl_mvm_scan_offload_start(struct iwl_mvm *mvm,
 +                             struct ieee80211_vif *vif,
 +                             struct cfg80211_sched_scan_request *req,
 +                             struct ieee80211_scan_ies *ies)
 +{
 +      int ret;
 +
 +      if ((mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN)) {
 +              ret = iwl_mvm_config_sched_scan_profiles(mvm, req);
 +              if (ret)
 +                      return ret;
 +              ret = iwl_mvm_unified_sched_scan_lmac(mvm, vif, req, ies);
 +      } else {
 +              ret = iwl_mvm_config_sched_scan(mvm, vif, req, ies);
 +              if (ret)
 +                      return ret;
 +              ret = iwl_mvm_config_sched_scan_profiles(mvm, req);
 +              if (ret)
 +                      return ret;
 +              ret = iwl_mvm_sched_scan_start(mvm, req);
 +      }
 +
 +      return ret;
 +}
 +
  static int iwl_mvm_send_scan_offload_abort(struct iwl_mvm *mvm)
  {
        int ret;
@@@ -1108,12 -1071,8 +1098,12 @@@ int iwl_mvm_scan_offload_stop(struct iw
        /*
         * Clear the scan status so the next scan requests will succeed. This
         * also ensures the Rx handler doesn't do anything, as the scan was
 -       * stopped from above.
 +       * stopped from above. Since the rx handler won't do anything now,
 +       * we have to release the scan reference here.
         */
 +      if (mvm->scan_status == IWL_MVM_SCAN_OS)
 +              iwl_mvm_unref(mvm, IWL_MVM_REF_SCAN);
 +
        mvm->scan_status = IWL_MVM_SCAN_NONE;
  
        if (notify) {
@@@ -1431,6 -1390,16 +1421,16 @@@ int iwl_mvm_unified_sched_scan_lmac(str
  
  int iwl_mvm_cancel_scan(struct iwl_mvm *mvm)
  {
+       if (mvm->scan_status == IWL_MVM_SCAN_NONE)
+               return 0;
+       if (iwl_mvm_is_radio_killed(mvm)) {
+               ieee80211_scan_completed(mvm->hw, true);
+               iwl_mvm_unref(mvm, IWL_MVM_REF_SCAN);
+               mvm->scan_status = IWL_MVM_SCAN_NONE;
+               return 0;
+       }
        if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN)
                return iwl_mvm_scan_offload_stop(mvm, true);
        return iwl_mvm_cancel_regular_scan(mvm);
index 40a290603eade3ffc9eb173e846ad9dd2a711a9f,dd2f3f8baa9dbea639005b3b65dbbbda9109408f..ea8efed25c6abadbe97001ce04e7823f2da8d157
@@@ -133,7 -133,7 +133,7 @@@ static void iwl_pcie_alloc_fw_monitor(s
                break;
        }
  
 -      if (!page)
 +      if (WARN_ON_ONCE(!page))
                return;
  
        trans_pcie->fw_mon_page = page;
@@@ -746,12 -746,15 +746,12 @@@ static int iwl_pcie_load_given_ucode(st
        int ret = 0;
        int first_ucode_section;
  
 -      IWL_DEBUG_FW(trans,
 -                   "working with %s image\n",
 -                   image->is_secure ? "Secured" : "Non Secured");
        IWL_DEBUG_FW(trans,
                     "working with %s CPU\n",
                     image->is_dual_cpus ? "Dual" : "Single");
  
        /* configure the ucode to be ready to get the secured image */
 -      if (image->is_secure) {
 +      if (iwl_has_secure_boot(trans->hw_rev, trans->cfg->device_family)) {
                /* set secure boot inspector addresses */
                iwl_write_prph(trans,
                               LMPM_SECURE_INSPECTOR_CODE_ADDR,
                               LMPM_SECURE_CPU2_HDR_MEM_SPACE);
  
                /* load to FW the binary sections of CPU2 */
 -              if (image->is_secure)
 +              if (iwl_has_secure_boot(trans->hw_rev,
 +                                      trans->cfg->device_family))
                        ret = iwl_pcie_load_cpu_secured_sections(
                                                        trans, image, 2,
                                                        &first_ucode_section);
        else
                iwl_write32(trans, CSR_RESET, 0);
  
 -      if (image->is_secure) {
 +      if (iwl_has_secure_boot(trans->hw_rev, trans->cfg->device_family)) {
                /* wait for image verification to complete  */
                ret = iwl_poll_prph_bit(trans,
                                        LMPM_SECURE_BOOT_CPU1_STATUS_ADDR,
@@@ -913,7 -915,8 +913,8 @@@ static void iwl_trans_pcie_stop_device(
         * restart. So don't process again if the device is
         * already dead.
         */
-       if (test_bit(STATUS_DEVICE_ENABLED, &trans->status)) {
+       if (test_and_clear_bit(STATUS_DEVICE_ENABLED, &trans->status)) {
+               IWL_DEBUG_INFO(trans, "DEVICE_ENABLED bit was set and is now cleared\n");
                iwl_pcie_tx_stop(trans);
                iwl_pcie_rx_stop(trans);
  
        /* clear all status bits */
        clear_bit(STATUS_SYNC_HCMD_ACTIVE, &trans->status);
        clear_bit(STATUS_INT_ENABLED, &trans->status);
-       clear_bit(STATUS_DEVICE_ENABLED, &trans->status);
        clear_bit(STATUS_TPOWER_PMI, &trans->status);
        clear_bit(STATUS_RFKILL, &trans->status);
  
@@@ -1021,6 -1023,14 +1021,6 @@@ static int iwl_trans_pcie_d3_resume(str
                return 0;
        }
  
 -      iwl_pcie_set_pwr(trans, false);
 -
 -      val = iwl_read32(trans, CSR_RESET);
 -      if (val & CSR_RESET_REG_FLAG_NEVO_RESET) {
 -              *status = IWL_D3_STATUS_RESET;
 -              return 0;
 -      }
 -
        /*
         * Also enables interrupts - none will happen as the device doesn't
         * know we're waking it up, only when the opmode actually tells it
                return ret;
        }
  
 +      iwl_pcie_set_pwr(trans, false);
 +
        iwl_trans_pcie_tx_reset(trans);
  
        ret = iwl_pcie_rx_init(trans);
                return ret;
        }
  
 -      *status = IWL_D3_STATUS_ALIVE;
 +      val = iwl_read32(trans, CSR_RESET);
 +      if (val & CSR_RESET_REG_FLAG_NEVO_RESET)
 +              *status = IWL_D3_STATUS_RESET;
 +      else
 +              *status = IWL_D3_STATUS_ALIVE;
 +
        return 0;
  }
  
        IWL_ERR(trans, "failed to create the trans debugfs entry\n");
        return -ENOMEM;
  }
 +#else
 +static int iwl_trans_pcie_dbgfs_register(struct iwl_trans *trans,
 +                                       struct dentry *dir)
 +{
 +      return 0;
 +}
 +#endif /*CONFIG_IWLWIFI_DEBUGFS */
  
  static u32 iwl_trans_pcie_get_cmdlen(struct iwl_tfd *tfd)
  {
@@@ -1898,8 -1894,7 +1898,7 @@@ static u32 iwl_trans_pcie_dump_prph(str
                int reg;
                __le32 *val;
  
-               prph_len += sizeof(*data) + sizeof(*prph) +
-                       num_bytes_in_chunk;
+               prph_len += sizeof(**data) + sizeof(*prph) + num_bytes_in_chunk;
  
                (*data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_PRPH);
                (*data)->len = cpu_to_le32(sizeof(*prph) +
@@@ -2049,6 -2044,13 +2048,6 @@@ struct iwl_trans_dump_data *iwl_trans_p
  
        return dump_data;
  }
 -#else
 -static int iwl_trans_pcie_dbgfs_register(struct iwl_trans *trans,
 -                                       struct dentry *dir)
 -{
 -      return 0;
 -}
 -#endif /*CONFIG_IWLWIFI_DEBUGFS */
  
  static const struct iwl_trans_ops trans_ops_pcie = {
        .start_hw = iwl_trans_pcie_start_hw,
        .release_nic_access = iwl_trans_pcie_release_nic_access,
        .set_bits_mask = iwl_trans_pcie_set_bits_mask,
  
 -#ifdef CONFIG_IWLWIFI_DEBUGFS
        .dump_data = iwl_trans_pcie_dump_data,
 -#endif
  };
  
  struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev,
index 209db62ee627e4efd9999efe5dda9d1bce01ea6c,c9ad4cf1adfb4b1d7a30068c2546ac7354345139..77fbf30350387b6800c5938cf558902e16b389e2
@@@ -412,9 -412,6 +412,9 @@@ struct mac80211_hwsim_data 
        struct mac_address addresses[2];
        int channels, idx;
        bool use_chanctx;
 +      bool destroy_on_close;
 +      struct work_struct destroy_work;
 +      u32 portid;
  
        struct ieee80211_channel *tmp_chan;
        struct delayed_work roc_done;
        /*
         * Only radios in the same group can communicate together (the
         * channel has to match too). Each bit represents a group. A
 -       * radio can be in more then one group.
 +       * radio can be in more than one group.
         */
        u64 group;
  
        s64 bcn_delta;
        /* absolute beacon transmission time. Used to cover up "tx" delay. */
        u64 abs_bcn_ts;
 +
 +      /* Stats */
 +      u64 tx_pkts;
 +      u64 rx_pkts;
 +      u64 tx_bytes;
 +      u64 rx_bytes;
 +      u64 tx_dropped;
 +      u64 tx_failed;
  };
  
  
@@@ -487,14 -476,6 +487,14 @@@ static struct genl_family hwsim_genl_fa
        .maxattr = HWSIM_ATTR_MAX,
  };
  
 +enum hwsim_multicast_groups {
 +      HWSIM_MCGRP_CONFIG,
 +};
 +
 +static const struct genl_multicast_group hwsim_mcgrps[] = {
 +      [HWSIM_MCGRP_CONFIG] = { .name = "config", },
 +};
 +
  /* MAC80211_HWSIM netlink policy */
  
  static const struct nla_policy hwsim_genl_policy[HWSIM_ATTR_MAX + 1] = {
        [HWSIM_ATTR_REG_CUSTOM_REG] = { .type = NLA_U32 },
        [HWSIM_ATTR_REG_STRICT_REG] = { .type = NLA_FLAG },
        [HWSIM_ATTR_SUPPORT_P2P_DEVICE] = { .type = NLA_FLAG },
 +      [HWSIM_ATTR_DESTROY_RADIO_ON_CLOSE] = { .type = NLA_FLAG },
 +      [HWSIM_ATTR_RADIO_NAME] = { .type = NLA_STRING },
 +      [HWSIM_ATTR_NO_VIF] = { .type = NLA_FLAG },
 +      [HWSIM_ATTR_FREQ] = { .type = NLA_U32 },
  };
  
  static void mac80211_hwsim_tx_frame(struct ieee80211_hw *hw,
@@@ -884,10 -861,8 +884,10 @@@ static void mac80211_hwsim_tx_frame_nl(
        /* If the queue contains MAX_QUEUE skb's drop some */
        if (skb_queue_len(&data->pending) >= MAX_QUEUE) {
                /* Droping until WARN_QUEUE level */
 -              while (skb_queue_len(&data->pending) >= WARN_QUEUE)
 -                      skb_dequeue(&data->pending);
 +              while (skb_queue_len(&data->pending) >= WARN_QUEUE) {
 +                      ieee80211_free_txskb(hw, skb_dequeue(&data->pending));
 +                      data->tx_dropped++;
 +              }
        }
  
        skb = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_ATOMIC);
        if (nla_put_u32(skb, HWSIM_ATTR_FLAGS, hwsim_flags))
                goto nla_put_failure;
  
 +      if (nla_put_u32(skb, HWSIM_ATTR_FREQ, data->channel->center_freq))
 +              goto nla_put_failure;
 +
        /* We get the tx control (rate and retries) info*/
  
        for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) {
  
        /* Enqueue the packet */
        skb_queue_tail(&data->pending, my_skb);
 +      data->tx_pkts++;
 +      data->tx_bytes += my_skb->len;
        return;
  
  nla_put_failure:
        printk(KERN_DEBUG "mac80211_hwsim: error occurred in %s\n", __func__);
 +      ieee80211_free_txskb(hw, my_skb);
 +      data->tx_failed++;
  }
  
  static bool hwsim_chans_compat(struct ieee80211_channel *c1,
@@@ -1098,8 -1066,6 +1098,8 @@@ static bool mac80211_hwsim_tx_frame_no_
                rx_status.mactime = now + data2->tsf_offset;
  
                memcpy(IEEE80211_SKB_RXCB(nskb), &rx_status, sizeof(rx_status));
 +              data2->rx_pkts++;
 +              data2->rx_bytes += nskb->len;
                ieee80211_rx_irqsafe(data2->hw, nskb);
        }
        spin_unlock(&hwsim_radio_lock);
@@@ -1167,8 -1133,6 +1167,8 @@@ static void mac80211_hwsim_tx(struct ie
                return mac80211_hwsim_tx_frame_nl(hw, skb, _portid);
  
        /* NO wmediumd detected, perfect medium simulation */
 +      data->tx_pkts++;
 +      data->tx_bytes += skb->len;
        ack = mac80211_hwsim_tx_frame_no_nl(hw, skb, channel);
  
        if (ack && skb->len >= 16) {
@@@ -1952,57 -1916,6 +1952,57 @@@ static void mac80211_hwsim_unassign_vif
        hwsim_check_chanctx_magic(ctx);
  }
  
 +static const char mac80211_hwsim_gstrings_stats[][ETH_GSTRING_LEN] = {
 +      "tx_pkts_nic",
 +      "tx_bytes_nic",
 +      "rx_pkts_nic",
 +      "rx_bytes_nic",
 +      "d_tx_dropped",
 +      "d_tx_failed",
 +      "d_ps_mode",
 +      "d_group",
 +      "d_tx_power",
 +};
 +
 +#define MAC80211_HWSIM_SSTATS_LEN ARRAY_SIZE(mac80211_hwsim_gstrings_stats)
 +
 +static void mac80211_hwsim_get_et_strings(struct ieee80211_hw *hw,
 +                                        struct ieee80211_vif *vif,
 +                                        u32 sset, u8 *data)
 +{
 +      if (sset == ETH_SS_STATS)
 +              memcpy(data, *mac80211_hwsim_gstrings_stats,
 +                     sizeof(mac80211_hwsim_gstrings_stats));
 +}
 +
 +static int mac80211_hwsim_get_et_sset_count(struct ieee80211_hw *hw,
 +                                          struct ieee80211_vif *vif, int sset)
 +{
 +      if (sset == ETH_SS_STATS)
 +              return MAC80211_HWSIM_SSTATS_LEN;
 +      return 0;
 +}
 +
 +static void mac80211_hwsim_get_et_stats(struct ieee80211_hw *hw,
 +                                      struct ieee80211_vif *vif,
 +                                      struct ethtool_stats *stats, u64 *data)
 +{
 +      struct mac80211_hwsim_data *ar = hw->priv;
 +      int i = 0;
 +
 +      data[i++] = ar->tx_pkts;
 +      data[i++] = ar->tx_bytes;
 +      data[i++] = ar->rx_pkts;
 +      data[i++] = ar->rx_bytes;
 +      data[i++] = ar->tx_dropped;
 +      data[i++] = ar->tx_failed;
 +      data[i++] = ar->ps;
 +      data[i++] = ar->group;
 +      data[i++] = ar->power_level;
 +
 +      WARN_ON(i != MAC80211_HWSIM_SSTATS_LEN);
 +}
 +
  static const struct ieee80211_ops mac80211_hwsim_ops = {
        .tx = mac80211_hwsim_tx,
        .start = mac80211_hwsim_start,
        .flush = mac80211_hwsim_flush,
        .get_tsf = mac80211_hwsim_get_tsf,
        .set_tsf = mac80211_hwsim_set_tsf,
 +      .get_et_sset_count = mac80211_hwsim_get_et_sset_count,
 +      .get_et_stats = mac80211_hwsim_get_et_stats,
 +      .get_et_strings = mac80211_hwsim_get_et_strings,
  };
  
  static struct ieee80211_ops mac80211_hwsim_mchan_ops;
  
 -static int mac80211_hwsim_create_radio(int channels, const char *reg_alpha2,
 -                                     const struct ieee80211_regdomain *regd,
 -                                     bool reg_strict, bool p2p_device,
 -                                     bool use_chanctx)
 +struct hwsim_new_radio_params {
 +      unsigned int channels;
 +      const char *reg_alpha2;
 +      const struct ieee80211_regdomain *regd;
 +      bool reg_strict;
 +      bool p2p_device;
 +      bool use_chanctx;
 +      bool destroy_on_close;
 +      const char *hwname;
 +      bool no_vif;
 +};
 +
 +static void hwsim_mcast_config_msg(struct sk_buff *mcast_skb,
 +                                 struct genl_info *info)
 +{
 +      if (info)
 +              genl_notify(&hwsim_genl_family, mcast_skb,
 +                          genl_info_net(info), info->snd_portid,
 +                          HWSIM_MCGRP_CONFIG, info->nlhdr, GFP_KERNEL);
 +      else
 +              genlmsg_multicast(&hwsim_genl_family, mcast_skb, 0,
 +                                HWSIM_MCGRP_CONFIG, GFP_KERNEL);
 +}
 +
 +static struct sk_buff *build_radio_msg(int cmd, int id,
 +                                     struct hwsim_new_radio_params *param)
 +{
 +      struct sk_buff *skb;
 +      void *data;
 +      int ret;
 +
 +      skb = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_KERNEL);
 +      if (!skb)
 +              return NULL;
 +
 +      data = genlmsg_put(skb, 0, 0, &hwsim_genl_family, 0, cmd);
 +      if (!data)
 +              goto error;
 +
 +      ret = nla_put_u32(skb, HWSIM_ATTR_RADIO_ID, id);
 +      if (ret < 0)
 +              goto error;
 +
 +      if (param->channels) {
 +              ret = nla_put_u32(skb, HWSIM_ATTR_CHANNELS, param->channels);
 +              if (ret < 0)
 +                      goto error;
 +      }
 +
 +      if (param->reg_alpha2) {
 +              ret = nla_put(skb, HWSIM_ATTR_REG_HINT_ALPHA2, 2,
 +                            param->reg_alpha2);
 +              if (ret < 0)
 +                      goto error;
 +      }
 +
 +      if (param->regd) {
 +              int i;
 +
 +              for (i = 0; hwsim_world_regdom_custom[i] != param->regd &&
 +                   i < ARRAY_SIZE(hwsim_world_regdom_custom); i++)
 +                      ;
 +
 +              if (i < ARRAY_SIZE(hwsim_world_regdom_custom)) {
 +                      ret = nla_put_u32(skb, HWSIM_ATTR_REG_CUSTOM_REG, i);
 +                      if (ret < 0)
 +                              goto error;
 +              }
 +      }
 +
 +      if (param->reg_strict) {
 +              ret = nla_put_flag(skb, HWSIM_ATTR_REG_STRICT_REG);
 +              if (ret < 0)
 +                      goto error;
 +      }
 +
 +      if (param->p2p_device) {
 +              ret = nla_put_flag(skb, HWSIM_ATTR_SUPPORT_P2P_DEVICE);
 +              if (ret < 0)
 +                      goto error;
 +      }
 +
 +      if (param->use_chanctx) {
 +              ret = nla_put_flag(skb, HWSIM_ATTR_USE_CHANCTX);
 +              if (ret < 0)
 +                      goto error;
 +      }
 +
 +      if (param->hwname) {
 +              ret = nla_put(skb, HWSIM_ATTR_RADIO_NAME,
 +                            strlen(param->hwname), param->hwname);
 +              if (ret < 0)
 +                      goto error;
 +      }
 +
 +      genlmsg_end(skb, data);
 +
 +      return skb;
 +
 +error:
 +      nlmsg_free(skb);
 +      return NULL;
 +}
 +
 +static void hswim_mcast_new_radio(int id, struct genl_info *info,
 +                                struct hwsim_new_radio_params *param)
 +{
 +      struct sk_buff *mcast_skb;
 +
 +      mcast_skb = build_radio_msg(HWSIM_CMD_NEW_RADIO, id, param);
 +      if (!mcast_skb)
 +              return;
 +
 +      hwsim_mcast_config_msg(mcast_skb, info);
 +}
 +
 +static int mac80211_hwsim_new_radio(struct genl_info *info,
 +                                  struct hwsim_new_radio_params *param)
  {
        int err;
        u8 addr[ETH_ALEN];
        const struct ieee80211_ops *ops = &mac80211_hwsim_ops;
        int idx;
  
 -      if (WARN_ON(channels > 1 && !use_chanctx))
 +      if (WARN_ON(param->channels > 1 && !param->use_chanctx))
                return -EINVAL;
  
        spin_lock_bh(&hwsim_radio_lock);
        idx = hwsim_radio_idx++;
        spin_unlock_bh(&hwsim_radio_lock);
  
 -      if (use_chanctx)
 +      if (param->use_chanctx)
                ops = &mac80211_hwsim_mchan_ops;
 -      hw = ieee80211_alloc_hw(sizeof(*data), ops);
 +      hw = ieee80211_alloc_hw_nm(sizeof(*data), ops, param->hwname);
        if (!hw) {
                printk(KERN_DEBUG "mac80211_hwsim: ieee80211_alloc_hw failed\n");
                err = -ENOMEM;
        if (err != 0) {
                printk(KERN_DEBUG "mac80211_hwsim: device_bind_driver failed (%d)\n",
                       err);
-               goto failed_hw;
+               goto failed_bind;
        }
  
        skb_queue_head_init(&data->pending);
        hw->wiphy->n_addresses = 2;
        hw->wiphy->addresses = data->addresses;
  
 -      data->channels = channels;
 -      data->use_chanctx = use_chanctx;
 +      data->channels = param->channels;
 +      data->use_chanctx = param->use_chanctx;
        data->idx = idx;
 +      data->destroy_on_close = param->destroy_on_close;
 +      if (info)
 +              data->portid = info->snd_portid;
  
        if (data->use_chanctx) {
                hw->wiphy->max_scan_ssids = 255;
                /* For channels > 1 DFS is not allowed */
                hw->wiphy->n_iface_combinations = 1;
                hw->wiphy->iface_combinations = &data->if_combination;
 -              if (p2p_device)
 +              if (param->p2p_device)
                        data->if_combination = hwsim_if_comb_p2p_dev[0];
                else
                        data->if_combination = hwsim_if_comb[0];
                data->if_combination.num_different_channels = data->channels;
 -      } else if (p2p_device) {
 +      } else if (param->p2p_device) {
                hw->wiphy->iface_combinations = hwsim_if_comb_p2p_dev;
                hw->wiphy->n_iface_combinations =
                        ARRAY_SIZE(hwsim_if_comb_p2p_dev);
                                     BIT(NL80211_IFTYPE_ADHOC) |
                                     BIT(NL80211_IFTYPE_MESH_POINT);
  
 -      if (p2p_device)
 +      if (param->p2p_device)
                hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_P2P_DEVICE);
  
        hw->flags = IEEE80211_HW_MFP_CAPABLE |
                sband->ht_cap.ht_supported = true;
                sband->ht_cap.cap = IEEE80211_HT_CAP_SUP_WIDTH_20_40 |
                                    IEEE80211_HT_CAP_GRN_FLD |
 +                                  IEEE80211_HT_CAP_SGI_20 |
                                    IEEE80211_HT_CAP_SGI_40 |
                                    IEEE80211_HT_CAP_DSSSCCK40;
                sband->ht_cap.ampdu_factor = 0x3;
        hw->max_rates = 4;
        hw->max_rate_tries = 11;
  
 -      if (reg_strict)
 +      if (param->reg_strict)
                hw->wiphy->regulatory_flags |= REGULATORY_STRICT_REG;
 -      if (regd) {
 +      if (param->regd) {
                hw->wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG;
 -              wiphy_apply_custom_regulatory(hw->wiphy, regd);
 +              wiphy_apply_custom_regulatory(hw->wiphy, param->regd);
                /* give the regulatory workqueue a chance to run */
                schedule_timeout_interruptible(1);
        }
  
 +      if (param->no_vif)
 +              hw->flags |= IEEE80211_HW_NO_AUTO_VIF;
 +
        err = ieee80211_register_hw(hw);
        if (err < 0) {
                printk(KERN_DEBUG "mac80211_hwsim: ieee80211_register_hw failed (%d)\n",
  
        wiphy_debug(hw->wiphy, "hwaddr %pM registered\n", hw->wiphy->perm_addr);
  
 -      if (reg_alpha2)
 -              regulatory_hint(hw->wiphy, reg_alpha2);
 +      if (param->reg_alpha2)
 +              regulatory_hint(hw->wiphy, param->reg_alpha2);
  
        data->debugfs = debugfs_create_dir("hwsim", hw->wiphy->debugfsdir);
        debugfs_create_file("ps", 0666, data->debugfs, data, &hwsim_fops_ps);
        list_add_tail(&data->list, &hwsim_radios);
        spin_unlock_bh(&hwsim_radio_lock);
  
 +      if (idx > 0)
 +              hswim_mcast_new_radio(idx, info, param);
 +
        return idx;
  
  failed_hw:
+       device_release_driver(data->dev);
+ failed_bind:
        device_unregister(data->dev);
  failed_drvdata:
        ieee80211_free_hw(hw);
@@@ -2404,48 -2192,8 +2406,48 @@@ failed
        return err;
  }
  
 -static void mac80211_hwsim_destroy_radio(struct mac80211_hwsim_data *data)
 +static void hwsim_mcast_del_radio(int id, const char *hwname,
 +                                struct genl_info *info)
 +{
 +      struct sk_buff *skb;
 +      void *data;
 +      int ret;
 +
 +      skb = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_KERNEL);
 +      if (!skb)
 +              return;
 +
 +      data = genlmsg_put(skb, 0, 0, &hwsim_genl_family, 0,
 +                         HWSIM_CMD_DEL_RADIO);
 +      if (!data)
 +              goto error;
 +
 +      ret = nla_put_u32(skb, HWSIM_ATTR_RADIO_ID, id);
 +      if (ret < 0)
 +              goto error;
 +
 +      if (hwname) {
 +              ret = nla_put(skb, HWSIM_ATTR_RADIO_NAME, strlen(hwname),
 +                            hwname);
 +              if (ret < 0)
 +                      goto error;
 +      }
 +
 +      genlmsg_end(skb, data);
 +
 +      hwsim_mcast_config_msg(skb, info);
 +
 +      return;
 +
 +error:
 +      nlmsg_free(skb);
 +}
 +
 +static void mac80211_hwsim_del_radio(struct mac80211_hwsim_data *data,
 +                                   const char *hwname,
 +                                   struct genl_info *info)
  {
 +      hwsim_mcast_del_radio(data->idx, hwname, info);
        debugfs_remove_recursive(data->debugfs);
        ieee80211_unregister_hw(data->hw);
        device_release_driver(data->dev);
@@@ -2463,7 -2211,7 +2465,7 @@@ static void mac80211_hwsim_free(void
                                                list))) {
                list_del(&data->list);
                spin_unlock_bh(&hwsim_radio_lock);
 -              mac80211_hwsim_destroy_radio(data);
 +              mac80211_hwsim_del_radio(data, NULL, NULL);
                spin_lock_bh(&hwsim_radio_lock);
        }
        spin_unlock_bh(&hwsim_radio_lock);
@@@ -2591,6 -2339,7 +2593,6 @@@ out
  static int hwsim_cloned_frame_received_nl(struct sk_buff *skb_2,
                                          struct genl_info *info)
  {
 -
        struct mac80211_hwsim_data *data2;
        struct ieee80211_rx_status rx_status;
        const u8 *dst;
  
        /* A frame is received from user space */
        memset(&rx_status, 0, sizeof(rx_status));
 +      /* TODO: Check ATTR_FREQ if it exists, and maybe throw away off-channel
 +       * packets?
 +       */
        rx_status.freq = data2->channel->center_freq;
        rx_status.band = data2->channel->band;
        rx_status.rate_idx = nla_get_u32(info->attrs[HWSIM_ATTR_RX_RATE]);
        rx_status.signal = nla_get_u32(info->attrs[HWSIM_ATTR_SIGNAL]);
  
        memcpy(IEEE80211_SKB_RXCB(skb), &rx_status, sizeof(rx_status));
 +      data2->rx_pkts++;
 +      data2->rx_bytes += skb->len;
        ieee80211_rx_irqsafe(data2->hw, skb);
  
        return 0;
  err:
        printk(KERN_DEBUG "mac80211_hwsim: error occurred in %s\n", __func__);
 -      goto out;
  out:
        dev_kfree_skb(skb);
        return -EINVAL;
@@@ -2684,72 -2429,54 +2686,72 @@@ static int hwsim_register_received_nl(s
        return 0;
  }
  
 -static int hwsim_create_radio_nl(struct sk_buff *msg, struct genl_info *info)
 +static int hwsim_new_radio_nl(struct sk_buff *msg, struct genl_info *info)
  {
 -      unsigned int chans = channels;
 -      const char *alpha2 = NULL;
 -      const struct ieee80211_regdomain *regd = NULL;
 -      bool reg_strict = info->attrs[HWSIM_ATTR_REG_STRICT_REG];
 -      bool p2p_device = info->attrs[HWSIM_ATTR_SUPPORT_P2P_DEVICE];
 -      bool use_chanctx;
 +      struct hwsim_new_radio_params param = { 0 };
 +
 +      param.reg_strict = info->attrs[HWSIM_ATTR_REG_STRICT_REG];
 +      param.p2p_device = info->attrs[HWSIM_ATTR_SUPPORT_P2P_DEVICE];
 +      param.channels = channels;
 +      param.destroy_on_close =
 +              info->attrs[HWSIM_ATTR_DESTROY_RADIO_ON_CLOSE];
  
        if (info->attrs[HWSIM_ATTR_CHANNELS])
 -              chans = nla_get_u32(info->attrs[HWSIM_ATTR_CHANNELS]);
 +              param.channels = nla_get_u32(info->attrs[HWSIM_ATTR_CHANNELS]);
 +
 +      if (info->attrs[HWSIM_ATTR_NO_VIF])
 +              param.no_vif = true;
 +
 +      if (info->attrs[HWSIM_ATTR_RADIO_NAME])
 +              param.hwname = nla_data(info->attrs[HWSIM_ATTR_RADIO_NAME]);
  
        if (info->attrs[HWSIM_ATTR_USE_CHANCTX])
 -              use_chanctx = true;
 +              param.use_chanctx = true;
        else
 -              use_chanctx = (chans > 1);
 +              param.use_chanctx = (param.channels > 1);
  
        if (info->attrs[HWSIM_ATTR_REG_HINT_ALPHA2])
 -              alpha2 = nla_data(info->attrs[HWSIM_ATTR_REG_HINT_ALPHA2]);
 +              param.reg_alpha2 =
 +                      nla_data(info->attrs[HWSIM_ATTR_REG_HINT_ALPHA2]);
  
        if (info->attrs[HWSIM_ATTR_REG_CUSTOM_REG]) {
                u32 idx = nla_get_u32(info->attrs[HWSIM_ATTR_REG_CUSTOM_REG]);
  
                if (idx >= ARRAY_SIZE(hwsim_world_regdom_custom))
                        return -EINVAL;
 -              regd = hwsim_world_regdom_custom[idx];
 +              param.regd = hwsim_world_regdom_custom[idx];
        }
  
 -      return mac80211_hwsim_create_radio(chans, alpha2, regd, reg_strict,
 -                                         p2p_device, use_chanctx);
 +      return mac80211_hwsim_new_radio(info, &param);
  }
  
 -static int hwsim_destroy_radio_nl(struct sk_buff *msg, struct genl_info *info)
 +static int hwsim_del_radio_nl(struct sk_buff *msg, struct genl_info *info)
  {
        struct mac80211_hwsim_data *data;
 -      int idx;
 +      s64 idx = -1;
 +      const char *hwname = NULL;
  
 -      if (!info->attrs[HWSIM_ATTR_RADIO_ID])
 +      if (info->attrs[HWSIM_ATTR_RADIO_ID])
 +              idx = nla_get_u32(info->attrs[HWSIM_ATTR_RADIO_ID]);
 +      else if (info->attrs[HWSIM_ATTR_RADIO_NAME])
 +              hwname = (void *)nla_data(info->attrs[HWSIM_ATTR_RADIO_NAME]);
 +      else
                return -EINVAL;
 -      idx = nla_get_u32(info->attrs[HWSIM_ATTR_RADIO_ID]);
  
        spin_lock_bh(&hwsim_radio_lock);
        list_for_each_entry(data, &hwsim_radios, list) {
 -              if (data->idx != idx)
 -                      continue;
 +              if (idx >= 0) {
 +                      if (data->idx != idx)
 +                              continue;
 +              } else {
 +                      if (hwname &&
 +                          strcmp(hwname, wiphy_name(data->hw->wiphy)))
 +                              continue;
 +              }
 +
                list_del(&data->list);
                spin_unlock_bh(&hwsim_radio_lock);
 -              mac80211_hwsim_destroy_radio(data);
 +              mac80211_hwsim_del_radio(data, hwname, info);
                return 0;
        }
        spin_unlock_bh(&hwsim_radio_lock);
@@@ -2776,42 -2503,19 +2778,42 @@@ static const struct genl_ops hwsim_ops[
                .doit = hwsim_tx_info_frame_received_nl,
        },
        {
 -              .cmd = HWSIM_CMD_CREATE_RADIO,
 +              .cmd = HWSIM_CMD_NEW_RADIO,
                .policy = hwsim_genl_policy,
 -              .doit = hwsim_create_radio_nl,
 +              .doit = hwsim_new_radio_nl,
                .flags = GENL_ADMIN_PERM,
        },
        {
 -              .cmd = HWSIM_CMD_DESTROY_RADIO,
 +              .cmd = HWSIM_CMD_DEL_RADIO,
                .policy = hwsim_genl_policy,
 -              .doit = hwsim_destroy_radio_nl,
 +              .doit = hwsim_del_radio_nl,
                .flags = GENL_ADMIN_PERM,
        },
  };
  
 +static void destroy_radio(struct work_struct *work)
 +{
 +      struct mac80211_hwsim_data *data =
 +              container_of(work, struct mac80211_hwsim_data, destroy_work);
 +
 +      mac80211_hwsim_del_radio(data, NULL, NULL);
 +}
 +
 +static void remove_user_radios(u32 portid)
 +{
 +      struct mac80211_hwsim_data *entry, *tmp;
 +
 +      spin_lock_bh(&hwsim_radio_lock);
 +      list_for_each_entry_safe(entry, tmp, &hwsim_radios, list) {
 +              if (entry->destroy_on_close && entry->portid == portid) {
 +                      list_del(&entry->list);
 +                      INIT_WORK(&entry->destroy_work, destroy_radio);
 +                      schedule_work(&entry->destroy_work);
 +              }
 +      }
 +      spin_unlock_bh(&hwsim_radio_lock);
 +}
 +
  static int mac80211_hwsim_netlink_notify(struct notifier_block *nb,
                                         unsigned long state,
                                         void *_notify)
        if (state != NETLINK_URELEASE)
                return NOTIFY_DONE;
  
 +      remove_user_radios(notify->portid);
 +
        if (notify->portid == wmediumd_portid) {
                printk(KERN_INFO "mac80211_hwsim: wmediumd released netlink"
                       " socket, switching to perfect channel medium\n");
@@@ -2842,9 -2544,7 +2844,9 @@@ static int hwsim_init_netlink(void
  
        printk(KERN_INFO "mac80211_hwsim: initializing netlink\n");
  
 -      rc = genl_register_family_with_ops(&hwsim_genl_family, hwsim_ops);
 +      rc = genl_register_family_with_ops_groups(&hwsim_genl_family,
 +                                                hwsim_ops,
 +                                                hwsim_mcgrps);
        if (rc)
                goto failure;
  
@@@ -2905,73 -2605,69 +2907,73 @@@ static int __init init_mac80211_hwsim(v
                goto out_unregister_driver;
        }
  
 +      err = hwsim_init_netlink();
 +      if (err < 0)
 +              goto out_unregister_driver;
 +
        for (i = 0; i < radios; i++) {
 -              const char *reg_alpha2 = NULL;
 -              const struct ieee80211_regdomain *regd = NULL;
 -              bool reg_strict = false;
 +              struct hwsim_new_radio_params param = { 0 };
 +
 +              param.channels = channels;
  
                switch (regtest) {
                case HWSIM_REGTEST_DIFF_COUNTRY:
                        if (i < ARRAY_SIZE(hwsim_alpha2s))
 -                              reg_alpha2 = hwsim_alpha2s[i];
 +                              param.reg_alpha2 = hwsim_alpha2s[i];
                        break;
                case HWSIM_REGTEST_DRIVER_REG_FOLLOW:
                        if (!i)
 -                              reg_alpha2 = hwsim_alpha2s[0];
 +                              param.reg_alpha2 = hwsim_alpha2s[0];
                        break;
                case HWSIM_REGTEST_STRICT_ALL:
 -                      reg_strict = true;
 +                      param.reg_strict = true;
                case HWSIM_REGTEST_DRIVER_REG_ALL:
 -                      reg_alpha2 = hwsim_alpha2s[0];
 +                      param.reg_alpha2 = hwsim_alpha2s[0];
                        break;
                case HWSIM_REGTEST_WORLD_ROAM:
                        if (i == 0)
 -                              regd = &hwsim_world_regdom_custom_01;
 +                              param.regd = &hwsim_world_regdom_custom_01;
                        break;
                case HWSIM_REGTEST_CUSTOM_WORLD:
 -                      regd = &hwsim_world_regdom_custom_01;
 +                      param.regd = &hwsim_world_regdom_custom_01;
                        break;
                case HWSIM_REGTEST_CUSTOM_WORLD_2:
                        if (i == 0)
 -                              regd = &hwsim_world_regdom_custom_01;
 +                              param.regd = &hwsim_world_regdom_custom_01;
                        else if (i == 1)
 -                              regd = &hwsim_world_regdom_custom_02;
 +                              param.regd = &hwsim_world_regdom_custom_02;
                        break;
                case HWSIM_REGTEST_STRICT_FOLLOW:
                        if (i == 0) {
 -                              reg_strict = true;
 -                              reg_alpha2 = hwsim_alpha2s[0];
 +                              param.reg_strict = true;
 +                              param.reg_alpha2 = hwsim_alpha2s[0];
                        }
                        break;
                case HWSIM_REGTEST_STRICT_AND_DRIVER_REG:
                        if (i == 0) {
 -                              reg_strict = true;
 -                              reg_alpha2 = hwsim_alpha2s[0];
 +                              param.reg_strict = true;
 +                              param.reg_alpha2 = hwsim_alpha2s[0];
                        } else if (i == 1) {
 -                              reg_alpha2 = hwsim_alpha2s[1];
 +                              param.reg_alpha2 = hwsim_alpha2s[1];
                        }
                        break;
                case HWSIM_REGTEST_ALL:
                        switch (i) {
                        case 0:
 -                              regd = &hwsim_world_regdom_custom_01;
 +                              param.regd = &hwsim_world_regdom_custom_01;
                                break;
                        case 1:
 -                              regd = &hwsim_world_regdom_custom_02;
 +                              param.regd = &hwsim_world_regdom_custom_02;
                                break;
                        case 2:
 -                              reg_alpha2 = hwsim_alpha2s[0];
 +                              param.reg_alpha2 = hwsim_alpha2s[0];
                                break;
                        case 3:
 -                              reg_alpha2 = hwsim_alpha2s[1];
 +                              param.reg_alpha2 = hwsim_alpha2s[1];
                                break;
                        case 4:
 -                              reg_strict = true;
 -                              reg_alpha2 = hwsim_alpha2s[2];
 +                              param.reg_strict = true;
 +                              param.reg_alpha2 = hwsim_alpha2s[2];
                                break;
                        }
                        break;
                        break;
                }
  
 -              err = mac80211_hwsim_create_radio(channels, reg_alpha2,
 -                                                regd, reg_strict,
 -                                                support_p2p_device,
 -                                                channels > 1);
 +              param.p2p_device = support_p2p_device;
 +              param.use_chanctx = channels > 1;
 +
 +              err = mac80211_hwsim_new_radio(NULL, &param);
                if (err < 0)
                        goto out_free_radios;
        }
        }
        rtnl_unlock();
  
 -      err = hwsim_init_netlink();
 -      if (err < 0)
 -              goto out_free_mon;
 -
        return 0;
  
  out_free_mon:
index 51a67f34c8cb093c0c022b779e56c71056929cd0,f55658d15c60710d44603d12b9fbab58414050fc..5a690d5210f02b5206ca139006adc534c4b88617
@@@ -48,11 -48,6 +48,11 @@@ enum 
        MWIFIEX_SYNC_CMD
  };
  
 +#define MWIFIEX_DRIVER_MODE_STA                       BIT(0)
 +#define MWIFIEX_DRIVER_MODE_UAP                       BIT(1)
 +#define MWIFIEX_DRIVER_MODE_P2P                       BIT(2)
 +#define MWIFIEX_DRIVER_MODE_BITMASK           (BIT(0) | BIT(1) | BIT(2))
 +
  #define MWIFIEX_MAX_AP                                64
  
  #define MWIFIEX_DEFAULT_WATCHDOG_TIMEOUT      (5 * HZ)
   */
  #define IS_CARD_RX_RCVD(adapter) (adapter->cmd_resp_received || \
                                adapter->event_received || \
 -                              ((adapter->iface_type != MWIFIEX_USB) && \
 -                              adapter->data_received) || \
 -                              ((adapter->iface_type == MWIFIEX_USB) && \
 -                              !skb_queue_empty(&adapter->usb_rx_data_q)))
 +                              adapter->data_received)
  
  #define MWIFIEX_TYPE_CMD                              1
  #define MWIFIEX_TYPE_DATA                             0
@@@ -506,11 -504,8 +506,11 @@@ struct mwifiex_private 
        struct mwifiex_wmm_desc wmm;
        atomic_t wmm_tx_pending[IEEE80211_NUM_ACS];
        struct list_head sta_list;
 -      /* spin lock for associated station list */
 +      /* spin lock for associated station/TDLS peers list */
        spinlock_t sta_list_spinlock;
 +      struct list_head auto_tdls_list;
 +      /* spin lock for auto TDLS peer list */
 +      spinlock_t auto_tdls_lock;
        struct list_head tx_ba_stream_tbl_ptr;
        /* spin lock for tx_ba_stream_tbl_ptr queue */
        spinlock_t tx_ba_stream_tbl_lock;
        bool hs2_enabled;
        struct station_parameters *sta_params;
        struct sk_buff_head tdls_txq;
 +      u8 check_tdls_tx;
 +      struct timer_list auto_tdls_timer;
 +      bool auto_tdls_timer_active;
  };
  
  enum mwifiex_ba_status {
@@@ -600,6 -592,7 +600,7 @@@ struct reorder_tmr_cnxt 
        struct timer_list timer;
        struct mwifiex_rx_reorder_tbl *ptr;
        struct mwifiex_private *priv;
+       u8 timer_is_set;
  };
  
  struct mwifiex_rx_reorder_tbl {
@@@ -677,17 -670,6 +678,17 @@@ struct mwifiex_sta_node 
        struct mwifiex_tdls_capab tdls_cap;
  };
  
 +struct mwifiex_auto_tdls_peer {
 +      struct list_head list;
 +      u8 mac_addr[ETH_ALEN];
 +      u8 tdls_status;
 +      int rssi;
 +      long rssi_jiffies;
 +      u8 failure_count;
 +      u8 do_discover;
 +      u8 do_setup;
 +};
 +
  struct mwifiex_if_ops {
        int (*init_if) (struct mwifiex_adapter *);
        void (*cleanup_if) (struct mwifiex_adapter *);
        void (*cleanup_mpa_buf) (struct mwifiex_adapter *);
        int (*cmdrsp_complete) (struct mwifiex_adapter *, struct sk_buff *);
        int (*event_complete) (struct mwifiex_adapter *, struct sk_buff *);
 -      int (*data_complete) (struct mwifiex_adapter *);
        int (*init_fw_port) (struct mwifiex_adapter *);
        int (*dnld_fw) (struct mwifiex_adapter *, struct mwifiex_fw_image *);
        void (*card_reset) (struct mwifiex_adapter *);
        void (*fw_dump)(struct mwifiex_adapter *);
        int (*clean_pcie_ring) (struct mwifiex_adapter *adapter);
        void (*iface_work)(struct work_struct *work);
 +      void (*submit_rem_rx_urbs)(struct mwifiex_adapter *adapter);
  };
  
  struct mwifiex_adapter {
        spinlock_t scan_pending_q_lock;
        /* spin lock for RX processing routine */
        spinlock_t rx_proc_lock;
 -      struct sk_buff_head usb_rx_data_q;
        u32 scan_processing;
        u16 region_code;
        struct mwifiex_802_11d_domain_reg domain_reg;
        u8 curr_mem_idx;
        bool scan_chan_gap_enabled;
        struct sk_buff_head rx_data_q;
 +      struct mwifiex_chan_stats *chan_stats;
 +      u32 num_in_chan_stats;
 +      int survey_idx;
 +      bool auto_tdls;
  };
  
  int mwifiex_init_lock_list(struct mwifiex_adapter *adapter);
@@@ -1052,8 -1031,7 +1053,8 @@@ void mwifiex_set_11ac_ba_params(struct 
  int mwifiex_cmd_802_11_scan_ext(struct mwifiex_private *priv,
                                struct host_cmd_ds_command *cmd,
                                void *data_buf);
 -int mwifiex_ret_802_11_scan_ext(struct mwifiex_private *priv);
 +int mwifiex_ret_802_11_scan_ext(struct mwifiex_private *priv,
 +                              struct host_cmd_ds_command *resp);
  int mwifiex_handle_event_ext_scan_report(struct mwifiex_private *priv,
                                         void *buf);
  
@@@ -1323,17 -1301,6 +1324,17 @@@ u8 mwifiex_get_center_freq_index(struc
                                 u32 pri_chan, u8 chan_bw);
  int mwifiex_init_channel_scan_gap(struct mwifiex_adapter *adapter);
  
 +int mwifiex_tdls_check_tx(struct mwifiex_private *priv, struct sk_buff *skb);
 +void mwifiex_flush_auto_tdls_list(struct mwifiex_private *priv);
 +void mwifiex_auto_tdls_update_peer_status(struct mwifiex_private *priv,
 +                                        const u8 *mac, u8 link_status);
 +void mwifiex_auto_tdls_update_peer_signal(struct mwifiex_private *priv,
 +                                        u8 *mac, s8 snr, s8 nflr);
 +void mwifiex_check_auto_tdls(unsigned long context);
 +void mwifiex_add_auto_tdls_peer(struct mwifiex_private *priv, const u8 *mac);
 +void mwifiex_setup_auto_tdls_timer(struct mwifiex_private *priv);
 +void mwifiex_clean_auto_tdls(struct mwifiex_private *priv);
 +
  #ifdef CONFIG_DEBUG_FS
  void mwifiex_debugfs_init(void);
  void mwifiex_debugfs_remove(void);
index 4be278f7df51fa210416bc05d93a895c8187102c,1e9570fa874fe6309962d2de2251fc923da424cb..9b4d8a63791511ae54b143dae0209f65cd2cb387
@@@ -800,7 -800,7 +800,7 @@@ static void _rtl8821ae_phy_set_txpower_
                                 "Invalid RateSection %d in Band 2.4G,Rf Path %d, %dTx in PHY_SetTxPowerByRateBase()\n",
                                 rate_section, path, txnum);
                        break;
 -              };
 +              }
        } else if (band == BAND_ON_5G) {
                switch (rate_section) {
                case OFDM:
                                "Invalid RateSection %d in Band 5G, Rf Path %d, %dTx in PHY_SetTxPowerByRateBase()\n",
                                rate_section, path, txnum);
                        break;
 -              };
 +              }
        } else {
                RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD,
                        "Invalid Band %d in PHY_SetTxPowerByRateBase()\n", band);
@@@ -870,7 -870,7 +870,7 @@@ static u8 _rtl8821ae_phy_get_txpower_by
                                 "Invalid RateSection %d in Band 2.4G, Rf Path %d, %dTx in PHY_GetTxPowerByRateBase()\n",
                                 rate_section, path, txnum);
                        break;
 -              };
 +              }
        } else if (band == BAND_ON_5G) {
                switch (rate_section) {
                case OFDM:
                                 "Invalid RateSection %d in Band 5G, Rf Path %d, %dTx in PHY_GetTxPowerByRateBase()\n",
                                 rate_section, path, txnum);
                        break;
 -              };
 +              }
        } else {
                RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD,
                         "Invalid Band %d in PHY_GetTxPowerByRateBase()\n", band);
@@@ -1889,15 -1889,18 +1889,18 @@@ static void _rtl8821ae_store_tx_power_b
        struct rtl_phy *rtlphy = &rtlpriv->phy;
        u8 rate_section = _rtl8821ae_get_rate_section_index(regaddr);
  
-       if (band != BAND_ON_2_4G && band != BAND_ON_5G)
+       if (band != BAND_ON_2_4G && band != BAND_ON_5G) {
                RT_TRACE(rtlpriv, COMP_INIT, DBG_WARNING, "Invalid Band %d\n", band);
-       if (rfpath >= MAX_RF_PATH)
+               band = BAND_ON_2_4G;
+       }
+       if (rfpath >= MAX_RF_PATH) {
                RT_TRACE(rtlpriv, COMP_INIT, DBG_WARNING, "Invalid RfPath %d\n", rfpath);
-       if (txnum >= MAX_RF_PATH)
+               rfpath = MAX_RF_PATH - 1;
+       }
+       if (txnum >= MAX_RF_PATH) {
                RT_TRACE(rtlpriv, COMP_INIT, DBG_WARNING, "Invalid TxNum %d\n", txnum);
+               txnum = MAX_RF_PATH - 1;
+       }
        rtlphy->tx_power_by_rate_offset[band][rfpath][txnum][rate_section] = data;
        RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD,
                 "TxPwrByRateOffset[Band %d][RfPath %d][TxNum %d][RateSection %d] = 0x%x\n",
@@@ -3743,7 -3746,7 +3746,7 @@@ static void _rtl8821ae_iqk_tx_fill_iqc(
                break;
        default:
                break;
 -      };
 +      }
  }
  
  static void _rtl8821ae_iqk_rx_fill_iqc(struct ieee80211_hw *hw,
                break;
        default:
                break;
 -      };
 +      }
  }
  
  #define cal_num 10
index a51c993ece7346ed27a40b69fd994d0b6b22fc2a,8c68da30595df7793545200c9ce102ae2d5e612d..842e0661fb57b2380675df99ad3960bfd2fdc6e1
@@@ -131,7 -131,7 +131,7 @@@ enum ieee80211_bss_corrupt_data_flags 
   *
   * These are bss flags that are attached to a bss in the
   * @valid_data field of &struct ieee80211_bss.  They show which parts
 - * of the data structure were recieved as a result of an un-corrupted
 + * of the data structure were received as a result of an un-corrupted
   * beacon/probe response.
   */
  enum ieee80211_bss_valid_data_flags {
@@@ -399,24 -399,6 +399,24 @@@ struct ieee80211_mgd_assoc_data 
        u8 ie[];
  };
  
 +struct ieee80211_sta_tx_tspec {
 +      /* timestamp of the first packet in the time slice */
 +      unsigned long time_slice_start;
 +
 +      u32 admitted_time; /* in usecs, unlike over the air */
 +      u8 tsid;
 +      s8 up; /* signed to be able to invalidate with -1 during teardown */
 +
 +      /* consumed TX time in microseconds in the time slice */
 +      u32 consumed_tx_time;
 +      enum {
 +              TX_TSPEC_ACTION_NONE = 0,
 +              TX_TSPEC_ACTION_DOWNGRADE,
 +              TX_TSPEC_ACTION_STOP_DOWNGRADE,
 +      } action;
 +      bool downgraded;
 +};
 +
  struct ieee80211_if_managed {
        struct timer_list timer;
        struct timer_list conn_mon_timer;
  
        unsigned int flags;
  
 +      bool csa_waiting_bcn;
 +
        bool beacon_crc_valid;
        u32 beacon_crc;
  
  
        u8 tdls_peer[ETH_ALEN] __aligned(2);
        struct delayed_work tdls_peer_del_work;
 +
 +      /* WMM-AC TSPEC support */
 +      struct ieee80211_sta_tx_tspec tx_tspec[IEEE80211_NUM_ACS];
 +      /* Use a separate work struct so that we can do something here
 +       * while the sdata->work is flushing the queues, for example.
 +       * otherwise, in scenarios where we hardly get any traffic out
 +       * on the BE queue, but there's a lot of VO traffic, we might
 +       * get stuck in a downgraded situation and flush takes forever.
 +       */
 +      struct delayed_work tx_tspec_wk;
  };
  
  struct ieee80211_if_ibss {
        } state;
  };
  
 +/**
 + * struct ieee80211_if_ocb - OCB mode state
 + *
 + * @housekeeping_timer: timer for periodic invocation of a housekeeping task
 + * @wrkq_flags: OCB deferred task action
 + * @incomplete_lock: delayed STA insertion lock
 + * @incomplete_stations: list of STAs waiting for delayed insertion
 + * @joined: indication if the interface is connected to an OCB network
 + */
 +struct ieee80211_if_ocb {
 +      struct timer_list housekeeping_timer;
 +      unsigned long wrkq_flags;
 +
 +      spinlock_t incomplete_lock;
 +      struct list_head incomplete_stations;
 +
 +      bool joined;
 +};
 +
  /**
   * struct ieee80211_mesh_sync_ops - Extensible synchronization framework interface
   *
@@@ -888,7 -839,6 +888,7 @@@ struct ieee80211_sub_if_data 
                struct ieee80211_if_managed mgd;
                struct ieee80211_if_ibss ibss;
                struct ieee80211_if_mesh mesh;
 +              struct ieee80211_if_ocb ocb;
                u32 mntr_flags;
        } u;
  
@@@ -1357,9 -1307,6 +1357,9 @@@ struct ieee80211_local 
        /* virtual monitor interface */
        struct ieee80211_sub_if_data __rcu *monitor_sdata;
        struct cfg80211_chan_def monitor_chandef;
 +
 +      /* extended capabilities provided by mac80211 */
 +      u8 ext_capa[8];
  };
  
  static inline struct ieee80211_sub_if_data *
@@@ -1507,7 -1454,6 +1507,7 @@@ void ieee80211_mgd_conn_tx_status(struc
                                  __le16 fc, bool acked);
  void ieee80211_mgd_quiesce(struct ieee80211_sub_if_data *sdata);
  void ieee80211_sta_restart(struct ieee80211_sub_if_data *sdata);
 +void ieee80211_sta_handle_tspec_ac_params(struct ieee80211_sub_if_data *sdata);
  
  /* IBSS code */
  void ieee80211_ibss_notify_scan_completed(struct ieee80211_local *local);
@@@ -1525,15 -1471,6 +1525,15 @@@ int ieee80211_ibss_csa_beacon(struct ie
  int ieee80211_ibss_finish_csa(struct ieee80211_sub_if_data *sdata);
  void ieee80211_ibss_stop(struct ieee80211_sub_if_data *sdata);
  
 +/* OCB code */
 +void ieee80211_ocb_work(struct ieee80211_sub_if_data *sdata);
 +void ieee80211_ocb_rx_no_sta(struct ieee80211_sub_if_data *sdata,
 +                           const u8 *bssid, const u8 *addr, u32 supp_rates);
 +void ieee80211_ocb_setup_sdata(struct ieee80211_sub_if_data *sdata);
 +int ieee80211_ocb_join(struct ieee80211_sub_if_data *sdata,
 +                     struct ocb_setup *setup);
 +int ieee80211_ocb_leave(struct ieee80211_sub_if_data *sdata);
 +
  /* mesh code */
  void ieee80211_mesh_work(struct ieee80211_sub_if_data *sdata);
  void ieee80211_mesh_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
@@@ -1705,7 -1642,6 +1705,6 @@@ void ieee80211_process_measurement_req(
   * ieee80211_parse_ch_switch_ie - parses channel switch IEs
   * @sdata: the sdata of the interface which has received the frame
   * @elems: parsed 802.11 elements received with the frame
-  * @beacon: indicates if the frame was a beacon or probe response
   * @current_band: indicates the current band
   * @sta_flags: contains information about own capabilities and restrictions
   *    to decide which channel switch announcements can be accepted. Only the
   * Return: 0 on success, <0 on error and >0 if there is nothing to parse.
   */
  int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata,
-                                struct ieee802_11_elems *elems, bool beacon,
+                                struct ieee802_11_elems *elems,
                                 enum ieee80211_band current_band,
                                 u32 sta_flags, u8 *bssid,
                                 struct ieee80211_csa_ie *csa_ie);
@@@ -1821,13 -1757,6 +1820,13 @@@ static inline bool ieee80211_rx_reorder
        return true;
  }
  
 +extern const int ieee802_1d_to_ac[8];
 +
 +static inline int ieee80211_ac_from_tid(int tid)
 +{
 +      return ieee802_1d_to_ac[tid & 7];
 +}
 +
  void ieee80211_dynamic_ps_enable_work(struct work_struct *work);
  void ieee80211_dynamic_ps_disable_work(struct work_struct *work);
  void ieee80211_dynamic_ps_timer(unsigned long data);
@@@ -1837,7 -1766,7 +1836,7 @@@ void ieee80211_send_nullfunc(struct iee
  void ieee80211_sta_rx_notify(struct ieee80211_sub_if_data *sdata,
                             struct ieee80211_hdr *hdr);
  void ieee80211_sta_tx_notify(struct ieee80211_sub_if_data *sdata,
 -                           struct ieee80211_hdr *hdr, bool ack);
 +                           struct ieee80211_hdr *hdr, bool ack, u16 tx_time);
  
  void ieee80211_wake_queues_by_reason(struct ieee80211_hw *hw,
                                     unsigned long queues,
@@@ -1903,10 -1832,8 +1902,10 @@@ int __ieee80211_request_smps_ap(struct 
  void ieee80211_recalc_smps(struct ieee80211_sub_if_data *sdata);
  void ieee80211_recalc_min_chandef(struct ieee80211_sub_if_data *sdata);
  
 -size_t ieee80211_ie_split(const u8 *ies, size_t ielen,
 -                        const u8 *ids, int n_ids, size_t offset);
 +size_t ieee80211_ie_split_ric(const u8 *ies, size_t ielen,
 +                            const u8 *ids, int n_ids,
 +                            const u8 *after_ric, int n_after_ric,
 +                            size_t offset);
  size_t ieee80211_ie_split_vendor(const u8 *ies, size_t ielen, size_t offset);
  u8 *ieee80211_ie_build_ht_cap(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap,
                              u16 cap);
diff --combined net/mac80211/iface.c
index 6b631c049eba0b8c7b877296987ff04413b867f9,653f5eb07a27f4432429a8fbbc4b32e7200cf4c3..9df26adb864aa3142792bfc98abceef3db5b9660
@@@ -258,15 -258,6 +258,15 @@@ static int ieee80211_check_concurrent_i
        /* we hold the RTNL here so can safely walk the list */
        list_for_each_entry(nsdata, &local->interfaces, list) {
                if (nsdata != sdata && ieee80211_sdata_running(nsdata)) {
 +                      /*
 +                       * Only OCB and monitor mode may coexist
 +                       */
 +                      if ((sdata->vif.type == NL80211_IFTYPE_OCB &&
 +                           nsdata->vif.type != NL80211_IFTYPE_MONITOR) ||
 +                          (sdata->vif.type != NL80211_IFTYPE_MONITOR &&
 +                           nsdata->vif.type == NL80211_IFTYPE_OCB))
 +                              return -EBUSY;
 +
                        /*
                         * Allow only a single IBSS interface to be up at any
                         * time. This is restricted because beacon distribution
@@@ -530,7 -521,6 +530,7 @@@ int ieee80211_do_open(struct wireless_d
        case NL80211_IFTYPE_MONITOR:
        case NL80211_IFTYPE_ADHOC:
        case NL80211_IFTYPE_P2P_DEVICE:
 +      case NL80211_IFTYPE_OCB:
                /* no special treatment */
                break;
        case NL80211_IFTYPE_UNSPECIFIED:
                case NL80211_IFTYPE_ADHOC:
                case NL80211_IFTYPE_AP:
                case NL80211_IFTYPE_MESH_POINT:
 +              case NL80211_IFTYPE_OCB:
                        netif_carrier_off(dev);
                        break;
                case NL80211_IFTYPE_WDS:
@@@ -777,10 -766,12 +777,12 @@@ static void ieee80211_do_stop(struct ie
        int i, flushed;
        struct ps_data *ps;
        struct cfg80211_chan_def chandef;
+       bool cancel_scan;
  
        clear_bit(SDATA_STATE_RUNNING, &sdata->state);
  
-       if (rcu_access_pointer(local->scan_sdata) == sdata)
+       cancel_scan = rcu_access_pointer(local->scan_sdata) == sdata;
+       if (cancel_scan)
                ieee80211_scan_cancel(local);
  
        /*
        sdata_lock(sdata);
        mutex_lock(&local->mtx);
        sdata->vif.csa_active = false;
 +      if (sdata->vif.type == NL80211_IFTYPE_STATION)
 +              sdata->u.mgd.csa_waiting_bcn = false;
        if (sdata->csa_block_tx) {
                ieee80211_wake_vif_queues(local, sdata,
                                          IEEE80211_QUEUE_STOP_REASON_CSA);
                list_del(&sdata->u.vlan.list);
                mutex_unlock(&local->mtx);
                RCU_INIT_POINTER(sdata->vif.chanctx_conf, NULL);
+               /* see comment in the default case below */
+               ieee80211_free_keys(sdata, true);
                /* no need to tell driver */
                break;
        case NL80211_IFTYPE_MONITOR:
                /*
                 * When we get here, the interface is marked down.
                 * Free the remaining keys, if there are any
-                * (shouldn't be, except maybe in WDS mode?)
+                * (which can happen in AP mode if userspace sets
+                * keys before the interface is operating, and maybe
+                * also in WDS mode)
                 *
                 * Force the key freeing to always synchronize_net()
                 * to wait for the RX path in case it is using this
-                * interface enqueuing frames at this very time on
+                * interface enqueuing frames at this very time on
                 * another CPU.
                 */
                ieee80211_free_keys(sdata, true);
-               /* fall through */
-       case NL80211_IFTYPE_AP:
                skb_queue_purge(&sdata->skb_queue);
        }
  
  
        ieee80211_recalc_ps(local, -1);
  
+       if (cancel_scan)
+               flush_delayed_work(&local->scan_work);
        if (local->open_count == 0) {
                ieee80211_stop_device(local);
  
@@@ -1292,9 -1285,6 +1298,9 @@@ static void ieee80211_iface_work(struc
                        break;
                ieee80211_mesh_work(sdata);
                break;
 +      case NL80211_IFTYPE_OCB:
 +              ieee80211_ocb_work(sdata);
 +              break;
        default:
                break;
        }
@@@ -1314,9 -1304,6 +1320,9 @@@ static void ieee80211_recalc_smps_work(
  static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata,
                                  enum nl80211_iftype type)
  {
 +      static const u8 bssid_wildcard[ETH_ALEN] = {0xff, 0xff, 0xff,
 +                                                  0xff, 0xff, 0xff};
 +
        /* clear type-dependent union */
        memset(&sdata->u, 0, sizeof(sdata->u));
  
                sdata->vif.bss_conf.bssid = sdata->u.mgd.bssid;
                ieee80211_sta_setup_sdata(sdata);
                break;
 +      case NL80211_IFTYPE_OCB:
 +              sdata->vif.bss_conf.bssid = bssid_wildcard;
 +              ieee80211_ocb_setup_sdata(sdata);
 +              break;
        case NL80211_IFTYPE_ADHOC:
                sdata->vif.bss_conf.bssid = sdata->u.ibss.bssid;
                ieee80211_ibss_setup_sdata(sdata);
@@@ -1419,7 -1402,6 +1425,7 @@@ static int ieee80211_runtime_change_ift
        case NL80211_IFTYPE_AP:
        case NL80211_IFTYPE_STATION:
        case NL80211_IFTYPE_ADHOC:
 +      case NL80211_IFTYPE_OCB:
                /*
                 * Could maybe also all others here?
                 * Just not sure how that interacts
        case NL80211_IFTYPE_AP:
        case NL80211_IFTYPE_STATION:
        case NL80211_IFTYPE_ADHOC:
 +      case NL80211_IFTYPE_OCB:
                /*
                 * Could probably support everything
                 * but WDS here (WDS do_open can fail
@@@ -1694,10 -1675,7 +1700,10 @@@ int ieee80211_if_add(struct ieee80211_l
                }
  
                ieee80211_assign_perm_addr(local, ndev->perm_addr, type);
 -              memcpy(ndev->dev_addr, ndev->perm_addr, ETH_ALEN);
 +              if (params && is_valid_ether_addr(params->macaddr))
 +                      memcpy(ndev->dev_addr, params->macaddr, ETH_ALEN);
 +              else
 +                      memcpy(ndev->dev_addr, ndev->perm_addr, ETH_ALEN);
                SET_NETDEV_DEV(ndev, wiphy_dev(local->hw.wiphy));
  
                /* don't use IEEE80211_DEV_TO_SUB_IF -- it checks too much */
diff --combined net/mac80211/mlme.c
index 213a420704a62f9dfecd25a7873fe25734a31ce7,93af0f1c9d991a52d82cbc58bcd842415b22a04a..0d166e766dadb062de28df2c8acd1683947d73c4
@@@ -775,30 -775,11 +775,30 @@@ static void ieee80211_send_assoc(struc
                        WLAN_EID_QOS_CAPA,
                        WLAN_EID_RRM_ENABLED_CAPABILITIES,
                        WLAN_EID_MOBILITY_DOMAIN,
 +                      WLAN_EID_FAST_BSS_TRANSITION,   /* reassoc only */
 +                      WLAN_EID_RIC_DATA,              /* reassoc only */
                        WLAN_EID_SUPPORTED_REGULATORY_CLASSES,
                };
 -              noffset = ieee80211_ie_split(assoc_data->ie, assoc_data->ie_len,
 -                                           before_ht, ARRAY_SIZE(before_ht),
 -                                           offset);
 +              static const u8 after_ric[] = {
 +                      WLAN_EID_SUPPORTED_REGULATORY_CLASSES,
 +                      WLAN_EID_HT_CAPABILITY,
 +                      WLAN_EID_BSS_COEX_2040,
 +                      WLAN_EID_EXT_CAPABILITY,
 +                      WLAN_EID_QOS_TRAFFIC_CAPA,
 +                      WLAN_EID_TIM_BCAST_REQ,
 +                      WLAN_EID_INTERWORKING,
 +                      /* 60GHz doesn't happen right now */
 +                      WLAN_EID_VHT_CAPABILITY,
 +                      WLAN_EID_OPMODE_NOTIF,
 +              };
 +
 +              noffset = ieee80211_ie_split_ric(assoc_data->ie,
 +                                               assoc_data->ie_len,
 +                                               before_ht,
 +                                               ARRAY_SIZE(before_ht),
 +                                               after_ric,
 +                                               ARRAY_SIZE(after_ric),
 +                                               offset);
                pos = skb_put(skb, noffset - offset);
                memcpy(pos, assoc_data->ie + offset, noffset - offset);
                offset = noffset;
                        WLAN_EID_TIM_BCAST_REQ,
                        WLAN_EID_INTERWORKING,
                };
 +
 +              /* RIC already taken above, so no need to handle here anymore */
                noffset = ieee80211_ie_split(assoc_data->ie, assoc_data->ie_len,
                                             before_vht, ARRAY_SIZE(before_vht),
                                             offset);
@@@ -1022,7 -1001,14 +1022,7 @@@ static void ieee80211_chswitch_work(str
        /* XXX: shouldn't really modify cfg80211-owned data! */
        ifmgd->associated->channel = sdata->csa_chandef.chan;
  
 -      sdata->vif.csa_active = false;
 -
 -      /* XXX: wait for a beacon first? */
 -      if (sdata->csa_block_tx) {
 -              ieee80211_wake_vif_queues(local, sdata,
 -                                        IEEE80211_QUEUE_STOP_REASON_CSA);
 -              sdata->csa_block_tx = false;
 -      }
 +      ifmgd->csa_waiting_bcn = true;
  
        ieee80211_sta_reset_beacon_monitor(sdata);
        ieee80211_sta_reset_conn_monitor(sdata);
        sdata_unlock(sdata);
  }
  
 +static void ieee80211_chswitch_post_beacon(struct ieee80211_sub_if_data *sdata)
 +{
 +      struct ieee80211_local *local = sdata->local;
 +      struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
 +      int ret;
 +
 +      sdata_assert_lock(sdata);
 +
 +      WARN_ON(!sdata->vif.csa_active);
 +
 +      if (sdata->csa_block_tx) {
 +              ieee80211_wake_vif_queues(local, sdata,
 +                                        IEEE80211_QUEUE_STOP_REASON_CSA);
 +              sdata->csa_block_tx = false;
 +      }
 +
 +      sdata->vif.csa_active = false;
 +      ifmgd->csa_waiting_bcn = false;
 +
 +      ret = drv_post_channel_switch(sdata);
 +      if (ret) {
 +              sdata_info(sdata,
 +                         "driver post channel switch failed, disconnecting\n");
 +              ieee80211_queue_work(&local->hw,
 +                                   &ifmgd->csa_connection_drop_work);
 +              return;
 +      }
 +}
 +
  void ieee80211_chswitch_done(struct ieee80211_vif *vif, bool success)
  {
        struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
@@@ -1089,8 -1046,7 +1089,8 @@@ static void ieee80211_chswitch_timer(un
  
  static void
  ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
 -                               u64 timestamp, struct ieee802_11_elems *elems,
 +                               u64 timestamp, u32 device_timestamp,
 +                               struct ieee802_11_elems *elems,
                                 bool beacon)
  {
        struct ieee80211_local *local = sdata->local;
        struct ieee80211_chanctx *chanctx;
        enum ieee80211_band current_band;
        struct ieee80211_csa_ie csa_ie;
 +      struct ieee80211_channel_switch ch_switch;
        int res;
  
        sdata_assert_lock(sdata);
  
        current_band = cbss->channel->band;
        memset(&csa_ie, 0, sizeof(csa_ie));
-       res = ieee80211_parse_ch_switch_ie(sdata, elems, beacon, current_band,
+       res = ieee80211_parse_ch_switch_ie(sdata, elems, current_band,
                                           ifmgd->flags,
                                           ifmgd->associated->bssid, &csa_ie);
        if (res < 0)
  
        chanctx = container_of(conf, struct ieee80211_chanctx, conf);
  
 -      if (local->use_chanctx) {
 -              u32 num_chanctx = 0;
 -              list_for_each_entry(chanctx, &local->chanctx_list, list)
 -                     num_chanctx++;
 +      if (local->use_chanctx &&
 +          !(local->hw.flags & IEEE80211_HW_CHANCTX_STA_CSA)) {
 +              sdata_info(sdata,
 +                         "driver doesn't support chan-switch with channel contexts\n");
 +              ieee80211_queue_work(&local->hw,
 +                                   &ifmgd->csa_connection_drop_work);
 +              mutex_unlock(&local->chanctx_mtx);
 +              mutex_unlock(&local->mtx);
 +              return;
 +      }
  
 -              if (num_chanctx > 1 ||
 -                  !(local->hw.flags & IEEE80211_HW_CHANCTX_STA_CSA)) {
 -                      sdata_info(sdata,
 -                                 "not handling chan-switch with channel contexts\n");
 -                      ieee80211_queue_work(&local->hw,
 -                                           &ifmgd->csa_connection_drop_work);
 -                      mutex_unlock(&local->chanctx_mtx);
 -                      mutex_unlock(&local->mtx);
 -                      return;
 -              }
 +      ch_switch.timestamp = timestamp;
 +      ch_switch.device_timestamp = device_timestamp;
 +      ch_switch.block_tx = csa_ie.mode;
 +      ch_switch.chandef = csa_ie.chandef;
 +      ch_switch.count = csa_ie.count;
 +
 +      if (drv_pre_channel_switch(sdata, &ch_switch)) {
 +              sdata_info(sdata,
 +                         "preparing for channel switch failed, disconnecting\n");
 +              ieee80211_queue_work(&local->hw,
 +                                   &ifmgd->csa_connection_drop_work);
 +              mutex_unlock(&local->chanctx_mtx);
 +              mutex_unlock(&local->mtx);
 +              return;
        }
  
        res = ieee80211_vif_reserve_chanctx(sdata, &csa_ie.chandef,
  
        if (local->ops->channel_switch) {
                /* use driver's channel switch callback */
 -              struct ieee80211_channel_switch ch_switch = {
 -                      .timestamp = timestamp,
 -                      .block_tx = csa_ie.mode,
 -                      .chandef = csa_ie.chandef,
 -                      .count = csa_ie.count,
 -              };
 -
 -              drv_channel_switch(local, &ch_switch);
 +              drv_channel_switch(local, sdata, &ch_switch);
                return;
        }
  
                ieee80211_queue_work(&local->hw, &ifmgd->chswitch_work);
        else
                mod_timer(&ifmgd->chswitch_timer,
-                         TU_TO_EXP_TIME(csa_ie.count * cbss->beacon_interval));
+                         TU_TO_EXP_TIME((csa_ie.count - 1) *
+                                        cbss->beacon_interval));
  }
  
  static bool
@@@ -1627,95 -1580,6 +1628,95 @@@ void ieee80211_dfs_cac_timer_work(struc
        mutex_unlock(&sdata->local->mtx);
  }
  
 +static bool
 +__ieee80211_sta_handle_tspec_ac_params(struct ieee80211_sub_if_data *sdata)
 +{
 +      struct ieee80211_local *local = sdata->local;
 +      struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
 +      bool ret;
 +      int ac;
 +
 +      if (local->hw.queues < IEEE80211_NUM_ACS)
 +              return false;
 +
 +      for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
 +              struct ieee80211_sta_tx_tspec *tx_tspec = &ifmgd->tx_tspec[ac];
 +              int non_acm_ac;
 +              unsigned long now = jiffies;
 +
 +              if (tx_tspec->action == TX_TSPEC_ACTION_NONE &&
 +                  tx_tspec->admitted_time &&
 +                  time_after(now, tx_tspec->time_slice_start + HZ)) {
 +                      tx_tspec->consumed_tx_time = 0;
 +                      tx_tspec->time_slice_start = now;
 +
 +                      if (tx_tspec->downgraded)
 +                              tx_tspec->action =
 +                                      TX_TSPEC_ACTION_STOP_DOWNGRADE;
 +              }
 +
 +              switch (tx_tspec->action) {
 +              case TX_TSPEC_ACTION_STOP_DOWNGRADE:
 +                      /* take the original parameters */
 +                      if (drv_conf_tx(local, sdata, ac, &sdata->tx_conf[ac]))
 +                              sdata_err(sdata,
 +                                        "failed to set TX queue parameters for queue %d\n",
 +                                        ac);
 +                      tx_tspec->action = TX_TSPEC_ACTION_NONE;
 +                      tx_tspec->downgraded = false;
 +                      ret = true;
 +                      break;
 +              case TX_TSPEC_ACTION_DOWNGRADE:
 +                      if (time_after(now, tx_tspec->time_slice_start + HZ)) {
 +                              tx_tspec->action = TX_TSPEC_ACTION_NONE;
 +                              ret = true;
 +                              break;
 +                      }
 +                      /* downgrade next lower non-ACM AC */
 +                      for (non_acm_ac = ac + 1;
 +                           non_acm_ac < IEEE80211_NUM_ACS;
 +                           non_acm_ac++)
 +                              if (!(sdata->wmm_acm & BIT(7 - 2 * non_acm_ac)))
 +                                      break;
 +                      /* The loop will result in using BK even if it requires
 +                       * admission control, such configuration makes no sense
 +                       * and we have to transmit somehow - the AC selection
 +                       * does the same thing.
 +                       */
 +                      if (drv_conf_tx(local, sdata, ac,
 +                                      &sdata->tx_conf[non_acm_ac]))
 +                              sdata_err(sdata,
 +                                        "failed to set TX queue parameters for queue %d\n",
 +                                        ac);
 +                      tx_tspec->action = TX_TSPEC_ACTION_NONE;
 +                      ret = true;
 +                      schedule_delayed_work(&ifmgd->tx_tspec_wk,
 +                              tx_tspec->time_slice_start + HZ - now + 1);
 +                      break;
 +              case TX_TSPEC_ACTION_NONE:
 +                      /* nothing now */
 +                      break;
 +              }
 +      }
 +
 +      return ret;
 +}
 +
 +void ieee80211_sta_handle_tspec_ac_params(struct ieee80211_sub_if_data *sdata)
 +{
 +      if (__ieee80211_sta_handle_tspec_ac_params(sdata))
 +              ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_QOS);
 +}
 +
 +static void ieee80211_sta_handle_tspec_ac_params_wk(struct work_struct *work)
 +{
 +      struct ieee80211_sub_if_data *sdata;
 +
 +      sdata = container_of(work, struct ieee80211_sub_if_data,
 +                           u.mgd.tx_tspec_wk.work);
 +      ieee80211_sta_handle_tspec_ac_params(sdata);
 +}
 +
  /* MLME */
  static bool ieee80211_sta_wmm_params(struct ieee80211_local *local,
                                     struct ieee80211_sub_if_data *sdata,
                params.uapsd = uapsd;
  
                mlme_dbg(sdata,
 -                       "WMM queue=%d aci=%d acm=%d aifs=%d cWmin=%d cWmax=%d txop=%d uapsd=%d\n",
 +                       "WMM queue=%d aci=%d acm=%d aifs=%d cWmin=%d cWmax=%d txop=%d uapsd=%d, downgraded=%d\n",
                         queue, aci, acm,
                         params.aifs, params.cw_min, params.cw_max,
 -                       params.txop, params.uapsd);
 +                       params.txop, params.uapsd,
 +                       ifmgd->tx_tspec[queue].downgraded);
                sdata->tx_conf[queue] = params;
 -              if (drv_conf_tx(local, sdata, queue, &params))
 +              if (!ifmgd->tx_tspec[queue].downgraded &&
 +                  drv_conf_tx(local, sdata, queue, &params))
                        sdata_err(sdata,
                                  "failed to set TX queue parameters for queue %d\n",
                                  queue);
@@@ -2062,7 -1924,6 +2063,7 @@@ static void ieee80211_set_disassoc(stru
        ieee80211_vif_release_channel(sdata);
  
        sdata->vif.csa_active = false;
 +      ifmgd->csa_waiting_bcn = false;
        if (sdata->csa_block_tx) {
                ieee80211_wake_vif_queues(local, sdata,
                                          IEEE80211_QUEUE_STOP_REASON_CSA);
        }
        mutex_unlock(&local->mtx);
  
 +      /* existing TX TSPEC sessions no longer exist */
 +      memset(ifmgd->tx_tspec, 0, sizeof(ifmgd->tx_tspec));
 +      cancel_delayed_work_sync(&ifmgd->tx_tspec_wk);
 +
        sdata->encrypt_headroom = IEEE80211_ENCRYPT_HEADROOM;
  }
  
        mutex_unlock(&local->mtx);
  }
  
 +static void ieee80211_sta_tx_wmm_ac_notify(struct ieee80211_sub_if_data *sdata,
 +                                         struct ieee80211_hdr *hdr,
 +                                         u16 tx_time)
 +{
 +      struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
 +      u16 tid = *ieee80211_get_qos_ctl(hdr) & IEEE80211_QOS_CTL_TID_MASK;
 +      int ac = ieee80211_ac_from_tid(tid);
 +      struct ieee80211_sta_tx_tspec *tx_tspec = &ifmgd->tx_tspec[ac];
 +      unsigned long now = jiffies;
 +
 +      if (likely(!tx_tspec->admitted_time))
 +              return;
 +
 +      if (time_after(now, tx_tspec->time_slice_start + HZ)) {
 +              tx_tspec->consumed_tx_time = 0;
 +              tx_tspec->time_slice_start = now;
 +
 +              if (tx_tspec->downgraded) {
 +                      tx_tspec->action = TX_TSPEC_ACTION_STOP_DOWNGRADE;
 +                      schedule_delayed_work(&ifmgd->tx_tspec_wk, 0);
 +              }
 +      }
 +
 +      if (tx_tspec->downgraded)
 +              return;
 +
 +      tx_tspec->consumed_tx_time += tx_time;
 +
 +      if (tx_tspec->consumed_tx_time >= tx_tspec->admitted_time) {
 +              tx_tspec->downgraded = true;
 +              tx_tspec->action = TX_TSPEC_ACTION_DOWNGRADE;
 +              schedule_delayed_work(&ifmgd->tx_tspec_wk, 0);
 +      }
 +}
 +
  void ieee80211_sta_tx_notify(struct ieee80211_sub_if_data *sdata,
 -                           struct ieee80211_hdr *hdr, bool ack)
 +                           struct ieee80211_hdr *hdr, bool ack, u16 tx_time)
  {
 +      ieee80211_sta_tx_wmm_ac_notify(sdata, hdr, tx_time);
 +
        if (!ieee80211_is_data(hdr->frame_control))
            return;
  
@@@ -2228,6 -2048,8 +2229,6 @@@ static void ieee80211_mgd_probe_ap_send
  
        ifmgd->probe_timeout = jiffies + msecs_to_jiffies(probe_wait_ms);
        run_again(sdata, ifmgd->probe_timeout);
 -      if (sdata->local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS)
 -              ieee80211_flush_queues(sdata->local, sdata);
  }
  
  static void ieee80211_mgd_probe_ap(struct ieee80211_sub_if_data *sdata,
@@@ -2350,7 -2172,6 +2351,7 @@@ static void __ieee80211_disconnect(stru
                               true, frame_buf);
        mutex_lock(&local->mtx);
        sdata->vif.csa_active = false;
 +      ifmgd->csa_waiting_bcn = false;
        if (sdata->csa_block_tx) {
                ieee80211_wake_vif_queues(local, sdata,
                                          IEEE80211_QUEUE_STOP_REASON_CSA);
@@@ -3375,9 -3196,6 +3376,9 @@@ static void ieee80211_rx_mgmt_beacon(st
                }
        }
  
 +      if (ifmgd->csa_waiting_bcn)
 +              ieee80211_chswitch_post_beacon(sdata);
 +
        if (ncrc == ifmgd->beacon_crc && ifmgd->beacon_crc_valid)
                return;
        ifmgd->beacon_crc = ncrc;
        ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems);
  
        ieee80211_sta_process_chanswitch(sdata, rx_status->mactime,
 +                                       rx_status->device_timestamp,
                                         &elems, true);
  
        if (!(ifmgd->flags & IEEE80211_STA_DISABLE_WMM) &&
@@@ -3518,9 -3335,8 +3519,9 @@@ void ieee80211_sta_rx_queued_mgmt(struc
                                break;
  
                        ieee80211_sta_process_chanswitch(sdata,
 -                                                       rx_status->mactime,
 -                                                       &elems, false);
 +                                               rx_status->mactime,
 +                                               rx_status->device_timestamp,
 +                                               &elems, false);
                } else if (mgmt->u.action.category == WLAN_CATEGORY_PUBLIC) {
                        ies_len = skb->len -
                                  offsetof(struct ieee80211_mgmt,
                                &mgmt->u.action.u.ext_chan_switch.data;
  
                        ieee80211_sta_process_chanswitch(sdata,
 -                                                       rx_status->mactime,
 -                                                       &elems, false);
 +                                               rx_status->mactime,
 +                                               rx_status->device_timestamp,
 +                                               &elems, false);
                }
                break;
        }
@@@ -3850,12 -3665,11 +3851,12 @@@ static void ieee80211_sta_bcn_mon_timer
        struct ieee80211_sub_if_data *sdata =
                (struct ieee80211_sub_if_data *) data;
        struct ieee80211_local *local = sdata->local;
 +      struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
  
        if (local->quiescing)
                return;
  
 -      if (sdata->vif.csa_active)
 +      if (sdata->vif.csa_active && !ifmgd->csa_waiting_bcn)
                return;
  
        sdata->u.mgd.connection_loss = false;
@@@ -3873,7 -3687,7 +3874,7 @@@ static void ieee80211_sta_conn_mon_time
        if (local->quiescing)
                return;
  
 -      if (sdata->vif.csa_active)
 +      if (sdata->vif.csa_active && !ifmgd->csa_waiting_bcn)
                return;
  
        ieee80211_queue_work(&local->hw, &ifmgd->monitor_work);
@@@ -3985,8 -3799,6 +3986,8 @@@ void ieee80211_sta_setup_sdata(struct i
                    (unsigned long) sdata);
        setup_timer(&ifmgd->chswitch_timer, ieee80211_chswitch_timer,
                    (unsigned long) sdata);
 +      INIT_DELAYED_WORK(&ifmgd->tx_tspec_wk,
 +                        ieee80211_sta_handle_tspec_ac_params_wk);
  
        ifmgd->flags = 0;
        ifmgd->powersave = sdata->wdev.ps;
diff --combined net/mac80211/rx.c
index bc63aa0c5401860105bf9a1db6618327d8b189ed,a37f9af634cb6d3c7bb73ada859473e0e085402d..a726bb16930239b7bb687064626855529ea2f1d5
@@@ -1032,7 -1032,6 +1032,7 @@@ ieee80211_rx_h_check(struct ieee80211_r
                      ieee80211_is_pspoll(hdr->frame_control)) &&
                     rx->sdata->vif.type != NL80211_IFTYPE_ADHOC &&
                     rx->sdata->vif.type != NL80211_IFTYPE_WDS &&
 +                   rx->sdata->vif.type != NL80211_IFTYPE_OCB &&
                     (!rx->sta || !test_sta_flag(rx->sta, WLAN_STA_ASSOC)))) {
                /*
                 * accept port control frames from the AP even when it's not
@@@ -1273,12 -1272,6 +1273,12 @@@ ieee80211_rx_h_sta_process(struct ieee8
                                sta->last_rx_rate_vht_nss = status->vht_nss;
                        }
                }
 +      } else if (rx->sdata->vif.type == NL80211_IFTYPE_OCB) {
 +              u8 *bssid = ieee80211_get_bssid(hdr, rx->skb->len,
 +                                              NL80211_IFTYPE_OCB);
 +              /* OCB uses wild-card BSSID */
 +              if (is_broadcast_ether_addr(bssid))
 +                      sta->last_rx = jiffies;
        } else if (!is_multicast_ether_addr(hdr->addr1)) {
                /*
                 * Mesh beacons will update last_rx when if they are found to
@@@ -1685,11 -1678,14 +1685,14 @@@ ieee80211_rx_h_defragment(struct ieee80
        sc = le16_to_cpu(hdr->seq_ctrl);
        frag = sc & IEEE80211_SCTL_FRAG;
  
-       if (likely((!ieee80211_has_morefrags(fc) && frag == 0) ||
-                  is_multicast_ether_addr(hdr->addr1))) {
-               /* not fragmented */
+       if (likely(!ieee80211_has_morefrags(fc) && frag == 0))
+               goto out;
+       if (is_multicast_ether_addr(hdr->addr1)) {
+               rx->local->dot11MulticastReceivedFrameCount++;
                goto out;
        }
        I802_DEBUG_INC(rx->local->rx_handlers_fragments);
  
        if (skb_linearize(rx->skb))
   out:
        if (rx->sta)
                rx->sta->rx_packets++;
-       if (is_multicast_ether_addr(hdr->addr1))
-               rx->local->dot11MulticastReceivedFrameCount++;
-       else
-               ieee80211_led_rx(rx->local);
+       ieee80211_led_rx(rx->local);
        return RX_CONTINUE;
  }
  
@@@ -2827,7 -2820,6 +2827,7 @@@ ieee80211_rx_h_mgmt(struct ieee80211_rx
  
        if (!ieee80211_vif_is_mesh(&sdata->vif) &&
            sdata->vif.type != NL80211_IFTYPE_ADHOC &&
 +          sdata->vif.type != NL80211_IFTYPE_OCB &&
            sdata->vif.type != NL80211_IFTYPE_STATION)
                return RX_DROP_MONITOR;
  
@@@ -3138,33 -3130,6 +3138,33 @@@ static bool prepare_for_handlers(struc
                                                 BIT(rate_idx));
                }
                break;
 +      case NL80211_IFTYPE_OCB:
 +              if (!bssid)
 +                      return false;
 +              if (ieee80211_is_beacon(hdr->frame_control)) {
 +                      return false;
 +              } else if (!is_broadcast_ether_addr(bssid)) {
 +                      ocb_dbg(sdata, "BSSID mismatch in OCB mode!\n");
 +                      return false;
 +              } else if (!multicast &&
 +                         !ether_addr_equal(sdata->dev->dev_addr,
 +                                           hdr->addr1)) {
 +                      /* if we are in promisc mode we also accept
 +                       * packets not destined for us
 +                       */
 +                      if (!(sdata->dev->flags & IFF_PROMISC))
 +                              return false;
 +                      rx->flags &= ~IEEE80211_RX_RA_MATCH;
 +              } else if (!rx->sta) {
 +                      int rate_idx;
 +                      if (status->flag & RX_FLAG_HT)
 +                              rate_idx = 0; /* TODO: HT rates */
 +                      else
 +                              rate_idx = status->rate_idx;
 +                      ieee80211_ocb_rx_no_sta(sdata, bssid, hdr->addr2,
 +                                              BIT(rate_idx));
 +              }
 +              break;
        case NL80211_IFTYPE_MESH_POINT:
                if (!multicast &&
                    !ether_addr_equal(sdata->vif.addr, hdr->addr1)) {