Merge remote branch 'wireless-next/master' into ath6kl-next
authorKalle Valo <kvalo@qca.qualcomm.com>
Fri, 16 Dec 2011 19:10:39 +0000 (21:10 +0200)
committerKalle Valo <kvalo@qca.qualcomm.com>
Fri, 16 Dec 2011 19:10:39 +0000 (21:10 +0200)
Conflicts:
drivers/net/wireless/ath/ath6kl/init.c

1  2 
drivers/net/wireless/ath/ath6kl/cfg80211.c
drivers/net/wireless/ath/ath6kl/debug.c
drivers/net/wireless/ath/ath6kl/debug.h
drivers/net/wireless/ath/ath6kl/init.c
drivers/net/wireless/ath/ath6kl/sdio.c

index abe3af3c61889a50d074a084fcb1b999a651f397,02526044acb925d4f360d2e6504e6326087f0c2a..6c59a217b1a133f390d1dd2f444494110660b9f7
@@@ -14,6 -14,8 +14,8 @@@
   * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
   */
  
+ #include <linux/moduleparam.h>
  #include "core.h"
  #include "cfg80211.h"
  #include "debug.h"
  #include "testmode.h"
  
  static unsigned int ath6kl_p2p;
 -static unsigned int multi_norm_if_support;
  
  module_param(ath6kl_p2p, uint, 0644);
 -module_param(multi_norm_if_support, uint, 0644);
  
  #define RATETAB_ENT(_rate, _rateid, _flags) {   \
        .bitrate    = (_rate),                  \
@@@ -123,37 -127,6 +125,37 @@@ static struct ieee80211_supported_band 
  
  #define CCKM_KRK_CIPHER_SUITE 0x004096ff /* use for KRK */
  
 +/* returns true if scheduled scan was stopped */
 +static bool __ath6kl_cfg80211_sscan_stop(struct ath6kl_vif *vif)
 +{
 +      struct ath6kl *ar = vif->ar;
 +
 +      if (ar->state != ATH6KL_STATE_SCHED_SCAN)
 +              return false;
 +
 +      del_timer_sync(&vif->sched_scan_timer);
 +
 +      ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx,
 +                                         ATH6KL_HOST_MODE_AWAKE);
 +
 +      ar->state = ATH6KL_STATE_ON;
 +
 +      return true;
 +}
 +
 +static void ath6kl_cfg80211_sscan_disable(struct ath6kl_vif *vif)
 +{
 +      struct ath6kl *ar = vif->ar;
 +      bool stopped;
 +
 +      stopped = __ath6kl_cfg80211_sscan_stop(vif);
 +
 +      if (!stopped)
 +              return;
 +
 +      cfg80211_sched_scan_stopped(ar->wiphy);
 +}
 +
  static int ath6kl_set_wpa_version(struct ath6kl_vif *vif,
                                  enum nl80211_wpa_versions wpa_version)
  {
@@@ -232,10 -205,6 +234,10 @@@ static int ath6kl_set_cipher(struct ath
                *ar_cipher = AES_CRYPT;
                *ar_cipher_len = 0;
                break;
 +      case WLAN_CIPHER_SUITE_SMS4:
 +              *ar_cipher = WAPI_CRYPT;
 +              *ar_cipher_len = 0;
 +              break;
        default:
                ath6kl_err("cipher 0x%x not supported\n", cipher);
                return -ENOTSUPP;
@@@ -386,7 -355,7 +388,7 @@@ static bool ath6kl_is_valid_iftype(stru
  
        if (type == NL80211_IFTYPE_STATION ||
            type == NL80211_IFTYPE_AP || type == NL80211_IFTYPE_ADHOC) {
 -              for (i = 0; i < MAX_NUM_VIF; i++) {
 +              for (i = 0; i < ar->vif_max; i++) {
                        if ((ar->avail_idx_map >> i) & BIT(0)) {
                                *if_idx = i;
                                return true;
  
        if (type == NL80211_IFTYPE_P2P_CLIENT ||
            type == NL80211_IFTYPE_P2P_GO) {
 -              for (i = ar->max_norm_iface; i < MAX_NUM_VIF; i++) {
 +              for (i = ar->max_norm_iface; i < ar->vif_max; i++) {
                        if ((ar->avail_idx_map >> i) & BIT(0)) {
                                *if_idx = i;
                                return true;
@@@ -413,9 -382,6 +415,9 @@@ static int ath6kl_cfg80211_connect(stru
        struct ath6kl *ar = ath6kl_priv(dev);
        struct ath6kl_vif *vif = netdev_priv(dev);
        int status;
 +      u8 nw_subtype = (ar->p2p) ? SUBTYPE_P2PDEV : SUBTYPE_NONE;
 +
 +      ath6kl_cfg80211_sscan_disable(vif);
  
        vif->sme_state = SME_CONNECTING;
  
  
        if (sme->ie && (sme->ie_len > 0)) {
                status = ath6kl_set_assoc_req_ies(vif, sme->ie, sme->ie_len);
 -              if (status)
 +              if (status) {
 +                      up(&ar->sem);
                        return status;
 -      }
 +              }
 +      } else
 +              ar->connect_ctrl_flags &= ~CONNECT_WPS_FLAG;
  
        if (test_bit(CONNECTED, &vif->flags) &&
            vif->ssid_len == sme->ssid_len &&
  
        vif->nw_type = vif->next_mode;
  
 +      if (vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT)
 +              nw_subtype = SUBTYPE_P2PCLIENT;
 +
        ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
                   "%s: connect called with authmode %d dot11 auth %d"
                   " PW crypto %d PW crypto len %d GRP crypto %d"
                                        vif->grp_crypto, vif->grp_crypto_len,
                                        vif->ssid_len, vif->ssid,
                                        vif->req_bssid, vif->ch_hint,
 -                                      ar->connect_ctrl_flags);
 +                                      ar->connect_ctrl_flags, nw_subtype);
  
        up(&ar->sem);
  
        return 0;
  }
  
 -static int ath6kl_add_bss_if_needed(struct ath6kl_vif *vif, const u8 *bssid,
 +static int ath6kl_add_bss_if_needed(struct ath6kl_vif *vif,
 +                                  enum network_type nw_type,
 +                                  const u8 *bssid,
                                    struct ieee80211_channel *chan,
                                    const u8 *beacon_ie, size_t beacon_ie_len)
  {
        struct ath6kl *ar = vif->ar;
        struct cfg80211_bss *bss;
 +      u16 cap_mask, cap_val;
        u8 *ie;
  
 +      if (nw_type & ADHOC_NETWORK) {
 +              cap_mask = WLAN_CAPABILITY_IBSS;
 +              cap_val = WLAN_CAPABILITY_IBSS;
 +      } else {
 +              cap_mask = WLAN_CAPABILITY_ESS;
 +              cap_val = WLAN_CAPABILITY_ESS;
 +      }
 +
        bss = cfg80211_get_bss(ar->wiphy, chan, bssid,
 -                             vif->ssid, vif->ssid_len, WLAN_CAPABILITY_ESS,
 -                             WLAN_CAPABILITY_ESS);
 +                             vif->ssid, vif->ssid_len,
 +                             cap_mask, cap_val);
        if (bss == NULL) {
                /*
                 * Since cfg80211 may not yet know about the BSS,
                memcpy(ie + 2, vif->ssid, vif->ssid_len);
                memcpy(ie + 2 + vif->ssid_len, beacon_ie, beacon_ie_len);
                bss = cfg80211_inform_bss(ar->wiphy, chan,
 -                                        bssid, 0, WLAN_CAPABILITY_ESS, 100,
 +                                        bssid, 0, cap_val, 100,
                                          ie, 2 + vif->ssid_len + beacon_ie_len,
                                          0, GFP_KERNEL);
                if (bss)
 -                      ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "added dummy bss for "
 -                                 "%pM prior to indicating connect/roamed "
 -                                 "event\n", bssid);
 +                      ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "added bss %pM to "
 +                                 "cfg80211\n", bssid);
                kfree(ie);
        } else
                ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "cfg80211 already has a bss "
@@@ -710,16 -660,16 +712,16 @@@ void ath6kl_cfg80211_connect_event(stru
  
        chan = ieee80211_get_channel(ar->wiphy, (int) channel);
  
 -
 -      if (nw_type & ADHOC_NETWORK) {
 -              cfg80211_ibss_joined(vif->ndev, bssid, GFP_KERNEL);
 +      if (ath6kl_add_bss_if_needed(vif, nw_type, bssid, chan, assoc_info,
 +                                   beacon_ie_len) < 0) {
 +              ath6kl_err("could not add cfg80211 bss entry\n");
                return;
        }
  
 -      if (ath6kl_add_bss_if_needed(vif, bssid, chan, assoc_info,
 -                                   beacon_ie_len) < 0) {
 -              ath6kl_err("could not add cfg80211 bss entry for "
 -                         "connect/roamed notification\n");
 +      if (nw_type & ADHOC_NETWORK) {
 +              ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "ad-hoc %s selected\n",
 +                         nw_type & ADHOC_CREATOR ? "creator" : "joiner");
 +              cfg80211_ibss_joined(vif->ndev, bssid, GFP_KERNEL);
                return;
        }
  
  static int ath6kl_cfg80211_disconnect(struct wiphy *wiphy,
                                      struct net_device *dev, u16 reason_code)
  {
 -      struct ath6kl *ar = (struct ath6kl *)ath6kl_priv(dev);
 +      struct ath6kl *ar = ath6kl_priv(dev);
        struct ath6kl_vif *vif = netdev_priv(dev);
  
        ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: reason=%u\n", __func__,
                   reason_code);
  
 +      ath6kl_cfg80211_sscan_disable(vif);
 +
        if (!ath6kl_cfg80211_ready(vif))
                return -EIO;
  
@@@ -841,7 -789,7 +843,7 @@@ void ath6kl_cfg80211_disconnect_event(s
  static int ath6kl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev,
                                struct cfg80211_scan_request *request)
  {
 -      struct ath6kl *ar = (struct ath6kl *)ath6kl_priv(ndev);
 +      struct ath6kl *ar = ath6kl_priv(ndev);
        struct ath6kl_vif *vif = netdev_priv(ndev);
        s8 n_channels = 0;
        u16 *channels = NULL;
        if (!ath6kl_cfg80211_ready(vif))
                return -EIO;
  
 +      ath6kl_cfg80211_sscan_disable(vif);
 +
        if (!ar->usr_bss_filter) {
                clear_bit(CLEAR_BSSFILTER_ON_BEACON, &vif->flags);
                ret = ath6kl_wmi_bssfilter_cmd(
                                                  request->ssids[i].ssid);
        }
  
 +      /*
 +       * FIXME: we should clear the IE in fw if it's not set so just
 +       * remove the check altogether
 +       */
        if (request->ie) {
                ret = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
                                               WMI_FRAME_PROBE_REQ,
        if (test_bit(CONNECTED, &vif->flags))
                force_fg_scan = 1;
  
 -      ret = ath6kl_wmi_startscan_cmd(ar->wmi, vif->fw_vif_idx, WMI_LONG_SCAN,
 -                                     force_fg_scan, false, 0, 0, n_channels,
 -                                     channels);
 +      if (test_bit(ATH6KL_FW_CAPABILITY_STA_P2PDEV_DUPLEX,
 +                  ar->fw_capabilities)) {
 +              /*
 +               * If capable of doing P2P mgmt operations using
 +               * station interface, send additional information like
 +               * supported rates to advertise and xmit rates for
 +               * probe requests
 +               */
 +              ret = ath6kl_wmi_beginscan_cmd(ar->wmi, vif->fw_vif_idx,
 +                                              WMI_LONG_SCAN, force_fg_scan,
 +                                              false, 0, 0, n_channels,
 +                                              channels, request->no_cck,
 +                                              request->rates);
 +      } else {
 +              ret = ath6kl_wmi_startscan_cmd(ar->wmi, vif->fw_vif_idx,
 +                                              WMI_LONG_SCAN, force_fg_scan,
 +                                              false, 0, 0, n_channels,
 +                                              channels);
 +      }
        if (ret)
                ath6kl_err("wmi_startscan_cmd failed\n");
        else
@@@ -979,7 -905,7 +981,7 @@@ static int ath6kl_cfg80211_add_key(stru
                                   const u8 *mac_addr,
                                   struct key_params *params)
  {
 -      struct ath6kl *ar = (struct ath6kl *)ath6kl_priv(ndev);
 +      struct ath6kl *ar = ath6kl_priv(ndev);
        struct ath6kl_vif *vif = netdev_priv(ndev);
        struct ath6kl_key *key = NULL;
        u8 key_usage;
                key_usage = GROUP_USAGE;
  
        if (params) {
 +              int seq_len = params->seq_len;
 +              if (params->cipher == WLAN_CIPHER_SUITE_SMS4 &&
 +                  seq_len > ATH6KL_KEY_SEQ_LEN) {
 +                      /* Only first half of the WPI PN is configured */
 +                      seq_len = ATH6KL_KEY_SEQ_LEN;
 +              }
                if (params->key_len > WLAN_MAX_KEY_LEN ||
 -                  params->seq_len > sizeof(key->seq))
 +                  seq_len > sizeof(key->seq))
                        return -EINVAL;
  
                key->key_len = params->key_len;
                memcpy(key->key, params->key, key->key_len);
 -              key->seq_len = params->seq_len;
 +              key->seq_len = seq_len;
                memcpy(key->seq, params->seq, key->seq_len);
                key->cipher = params->cipher;
        }
        case WLAN_CIPHER_SUITE_CCMP:
                key_type = AES_CRYPT;
                break;
 +      case WLAN_CIPHER_SUITE_SMS4:
 +              key_type = WAPI_CRYPT;
 +              break;
  
        default:
                return -ENOTSUPP;
                   __func__, key_index, key->key_len, key_type,
                   key_usage, key->seq_len);
  
 -      vif->def_txkey_index = key_index;
 -
        if (vif->nw_type == AP_NETWORK && !pairwise &&
 -          (key_type == TKIP_CRYPT || key_type == AES_CRYPT) && params) {
 +          (key_type == TKIP_CRYPT || key_type == AES_CRYPT ||
 +           key_type == WAPI_CRYPT) && params) {
                ar->ap_mode_bkey.valid = true;
                ar->ap_mode_bkey.key_index = key_index;
                ar->ap_mode_bkey.key_type = key_type;
                return 0;
        }
  
 -      return ath6kl_wmi_addkey_cmd(ar->wmi, vif->fw_vif_idx,
 -                                   vif->def_txkey_index,
 +      return ath6kl_wmi_addkey_cmd(ar->wmi, vif->fw_vif_idx, key_index,
                                     key_type, key_usage, key->key_len,
                                     key->seq, key->seq_len, key->key,
                                     KEY_OP_INIT_VAL,
@@@ -1105,7 -1024,7 +1107,7 @@@ static int ath6kl_cfg80211_del_key(stru
                                   u8 key_index, bool pairwise,
                                   const u8 *mac_addr)
  {
 -      struct ath6kl *ar = (struct ath6kl *)ath6kl_priv(ndev);
 +      struct ath6kl *ar = ath6kl_priv(ndev);
        struct ath6kl_vif *vif = netdev_priv(ndev);
  
        ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: index %d\n", __func__, key_index);
@@@ -1171,7 -1090,7 +1173,7 @@@ static int ath6kl_cfg80211_set_default_
                                           u8 key_index, bool unicast,
                                           bool multicast)
  {
 -      struct ath6kl *ar = (struct ath6kl *)ath6kl_priv(ndev);
 +      struct ath6kl *ar = ath6kl_priv(ndev);
        struct ath6kl_vif *vif = netdev_priv(ndev);
        struct ath6kl_key *key = NULL;
        u8 key_usage;
@@@ -1262,12 -1181,11 +1264,12 @@@ static int ath6kl_cfg80211_set_wiphy_pa
  */
  static int ath6kl_cfg80211_set_txpower(struct wiphy *wiphy,
                                       enum nl80211_tx_power_setting type,
 -                                     int dbm)
 +                                     int mbm)
  {
        struct ath6kl *ar = (struct ath6kl *)wiphy_priv(wiphy);
        struct ath6kl_vif *vif;
        u8 ath6kl_dbm;
 +      int dbm = MBM_TO_DBM(mbm);
  
        ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: type 0x%x, dbm %d\n", __func__,
                   type, dbm);
@@@ -1370,7 -1288,7 +1372,7 @@@ static struct net_device *ath6kl_cfg802
        struct net_device *ndev;
        u8 if_idx, nw_type;
  
 -      if (ar->num_vif == MAX_NUM_VIF) {
 +      if (ar->num_vif == ar->vif_max) {
                ath6kl_err("Reached maximum number of supported vif\n");
                return ERR_PTR(-EINVAL);
        }
@@@ -1415,6 -1333,9 +1417,6 @@@ static int ath6kl_cfg80211_change_iface
  
        ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: type %u\n", __func__, type);
  
 -      if (!ath6kl_cfg80211_ready(vif))
 -              return -EIO;
 -
        switch (type) {
        case NL80211_IFTYPE_STATION:
                vif->next_mode = INFRA_NETWORK;
@@@ -1505,7 -1426,7 +1507,7 @@@ static int ath6kl_cfg80211_join_ibss(st
                                        vif->grp_crypto, vif->grp_crypto_len,
                                        vif->ssid_len, vif->ssid,
                                        vif->req_bssid, vif->ch_hint,
 -                                      ar->connect_ctrl_flags);
 +                                      ar->connect_ctrl_flags, SUBTYPE_NONE);
        set_bit(CONNECT_PEND, &vif->flags);
  
        return 0;
@@@ -1532,7 -1453,6 +1534,7 @@@ static const u32 cipher_suites[] = 
        WLAN_CIPHER_SUITE_TKIP,
        WLAN_CIPHER_SUITE_CCMP,
        CCKM_KRK_CIPHER_SUITE,
 +      WLAN_CIPHER_SUITE_SMS4,
  };
  
  static bool is_rate_legacy(s32 rate)
@@@ -1859,7 -1779,7 +1861,7 @@@ int ath6kl_cfg80211_suspend(struct ath6
  
        case ATH6KL_CFG_SUSPEND_DEEPSLEEP:
  
 -              ath6kl_cfg80211_stop(ar);
 +              ath6kl_cfg80211_stop_all(ar);
  
                /* save the current power mode before enabling power save */
                ar->wmi->saved_pwr_mode = ar->wmi->pwr_mode;
  
        case ATH6KL_CFG_SUSPEND_CUTPOWER:
  
 -              ath6kl_cfg80211_stop(ar);
 +              ath6kl_cfg80211_stop_all(ar);
  
                if (ar->state == ATH6KL_STATE_OFF) {
                        ath6kl_dbg(ATH6KL_DBG_SUSPEND,
  
                break;
  
 +      case ATH6KL_CFG_SUSPEND_SCHED_SCAN:
 +              /*
 +               * Nothing needed for schedule scan, firmware is already in
 +               * wow mode and sleeping most of the time.
 +               */
 +              break;
 +
        default:
                break;
        }
@@@ -1951,9 -1864,6 +1953,9 @@@ int ath6kl_cfg80211_resume(struct ath6k
                }
                break;
  
 +      case ATH6KL_STATE_SCHED_SCAN:
 +              break;
 +
        default:
                break;
        }
@@@ -2077,7 -1987,7 +2079,7 @@@ static int ath6kl_ap_beacon(struct wiph
        int ies_len;
        struct wmi_connect_cmd p;
        int res;
 -      int i;
 +      int i, ret;
  
        ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: add=%d\n", __func__, add);
  
        if (info->hidden_ssid != NL80211_HIDDEN_SSID_NOT_IN_USE)
                return -EOPNOTSUPP; /* TODO */
  
 -      vif->dot11_auth_mode = OPEN_AUTH;
 +      ret = ath6kl_set_auth_type(vif, info->auth_type);
 +      if (ret)
 +              return ret;
  
        memset(&p, 0, sizeof(p));
  
                case WLAN_CIPHER_SUITE_CCMP:
                        p.prwise_crypto_type |= AES_CRYPT;
                        break;
 +              case WLAN_CIPHER_SUITE_SMS4:
 +                      p.prwise_crypto_type |= WAPI_CRYPT;
 +                      break;
                }
        }
        if (p.prwise_crypto_type == 0) {
        case WLAN_CIPHER_SUITE_CCMP:
                p.grp_crypto_type = AES_CRYPT;
                break;
 +      case WLAN_CIPHER_SUITE_SMS4:
 +              p.grp_crypto_type = WAPI_CRYPT;
 +              break;
        default:
                p.grp_crypto_type = NONE_CRYPT;
                break;
        p.dot11_auth_mode = vif->dot11_auth_mode;
        p.ch = cpu_to_le16(vif->next_chan);
  
 +      if (vif->wdev.iftype == NL80211_IFTYPE_P2P_GO) {
 +              p.nw_subtype = SUBTYPE_P2PGO;
 +      } else {
 +              /*
 +               * Due to firmware limitation, it is not possible to
 +               * do P2P mgmt operations in AP mode
 +               */
 +              p.nw_subtype = SUBTYPE_NONE;
 +      }
 +
        res = ath6kl_wmi_ap_profile_commit(ar->wmi, vif->fw_vif_idx, &p);
        if (res < 0)
                return res;
@@@ -2387,23 -2279,9 +2389,23 @@@ static int ath6kl_mgmt_tx(struct wiphy 
        }
  
        *cookie = id;
 -      return ath6kl_wmi_send_action_cmd(ar->wmi, vif->fw_vif_idx, id,
 -                                        chan->center_freq, wait,
 -                                        buf, len);
 +
 +      if (test_bit(ATH6KL_FW_CAPABILITY_STA_P2PDEV_DUPLEX,
 +                  ar->fw_capabilities)) {
 +              /*
 +               * If capable of doing P2P mgmt operations using
 +               * station interface, send additional information like
 +               * supported rates to advertise and xmit rates for
 +               * probe requests
 +               */
 +              return ath6kl_wmi_send_mgmt_cmd(ar->wmi, vif->fw_vif_idx, id,
 +                                              chan->center_freq, wait,
 +                                              buf, len, no_cck);
 +      } else {
 +              return ath6kl_wmi_send_action_cmd(ar->wmi, vif->fw_vif_idx, id,
 +                                                chan->center_freq, wait,
 +                                                buf, len);
 +      }
  }
  
  static void ath6kl_mgmt_frame_register(struct wiphy *wiphy,
        }
  }
  
 +static int ath6kl_cfg80211_sscan_start(struct wiphy *wiphy,
 +                      struct net_device *dev,
 +                      struct cfg80211_sched_scan_request *request)
 +{
 +      struct ath6kl *ar = ath6kl_priv(dev);
 +      struct ath6kl_vif *vif = netdev_priv(dev);
 +      u16 interval;
 +      int ret;
 +      u8 i;
 +
 +      if (ar->state != ATH6KL_STATE_ON)
 +              return -EIO;
 +
 +      if (vif->sme_state != SME_DISCONNECTED)
 +              return -EBUSY;
 +
 +      for (i = 0; i < ar->wiphy->max_sched_scan_ssids; i++) {
 +              ath6kl_wmi_probedssid_cmd(ar->wmi, vif->fw_vif_idx,
 +                                        i, DISABLE_SSID_FLAG,
 +                                        0, NULL);
 +      }
 +
 +      /* fw uses seconds, also make sure that it's >0 */
 +      interval = max_t(u16, 1, request->interval / 1000);
 +
 +      ath6kl_wmi_scanparams_cmd(ar->wmi, vif->fw_vif_idx,
 +                                interval, interval,
 +                                10, 0, 0, 0, 3, 0, 0, 0);
 +
 +      if (request->n_ssids && request->ssids[0].ssid_len) {
 +              for (i = 0; i < request->n_ssids; i++) {
 +                      ath6kl_wmi_probedssid_cmd(ar->wmi, vif->fw_vif_idx,
 +                                                i, SPECIFIC_SSID_FLAG,
 +                                                request->ssids[i].ssid_len,
 +                                                request->ssids[i].ssid);
 +              }
 +      }
 +
 +      ret = ath6kl_wmi_set_wow_mode_cmd(ar->wmi, vif->fw_vif_idx,
 +                                        ATH6KL_WOW_MODE_ENABLE,
 +                                        WOW_FILTER_SSID,
 +                                        WOW_HOST_REQ_DELAY);
 +      if (ret) {
 +              ath6kl_warn("Failed to enable wow with ssid filter: %d\n", ret);
 +              return ret;
 +      }
 +
 +      /* this also clears IE in fw if it's not set */
 +      ret = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
 +                                     WMI_FRAME_PROBE_REQ,
 +                                     request->ie, request->ie_len);
 +      if (ret) {
 +              ath6kl_warn("Failed to set probe request IE for scheduled scan: %d",
 +                          ret);
 +              return ret;
 +      }
 +
 +      ret = ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx,
 +                                               ATH6KL_HOST_MODE_ASLEEP);
 +      if (ret) {
 +              ath6kl_warn("Failed to enable host sleep mode for sched scan: %d\n",
 +                          ret);
 +              return ret;
 +      }
 +
 +      ar->state = ATH6KL_STATE_SCHED_SCAN;
 +
 +      return ret;
 +}
 +
 +static int ath6kl_cfg80211_sscan_stop(struct wiphy *wiphy,
 +                                    struct net_device *dev)
 +{
 +      struct ath6kl_vif *vif = netdev_priv(dev);
 +      bool stopped;
 +
 +      stopped = __ath6kl_cfg80211_sscan_stop(vif);
 +
 +      if (!stopped)
 +              return -EIO;
 +
 +      return 0;
 +}
 +
  static const struct ieee80211_txrx_stypes
  ath6kl_mgmt_stypes[NUM_NL80211_IFTYPES] = {
        [NL80211_IFTYPE_STATION] = {
@@@ -2565,17 -2359,25 +2567,17 @@@ static struct cfg80211_ops ath6kl_cfg80
        .cancel_remain_on_channel = ath6kl_cancel_remain_on_channel,
        .mgmt_tx = ath6kl_mgmt_tx,
        .mgmt_frame_register = ath6kl_mgmt_frame_register,
 +      .sched_scan_start = ath6kl_cfg80211_sscan_start,
 +      .sched_scan_stop = ath6kl_cfg80211_sscan_stop,
  };
  
 -void ath6kl_cfg80211_stop(struct ath6kl *ar)
 +void ath6kl_cfg80211_stop(struct ath6kl_vif *vif)
  {
 -      struct ath6kl_vif *vif;
 -
 -      /* FIXME: for multi vif */
 -      vif = ath6kl_vif_first(ar);
 -      if (!vif) {
 -              /* save the current power mode before enabling power save */
 -              ar->wmi->saved_pwr_mode = ar->wmi->pwr_mode;
 -
 -              if (ath6kl_wmi_powermode_cmd(ar->wmi, 0, REC_POWER) != 0)
 -                      ath6kl_warn("ath6kl_deep_sleep_enable: "
 -                                  "wmi_powermode_cmd failed\n");
 -              return;
 -      }
 +      ath6kl_cfg80211_sscan_disable(vif);
  
        switch (vif->sme_state) {
 +      case SME_DISCONNECTED:
 +              break;
        case SME_CONNECTING:
                cfg80211_connect_result(vif->ndev, vif->bssid, NULL, 0,
                                        NULL, 0,
                                        GFP_KERNEL);
                break;
        case SME_CONNECTED:
 -      default:
 -              /*
 -               * FIXME: oddly enough smeState is in DISCONNECTED during
 -               * suspend, why? Need to send disconnected event in that
 -               * state.
 -               */
                cfg80211_disconnected(vif->ndev, 0, NULL, 0, GFP_KERNEL);
                break;
        }
  
        if (test_bit(CONNECTED, &vif->flags) ||
            test_bit(CONNECT_PEND, &vif->flags))
 -              ath6kl_wmi_disconnect_cmd(ar->wmi, vif->fw_vif_idx);
 +              ath6kl_wmi_disconnect_cmd(vif->ar->wmi, vif->fw_vif_idx);
  
        vif->sme_state = SME_DISCONNECTED;
        clear_bit(CONNECTED, &vif->flags);
        clear_bit(CONNECT_PEND, &vif->flags);
  
        /* disable scanning */
 -      if (ath6kl_wmi_scanparams_cmd(ar->wmi, vif->fw_vif_idx, 0xFFFF, 0, 0,
 -                                    0, 0, 0, 0, 0, 0, 0) != 0)
 -              printk(KERN_WARNING "ath6kl: failed to disable scan "
 -                     "during suspend\n");
 +      if (ath6kl_wmi_scanparams_cmd(vif->ar->wmi, vif->fw_vif_idx, 0xFFFF,
 +                                    0, 0, 0, 0, 0, 0, 0, 0, 0) != 0)
 +              ath6kl_warn("failed to disable scan during stop\n");
  
        ath6kl_cfg80211_scan_complete_event(vif, true);
  }
  
 +void ath6kl_cfg80211_stop_all(struct ath6kl *ar)
 +{
 +      struct ath6kl_vif *vif;
 +
 +      vif = ath6kl_vif_first(ar);
 +      if (!vif) {
 +              /* save the current power mode before enabling power save */
 +              ar->wmi->saved_pwr_mode = ar->wmi->pwr_mode;
 +
 +              if (ath6kl_wmi_powermode_cmd(ar->wmi, 0, REC_POWER) != 0)
 +                      ath6kl_warn("ath6kl_deep_sleep_enable: "
 +                                  "wmi_powermode_cmd failed\n");
 +              return;
 +      }
 +
 +      /*
 +       * FIXME: we should take ar->list_lock to protect changes in the
 +       * vif_list, but that's not trivial to do as ath6kl_cfg80211_stop()
 +       * sleeps.
 +       */
 +      list_for_each_entry(vif, &ar->vif_list, list)
 +              ath6kl_cfg80211_stop(vif);
 +}
 +
  struct ath6kl *ath6kl_core_alloc(struct device *dev)
  {
        struct ath6kl *ar;
        }
  
        ar = wiphy_priv(wiphy);
 -      if (!multi_norm_if_support)
 -              ar->p2p = !!ath6kl_p2p;
 +      ar->p2p = !!ath6kl_p2p;
        ar->wiphy = wiphy;
        ar->dev = dev;
  
 -      if (multi_norm_if_support)
 -              ar->max_norm_iface = 2;
 -      else
 -              ar->max_norm_iface = 1;
 +      ar->vif_max = 1;
  
 -      /* FIXME: Remove this once the multivif support is enabled */
        ar->max_norm_iface = 1;
  
        spin_lock_init(&ar->lock);
        ar->tx_pwr = 0;
  
        ar->intra_bss = 1;
 -      memset(&ar->sc_params, 0, sizeof(ar->sc_params));
 -      ar->sc_params.short_scan_ratio = WMI_SHORTSCANRATIO_DEFAULT;
 -      ar->sc_params.scan_ctrl_flags = DEFAULT_SCAN_CTRL_FLAGS;
        ar->lrssi_roam_threshold = DEF_LRSSI_ROAM_THRESHOLD;
  
        ar->state = ATH6KL_STATE_OFF;
@@@ -2729,8 -2522,6 +2731,8 @@@ int ath6kl_register_ieee80211_hw(struc
        wiphy->wowlan.pattern_min_len = 1;
        wiphy->wowlan.pattern_max_len = WOW_PATTERN_SIZE;
  
 +      wiphy->max_sched_scan_ssids = 10;
 +
        ret = wiphy_register(wiphy);
        if (ret < 0) {
                ath6kl_err("couldn't register wiphy device\n");
@@@ -2750,9 -2541,6 +2752,9 @@@ static int ath6kl_init_if_data(struct a
  
        setup_timer(&vif->disconnect_timer, disconnect_timer_handler,
                    (unsigned long) vif->ndev);
 +      setup_timer(&vif->sched_scan_timer, ath6kl_wmi_sscan_timer,
 +                  (unsigned long) vif);
 +
        set_bit(WMM_ENABLED, &vif->flags);
        spin_lock_init(&vif->if_lock);
  
index cf513a80b0616787d556a70fdc7cc537c11cc720,9eff0d010bb161812f70712bd51e9c7700acc2f9..eb808b46f94cec1010a16e0733f9dacbf5ee4147
@@@ -19,6 -19,7 +19,7 @@@
  #include <linux/circ_buf.h>
  #include <linux/fs.h>
  #include <linux/vmalloc.h>
+ #include <linux/export.h>
  
  #include "debug.h"
  #include "target.h"
@@@ -1550,10 -1551,10 +1551,10 @@@ static ssize_t ath6kl_listen_int_read(s
                                                size_t count, loff_t *ppos)
  {
        struct ath6kl *ar = file->private_data;
 -      char buf[16];
 +      char buf[32];
        int len;
  
 -      len = snprintf(buf, sizeof(buf), "%u %u\n", ar->listen_intvl_t,
 +      len = scnprintf(buf, sizeof(buf), "%u %u\n", ar->listen_intvl_t,
                                        ar->listen_intvl_b);
  
        return simple_read_from_buffer(user_buf, count, ppos, buf, len);
index 9b779aac83e779862d7a1b040ee982438b3516b1,9853c9c125c1417c9ab87de1afc6aea8b8fe3160..e569c652e35c81ebd90810588c0181d759a825c2
@@@ -41,13 -41,12 +41,13 @@@ enum ATH6K_DEBUG_MASK 
        ATH6KL_DBG_BOOT         = BIT(18),    /* driver init and fw boot */
        ATH6KL_DBG_WMI_DUMP     = BIT(19),
        ATH6KL_DBG_SUSPEND      = BIT(20),
 +      ATH6KL_DBG_USB          = BIT(21),
        ATH6KL_DBG_ANY          = 0xffffffff  /* enable all logs */
  };
  
  extern unsigned int debug_mask;
- extern int ath6kl_printk(const char *level, const char *fmt, ...)
      __attribute__ ((format (printf, 2, 3)));
+ extern __printf(2, 3)
int ath6kl_printk(const char *level, const char *fmt, ...);
  
  #define ath6kl_info(fmt, ...)                         \
        ath6kl_printk(KERN_INFO, fmt, ##__VA_ARGS__)
index c614049d7b2ea6a431d48d0eb464b1245aaf466f,30050af9d4c67a4bb1a774e040ea045d5e3c1d7f..368ecbd172a3d03225d7d7ccfc6a19d21aee61f9
@@@ -33,80 -33,6 +33,80 @@@ module_param(debug_mask, uint, 0644)
  module_param(testmode, uint, 0644);
  module_param(suspend_cutpower, bool, 0444);
  
 +static const struct ath6kl_hw hw_list[] = {
 +      {
 +              .id                             = AR6003_HW_2_0_VERSION,
 +              .name                           = "ar6003 hw 2.0",
 +              .dataset_patch_addr             = 0x57e884,
 +              .app_load_addr                  = 0x543180,
 +              .board_ext_data_addr            = 0x57e500,
 +              .reserved_ram_size              = 6912,
 +              .refclk_hz                      = 26000000,
 +              .uarttx_pin                     = 8,
 +
 +              /* hw2.0 needs override address hardcoded */
 +              .app_start_override_addr        = 0x944C00,
 +
 +              .fw_otp                 = AR6003_HW_2_0_OTP_FILE,
 +              .fw                     = AR6003_HW_2_0_FIRMWARE_FILE,
 +              .fw_tcmd                = AR6003_HW_2_0_TCMD_FIRMWARE_FILE,
 +              .fw_patch               = AR6003_HW_2_0_PATCH_FILE,
 +              .fw_api2                = AR6003_HW_2_0_FIRMWARE_2_FILE,
 +              .fw_board               = AR6003_HW_2_0_BOARD_DATA_FILE,
 +              .fw_default_board       = AR6003_HW_2_0_DEFAULT_BOARD_DATA_FILE,
 +      },
 +      {
 +              .id                             = AR6003_HW_2_1_1_VERSION,
 +              .name                           = "ar6003 hw 2.1.1",
 +              .dataset_patch_addr             = 0x57ff74,
 +              .app_load_addr                  = 0x1234,
 +              .board_ext_data_addr            = 0x542330,
 +              .reserved_ram_size              = 512,
 +              .refclk_hz                      = 26000000,
 +              .uarttx_pin                     = 8,
 +
 +              .fw_otp                 = AR6003_HW_2_1_1_OTP_FILE,
 +              .fw                     = AR6003_HW_2_1_1_FIRMWARE_FILE,
 +              .fw_tcmd                = AR6003_HW_2_1_1_TCMD_FIRMWARE_FILE,
 +              .fw_patch               = AR6003_HW_2_1_1_PATCH_FILE,
 +              .fw_api2                = AR6003_HW_2_1_1_FIRMWARE_2_FILE,
 +              .fw_board               = AR6003_HW_2_1_1_BOARD_DATA_FILE,
 +              .fw_default_board       = AR6003_HW_2_1_1_DEFAULT_BOARD_DATA_FILE,
 +      },
 +      {
 +              .id                             = AR6004_HW_1_0_VERSION,
 +              .name                           = "ar6004 hw 1.0",
 +              .dataset_patch_addr             = 0x57e884,
 +              .app_load_addr                  = 0x1234,
 +              .board_ext_data_addr            = 0x437000,
 +              .reserved_ram_size              = 19456,
 +              .board_addr                     = 0x433900,
 +              .refclk_hz                      = 26000000,
 +              .uarttx_pin                     = 11,
 +
 +              .fw                     = AR6004_HW_1_0_FIRMWARE_FILE,
 +              .fw_api2                = AR6004_HW_1_0_FIRMWARE_2_FILE,
 +              .fw_board               = AR6004_HW_1_0_BOARD_DATA_FILE,
 +              .fw_default_board       = AR6004_HW_1_0_DEFAULT_BOARD_DATA_FILE,
 +      },
 +      {
 +              .id                             = AR6004_HW_1_1_VERSION,
 +              .name                           = "ar6004 hw 1.1",
 +              .dataset_patch_addr             = 0x57e884,
 +              .app_load_addr                  = 0x1234,
 +              .board_ext_data_addr            = 0x437000,
 +              .reserved_ram_size              = 11264,
 +              .board_addr                     = 0x43d400,
 +              .refclk_hz                      = 40000000,
 +              .uarttx_pin                     = 11,
 +
 +              .fw                     = AR6004_HW_1_1_FIRMWARE_FILE,
 +              .fw_api2                = AR6004_HW_1_1_FIRMWARE_2_FILE,
 +              .fw_board               = AR6004_HW_1_1_BOARD_DATA_FILE,
 +              .fw_default_board       = AR6004_HW_1_1_DEFAULT_BOARD_DATA_FILE,
 +      },
 +};
 +
  /*
   * Include definitions here that can be used to tune the WLAN module
   * behavior. Different customers can tune the behavior as per their needs,
   */
  #define WLAN_CONFIG_DISCONNECT_TIMEOUT 10
  
 -#define CONFIG_AR600x_DEBUG_UART_TX_PIN 8
  
  #define ATH6KL_DATA_OFFSET    64
  struct sk_buff *ath6kl_buf_alloc(int size)
@@@ -421,7 -348,11 +421,7 @@@ static int ath6kl_target_config_wlan_pa
                        status = -EIO;
                }
  
 -      /*
 -       * FIXME: Make sure p2p configurations are not applied to
 -       * non-p2p capable interfaces when multivif support is enabled.
 -       */
 -      if (ar->p2p) {
 +      if (ar->p2p && (ar->vif_max == 1 || idx)) {
                ret = ath6kl_wmi_info_req_cmd(ar->wmi, idx,
                                              P2P_FLAG_CAPABILITIES_REQ |
                                              P2P_FLAG_MACADDR_REQ |
                }
        }
  
 -      /*
 -       * FIXME: Make sure p2p configurations are not applied to
 -       * non-p2p capable interfaces when multivif support is enabled.
 -       */
 -      if (ar->p2p) {
 +      if (ar->p2p && (ar->vif_max == 1 || idx)) {
                /* Enable Probe Request reporting for P2P */
                ret = ath6kl_wmi_probe_report_req_cmd(ar->wmi, idx, true);
                if (ret) {
@@@ -450,7 -385,7 +450,7 @@@ int ath6kl_configure_target(struct ath6
  {
        u32 param, ram_reserved_size;
        u8 fw_iftype, fw_mode = 0, fw_submode = 0;
 -      int i;
 +      int i, status;
  
        /*
         * Note: Even though the firmware interface type is
         */
        fw_iftype = HI_OPTION_FW_MODE_BSS_STA;
  
 -      for (i = 0; i < MAX_NUM_VIF; i++)
 +      for (i = 0; i < ar->vif_max; i++)
                fw_mode |= fw_iftype << (i * HI_OPTION_FW_MODE_BITS);
  
        /*
                fw_submode |= HI_OPTION_FW_SUBMODE_NONE <<
                              (i * HI_OPTION_FW_SUBMODE_BITS);
  
 -      for (i = ar->max_norm_iface; i < MAX_NUM_VIF; i++)
 +      for (i = ar->max_norm_iface; i < ar->vif_max; i++)
                fw_submode |= HI_OPTION_FW_SUBMODE_P2PDEV <<
                              (i * HI_OPTION_FW_SUBMODE_BITS);
  
 -      /*
 -       * FIXME: This needs to be removed once the multivif
 -       * support is enabled.
 -       */
 -      if (ar->p2p)
 +      if (ar->p2p && ar->vif_max == 1)
                fw_submode = HI_OPTION_FW_SUBMODE_P2PDEV;
  
        param = HTC_PROTOCOL_VERSION;
                return -EIO;
        }
  
 -      param |= (MAX_NUM_VIF << HI_OPTION_NUM_DEV_SHIFT);
 +      param |= (ar->vif_max << HI_OPTION_NUM_DEV_SHIFT);
        param |= fw_mode << HI_OPTION_FW_MODE_SHIFT;
        param |= fw_submode << HI_OPTION_FW_SUBMODE_SHIFT;
  
                /* use default number of control buffers */
                return -EIO;
  
 +      /* Configure GPIO AR600x UART */
 +      param = ar->hw.uarttx_pin;
 +      status = ath6kl_bmi_write(ar,
 +                              ath6kl_get_hi_item_addr(ar,
 +                              HI_ITEM(hi_dbg_uart_txpin)),
 +                              (u8 *)&param, 4);
 +      if (status)
 +              return status;
 +
 +      /* Configure target refclk_hz */
 +      param =  ar->hw.refclk_hz;
 +      status = ath6kl_bmi_write(ar,
 +                              ath6kl_get_hi_item_addr(ar,
 +                              HI_ITEM(hi_refclk_hz)),
 +                              (u8 *)&param, 4);
 +      if (status)
 +              return status;
 +
        return 0;
  }
  
@@@ -629,11 -550,11 +629,11 @@@ static int ath6kl_get_fw(struct ath6kl 
  static const char *get_target_ver_dir(const struct ath6kl *ar)
  {
        switch (ar->version.target_ver) {
 -      case AR6003_REV1_VERSION:
 +      case AR6003_HW_1_0_VERSION:
                return "ath6k/AR6003/hw1.0";
 -      case AR6003_REV2_VERSION:
 +      case AR6003_HW_2_0_VERSION:
                return "ath6k/AR6003/hw2.0";
 -      case AR6003_REV3_VERSION:
 +      case AR6003_HW_2_1_1_VERSION:
                return "ath6k/AR6003/hw2.1.1";
        }
        ath6kl_warn("%s: unsupported target version 0x%x.\n", __func__,
@@@ -691,10 -612,17 +691,10 @@@ static int ath6kl_fetch_board_file(stru
        if (ar->fw_board != NULL)
                return 0;
  
 -      switch (ar->version.target_ver) {
 -      case AR6003_REV2_VERSION:
 -              filename = AR6003_REV2_BOARD_DATA_FILE;
 -              break;
 -      case AR6004_REV1_VERSION:
 -              filename = AR6004_REV1_BOARD_DATA_FILE;
 -              break;
 -      default:
 -              filename = AR6003_REV3_BOARD_DATA_FILE;
 -              break;
 -      }
 +      if (WARN_ON(ar->hw.fw_board == NULL))
 +              return -EINVAL;
 +
 +      filename = ar->hw.fw_board;
  
        ret = ath6kl_get_fw(ar, filename, &ar->fw_board,
                            &ar->fw_board_len);
        ath6kl_warn("Failed to get board file %s (%d), trying to find default board file.\n",
                    filename, ret);
  
 -      switch (ar->version.target_ver) {
 -      case AR6003_REV2_VERSION:
 -              filename = AR6003_REV2_DEFAULT_BOARD_DATA_FILE;
 -              break;
 -      case AR6004_REV1_VERSION:
 -              filename = AR6004_REV1_DEFAULT_BOARD_DATA_FILE;
 -              break;
 -      default:
 -              filename = AR6003_REV3_DEFAULT_BOARD_DATA_FILE;
 -              break;
 -      }
 +      filename = ar->hw.fw_default_board;
  
        ret = ath6kl_get_fw(ar, filename, &ar->fw_board,
                            &ar->fw_board_len);
@@@ -736,14 -674,19 +736,14 @@@ static int ath6kl_fetch_otp_file(struc
        if (ar->fw_otp != NULL)
                return 0;
  
 -      switch (ar->version.target_ver) {
 -      case AR6003_REV2_VERSION:
 -              filename = AR6003_REV2_OTP_FILE;
 -              break;
 -      case AR6004_REV1_VERSION:
 -              ath6kl_dbg(ATH6KL_DBG_TRC, "AR6004 doesn't need OTP file\n");
 +      if (ar->hw.fw_otp == NULL) {
 +              ath6kl_dbg(ATH6KL_DBG_BOOT,
 +                         "no OTP file configured for this hw\n");
                return 0;
 -              break;
 -      default:
 -              filename = AR6003_REV3_OTP_FILE;
 -              break;
        }
  
 +      filename = ar->hw.fw_otp;
 +
        ret = ath6kl_get_fw(ar, filename, &ar->fw_otp,
                            &ar->fw_otp_len);
        if (ret) {
@@@ -764,22 -707,38 +764,22 @@@ static int ath6kl_fetch_fw_file(struct 
                return 0;
  
        if (testmode) {
 -              switch (ar->version.target_ver) {
 -              case AR6003_REV2_VERSION:
 -                      filename = AR6003_REV2_TCMD_FIRMWARE_FILE;
 -                      break;
 -              case AR6003_REV3_VERSION:
 -                      filename = AR6003_REV3_TCMD_FIRMWARE_FILE;
 -                      break;
 -              case AR6004_REV1_VERSION:
 -                      ath6kl_warn("testmode not supported with ar6004\n");
 +              if (ar->hw.fw_tcmd == NULL) {
 +                      ath6kl_warn("testmode not supported\n");
                        return -EOPNOTSUPP;
 -              default:
 -                      ath6kl_warn("unknown target version: 0x%x\n",
 -                                     ar->version.target_ver);
 -                      return -EINVAL;
                }
  
 +              filename = ar->hw.fw_tcmd;
 +
                set_bit(TESTMODE, &ar->flag);
  
                goto get_fw;
        }
  
 -      switch (ar->version.target_ver) {
 -      case AR6003_REV2_VERSION:
 -              filename = AR6003_REV2_FIRMWARE_FILE;
 -              break;
 -      case AR6004_REV1_VERSION:
 -              filename = AR6004_REV1_FIRMWARE_FILE;
 -              break;
 -      default:
 -              filename = AR6003_REV3_FIRMWARE_FILE;
 -              break;
 -      }
 +      if (WARN_ON(ar->hw.fw == NULL))
 +              return -EINVAL;
 +
 +      filename = ar->hw.fw;
  
  get_fw:
        ret = ath6kl_get_fw(ar, filename, &ar->fw, &ar->fw_len);
@@@ -797,20 -756,27 +797,20 @@@ static int ath6kl_fetch_patch_file(stru
        const char *filename;
        int ret;
  
 -      switch (ar->version.target_ver) {
 -      case AR6003_REV2_VERSION:
 -              filename = AR6003_REV2_PATCH_FILE;
 -              break;
 -      case AR6004_REV1_VERSION:
 -              /* FIXME: implement for AR6004 */
 +      if (ar->fw_patch != NULL)
                return 0;
 -              break;
 -      default:
 -              filename = AR6003_REV3_PATCH_FILE;
 -              break;
 -      }
  
 -      if (ar->fw_patch == NULL) {
 -              ret = ath6kl_get_fw(ar, filename, &ar->fw_patch,
 -                                  &ar->fw_patch_len);
 -              if (ret) {
 -                      ath6kl_err("Failed to get patch file %s: %d\n",
 -                                 filename, ret);
 -                      return ret;
 -              }
 +      if (ar->hw.fw_patch == NULL)
 +              return 0;
 +
 +      filename = ar->hw.fw_patch;
 +
 +      ret = ath6kl_get_fw(ar, filename, &ar->fw_patch,
 +                          &ar->fw_patch_len);
 +      if (ret) {
 +              ath6kl_err("Failed to get patch file %s: %d\n",
 +                         filename, ret);
 +              return ret;
        }
  
        return 0;
@@@ -845,10 -811,19 +845,10 @@@ static int ath6kl_fetch_fw_api2(struct 
        int ret, ie_id, i, index, bit;
        __le32 *val;
  
 -      switch (ar->version.target_ver) {
 -      case AR6003_REV2_VERSION:
 -              filename = AR6003_REV2_FIRMWARE_2_FILE;
 -              break;
 -      case AR6003_REV3_VERSION:
 -              filename = AR6003_REV3_FIRMWARE_2_FILE;
 -              break;
 -      case AR6004_REV1_VERSION:
 -              filename = AR6004_REV1_FIRMWARE_2_FILE;
 -              break;
 -      default:
 +      if (ar->hw.fw_api2 == NULL)
                return -EOPNOTSUPP;
 -      }
 +
 +      filename = ar->hw.fw_api2;
  
        ret = request_firmware(&fw, filename, ar->dev);
        if (ret)
                                   ar->hw.reserved_ram_size);
                        break;
                case ATH6KL_FW_IE_CAPABILITIES:
 +                      if (ie_len < DIV_ROUND_UP(ATH6KL_FW_CAPABILITY_MAX, 8))
 +                              break;
 +
                        ath6kl_dbg(ATH6KL_DBG_BOOT,
                                   "found firmware capabilities ie (%zd B)\n",
                                   ie_len);
  
                        for (i = 0; i < ATH6KL_FW_CAPABILITY_MAX; i++) {
 -                              index = ALIGN(i, 8) / 8;
 +                              index = i / 8;
                                bit = i % 8;
  
                                if (data[index] & (1 << bit))
                        ar->hw.dataset_patch_addr = le32_to_cpup(val);
  
                        ath6kl_dbg(ATH6KL_DBG_BOOT,
 -                                 "found patch address ie 0x%d\n",
 +                                 "found patch address ie 0x%x\n",
                                   ar->hw.dataset_patch_addr);
                        break;
 +              case ATH6KL_FW_IE_BOARD_ADDR:
 +                      if (ie_len != sizeof(*val))
 +                              break;
 +
 +                      val = (__le32 *) data;
 +                      ar->hw.board_addr = le32_to_cpup(val);
 +
 +                      ath6kl_dbg(ATH6KL_DBG_BOOT,
 +                                 "found board address ie 0x%x\n",
 +                                 ar->hw.board_addr);
 +                      break;
 +              case ATH6KL_FW_IE_VIF_MAX:
 +                      if (ie_len != sizeof(*val))
 +                              break;
 +
 +                      val = (__le32 *) data;
 +                      ar->vif_max = min_t(unsigned int, le32_to_cpup(val),
 +                                          ATH6KL_VIF_MAX);
 +
 +                      if (ar->vif_max > 1 && !ar->p2p)
 +                              ar->max_norm_iface = 2;
 +
 +                      ath6kl_dbg(ATH6KL_DBG_BOOT,
 +                                 "found vif max ie %d\n", ar->vif_max);
 +                      break;
                default:
                        ath6kl_dbg(ATH6KL_DBG_BOOT, "Unknown fw ie: %u\n",
                                   le32_to_cpup(&hdr->id));
@@@ -1047,8 -994,8 +1047,8 @@@ static int ath6kl_upload_board_file(str
         * For AR6004, host determine Target RAM address for
         * writing board data.
         */
 -      if (ar->target_type == TARGET_TYPE_AR6004) {
 -              board_address = AR6004_REV1_BOARD_DATA_ADDRESS;
 +      if (ar->hw.board_addr != 0) {
 +              board_address = ar->hw.board_addr;
                ath6kl_bmi_write(ar,
                                ath6kl_get_hi_item_addr(ar,
                                HI_ITEM(hi_board_data)),
                        HI_ITEM(hi_board_ext_data)),
                        (u8 *) &board_ext_address, 4);
  
 -      if (board_ext_address == 0) {
 +      if (ar->target_type == TARGET_TYPE_AR6003 &&
 +          board_ext_address == 0) {
                ath6kl_err("Failed to get board file target address.\n");
                return -EINVAL;
        }
                break;
        }
  
 -      if (ar->fw_board_len == (board_data_size +
 -                               board_ext_data_size)) {
 +      if (board_ext_address &&
 +          ar->fw_board_len == (board_data_size + board_ext_data_size)) {
  
                /* write extended board data */
                ath6kl_dbg(ATH6KL_DBG_BOOT,
@@@ -1146,8 -1092,8 +1146,8 @@@ static int ath6kl_upload_otp(struct ath
        bool from_hw = false;
        int ret;
  
 -      if (WARN_ON(ar->fw_otp == NULL))
 -              return -ENOENT;
 +      if (ar->fw_otp == NULL)
 +              return 0;
  
        address = ar->hw.app_load_addr;
  
@@@ -1196,7 -1142,7 +1196,7 @@@ static int ath6kl_upload_firmware(struc
        int ret;
  
        if (WARN_ON(ar->fw == NULL))
 -              return -ENOENT;
 +              return 0;
  
        address = ar->hw.app_load_addr;
  
@@@ -1226,8 -1172,8 +1226,8 @@@ static int ath6kl_upload_patch(struct a
        u32 address, param;
        int ret;
  
 -      if (WARN_ON(ar->fw_patch == NULL))
 -              return -ENOENT;
 +      if (ar->fw_patch == NULL)
 +              return 0;
  
        address = ar->hw.dataset_patch_addr;
  
@@@ -1312,7 -1258,7 +1312,7 @@@ static int ath6kl_init_upload(struct at
                return status;
  
        /* WAR to avoid SDIO CRC err */
 -      if (ar->version.target_ver == AR6003_REV2_VERSION) {
 +      if (ar->version.target_ver == AR6003_HW_2_0_VERSION) {
                ath6kl_err("temporary war to avoid sdio crc error\n");
  
                param = 0x20;
        if (status)
                return status;
  
 -      /* Configure GPIO AR6003 UART */
 -      param = CONFIG_AR600x_DEBUG_UART_TX_PIN;
 -      status = ath6kl_bmi_write(ar,
 -                                ath6kl_get_hi_item_addr(ar,
 -                                HI_ITEM(hi_dbg_uart_txpin)),
 -                                (u8 *)&param, 4);
 -
        return status;
  }
  
  static int ath6kl_init_hw_params(struct ath6kl *ar)
  {
 -      switch (ar->version.target_ver) {
 -      case AR6003_REV2_VERSION:
 -              ar->hw.dataset_patch_addr = AR6003_REV2_DATASET_PATCH_ADDRESS;
 -              ar->hw.app_load_addr = AR6003_REV2_APP_LOAD_ADDRESS;
 -              ar->hw.board_ext_data_addr = AR6003_REV2_BOARD_EXT_DATA_ADDRESS;
 -              ar->hw.reserved_ram_size = AR6003_REV2_RAM_RESERVE_SIZE;
 +      const struct ath6kl_hw *hw;
 +      int i;
  
 -              /* hw2.0 needs override address hardcoded */
 -              ar->hw.app_start_override_addr = 0x944C00;
 +      for (i = 0; i < ARRAY_SIZE(hw_list); i++) {
 +              hw = &hw_list[i];
  
 -              break;
 -      case AR6003_REV3_VERSION:
 -              ar->hw.dataset_patch_addr = AR6003_REV3_DATASET_PATCH_ADDRESS;
 -              ar->hw.app_load_addr = 0x1234;
 -              ar->hw.board_ext_data_addr = AR6003_REV3_BOARD_EXT_DATA_ADDRESS;
 -              ar->hw.reserved_ram_size = AR6003_REV3_RAM_RESERVE_SIZE;
 -              break;
 -      case AR6004_REV1_VERSION:
 -              ar->hw.dataset_patch_addr = AR6003_REV2_DATASET_PATCH_ADDRESS;
 -              ar->hw.app_load_addr = AR6003_REV3_APP_LOAD_ADDRESS;
 -              ar->hw.board_ext_data_addr = AR6004_REV1_BOARD_EXT_DATA_ADDRESS;
 -              ar->hw.reserved_ram_size = AR6004_REV1_RAM_RESERVE_SIZE;
 -              break;
 -      default:
 +              if (hw->id == ar->version.target_ver)
 +                      break;
 +      }
 +
 +      if (i == ARRAY_SIZE(hw_list)) {
                ath6kl_err("Unsupported hardware version: 0x%x\n",
                           ar->version.target_ver);
                return -EINVAL;
        }
  
 +      ar->hw = *hw;
 +
        ath6kl_dbg(ATH6KL_DBG_BOOT,
                   "target_ver 0x%x target_type 0x%x dataset_patch 0x%x app_load_addr 0x%x\n",
                   ar->version.target_ver, ar->target_type,
                   "app_start_override_addr 0x%x board_ext_data_addr 0x%x reserved_ram_size 0x%x",
                   ar->hw.app_start_override_addr, ar->hw.board_ext_data_addr,
                   ar->hw.reserved_ram_size);
 +      ath6kl_dbg(ATH6KL_DBG_BOOT,
 +                 "refclk_hz %d uarttx_pin %d",
 +                 ar->hw.refclk_hz, ar->hw.uarttx_pin);
  
        return 0;
  }
  
 +static const char *ath6kl_init_get_hif_name(enum ath6kl_hif_type type)
 +{
 +      switch (type) {
 +      case ATH6KL_HIF_TYPE_SDIO:
 +              return "sdio";
 +      case ATH6KL_HIF_TYPE_USB:
 +              return "usb";
 +      }
 +
 +      return NULL;
 +}
 +
  int ath6kl_init_hw_start(struct ath6kl *ar)
  {
        long timeleft;
  
        ath6kl_dbg(ATH6KL_DBG_BOOT, "firmware booted\n");
  
 +
 +      if (test_and_clear_bit(FIRST_BOOT, &ar->flag)) {
 +              ath6kl_info("%s %s fw %s%s\n",
 +                          ar->hw.name,
 +                          ath6kl_init_get_hif_name(ar->hif_type),
 +                          ar->wiphy->fw_version,
 +                          test_bit(TESTMODE, &ar->flag) ? " testmode" : "");
 +      }
 +
        if (ar->version.abi_ver != ATH6KL_ABI_VERSION) {
                ath6kl_err("abi version mismatch: host(0x%x), target(0x%x)\n",
                           ATH6KL_ABI_VERSION, ar->version.abi_ver);
        if ((ath6kl_set_host_app_area(ar)) != 0)
                ath6kl_err("unable to set the host app area\n");
  
 -      for (i = 0; i < MAX_NUM_VIF; i++) {
 +      for (i = 0; i < ar->vif_max; i++) {
                ret = ath6kl_target_config_wlan_params(ar, i);
                if (ret)
                        goto err_htc_stop;
@@@ -1618,7 -1558,7 +1618,7 @@@ int ath6kl_core_init(struct ath6kl *ar
                goto err_node_cleanup;
        }
  
 -      for (i = 0; i < MAX_NUM_VIF; i++)
 +      for (i = 0; i < ar->vif_max; i++)
                ar->avail_idx_map |= BIT(i);
  
        rtnl_lock();
  
        ar->wiphy->flags |= WIPHY_FLAG_SUPPORTS_FW_ROAM |
                            WIPHY_FLAG_HAVE_AP_SME |
 -                          WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL;
++                          WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL |
 +                          WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD;
 +
 +      if (test_bit(ATH6KL_FW_CAPABILITY_SCHED_SCAN, ar->fw_capabilities))
 +              ar->wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN;
 +
 +      ar->wiphy->probe_resp_offload =
 +              NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS |
 +              NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2 |
 +              NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P |
 +              NL80211_PROBE_RESP_OFFLOAD_SUPPORT_80211U;
  
        set_bit(FIRST_BOOT, &ar->flag);
  
index 07903e6114d844a6dd30e4cccaf4372f3651c884,e69ca5ee5bb193e1e1e5d1f3c5f47fe8f1ee981e..15c3f56caf4f9da4bc4e8450113feea02cb96a0d
@@@ -14,6 -14,7 +14,7 @@@
   * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
   */
  
+ #include <linux/module.h>
  #include <linux/mmc/card.h>
  #include <linux/mmc/mmc.h>
  #include <linux/mmc/host.h>
@@@ -39,12 -40,8 +40,12 @@@ struct ath6kl_sdio 
        struct bus_request bus_req[BUS_REQUEST_MAX_NUM];
  
        struct ath6kl *ar;
 +
        u8 *dma_buffer;
  
 +      /* protects access to dma_buffer */
 +      struct mutex dma_buffer_mutex;
 +
        /* scatter request list head */
        struct list_head scat_req;
  
@@@ -399,7 -396,6 +400,7 @@@ static int ath6kl_sdio_read_write_sync(
        if (buf_needs_bounce(buf)) {
                if (!ar_sdio->dma_buffer)
                        return -ENOMEM;
 +              mutex_lock(&ar_sdio->dma_buffer_mutex);
                tbuf = ar_sdio->dma_buffer;
                memcpy(tbuf, buf, len);
                bounced = true;
        if ((request & HIF_READ) && bounced)
                memcpy(buf, tbuf, len);
  
 +      if (bounced)
 +              mutex_unlock(&ar_sdio->dma_buffer_mutex);
 +
        return ret;
  }
  
@@@ -806,28 -799,7 +807,28 @@@ static int ath6kl_sdio_suspend(struct a
                return ret;
        }
  
 -      if ((flags & MMC_PM_WAKE_SDIO_IRQ) && wow) {
 +      if (!(flags & MMC_PM_WAKE_SDIO_IRQ))
 +              goto deepsleep;
 +
 +      /* sdio irq wakes up host */
 +
 +      if (ar->state == ATH6KL_STATE_SCHED_SCAN) {
 +              ret =  ath6kl_cfg80211_suspend(ar,
 +                                             ATH6KL_CFG_SUSPEND_SCHED_SCAN,
 +                                             NULL);
 +              if (ret) {
 +                      ath6kl_warn("Schedule scan suspend failed: %d", ret);
 +                      return ret;
 +              }
 +
 +              ret = sdio_set_host_pm_flags(func, MMC_PM_WAKE_SDIO_IRQ);
 +              if (ret)
 +                      ath6kl_warn("set sdio wake irq flag failed: %d\n", ret);
 +
 +              return ret;
 +      }
 +
 +      if (wow) {
                /*
                 * The host sdio controller is capable of keep power and
                 * sdio irq wake up at this point. It's fine to continue
                return ret;
        }
  
 +deepsleep:
        return ath6kl_cfg80211_suspend(ar, ATH6KL_CFG_SUSPEND_DEEPSLEEP, NULL);
  }
  
@@@ -868,8 -839,6 +869,8 @@@ static int ath6kl_sdio_resume(struct at
  
        case ATH6KL_STATE_WOW:
                break;
 +      case ATH6KL_STATE_SCHED_SCAN:
 +              break;
        }
  
        ath6kl_cfg80211_resume(ar);
        return 0;
  }
  
 +/* set the window address register (using 4-byte register access ). */
 +static int ath6kl_set_addrwin_reg(struct ath6kl *ar, u32 reg_addr, u32 addr)
 +{
 +      int status;
 +      u8 addr_val[4];
 +      s32 i;
 +
 +      /*
 +       * Write bytes 1,2,3 of the register to set the upper address bytes,
 +       * the LSB is written last to initiate the access cycle
 +       */
 +
 +      for (i = 1; i <= 3; i++) {
 +              /*
 +               * Fill the buffer with the address byte value we want to
 +               * hit 4 times.
 +               */
 +              memset(addr_val, ((u8 *)&addr)[i], 4);
 +
 +              /*
 +               * Hit each byte of the register address with a 4-byte
 +               * write operation to the same address, this is a harmless
 +               * operation.
 +               */
 +              status = ath6kl_sdio_read_write_sync(ar, reg_addr + i, addr_val,
 +                                           4, HIF_WR_SYNC_BYTE_FIX);
 +              if (status)
 +                      break;
 +      }
 +
 +      if (status) {
 +              ath6kl_err("%s: failed to write initial bytes of 0x%x "
 +                         "to window reg: 0x%X\n", __func__,
 +                         addr, reg_addr);
 +              return status;
 +      }
 +
 +      /*
 +       * Write the address register again, this time write the whole
 +       * 4-byte value. The effect here is that the LSB write causes the
 +       * cycle to start, the extra 3 byte write to bytes 1,2,3 has no
 +       * effect since we are writing the same values again
 +       */
 +      status = ath6kl_sdio_read_write_sync(ar, reg_addr, (u8 *)(&addr),
 +                                   4, HIF_WR_SYNC_BYTE_INC);
 +
 +      if (status) {
 +              ath6kl_err("%s: failed to write 0x%x to window reg: 0x%X\n",
 +                         __func__, addr, reg_addr);
 +              return status;
 +      }
 +
 +      return 0;
 +}
 +
 +static int ath6kl_sdio_diag_read32(struct ath6kl *ar, u32 address, u32 *data)
 +{
 +      int status;
 +
 +      /* set window register to start read cycle */
 +      status = ath6kl_set_addrwin_reg(ar, WINDOW_READ_ADDR_ADDRESS,
 +                                      address);
 +
 +      if (status)
 +              return status;
 +
 +      /* read the data */
 +      status = ath6kl_sdio_read_write_sync(ar, WINDOW_DATA_ADDRESS,
 +                              (u8 *)data, sizeof(u32), HIF_RD_SYNC_BYTE_INC);
 +      if (status) {
 +              ath6kl_err("%s: failed to read from window data addr\n",
 +                      __func__);
 +              return status;
 +      }
 +
 +      return status;
 +}
 +
 +static int ath6kl_sdio_diag_write32(struct ath6kl *ar, u32 address,
 +                                  __le32 data)
 +{
 +      int status;
 +      u32 val = (__force u32) data;
 +
 +      /* set write data */
 +      status = ath6kl_sdio_read_write_sync(ar, WINDOW_DATA_ADDRESS,
 +                              (u8 *) &val, sizeof(u32), HIF_WR_SYNC_BYTE_INC);
 +      if (status) {
 +              ath6kl_err("%s: failed to write 0x%x to window data addr\n",
 +                         __func__, data);
 +              return status;
 +      }
 +
 +      /* set window register, which starts the write cycle */
 +      return ath6kl_set_addrwin_reg(ar, WINDOW_WRITE_ADDR_ADDRESS,
 +                                    address);
 +}
 +
 +static int ath6kl_sdio_bmi_credits(struct ath6kl *ar)
 +{
 +      u32 addr;
 +      unsigned long timeout;
 +      int ret;
 +
 +      ar->bmi.cmd_credits = 0;
 +
 +      /* Read the counter register to get the command credits */
 +      addr = COUNT_DEC_ADDRESS + (HTC_MAILBOX_NUM_MAX + ENDPOINT1) * 4;
 +
 +      timeout = jiffies + msecs_to_jiffies(BMI_COMMUNICATION_TIMEOUT);
 +      while (time_before(jiffies, timeout) && !ar->bmi.cmd_credits) {
 +
 +              /*
 +               * Hit the credit counter with a 4-byte access, the first byte
 +               * read will hit the counter and cause a decrement, while the
 +               * remaining 3 bytes has no effect. The rationale behind this
 +               * is to make all HIF accesses 4-byte aligned.
 +               */
 +              ret = ath6kl_sdio_read_write_sync(ar, addr,
 +                                       (u8 *)&ar->bmi.cmd_credits, 4,
 +                                       HIF_RD_SYNC_BYTE_INC);
 +              if (ret) {
 +                      ath6kl_err("Unable to decrement the command credit "
 +                                              "count register: %d\n", ret);
 +                      return ret;
 +              }
 +
 +              /* The counter is only 8 bits.
 +               * Ignore anything in the upper 3 bytes
 +               */
 +              ar->bmi.cmd_credits &= 0xFF;
 +      }
 +
 +      if (!ar->bmi.cmd_credits) {
 +              ath6kl_err("bmi communication timeout\n");
 +              return -ETIMEDOUT;
 +      }
 +
 +      return 0;
 +}
 +
 +static int ath6kl_bmi_get_rx_lkahd(struct ath6kl *ar)
 +{
 +      unsigned long timeout;
 +      u32 rx_word = 0;
 +      int ret = 0;
 +
 +      timeout = jiffies + msecs_to_jiffies(BMI_COMMUNICATION_TIMEOUT);
 +      while ((time_before(jiffies, timeout)) && !rx_word) {
 +              ret = ath6kl_sdio_read_write_sync(ar,
 +                                      RX_LOOKAHEAD_VALID_ADDRESS,
 +                                      (u8 *)&rx_word, sizeof(rx_word),
 +                                      HIF_RD_SYNC_BYTE_INC);
 +              if (ret) {
 +                      ath6kl_err("unable to read RX_LOOKAHEAD_VALID\n");
 +                      return ret;
 +              }
 +
 +               /* all we really want is one bit */
 +              rx_word &= (1 << ENDPOINT1);
 +      }
 +
 +      if (!rx_word) {
 +              ath6kl_err("bmi_recv_buf FIFO empty\n");
 +              return -EINVAL;
 +      }
 +
 +      return ret;
 +}
 +
 +static int ath6kl_sdio_bmi_write(struct ath6kl *ar, u8 *buf, u32 len)
 +{
 +      int ret;
 +      u32 addr;
 +
 +      ret = ath6kl_sdio_bmi_credits(ar);
 +      if (ret)
 +              return ret;
 +
 +      addr = ar->mbox_info.htc_addr;
 +
 +      ret = ath6kl_sdio_read_write_sync(ar, addr, buf, len,
 +                                        HIF_WR_SYNC_BYTE_INC);
 +      if (ret)
 +              ath6kl_err("unable to send the bmi data to the device\n");
 +
 +      return ret;
 +}
 +
 +static int ath6kl_sdio_bmi_read(struct ath6kl *ar, u8 *buf, u32 len)
 +{
 +      int ret;
 +      u32 addr;
 +
 +      /*
 +       * During normal bootup, small reads may be required.
 +       * Rather than issue an HIF Read and then wait as the Target
 +       * adds successive bytes to the FIFO, we wait here until
 +       * we know that response data is available.
 +       *
 +       * This allows us to cleanly timeout on an unexpected
 +       * Target failure rather than risk problems at the HIF level.
 +       * In particular, this avoids SDIO timeouts and possibly garbage
 +       * data on some host controllers.  And on an interconnect
 +       * such as Compact Flash (as well as some SDIO masters) which
 +       * does not provide any indication on data timeout, it avoids
 +       * a potential hang or garbage response.
 +       *
 +       * Synchronization is more difficult for reads larger than the
 +       * size of the MBOX FIFO (128B), because the Target is unable
 +       * to push the 129th byte of data until AFTER the Host posts an
 +       * HIF Read and removes some FIFO data.  So for large reads the
 +       * Host proceeds to post an HIF Read BEFORE all the data is
 +       * actually available to read.  Fortunately, large BMI reads do
 +       * not occur in practice -- they're supported for debug/development.
 +       *
 +       * So Host/Target BMI synchronization is divided into these cases:
 +       *  CASE 1: length < 4
 +       *        Should not happen
 +       *
 +       *  CASE 2: 4 <= length <= 128
 +       *        Wait for first 4 bytes to be in FIFO
 +       *        If CONSERVATIVE_BMI_READ is enabled, also wait for
 +       *        a BMI command credit, which indicates that the ENTIRE
 +       *        response is available in the the FIFO
 +       *
 +       *  CASE 3: length > 128
 +       *        Wait for the first 4 bytes to be in FIFO
 +       *
 +       * For most uses, a small timeout should be sufficient and we will
 +       * usually see a response quickly; but there may be some unusual
 +       * (debug) cases of BMI_EXECUTE where we want an larger timeout.
 +       * For now, we use an unbounded busy loop while waiting for
 +       * BMI_EXECUTE.
 +       *
 +       * If BMI_EXECUTE ever needs to support longer-latency execution,
 +       * especially in production, this code needs to be enhanced to sleep
 +       * and yield.  Also note that BMI_COMMUNICATION_TIMEOUT is currently
 +       * a function of Host processor speed.
 +       */
 +      if (len >= 4) { /* NB: Currently, always true */
 +              ret = ath6kl_bmi_get_rx_lkahd(ar);
 +              if (ret)
 +                      return ret;
 +      }
 +
 +      addr = ar->mbox_info.htc_addr;
 +      ret = ath6kl_sdio_read_write_sync(ar, addr, buf, len,
 +                                HIF_RD_SYNC_BYTE_INC);
 +      if (ret) {
 +              ath6kl_err("Unable to read the bmi data from the device: %d\n",
 +                         ret);
 +              return ret;
 +      }
 +
 +      return 0;
 +}
 +
  static void ath6kl_sdio_stop(struct ath6kl *ar)
  {
        struct ath6kl_sdio *ar_sdio = ath6kl_sdio_priv(ar);
@@@ -1179,10 -890,6 +1180,10 @@@ static const struct ath6kl_hif_ops ath6
        .cleanup_scatter = ath6kl_sdio_cleanup_scatter,
        .suspend = ath6kl_sdio_suspend,
        .resume = ath6kl_sdio_resume,
 +      .diag_read32 = ath6kl_sdio_diag_read32,
 +      .diag_write32 = ath6kl_sdio_diag_write32,
 +      .bmi_read = ath6kl_sdio_bmi_read,
 +      .bmi_write = ath6kl_sdio_bmi_write,
        .power_on = ath6kl_sdio_power_on,
        .power_off = ath6kl_sdio_power_off,
        .stop = ath6kl_sdio_stop,
@@@ -1251,7 -958,6 +1252,7 @@@ static int ath6kl_sdio_probe(struct sdi
        spin_lock_init(&ar_sdio->lock);
        spin_lock_init(&ar_sdio->scat_lock);
        spin_lock_init(&ar_sdio->wr_async_lock);
 +      mutex_init(&ar_sdio->dma_buffer_mutex);
  
        INIT_LIST_HEAD(&ar_sdio->scat_req);
        INIT_LIST_HEAD(&ar_sdio->bus_req_freeq);
        }
  
        ar_sdio->ar = ar;
 +      ar->hif_type = ATH6KL_HIF_TYPE_SDIO;
        ar->hif_priv = ar_sdio;
        ar->hif_ops = &ath6kl_sdio_ops;
 +      ar->bmi.max_data_size = 256;
  
        ath6kl_sdio_set_mbox_info(ar);
  
@@@ -1323,15 -1027,13 +1324,15 @@@ static void ath6kl_sdio_remove(struct s
  static const struct sdio_device_id ath6kl_sdio_devices[] = {
        {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_AR6003_BASE | 0x0))},
        {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_AR6003_BASE | 0x1))},
 +      {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_AR6004_BASE | 0x0))},
 +      {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_AR6004_BASE | 0x1))},
        {},
  };
  
  MODULE_DEVICE_TABLE(sdio, ath6kl_sdio_devices);
  
  static struct sdio_driver ath6kl_sdio_driver = {
 -      .name = "ath6kl",
 +      .name = "ath6kl_sdio",
        .id_table = ath6kl_sdio_devices,
        .probe = ath6kl_sdio_probe,
        .remove = ath6kl_sdio_remove,
@@@ -1361,19 -1063,13 +1362,19 @@@ MODULE_AUTHOR("Atheros Communications, 
  MODULE_DESCRIPTION("Driver support for Atheros AR600x SDIO devices");
  MODULE_LICENSE("Dual BSD/GPL");
  
 -MODULE_FIRMWARE(AR6003_REV2_OTP_FILE);
 -MODULE_FIRMWARE(AR6003_REV2_FIRMWARE_FILE);
 -MODULE_FIRMWARE(AR6003_REV2_PATCH_FILE);
 -MODULE_FIRMWARE(AR6003_REV2_BOARD_DATA_FILE);
 -MODULE_FIRMWARE(AR6003_REV2_DEFAULT_BOARD_DATA_FILE);
 -MODULE_FIRMWARE(AR6003_REV3_OTP_FILE);
 -MODULE_FIRMWARE(AR6003_REV3_FIRMWARE_FILE);
 -MODULE_FIRMWARE(AR6003_REV3_PATCH_FILE);
 -MODULE_FIRMWARE(AR6003_REV3_BOARD_DATA_FILE);
 -MODULE_FIRMWARE(AR6003_REV3_DEFAULT_BOARD_DATA_FILE);
 +MODULE_FIRMWARE(AR6003_HW_2_0_OTP_FILE);
 +MODULE_FIRMWARE(AR6003_HW_2_0_FIRMWARE_FILE);
 +MODULE_FIRMWARE(AR6003_HW_2_0_PATCH_FILE);
 +MODULE_FIRMWARE(AR6003_HW_2_0_BOARD_DATA_FILE);
 +MODULE_FIRMWARE(AR6003_HW_2_0_DEFAULT_BOARD_DATA_FILE);
 +MODULE_FIRMWARE(AR6003_HW_2_1_1_OTP_FILE);
 +MODULE_FIRMWARE(AR6003_HW_2_1_1_FIRMWARE_FILE);
 +MODULE_FIRMWARE(AR6003_HW_2_1_1_PATCH_FILE);
 +MODULE_FIRMWARE(AR6003_HW_2_1_1_BOARD_DATA_FILE);
 +MODULE_FIRMWARE(AR6003_HW_2_1_1_DEFAULT_BOARD_DATA_FILE);
 +MODULE_FIRMWARE(AR6004_HW_1_0_FIRMWARE_FILE);
 +MODULE_FIRMWARE(AR6004_HW_1_0_BOARD_DATA_FILE);
 +MODULE_FIRMWARE(AR6004_HW_1_0_DEFAULT_BOARD_DATA_FILE);
 +MODULE_FIRMWARE(AR6004_HW_1_1_FIRMWARE_FILE);
 +MODULE_FIRMWARE(AR6004_HW_1_1_BOARD_DATA_FILE);
 +MODULE_FIRMWARE(AR6004_HW_1_1_DEFAULT_BOARD_DATA_FILE);