ath6kl: logical continuations should be on the previous line
[cascardo/linux.git] / drivers / net / wireless / ath / ath6kl / cfg80211.c
index 6c59a21..e14d1a0 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
@@ -15,6 +16,8 @@
  */
 
 #include <linux/moduleparam.h>
+#include <linux/inetdevice.h>
+#include <linux/export.h>
 
 #include "core.h"
 #include "cfg80211.h"
 #include "hif-ops.h"
 #include "testmode.h"
 
-static unsigned int ath6kl_p2p;
-
-module_param(ath6kl_p2p, uint, 0644);
-
 #define RATETAB_ENT(_rate, _rateid, _flags) {   \
        .bitrate    = (_rate),                  \
        .flags      = (_flags),                 \
@@ -70,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),
@@ -114,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 = {
@@ -121,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 */
@@ -196,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;
        }
 
@@ -383,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 ||
@@ -416,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);
 
@@ -461,13 +469,13 @@ static int ath6kl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev,
                }
        }
 
-       if (sme->ie && (sme->ie_len > 0)) {
-               status = ath6kl_set_assoc_req_ies(vif, sme->ie, sme->ie_len);
-               if (status) {
-                       up(&ar->sem);
-                       return status;
-               }
-       } else
+       status = ath6kl_set_assoc_req_ies(vif, sme->ie, sme->ie_len);
+       if (status) {
+               up(&ar->sem);
+               return status;
+       }
+
+       if (sme->ie == NULL || sme->ie_len == 0)
                ar->connect_ctrl_flags &= ~CONNECT_WPS_FLAG;
 
        if (test_bit(CONNECTED, &vif->flags) &&
@@ -523,8 +531,7 @@ static int ath6kl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev,
            (vif->prwise_crypto == WEP_CRYPT)) {
                struct ath6kl_key *key = NULL;
 
-               if (sme->key_idx < WMI_MIN_KEY_INDEX ||
-                   sme->key_idx > WMI_MAX_KEY_INDEX) {
+               if (sme->key_idx > WMI_MAX_KEY_INDEX) {
                        ath6kl_err("key index %d out of bounds\n",
                                   sme->key_idx);
                        up(&ar->sem);
@@ -549,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;
@@ -571,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,
@@ -593,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));
        }
@@ -605,11 +626,13 @@ static int ath6kl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev,
        return 0;
 }
 
-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)
+static struct cfg80211_bss *
+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;
@@ -638,7 +661,7 @@ static int ath6kl_add_bss_if_needed(struct ath6kl_vif *vif,
                 */
                ie = kmalloc(2 + vif->ssid_len + beacon_ie_len, GFP_KERNEL);
                if (ie == NULL)
-                       return -ENOMEM;
+                       return NULL;
                ie[0] = WLAN_EID_SSID;
                ie[1] = vif->ssid_len;
                memcpy(ie + 2, vif->ssid, vif->ssid_len);
@@ -652,15 +675,9 @@ static int ath6kl_add_bss_if_needed(struct ath6kl_vif *vif,
                                   "cfg80211\n", bssid);
                kfree(ie);
        } else
-               ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "cfg80211 already has a bss "
-                          "entry\n");
-
-       if (bss == NULL)
-               return -ENOMEM;
-
-       cfg80211_put_bss(bss);
+               ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "cfg80211 already has a bss\n");
 
-       return 0;
+       return bss;
 }
 
 void ath6kl_cfg80211_connect_event(struct ath6kl_vif *vif, u16 channel,
@@ -672,6 +689,7 @@ void ath6kl_cfg80211_connect_event(struct ath6kl_vif *vif, u16 channel,
 {
        struct ieee80211_channel *chan;
        struct ath6kl *ar = vif->ar;
+       struct cfg80211_bss *bss;
 
        /* capinfo + listen interval */
        u8 assoc_req_ie_offset = sizeof(u16) + sizeof(u16);
@@ -712,8 +730,9 @@ void ath6kl_cfg80211_connect_event(struct ath6kl_vif *vif, u16 channel,
 
        chan = ieee80211_get_channel(ar->wiphy, (int) channel);
 
-       if (ath6kl_add_bss_if_needed(vif, nw_type, bssid, chan, assoc_info,
-                                    beacon_ie_len) < 0) {
+       bss = ath6kl_add_bss_if_needed(vif, nw_type, bssid, chan,
+                                      assoc_info, beacon_ie_len);
+       if (!bss) {
                ath6kl_err("could not add cfg80211 bss entry\n");
                return;
        }
@@ -722,6 +741,7 @@ void ath6kl_cfg80211_connect_event(struct ath6kl_vif *vif, u16 channel,
                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);
+               cfg80211_put_bss(bss);
                return;
        }
 
@@ -732,11 +752,11 @@ void ath6kl_cfg80211_connect_event(struct ath6kl_vif *vif, u16 channel,
                                        assoc_req_ie, assoc_req_len,
                                        assoc_resp_ie, assoc_resp_len,
                                        WLAN_STATUS_SUCCESS, GFP_KERNEL);
+               cfg80211_put_bss(bss);
        } else if (vif->sme_state == SME_CONNECTED) {
                /* inform roam event to cfg80211 */
-               cfg80211_roamed(vif->ndev, chan, bssid,
-                               assoc_req_ie, assoc_req_len,
-                               assoc_resp_ie, assoc_resp_len, GFP_KERNEL);
+               cfg80211_roamed_bss(vif->ndev, bss, assoc_req_ie, assoc_req_len,
+                                   assoc_resp_ie, assoc_resp_len, GFP_KERNEL);
        }
 }
 
@@ -828,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;
@@ -880,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;
        }
 
        /*
@@ -921,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
@@ -930,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");
@@ -984,6 +1002,7 @@ static int ath6kl_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev,
        struct ath6kl *ar = ath6kl_priv(ndev);
        struct ath6kl_vif *vif = netdev_priv(ndev);
        struct ath6kl_key *key = NULL;
+       int seq_len;
        u8 key_usage;
        u8 key_type;
 
@@ -997,7 +1016,7 @@ static int ath6kl_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev,
                                              params->key);
        }
 
-       if (key_index < WMI_MIN_KEY_INDEX || key_index > WMI_MAX_KEY_INDEX) {
+       if (key_index > WMI_MAX_KEY_INDEX) {
                ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
                           "%s: key index %d out of bounds\n", __func__,
                           key_index);
@@ -1012,23 +1031,21 @@ static int ath6kl_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev,
        else
                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 ||
-                   seq_len > sizeof(key->seq))
-                       return -EINVAL;
-
-               key->key_len = params->key_len;
-               memcpy(key->key, params->key, key->key_len);
-               key->seq_len = seq_len;
-               memcpy(key->seq, params->seq, key->seq_len);
-               key->cipher = params->cipher;
+       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 ||
+           seq_len > sizeof(key->seq))
+               return -EINVAL;
+
+       key->key_len = params->key_len;
+       memcpy(key->key, params->key, key->key_len);
+       key->seq_len = seq_len;
+       memcpy(key->seq, params->seq, key->seq_len);
+       key->cipher = params->cipher;
 
        switch (key->cipher) {
        case WLAN_CIPHER_SUITE_WEP40:
@@ -1051,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,
@@ -1063,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;
@@ -1115,7 +1132,7 @@ static int ath6kl_cfg80211_del_key(struct wiphy *wiphy, struct net_device *ndev,
        if (!ath6kl_cfg80211_ready(vif))
                return -EIO;
 
-       if (key_index < WMI_MIN_KEY_INDEX || key_index > WMI_MAX_KEY_INDEX) {
+       if (key_index > WMI_MAX_KEY_INDEX) {
                ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
                           "%s: key index %d out of bounds\n", __func__,
                           key_index);
@@ -1148,7 +1165,7 @@ static int ath6kl_cfg80211_get_key(struct wiphy *wiphy, struct net_device *ndev,
        if (!ath6kl_cfg80211_ready(vif))
                return -EIO;
 
-       if (key_index < WMI_MIN_KEY_INDEX || key_index > WMI_MAX_KEY_INDEX) {
+       if (key_index > WMI_MAX_KEY_INDEX) {
                ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
                           "%s: key index %d out of bounds\n", __func__,
                           key_index);
@@ -1184,7 +1201,7 @@ static int ath6kl_cfg80211_set_default_key(struct wiphy *wiphy,
        if (!ath6kl_cfg80211_ready(vif))
                return -EIO;
 
-       if (key_index < WMI_MIN_KEY_INDEX || key_index > WMI_MAX_KEY_INDEX) {
+       if (key_index > WMI_MAX_KEY_INDEX) {
                ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
                           "%s: key index %d out of bounds\n",
                           __func__, key_index);
@@ -1354,7 +1371,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;
        }
@@ -1403,7 +1420,7 @@ static int ath6kl_cfg80211_del_iface(struct wiphy *wiphy,
 
        ath6kl_cleanup_vif(vif, test_bit(WMI_READY, &ar->flag));
 
-       ath6kl_deinit_if_data(vif);
+       ath6kl_cfg80211_vif_cleanup(vif);
 
        return 0;
 }
@@ -1728,29 +1745,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 ath6kl_vif *vif;
-       int ret, pos, left;
-       u32 filter = 0;
-       u16 i;
+       int ret, pos;
        u8 mask[WOW_MASK_SIZE];
+       u16 i;
 
-       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++) {
 
                /*
@@ -1773,29 +1775,249 @@ 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;
+               *filter |= WOW_FILTER_OPTION_NWK_DISASSOC;
 
        if (wow->magic_pkt)
-               filter |= WOW_FILTER_OPTION_MAGIC_PACKET;
+               *filter |= WOW_FILTER_OPTION_MAGIC_PACKET;
 
        if (wow->gtk_rekey_failure)
-               filter |= WOW_FILTER_OPTION_GTK_ERROR;
+               *filter |= WOW_FILTER_OPTION_GTK_ERROR;
 
        if (wow->eap_identity_req)
-               filter |= WOW_FILTER_OPTION_EAP_REQ;
+               *filter |= WOW_FILTER_OPTION_EAP_REQ;
 
        if (wow->four_way_handshake)
-               filter |= WOW_FILTER_OPTION_8021X_4WAYHS;
+               *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)
+               goto skip_arp;
+
+       ifa = in_dev->ifa_list;
+       memset(&ips, 0, sizeof(ips));
+
+       /* Configure IP addr only if IP address count < MAX_IP_ADDRS */
+       while (index < MAX_IP_ADDRS && ifa) {
+               ips[index] = ifa->ifa_local;
+               ifa = ifa->ifa_next;
+               index++;
+       }
+
+       if (ifa) {
+               ath6kl_err("total IP addr count is exceeding fw limit\n");
+               return -EINVAL;
+       }
+
+       ret = ath6kl_wmi_set_ip_cmd(ar->wmi, vif->fw_vif_idx, ips[0], ips[1]);
+       if (ret) {
+               ath6kl_err("fail to setup ip for arp agent\n");
+               return ret;
+       }
+
+skip_arp:
        ret = ath6kl_wmi_set_wow_mode_cmd(ar->wmi, vif->fw_vif_idx,
                                          ATH6KL_WOW_MODE_ENABLE,
                                          filter,
@@ -1803,11 +2025,26 @@ static int ath6kl_wow_suspend(struct ath6kl *ar, struct cfg80211_wowlan *wow)
        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);
@@ -1832,15 +2069,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) {
@@ -1851,11 +2119,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;
 
@@ -1911,6 +2182,7 @@ int ath6kl_cfg80211_suspend(struct ath6kl *ar,
 
        return 0;
 }
+EXPORT_SYMBOL(ath6kl_cfg80211_suspend);
 
 int ath6kl_cfg80211_resume(struct ath6kl *ar)
 {
@@ -1926,7 +2198,6 @@ int ath6kl_cfg80211_resume(struct ath6kl *ar)
                        return ret;
                }
 
-               ar->state = ATH6KL_STATE_ON;
                break;
 
        case ATH6KL_STATE_DEEPSLEEP:
@@ -1962,6 +2233,7 @@ int ath6kl_cfg80211_resume(struct ath6kl *ar)
 
        return 0;
 }
+EXPORT_SYMBOL(ath6kl_cfg80211_resume);
 
 #ifdef CONFIG_PM
 
@@ -1999,6 +2271,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);
 }
@@ -2014,7 +2289,18 @@ static int ath6kl_set_channel(struct wiphy *wiphy, struct net_device *dev,
                              struct ieee80211_channel *chan,
                              enum nl80211_channel_type channel_type)
 {
-       struct ath6kl_vif *vif = netdev_priv(dev);
+       struct ath6kl_vif *vif;
+
+       /*
+        * 'dev' could be NULL if a channel change is required for the hardware
+        * device itself, instead of a particular VIF.
+        *
+        * FIXME: To be handled properly when monitor mode is supported.
+        */
+       if (!dev)
+               return -EBUSY;
+
+       vif = netdev_priv(dev);
 
        if (!ath6kl_cfg80211_ready(vif))
                return -EIO;
@@ -2069,25 +2355,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,
@@ -2097,12 +2369,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,
@@ -2112,8 +2386,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;
 
@@ -2122,20 +2419,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)
@@ -2214,6 +2515,11 @@ static int ath6kl_ap_beacon(struct wiphy *wiphy, struct net_device *dev,
        p.dot11_auth_mode = vif->dot11_auth_mode;
        p.ch = cpu_to_le16(vif->next_chan);
 
+       /* Enable uAPSD support by default */
+       res = ath6kl_wmi_ap_set_apsd(ar->wmi, vif->fw_vif_idx, true);
+       if (res < 0)
+               return res;
+
        if (vif->wdev.iftype == NL80211_IFTYPE_P2P_GO) {
                p.nw_subtype = SUBTYPE_P2PGO;
        } else {
@@ -2231,19 +2537,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);
@@ -2259,6 +2567,19 @@ static int ath6kl_del_beacon(struct wiphy *wiphy, struct net_device *dev)
        return 0;
 }
 
+static const u8 bcast_addr[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+
+static int ath6kl_del_station(struct wiphy *wiphy, struct net_device *dev,
+                             u8 *mac)
+{
+       struct ath6kl *ar = ath6kl_priv(dev);
+       struct ath6kl_vif *vif = netdev_priv(dev);
+       const u8 *addr = mac ? mac : bcast_addr;
+
+       return ath6kl_wmi_ap_set_mlme(ar->wmi, vif->fw_vif_idx, WMI_AP_DEAUTH,
+                                     addr, WLAN_REASON_PREV_AUTH_NOT_VALID);
+}
+
 static int ath6kl_change_station(struct wiphy *wiphy, struct net_device *dev,
                                 u8 *mac, struct station_parameters *params)
 {
@@ -2354,6 +2675,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,
@@ -2365,6 +2756,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 &&
@@ -2390,22 +2782,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,
@@ -2518,6 +2907,12 @@ ath6kl_mgmt_stypes[NUM_NL80211_IFTYPES] = {
                .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
                BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
        },
+       [NL80211_IFTYPE_AP] = {
+               .tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
+               BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
+               .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
+               BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
+       },
        [NL80211_IFTYPE_P2P_CLIENT] = {
                .tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
                BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
@@ -2559,9 +2954,10 @@ 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,
        .cancel_remain_on_channel = ath6kl_cancel_remain_on_channel,
@@ -2629,122 +3025,9 @@ void ath6kl_cfg80211_stop_all(struct ath6kl *ar)
                ath6kl_cfg80211_stop(vif);
 }
 
-struct ath6kl *ath6kl_core_alloc(struct device *dev)
-{
-       struct ath6kl *ar;
-       struct wiphy *wiphy;
-       u8 ctr;
-
-       /* create a new wiphy for use with cfg80211 */
-       wiphy = wiphy_new(&ath6kl_cfg80211_ops, sizeof(struct ath6kl));
-
-       if (!wiphy) {
-               ath6kl_err("couldn't allocate wiphy device\n");
-               return NULL;
-       }
-
-       ar = wiphy_priv(wiphy);
-       ar->p2p = !!ath6kl_p2p;
-       ar->wiphy = wiphy;
-       ar->dev = dev;
-
-       ar->vif_max = 1;
-
-       ar->max_norm_iface = 1;
-
-       spin_lock_init(&ar->lock);
-       spin_lock_init(&ar->mcastpsq_lock);
-       spin_lock_init(&ar->list_lock);
-
-       init_waitqueue_head(&ar->event_wq);
-       sema_init(&ar->sem, 1);
-
-       INIT_LIST_HEAD(&ar->amsdu_rx_buffer_queue);
-       INIT_LIST_HEAD(&ar->vif_list);
-
-       clear_bit(WMI_ENABLED, &ar->flag);
-       clear_bit(SKIP_SCAN, &ar->flag);
-       clear_bit(DESTROY_IN_PROGRESS, &ar->flag);
-
-       ar->listen_intvl_t = A_DEFAULT_LISTEN_INTERVAL;
-       ar->listen_intvl_b = 0;
-       ar->tx_pwr = 0;
-
-       ar->intra_bss = 1;
-       ar->lrssi_roam_threshold = DEF_LRSSI_ROAM_THRESHOLD;
-
-       ar->state = ATH6KL_STATE_OFF;
-
-       memset((u8 *)ar->sta_list, 0,
-              AP_MAX_NUM_STA * sizeof(struct ath6kl_sta));
-
-       /* Init the PS queues */
-       for (ctr = 0; ctr < AP_MAX_NUM_STA; ctr++) {
-               spin_lock_init(&ar->sta_list[ctr].psq_lock);
-               skb_queue_head_init(&ar->sta_list[ctr].psq);
-       }
-
-       skb_queue_head_init(&ar->mcastpsq);
-
-       memcpy(ar->ap_country_code, DEF_AP_COUNTRY_CODE, 3);
-
-       return ar;
-}
-
-int ath6kl_register_ieee80211_hw(struct ath6kl *ar)
-{
-       struct wiphy *wiphy = ar->wiphy;
-       int ret;
-
-       wiphy->mgmt_stypes = ath6kl_mgmt_stypes;
-
-       wiphy->max_remain_on_channel_duration = 5000;
-
-       /* set device pointer for wiphy */
-       set_wiphy_dev(wiphy, ar->dev);
-
-       wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
-                                BIT(NL80211_IFTYPE_ADHOC) |
-                                BIT(NL80211_IFTYPE_AP);
-       if (ar->p2p) {
-               wiphy->interface_modes |= BIT(NL80211_IFTYPE_P2P_GO) |
-                                         BIT(NL80211_IFTYPE_P2P_CLIENT);
-       }
-
-       /* max num of ssids that can be probed during scanning */
-       wiphy->max_scan_ssids = MAX_PROBED_SSID_INDEX;
-       wiphy->max_scan_ie_len = 1000; /* FIX: what is correct limit? */
-       wiphy->bands[IEEE80211_BAND_2GHZ] = &ath6kl_band_2ghz;
-       wiphy->bands[IEEE80211_BAND_5GHZ] = &ath6kl_band_5ghz;
-       wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
-
-       wiphy->cipher_suites = cipher_suites;
-       wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites);
-
-       wiphy->wowlan.flags = WIPHY_WOWLAN_MAGIC_PKT |
-                             WIPHY_WOWLAN_DISCONNECT |
-                             WIPHY_WOWLAN_GTK_REKEY_FAILURE  |
-                             WIPHY_WOWLAN_SUPPORTS_GTK_REKEY |
-                             WIPHY_WOWLAN_EAP_IDENTITY_REQ   |
-                             WIPHY_WOWLAN_4WAY_HANDSHAKE;
-       wiphy->wowlan.n_patterns = WOW_MAX_FILTERS_PER_LIST;
-       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");
-               return ret;
-       }
-
-       return 0;
-}
-
-static int ath6kl_init_if_data(struct ath6kl_vif *vif)
+static int ath6kl_cfg80211_vif_init(struct ath6kl_vif *vif)
 {
-       vif->aggr_cntxt = aggr_init(vif->ndev);
+       vif->aggr_cntxt = aggr_init(vif);
        if (!vif->aggr_cntxt) {
                ath6kl_err("failed to initialize aggr\n");
                return -ENOMEM;
@@ -2758,12 +3041,15 @@ static int ath6kl_init_if_data(struct ath6kl_vif *vif)
        set_bit(WMM_ENABLED, &vif->flags);
        spin_lock_init(&vif->if_lock);
 
+       INIT_LIST_HEAD(&vif->mc_filter);
+
        return 0;
 }
 
-void ath6kl_deinit_if_data(struct ath6kl_vif *vif)
+void ath6kl_cfg80211_vif_cleanup(struct ath6kl_vif *vif)
 {
        struct ath6kl *ar = vif->ar;
+       struct ath6kl_mc_filter *mc_filter, *tmp;
 
        aggr_module_destroy(vif->aggr_cntxt);
 
@@ -2772,6 +3058,11 @@ void ath6kl_deinit_if_data(struct ath6kl_vif *vif)
        if (vif->nw_type == ADHOC_NETWORK)
                ar->ibss_if_active = false;
 
+       list_for_each_entry_safe(mc_filter, tmp, &vif->mc_filter, list) {
+               list_del(&mc_filter->list);
+               kfree(mc_filter);
+       }
+
        unregister_netdevice(vif->ndev);
 
        ar->num_vif--;
@@ -2798,6 +3089,8 @@ struct net_device *ath6kl_interface_add(struct ath6kl *ar, char *name,
        vif->wdev.iftype = type;
        vif->fw_vif_idx = fw_vif_idx;
        vif->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)
@@ -2808,8 +3101,7 @@ struct net_device *ath6kl_interface_add(struct ath6kl *ar, char *name,
 
        ath6kl_init_control_info(vif);
 
-       /* TODO: Pass interface specific pointer instead of ar */
-       if (ath6kl_init_if_data(vif))
+       if (ath6kl_cfg80211_vif_init(vif))
                goto err;
 
        if (register_netdevice(ndev))
@@ -2836,8 +3128,107 @@ err:
        return NULL;
 }
 
-void ath6kl_deinit_ieee80211_hw(struct ath6kl *ar)
+int ath6kl_cfg80211_init(struct ath6kl *ar)
+{
+       struct wiphy *wiphy = ar->wiphy;
+       int ret;
+
+       wiphy->mgmt_stypes = ath6kl_mgmt_stypes;
+
+       wiphy->max_remain_on_channel_duration = 5000;
+
+       /* set device pointer for wiphy */
+       set_wiphy_dev(wiphy, ar->dev);
+
+       wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
+                                BIT(NL80211_IFTYPE_ADHOC) |
+                                BIT(NL80211_IFTYPE_AP);
+       if (ar->p2p) {
+               wiphy->interface_modes |= BIT(NL80211_IFTYPE_P2P_GO) |
+                                         BIT(NL80211_IFTYPE_P2P_CLIENT);
+       }
+
+       /* max num of ssids that can be probed during scanning */
+       wiphy->max_scan_ssids = MAX_PROBED_SSID_INDEX;
+       wiphy->max_scan_ie_len = 1000; /* FIX: what is correct limit? */
+       wiphy->bands[IEEE80211_BAND_2GHZ] = &ath6kl_band_2ghz;
+       wiphy->bands[IEEE80211_BAND_5GHZ] = &ath6kl_band_5ghz;
+       wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
+
+       wiphy->cipher_suites = cipher_suites;
+       wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites);
+
+       wiphy->wowlan.flags = WIPHY_WOWLAN_MAGIC_PKT |
+                             WIPHY_WOWLAN_DISCONNECT |
+                             WIPHY_WOWLAN_GTK_REKEY_FAILURE  |
+                             WIPHY_WOWLAN_SUPPORTS_GTK_REKEY |
+                             WIPHY_WOWLAN_EAP_IDENTITY_REQ   |
+                             WIPHY_WOWLAN_4WAY_HANDSHAKE;
+       wiphy->wowlan.n_patterns = WOW_MAX_FILTERS_PER_LIST;
+       wiphy->wowlan.pattern_min_len = 1;
+       wiphy->wowlan.pattern_max_len = WOW_PATTERN_SIZE;
+
+       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)
+{
+       struct ath6kl *ar;
+       struct wiphy *wiphy;
+
+       /* create a new wiphy for use with cfg80211 */
+       wiphy = wiphy_new(&ath6kl_cfg80211_ops, sizeof(struct ath6kl));
+
+       if (!wiphy) {
+               ath6kl_err("couldn't allocate wiphy device\n");
+               return NULL;
+       }
+
+       ar = wiphy_priv(wiphy);
+       ar->wiphy = wiphy;
+
+       return ar;
+}
+
+/* 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);
 }
+