ath6kl: remove multiple assignments
[cascardo/linux.git] / drivers / net / wireless / ath / ath6kl / cfg80211.c
index 8bf447a..9b87e6b 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright (c) 2004-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2012 Qualcomm Atheros, Inc.
  *
  * Permission to use, copy, modify, and/or distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -68,6 +69,10 @@ static struct ieee80211_rate ath6kl_rates[] = {
 #define ath6kl_g_rates     (ath6kl_rates + 0)
 #define ath6kl_g_rates_size    12
 
+#define ath6kl_g_htcap (IEEE80211_HT_CAP_SUP_WIDTH_20_40 | \
+                       IEEE80211_HT_CAP_SGI_20          | \
+                       IEEE80211_HT_CAP_SGI_40)
+
 static struct ieee80211_channel ath6kl_2ghz_channels[] = {
        CHAN2G(1, 2412, 0),
        CHAN2G(2, 2417, 0),
@@ -112,6 +117,8 @@ static struct ieee80211_supported_band ath6kl_band_2ghz = {
        .channels = ath6kl_2ghz_channels,
        .n_bitrates = ath6kl_g_rates_size,
        .bitrates = ath6kl_g_rates,
+       .ht_cap.cap = ath6kl_g_htcap,
+       .ht_cap.ht_supported = true,
 };
 
 static struct ieee80211_supported_band ath6kl_band_5ghz = {
@@ -119,6 +126,8 @@ static struct ieee80211_supported_band ath6kl_band_5ghz = {
        .channels = ath6kl_5ghz_a_channels,
        .n_bitrates = ath6kl_a_rates_size,
        .bitrates = ath6kl_a_rates,
+       .ht_cap.cap = ath6kl_g_htcap,
+       .ht_cap.ht_supported = true,
 };
 
 #define CCKM_KRK_CIPHER_SUITE 0x004096ff /* use for KRK */
@@ -194,7 +203,7 @@ static int ath6kl_set_auth_type(struct ath6kl_vif *vif,
                break;
 
        default:
-               ath6kl_err("%s: 0x%x not spported\n", __func__, auth_type);
+               ath6kl_err("%s: 0x%x not supported\n", __func__, auth_type);
                return -ENOTSUPP;
        }
 
@@ -381,7 +390,7 @@ static bool ath6kl_is_valid_iftype(struct ath6kl *ar, enum nl80211_iftype type,
                return false;
 
        if (ar->ibss_if_active || ((type == NL80211_IFTYPE_ADHOC) &&
-           ar->num_vif))
+                                  ar->num_vif))
                return false;
 
        if (type == NL80211_IFTYPE_STATION ||
@@ -414,6 +423,7 @@ static int ath6kl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev,
        struct ath6kl_vif *vif = netdev_priv(dev);
        int status;
        u8 nw_subtype = (ar->p2p) ? SUBTYPE_P2PDEV : SUBTYPE_NONE;
+       u16 interval;
 
        ath6kl_cfg80211_sscan_disable(vif);
 
@@ -546,7 +556,7 @@ static int ath6kl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev,
        if (!ar->usr_bss_filter) {
                clear_bit(CLEAR_BSSFILTER_ON_BEACON, &vif->flags);
                if (ath6kl_wmi_bssfilter_cmd(ar->wmi, vif->fw_vif_idx,
-                   ALL_BSS_FILTER, 0) != 0) {
+                                            ALL_BSS_FILTER, 0) != 0) {
                        ath6kl_err("couldn't set bss filtering\n");
                        up(&ar->sem);
                        return -EIO;
@@ -568,6 +578,20 @@ static int ath6kl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev,
                   vif->grp_crypto_len, vif->ch_hint);
 
        vif->reconnect_flag = 0;
+
+       if (vif->nw_type == INFRA_NETWORK) {
+               interval = max(vif->listen_intvl_t,
+                              (u16) ATH6KL_MAX_WOW_LISTEN_INTL);
+               status = ath6kl_wmi_listeninterval_cmd(ar->wmi, vif->fw_vif_idx,
+                                                      interval,
+                                                      0);
+               if (status) {
+                       ath6kl_err("couldn't set listen intervel\n");
+                       up(&ar->sem);
+                       return status;
+               }
+       }
+
        status = ath6kl_wmi_connect_cmd(ar->wmi, vif->fw_vif_idx, vif->nw_type,
                                        vif->dot11_auth_mode, vif->auth_mode,
                                        vif->prwise_crypto,
@@ -590,8 +614,8 @@ static int ath6kl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev,
        }
 
        if ((!(ar->connect_ctrl_flags & CONNECT_DO_WPA_OFFLOAD)) &&
-           ((vif->auth_mode == WPA_PSK_AUTH)
-            || (vif->auth_mode == WPA2_PSK_AUTH))) {
+           ((vif->auth_mode == WPA_PSK_AUTH) ||
+            (vif->auth_mode == WPA2_PSK_AUTH))) {
                mod_timer(&vif->disconnect_timer,
                          jiffies + msecs_to_jiffies(DISCON_TIMER_INTVAL));
        }
@@ -824,13 +848,13 @@ void ath6kl_cfg80211_disconnect_event(struct ath6kl_vif *vif, u8 reason,
 
        if (vif->sme_state == SME_CONNECTING) {
                cfg80211_connect_result(vif->ndev,
-                               bssid, NULL, 0,
-                               NULL, 0,
-                               WLAN_STATUS_UNSPECIFIED_FAILURE,
-                               GFP_KERNEL);
+                                       bssid, NULL, 0,
+                                       NULL, 0,
+                                       WLAN_STATUS_UNSPECIFIED_FAILURE,
+                                       GFP_KERNEL);
        } else if (vif->sme_state == SME_CONNECTED) {
                cfg80211_disconnected(vif->ndev, reason,
-                               NULL, 0, GFP_KERNEL);
+                                     NULL, 0, GFP_KERNEL);
        }
 
        vif->sme_state = SME_DISCONNECTED;
@@ -876,19 +900,14 @@ static int ath6kl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev,
                                                  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,
-                                              request->ie, request->ie_len);
-               if (ret) {
-                       ath6kl_err("failed to set Probe Request appie for "
-                                  "scan");
-                       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_err("failed to set Probe Request appie for "
+                          "scan");
+               return ret;
        }
 
        /*
@@ -917,7 +936,7 @@ static int ath6kl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev,
                force_fg_scan = 1;
 
        if (test_bit(ATH6KL_FW_CAPABILITY_STA_P2PDEV_DUPLEX,
-                   ar->fw_capabilities)) {
+                    ar->fw_capabilities)) {
                /*
                 * If capable of doing P2P mgmt operations using
                 * station interface, send additional information like
@@ -926,14 +945,17 @@ static int ath6kl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev,
                 */
                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,
+                                               false, 0,
+                                               ATH6KL_FG_SCAN_INTERVAL,
+                                               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);
+                                               false, 0,
+                                               ATH6KL_FG_SCAN_INTERVAL,
+                                               n_channels, channels);
        }
        if (ret)
                ath6kl_err("wmi_startscan_cmd failed\n");
@@ -1046,9 +1068,9 @@ static int ath6kl_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev,
                return -ENOTSUPP;
        }
 
-       if (((vif->auth_mode == WPA_PSK_AUTH)
-            || (vif->auth_mode == WPA2_PSK_AUTH))
-           && (key_usage & GROUP_USAGE))
+       if (((vif->auth_mode == WPA_PSK_AUTH) ||
+            (vif->auth_mode == WPA2_PSK_AUTH)) &&
+           (key_usage & GROUP_USAGE))
                del_timer(&vif->disconnect_timer);
 
        ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
@@ -1058,7 +1080,7 @@ static int ath6kl_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev,
 
        if (vif->nw_type == AP_NETWORK && !pairwise &&
            (key_type == TKIP_CRYPT || key_type == AES_CRYPT ||
-            key_type == WAPI_CRYPT) && params) {
+            key_type == WAPI_CRYPT)) {
                ar->ap_mode_bkey.valid = true;
                ar->ap_mode_bkey.key_index = key_index;
                ar->ap_mode_bkey.key_type = key_type;
@@ -1263,7 +1285,6 @@ static int ath6kl_cfg80211_set_txpower(struct wiphy *wiphy,
 {
        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__,
@@ -1280,7 +1301,7 @@ static int ath6kl_cfg80211_set_txpower(struct wiphy *wiphy,
        case NL80211_TX_POWER_AUTOMATIC:
                return 0;
        case NL80211_TX_POWER_LIMITED:
-               ar->tx_pwr = ath6kl_dbm = dbm;
+               ar->tx_pwr = dbm;
                break;
        default:
                ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: type 0x%x not supported\n",
@@ -1288,7 +1309,7 @@ static int ath6kl_cfg80211_set_txpower(struct wiphy *wiphy,
                return -EOPNOTSUPP;
        }
 
-       ath6kl_wmi_set_tx_pwr_cmd(ar->wmi, vif->fw_vif_idx, ath6kl_dbm);
+       ath6kl_wmi_set_tx_pwr_cmd(ar->wmi, vif->fw_vif_idx, dbm);
 
        return 0;
 }
@@ -1349,7 +1370,7 @@ static int ath6kl_cfg80211_set_power_mgmt(struct wiphy *wiphy,
        }
 
        if (ath6kl_wmi_powermode_cmd(ar->wmi, vif->fw_vif_idx,
-            mode.pwr_mode) != 0) {
+                                    mode.pwr_mode) != 0) {
                ath6kl_err("wmi_powermode_cmd failed\n");
                return -EIO;
        }
@@ -1723,32 +1744,14 @@ static int ath6kl_flush_pmksa(struct wiphy *wiphy, struct net_device *netdev)
        return 0;
 }
 
-static int ath6kl_wow_suspend(struct ath6kl *ar, struct cfg80211_wowlan *wow)
+static int ath6kl_wow_usr(struct ath6kl *ar, struct ath6kl_vif *vif,
+                         struct cfg80211_wowlan *wow, u32 *filter)
 {
-       struct in_device *in_dev;
-       struct in_ifaddr *ifa;
-       struct ath6kl_vif *vif;
-       int ret, pos, left;
-       u32 filter = 0;
+       int ret, pos;
+       u8 mask[WOW_MASK_SIZE];
        u16 i;
-       u8 mask[WOW_MASK_SIZE], index = 0;
-       __be32 ips[MAX_IP_ADDRS];
-
-       vif = ath6kl_vif_first(ar);
-       if (!vif)
-               return -EIO;
-
-       if (!ath6kl_cfg80211_ready(vif))
-               return -EIO;
 
-       if (!test_bit(CONNECTED, &vif->flags))
-               return -EINVAL;
-
-       /* Clear existing WOW patterns */
-       for (i = 0; i < WOW_MAX_FILTERS_PER_LIST; i++)
-               ath6kl_wmi_del_wow_pattern_cmd(ar->wmi, vif->fw_vif_idx,
-                                              WOW_LIST_ID, i);
-       /* Configure new WOW patterns */
+       /* Configure the patterns that we received from the user. */
        for (i = 0; i < wow->n_patterns; i++) {
 
                /*
@@ -1771,14 +1774,222 @@ static int ath6kl_wow_suspend(struct ath6kl *ar, struct cfg80211_wowlan *wow)
                 * matched from the first byte of received pkt in the firmware.
                 */
                ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi,
-                                       vif->fw_vif_idx, WOW_LIST_ID,
-                                       wow->patterns[i].pattern_len,
-                                       0 /* pattern offset */,
-                                       wow->patterns[i].pattern, mask);
+                               vif->fw_vif_idx, WOW_LIST_ID,
+                               wow->patterns[i].pattern_len,
+                               0 /* pattern offset */,
+                               wow->patterns[i].pattern, mask);
+               if (ret)
+                       return ret;
+       }
+
+       if (wow->disconnect)
+               *filter |= WOW_FILTER_OPTION_NWK_DISASSOC;
+
+       if (wow->magic_pkt)
+               *filter |= WOW_FILTER_OPTION_MAGIC_PACKET;
+
+       if (wow->gtk_rekey_failure)
+               *filter |= WOW_FILTER_OPTION_GTK_ERROR;
+
+       if (wow->eap_identity_req)
+               *filter |= WOW_FILTER_OPTION_EAP_REQ;
+
+       if (wow->four_way_handshake)
+               *filter |= WOW_FILTER_OPTION_8021X_4WAYHS;
+
+       return 0;
+}
+
+static int ath6kl_wow_ap(struct ath6kl *ar, struct ath6kl_vif *vif)
+{
+       static const u8 unicst_pattern[] = { 0x00, 0x00, 0x00,
+               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+               0x00, 0x08 };
+       static const u8 unicst_mask[] = { 0x01, 0x00, 0x00,
+               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+               0x00, 0x7f };
+       u8 unicst_offset = 0;
+       static const u8 arp_pattern[] = { 0x08, 0x06 };
+       static const u8 arp_mask[] = { 0xff, 0xff };
+       u8 arp_offset = 20;
+       static const u8 discvr_pattern[] = { 0xe0, 0x00, 0x00, 0xf8 };
+       static const u8 discvr_mask[] = { 0xf0, 0x00, 0x00, 0xf8 };
+       u8 discvr_offset = 38;
+       static const u8 dhcp_pattern[] = { 0xff, 0xff, 0xff, 0xff,
+               0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00,
+               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+               0x00, 0x00, 0x00, 0x00, 0x00, 0x43 /* port 67 */ };
+       static const u8 dhcp_mask[] = { 0xff, 0xff, 0xff, 0xff,
+               0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff,
+               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+               0x00, 0x00, 0x00, 0x00, 0xff, 0xff /* port 67 */ };
+       u8 dhcp_offset = 0;
+       int ret;
+
+       /* Setup unicast IP, EAPOL-like and ARP pkt pattern */
+       ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi,
+                       vif->fw_vif_idx, WOW_LIST_ID,
+                       sizeof(unicst_pattern), unicst_offset,
+                       unicst_pattern, unicst_mask);
+       if (ret) {
+               ath6kl_err("failed to add WOW unicast IP pattern\n");
+               return ret;
+       }
+
+       /* Setup all ARP pkt pattern */
+       ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi,
+                       vif->fw_vif_idx, WOW_LIST_ID,
+                       sizeof(arp_pattern), arp_offset,
+                       arp_pattern, arp_mask);
+       if (ret) {
+               ath6kl_err("failed to add WOW ARP pattern\n");
+               return ret;
+       }
+
+       /*
+        * Setup multicast pattern for mDNS 224.0.0.251,
+        * SSDP 239.255.255.250 and LLMNR  224.0.0.252
+        */
+       ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi,
+                       vif->fw_vif_idx, WOW_LIST_ID,
+                       sizeof(discvr_pattern), discvr_offset,
+                       discvr_pattern, discvr_mask);
+       if (ret) {
+               ath6kl_err("failed to add WOW mDNS/SSDP/LLMNR pattern\n");
+               return ret;
+       }
+
+       /* Setup all DHCP broadcast pkt pattern */
+       ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi,
+                       vif->fw_vif_idx, WOW_LIST_ID,
+                       sizeof(dhcp_pattern), dhcp_offset,
+                       dhcp_pattern, dhcp_mask);
+       if (ret) {
+               ath6kl_err("failed to add WOW DHCP broadcast pattern\n");
+               return ret;
+       }
+
+       return 0;
+}
+
+static int ath6kl_wow_sta(struct ath6kl *ar, struct ath6kl_vif *vif)
+{
+       struct net_device *ndev = vif->ndev;
+       static const u8 discvr_pattern[] = { 0xe0, 0x00, 0x00, 0xf8 };
+       static const u8 discvr_mask[] = { 0xf0, 0x00, 0x00, 0xf8 };
+       u8 discvr_offset = 38;
+       u8 mac_mask[ETH_ALEN];
+       int ret;
+
+       /* Setup unicast pkt pattern */
+       memset(mac_mask, 0xff, ETH_ALEN);
+       ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi,
+                               vif->fw_vif_idx, WOW_LIST_ID,
+                               ETH_ALEN, 0, ndev->dev_addr,
+                               mac_mask);
+       if (ret) {
+               ath6kl_err("failed to add WOW unicast pattern\n");
+               return ret;
+       }
+
+       /*
+        * Setup multicast pattern for mDNS 224.0.0.251,
+        * SSDP 239.255.255.250 and LLMNR 224.0.0.252
+        */
+       if ((ndev->flags & IFF_ALLMULTI) ||
+           (ndev->flags & IFF_MULTICAST && netdev_mc_count(ndev) > 0)) {
+               ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi,
+                               vif->fw_vif_idx, WOW_LIST_ID,
+                               sizeof(discvr_pattern), discvr_offset,
+                               discvr_pattern, discvr_mask);
+               if (ret) {
+                       ath6kl_err("failed to add WOW mDNS/SSDP/LLMNR "
+                                  "pattern\n");
+                       return ret;
+               }
+       }
+
+       return 0;
+}
+
+static int ath6kl_wow_suspend(struct ath6kl *ar, struct cfg80211_wowlan *wow)
+{
+       struct in_device *in_dev;
+       struct in_ifaddr *ifa;
+       struct ath6kl_vif *vif;
+       int ret, left;
+       u32 filter = 0;
+       u16 i, bmiss_time;
+       u8 index = 0;
+       __be32 ips[MAX_IP_ADDRS];
+
+       vif = ath6kl_vif_first(ar);
+       if (!vif)
+               return -EIO;
+
+       if (!ath6kl_cfg80211_ready(vif))
+               return -EIO;
+
+       if (!test_bit(CONNECTED, &vif->flags))
+               return -ENOTCONN;
+
+       if (wow && (wow->n_patterns > WOW_MAX_FILTERS_PER_LIST))
+               return -EINVAL;
+
+       /* Clear existing WOW patterns */
+       for (i = 0; i < WOW_MAX_FILTERS_PER_LIST; i++)
+               ath6kl_wmi_del_wow_pattern_cmd(ar->wmi, vif->fw_vif_idx,
+                                              WOW_LIST_ID, i);
+
+       /*
+        * Skip the default WOW pattern configuration
+        * if the driver receives any WOW patterns from
+        * the user.
+        */
+       if (wow)
+               ret = ath6kl_wow_usr(ar, vif, wow, &filter);
+       else if (vif->nw_type == AP_NETWORK)
+               ret = ath6kl_wow_ap(ar, vif);
+       else
+               ret = ath6kl_wow_sta(ar, vif);
+
+       if (ret)
+               return ret;
+
+       netif_stop_queue(vif->ndev);
+
+       if (vif->nw_type != AP_NETWORK) {
+               ret = ath6kl_wmi_listeninterval_cmd(ar->wmi, vif->fw_vif_idx,
+                                                   ATH6KL_MAX_WOW_LISTEN_INTL,
+                                                   0);
+               if (ret)
+                       return ret;
+
+               /* Set listen interval x 15 times as bmiss time */
+               bmiss_time = ATH6KL_MAX_WOW_LISTEN_INTL * 15;
+               if (bmiss_time > ATH6KL_MAX_BMISS_TIME)
+                       bmiss_time = ATH6KL_MAX_BMISS_TIME;
+
+               ret = ath6kl_wmi_bmisstime_cmd(ar->wmi, vif->fw_vif_idx,
+                                              bmiss_time, 0);
+               if (ret)
+                       return ret;
+
+               ret = ath6kl_wmi_scanparams_cmd(ar->wmi, vif->fw_vif_idx,
+                                               0xFFFF, 0, 0xFFFF, 0, 0, 0,
+                                               0, 0, 0, 0);
                if (ret)
                        return ret;
        }
 
+       ar->state = ATH6KL_STATE_SUSPENDING;
+
        /* Setup own IP addr for ARP agent. */
        in_dev = __in_dev_get_rtnl(vif->ndev);
        if (!in_dev)
@@ -1806,21 +2017,6 @@ static int ath6kl_wow_suspend(struct ath6kl *ar, struct cfg80211_wowlan *wow)
        }
 
 skip_arp:
-       if (wow->disconnect)
-               filter |= WOW_FILTER_OPTION_NWK_DISASSOC;
-
-       if (wow->magic_pkt)
-               filter |= WOW_FILTER_OPTION_MAGIC_PACKET;
-
-       if (wow->gtk_rekey_failure)
-               filter |= WOW_FILTER_OPTION_GTK_ERROR;
-
-       if (wow->eap_identity_req)
-               filter |= WOW_FILTER_OPTION_EAP_REQ;
-
-       if (wow->four_way_handshake)
-               filter |= WOW_FILTER_OPTION_8021X_4WAYHS;
-
        ret = ath6kl_wmi_set_wow_mode_cmd(ar->wmi, vif->fw_vif_idx,
                                          ATH6KL_WOW_MODE_ENABLE,
                                          filter,
@@ -1828,11 +2024,26 @@ skip_arp:
        if (ret)
                return ret;
 
+       clear_bit(HOST_SLEEP_MODE_CMD_PROCESSED, &vif->flags);
+
        ret = ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx,
                                                 ATH6KL_HOST_MODE_ASLEEP);
        if (ret)
                return ret;
 
+       left = wait_event_interruptible_timeout(ar->event_wq,
+                       test_bit(HOST_SLEEP_MODE_CMD_PROCESSED, &vif->flags),
+                       WMI_TIMEOUT);
+       if (left == 0) {
+               ath6kl_warn("timeout, didn't get host sleep cmd "
+                           "processed event\n");
+               ret = -ETIMEDOUT;
+       } else if (left < 0) {
+               ath6kl_warn("error while waiting for host sleep cmd "
+                           "processed event %d\n", left);
+               ret = left;
+       }
+
        if (ar->tx_pending[ar->ctrl_ep]) {
                left = wait_event_interruptible_timeout(ar->event_wq,
                                ar->tx_pending[ar->ctrl_ep] == 0, WMI_TIMEOUT);
@@ -1857,15 +2068,46 @@ static int ath6kl_wow_resume(struct ath6kl *ar)
        if (!vif)
                return -EIO;
 
+       ar->state = ATH6KL_STATE_RESUMING;
+
        ret = ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx,
                                                 ATH6KL_HOST_MODE_AWAKE);
-       return ret;
+       if (ret) {
+               ath6kl_warn("Failed to configure host sleep mode for "
+                           "wow resume: %d\n", ret);
+               ar->state = ATH6KL_STATE_WOW;
+               return ret;
+       }
+
+       if (vif->nw_type != AP_NETWORK) {
+               ret = ath6kl_wmi_scanparams_cmd(ar->wmi, vif->fw_vif_idx,
+                                               0, 0, 0, 0, 0, 0, 3, 0, 0, 0);
+               if (ret)
+                       return ret;
+
+               ret = ath6kl_wmi_listeninterval_cmd(ar->wmi, vif->fw_vif_idx,
+                                                   vif->listen_intvl_t, 0);
+               if (ret)
+                       return ret;
+
+               ret = ath6kl_wmi_bmisstime_cmd(ar->wmi, vif->fw_vif_idx,
+                                              vif->bmiss_time_t, 0);
+               if (ret)
+                       return ret;
+       }
+
+       ar->state = ATH6KL_STATE_ON;
+
+       netif_wake_queue(vif->ndev);
+
+       return 0;
 }
 
 int ath6kl_cfg80211_suspend(struct ath6kl *ar,
                            enum ath6kl_cfg_suspend_mode mode,
                            struct cfg80211_wowlan *wow)
 {
+       enum ath6kl_state prev_state;
        int ret;
 
        switch (mode) {
@@ -1876,11 +2118,14 @@ int ath6kl_cfg80211_suspend(struct ath6kl *ar,
                /* Flush all non control pkts in TX path */
                ath6kl_tx_data_cleanup(ar);
 
+               prev_state = ar->state;
+
                ret = ath6kl_wow_suspend(ar, wow);
                if (ret) {
-                       ath6kl_err("wow suspend failed: %d\n", ret);
+                       ar->state = prev_state;
                        return ret;
                }
+
                ar->state = ATH6KL_STATE_WOW;
                break;
 
@@ -1952,7 +2197,6 @@ int ath6kl_cfg80211_resume(struct ath6kl *ar)
                        return ret;
                }
 
-               ar->state = ATH6KL_STATE_ON;
                break;
 
        case ATH6KL_STATE_DEEPSLEEP:
@@ -2026,6 +2270,9 @@ static int __ath6kl_cfg80211_resume(struct wiphy *wiphy)
  */
 void ath6kl_check_wow_status(struct ath6kl *ar)
 {
+       if (ar->state == ATH6KL_STATE_SUSPENDING)
+               return;
+
        if (ar->state == ATH6KL_STATE_WOW)
                ath6kl_cfg80211_resume(ar);
 }
@@ -2107,25 +2354,11 @@ static int ath6kl_set_ap_probe_resp_ies(struct ath6kl_vif *vif,
        return ret;
 }
 
-static int ath6kl_ap_beacon(struct wiphy *wiphy, struct net_device *dev,
-                           struct beacon_parameters *info, bool add)
+static int ath6kl_set_ies(struct ath6kl_vif *vif,
+                         struct cfg80211_beacon_data *info)
 {
-       struct ath6kl *ar = ath6kl_priv(dev);
-       struct ath6kl_vif *vif = netdev_priv(dev);
-       struct ieee80211_mgmt *mgmt;
-       u8 *ies;
-       int ies_len;
-       struct wmi_connect_cmd p;
+       struct ath6kl *ar = vif->ar;
        int res;
-       int i, ret;
-
-       ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: add=%d\n", __func__, add);
-
-       if (!ath6kl_cfg80211_ready(vif))
-               return -EIO;
-
-       if (vif->next_mode != AP_NETWORK)
-               return -EOPNOTSUPP;
 
        if (info->beacon_ies) {
                res = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
@@ -2135,12 +2368,14 @@ static int ath6kl_ap_beacon(struct wiphy *wiphy, struct net_device *dev,
                if (res)
                        return res;
        }
+
        if (info->proberesp_ies) {
                res = ath6kl_set_ap_probe_resp_ies(vif, info->proberesp_ies,
                                                   info->proberesp_ies_len);
                if (res)
                        return res;
        }
+
        if (info->assocresp_ies) {
                res = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
                                               WMI_FRAME_ASSOC_RESP,
@@ -2150,8 +2385,31 @@ static int ath6kl_ap_beacon(struct wiphy *wiphy, struct net_device *dev,
                        return res;
        }
 
-       if (!add)
-               return 0;
+       return 0;
+}
+
+static int ath6kl_start_ap(struct wiphy *wiphy, struct net_device *dev,
+                          struct cfg80211_ap_settings *info)
+{
+       struct ath6kl *ar = ath6kl_priv(dev);
+       struct ath6kl_vif *vif = netdev_priv(dev);
+       struct ieee80211_mgmt *mgmt;
+       bool hidden = false;
+       u8 *ies;
+       int ies_len;
+       struct wmi_connect_cmd p;
+       int res;
+       int i, ret;
+
+       ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s:\n", __func__);
+
+       if (!ath6kl_cfg80211_ready(vif))
+               return -EIO;
+
+       if (vif->next_mode != AP_NETWORK)
+               return -EOPNOTSUPP;
+
+       res = ath6kl_set_ies(vif, &info->beacon);
 
        ar->ap_mode_bkey.valid = false;
 
@@ -2160,20 +2418,24 @@ static int ath6kl_ap_beacon(struct wiphy *wiphy, struct net_device *dev,
         * info->dtim_period
         */
 
-       if (info->head == NULL)
+       if (info->beacon.head == NULL)
                return -EINVAL;
-       mgmt = (struct ieee80211_mgmt *) info->head;
+       mgmt = (struct ieee80211_mgmt *) info->beacon.head;
        ies = mgmt->u.beacon.variable;
-       if (ies > info->head + info->head_len)
+       if (ies > info->beacon.head + info->beacon.head_len)
                return -EINVAL;
-       ies_len = info->head + info->head_len - ies;
+       ies_len = info->beacon.head + info->beacon.head_len - ies;
 
        if (info->ssid == NULL)
                return -EINVAL;
        memcpy(vif->ssid, info->ssid, info->ssid_len);
        vif->ssid_len = info->ssid_len;
        if (info->hidden_ssid != NL80211_HIDDEN_SSID_NOT_IN_USE)
-               return -EOPNOTSUPP; /* TODO */
+               hidden = true;
+
+       res = ath6kl_wmi_ap_hidden_ssid(ar->wmi, vif->fw_vif_idx, hidden);
+       if (res)
+               return res;
 
        ret = ath6kl_set_auth_type(vif, info->auth_type);
        if (ret)
@@ -2274,19 +2536,21 @@ static int ath6kl_ap_beacon(struct wiphy *wiphy, struct net_device *dev,
        return 0;
 }
 
-static int ath6kl_add_beacon(struct wiphy *wiphy, struct net_device *dev,
-                            struct beacon_parameters *info)
+static int ath6kl_change_beacon(struct wiphy *wiphy, struct net_device *dev,
+                               struct cfg80211_beacon_data *beacon)
 {
-       return ath6kl_ap_beacon(wiphy, dev, info, true);
-}
+       struct ath6kl_vif *vif = netdev_priv(dev);
 
-static int ath6kl_set_beacon(struct wiphy *wiphy, struct net_device *dev,
-                            struct beacon_parameters *info)
-{
-       return ath6kl_ap_beacon(wiphy, dev, info, false);
+       if (!ath6kl_cfg80211_ready(vif))
+               return -EIO;
+
+       if (vif->next_mode != AP_NETWORK)
+               return -EOPNOTSUPP;
+
+       return ath6kl_set_ies(vif, beacon);
 }
 
-static int ath6kl_del_beacon(struct wiphy *wiphy, struct net_device *dev)
+static int ath6kl_stop_ap(struct wiphy *wiphy, struct net_device *dev)
 {
        struct ath6kl *ar = ath6kl_priv(dev);
        struct ath6kl_vif *vif = netdev_priv(dev);
@@ -2410,6 +2674,76 @@ static int ath6kl_send_go_probe_resp(struct ath6kl_vif *vif,
        return ret;
 }
 
+static bool ath6kl_mgmt_powersave_ap(struct ath6kl_vif *vif,
+                                    u32 id,
+                                    u32 freq,
+                                    u32 wait,
+                                    const u8 *buf,
+                                    size_t len,
+                                    bool *more_data,
+                                    bool no_cck)
+{
+       struct ieee80211_mgmt *mgmt;
+       struct ath6kl_sta *conn;
+       bool is_psq_empty = false;
+       struct ath6kl_mgmt_buff *mgmt_buf;
+       size_t mgmt_buf_size;
+       struct ath6kl *ar = vif->ar;
+
+       mgmt = (struct ieee80211_mgmt *) buf;
+       if (is_multicast_ether_addr(mgmt->da))
+               return false;
+
+       conn = ath6kl_find_sta(vif, mgmt->da);
+       if (!conn)
+               return false;
+
+       if (conn->sta_flags & STA_PS_SLEEP) {
+               if (!(conn->sta_flags & STA_PS_POLLED)) {
+                       /* Queue the frames if the STA is sleeping */
+                       mgmt_buf_size = len + sizeof(struct ath6kl_mgmt_buff);
+                       mgmt_buf = kmalloc(mgmt_buf_size, GFP_KERNEL);
+                       if (!mgmt_buf)
+                               return false;
+
+                       INIT_LIST_HEAD(&mgmt_buf->list);
+                       mgmt_buf->id = id;
+                       mgmt_buf->freq = freq;
+                       mgmt_buf->wait = wait;
+                       mgmt_buf->len = len;
+                       mgmt_buf->no_cck = no_cck;
+                       memcpy(mgmt_buf->buf, buf, len);
+                       spin_lock_bh(&conn->psq_lock);
+                       is_psq_empty = skb_queue_empty(&conn->psq) &&
+                                       (conn->mgmt_psq_len == 0);
+                       list_add_tail(&mgmt_buf->list, &conn->mgmt_psq);
+                       conn->mgmt_psq_len++;
+                       spin_unlock_bh(&conn->psq_lock);
+
+                       /*
+                        * If this is the first pkt getting queued
+                        * for this STA, update the PVB for this
+                        * STA.
+                        */
+                       if (is_psq_empty)
+                               ath6kl_wmi_set_pvb_cmd(ar->wmi, vif->fw_vif_idx,
+                                                      conn->aid, 1);
+                       return true;
+               }
+
+               /*
+                * This tx is because of a PsPoll.
+                * Determine if MoreData bit has to be set.
+                */
+               spin_lock_bh(&conn->psq_lock);
+               if (!skb_queue_empty(&conn->psq) || (conn->mgmt_psq_len != 0))
+                       *more_data = true;
+               spin_unlock_bh(&conn->psq_lock);
+       }
+
+       return false;
+}
+
 static int ath6kl_mgmt_tx(struct wiphy *wiphy, struct net_device *dev,
                          struct ieee80211_channel *chan, bool offchan,
                          enum nl80211_channel_type channel_type,
@@ -2421,6 +2755,7 @@ static int ath6kl_mgmt_tx(struct wiphy *wiphy, struct net_device *dev,
        struct ath6kl_vif *vif = netdev_priv(dev);
        u32 id;
        const struct ieee80211_mgmt *mgmt;
+       bool more_data, queued;
 
        mgmt = (const struct ieee80211_mgmt *) buf;
        if (buf + len >= mgmt->u.probe_resp.variable &&
@@ -2446,22 +2781,19 @@ static int ath6kl_mgmt_tx(struct wiphy *wiphy, struct net_device *dev,
 
        *cookie = id;
 
-       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);
+       /* AP mode Power saving processing */
+       if (vif->nw_type == AP_NETWORK) {
+               queued = ath6kl_mgmt_powersave_ap(vif,
+                                       id, chan->center_freq,
+                                       wait, buf,
+                                       len, &more_data, no_cck);
+               if (queued)
+                       return 0;
        }
+
+       return ath6kl_wmi_send_mgmt_cmd(ar->wmi, vif->fw_vif_idx, id,
+                                       chan->center_freq, wait,
+                                       buf, len, no_cck);
 }
 
 static void ath6kl_mgmt_frame_register(struct wiphy *wiphy,
@@ -2621,9 +2953,9 @@ static struct cfg80211_ops ath6kl_cfg80211_ops = {
        .resume = __ath6kl_cfg80211_resume,
 #endif
        .set_channel = ath6kl_set_channel,
-       .add_beacon = ath6kl_add_beacon,
-       .set_beacon = ath6kl_set_beacon,
-       .del_beacon = ath6kl_del_beacon,
+       .start_ap = ath6kl_start_ap,
+       .change_beacon = ath6kl_change_beacon,
+       .stop_ap = ath6kl_stop_ap,
        .del_station = ath6kl_del_station,
        .change_station = ath6kl_change_station,
        .remain_on_channel = ath6kl_remain_on_channel,
@@ -2755,7 +3087,10 @@ struct net_device *ath6kl_interface_add(struct ath6kl *ar, char *name,
        vif->wdev.netdev = ndev;
        vif->wdev.iftype = type;
        vif->fw_vif_idx = fw_vif_idx;
-       vif->nw_type = vif->next_mode = nw_type;
+       vif->nw_type = nw_type;
+       vif->next_mode = nw_type;
+       vif->listen_intvl_t = ATH6KL_DEFAULT_LISTEN_INTVAL;
+       vif->bmiss_time_t = ATH6KL_DEFAULT_BMISS_TIME;
 
        memcpy(ndev->dev_addr, ar->mac_addr, ETH_ALEN);
        if (fw_vif_idx != 0)
@@ -2835,18 +3170,36 @@ int ath6kl_cfg80211_init(struct ath6kl *ar)
 
        wiphy->max_sched_scan_ssids = 10;
 
+       ar->wiphy->flags |= WIPHY_FLAG_SUPPORTS_FW_ROAM |
+                           WIPHY_FLAG_HAVE_AP_SME |
+                           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;
+
        ret = wiphy_register(wiphy);
        if (ret < 0) {
                ath6kl_err("couldn't register wiphy device\n");
                return ret;
        }
 
+       ar->wiphy_registered = true;
+
        return 0;
 }
 
 void ath6kl_cfg80211_cleanup(struct ath6kl *ar)
 {
        wiphy_unregister(ar->wiphy);
+
+       ar->wiphy_registered = false;
 }
 
 struct ath6kl *ath6kl_cfg80211_create(void)
@@ -2871,6 +3224,11 @@ struct ath6kl *ath6kl_cfg80211_create(void)
 /* Note: ar variable must not be accessed after calling this! */
 void ath6kl_cfg80211_destroy(struct ath6kl *ar)
 {
+       int i;
+
+       for (i = 0; i < AP_MAX_NUM_STA; i++)
+               kfree(ar->sta_list[i].aggr_conn);
+
        wiphy_free(ar->wiphy);
 }