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

Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
index e418969,0000000..f8a9dfa
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 72e87b5,0000000..0b0d51a
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;
 +      }
 +}
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge