Merge branch 'for-linville' of git://github.com/kvalo/ath6kl
authorJohn W. Linville <linville@tuxdriver.com>
Mon, 3 Oct 2011 18:59:35 +0000 (14:59 -0400)
committerJohn W. Linville <linville@tuxdriver.com>
Mon, 3 Oct 2011 18:59:35 +0000 (14:59 -0400)
21 files changed:
drivers/net/wireless/ath/ath6kl/Makefile
drivers/net/wireless/ath/ath6kl/bmi.c
drivers/net/wireless/ath/ath6kl/bmi.h
drivers/net/wireless/ath/ath6kl/cfg80211.c
drivers/net/wireless/ath/ath6kl/common.h
drivers/net/wireless/ath/ath6kl/core.h
drivers/net/wireless/ath/ath6kl/debug.c
drivers/net/wireless/ath/ath6kl/debug.h
drivers/net/wireless/ath/ath6kl/hif-ops.h
drivers/net/wireless/ath/ath6kl/hif.h
drivers/net/wireless/ath/ath6kl/htc.c
drivers/net/wireless/ath/ath6kl/init.c
drivers/net/wireless/ath/ath6kl/main.c
drivers/net/wireless/ath/ath6kl/node.c [deleted file]
drivers/net/wireless/ath/ath6kl/sdio.c
drivers/net/wireless/ath/ath6kl/target.h
drivers/net/wireless/ath/ath6kl/testmode.c [new file with mode: 0644]
drivers/net/wireless/ath/ath6kl/testmode.h [new file with mode: 0644]
drivers/net/wireless/ath/ath6kl/txrx.c
drivers/net/wireless/ath/ath6kl/wmi.c
drivers/net/wireless/ath/ath6kl/wmi.h

index e1bb07ea8e80ca325a086314813059aa186e9798..8f7a0d1c290c67b2c1783c5dba172689c76e3bb0 100644 (file)
@@ -31,5 +31,7 @@ ath6kl-y += init.o
 ath6kl-y += main.o
 ath6kl-y += txrx.o
 ath6kl-y += wmi.o
-ath6kl-y += node.o
 ath6kl-y += sdio.o
+ath6kl-$(CONFIG_NL80211_TESTMODE) += testmode.o
+
+ccflags-y += -D__CHECK_ENDIAN__
index 84676697d7ebb5fdeeb71e31bab561206585a16f..c5d11cc536e04791d75050f4fd356fde2ec4a16f 100644 (file)
@@ -62,14 +62,14 @@ static int ath6kl_get_bmi_cmd_credits(struct ath6kl *ar)
        return 0;
 }
 
-static int ath6kl_bmi_get_rx_lkahd(struct ath6kl *ar, bool need_timeout)
+static int ath6kl_bmi_get_rx_lkahd(struct ath6kl *ar)
 {
        unsigned long timeout;
        u32 rx_word = 0;
        int ret = 0;
 
        timeout = jiffies + msecs_to_jiffies(BMI_COMMUNICATION_TIMEOUT);
-       while ((!need_timeout || time_before(jiffies, timeout)) && !rx_word) {
+       while (time_before(jiffies, timeout) && !rx_word) {
                ret = hif_read_write_sync(ar, RX_LOOKAHEAD_VALID_ADDRESS,
                                          (u8 *)&rx_word, sizeof(rx_word),
                                          HIF_RD_SYNC_BYTE_INC);
@@ -109,8 +109,7 @@ static int ath6kl_bmi_send_buf(struct ath6kl *ar, u8 *buf, u32 len)
        return ret;
 }
 
-static int ath6kl_bmi_recv_buf(struct ath6kl *ar,
-                       u8 *buf, u32 len, bool want_timeout)
+static int ath6kl_bmi_recv_buf(struct ath6kl *ar, u8 *buf, u32 len)
 {
        int ret;
        u32 addr;
@@ -162,7 +161,7 @@ static int ath6kl_bmi_recv_buf(struct ath6kl *ar,
         * a function of Host processor speed.
         */
        if (len >= 4) { /* NB: Currently, always true */
-               ret = ath6kl_bmi_get_rx_lkahd(ar, want_timeout);
+               ret = ath6kl_bmi_get_rx_lkahd(ar);
                if (ret)
                        return ret;
        }
@@ -220,7 +219,7 @@ int ath6kl_bmi_get_target_info(struct ath6kl *ar,
        }
 
        ret = ath6kl_bmi_recv_buf(ar, (u8 *)&targ_info->version,
-                       sizeof(targ_info->version), true);
+                                 sizeof(targ_info->version));
        if (ret) {
                ath6kl_err("Unable to recv target info: %d\n", ret);
                return ret;
@@ -230,8 +229,7 @@ int ath6kl_bmi_get_target_info(struct ath6kl *ar,
                /* Determine how many bytes are in the Target's targ_info */
                ret = ath6kl_bmi_recv_buf(ar,
                                   (u8 *)&targ_info->byte_count,
-                                  sizeof(targ_info->byte_count),
-                                  true);
+                                  sizeof(targ_info->byte_count));
                if (ret) {
                        ath6kl_err("unable to read target info byte count: %d\n",
                                   ret);
@@ -252,8 +250,7 @@ int ath6kl_bmi_get_target_info(struct ath6kl *ar,
                                   ((u8 *)targ_info) +
                                   sizeof(targ_info->byte_count),
                                   sizeof(*targ_info) -
-                                  sizeof(targ_info->byte_count),
-                                  true);
+                                  sizeof(targ_info->byte_count));
 
                if (ret) {
                        ath6kl_err("Unable to read target info (%d bytes): %d\n",
@@ -311,7 +308,7 @@ int ath6kl_bmi_read(struct ath6kl *ar, u32 addr, u8 *buf, u32 len)
                                   ret);
                        return ret;
                }
-               ret = ath6kl_bmi_recv_buf(ar, ar->bmi.cmd_buf, rx_len, true);
+               ret = ath6kl_bmi_recv_buf(ar, ar->bmi.cmd_buf, rx_len);
                if (ret) {
                        ath6kl_err("Unable to read from the device: %d\n",
                                   ret);
@@ -424,7 +421,7 @@ int ath6kl_bmi_execute(struct ath6kl *ar, u32 addr, u32 *param)
                return ret;
        }
 
-       ret = ath6kl_bmi_recv_buf(ar, ar->bmi.cmd_buf, sizeof(*param), false);
+       ret = ath6kl_bmi_recv_buf(ar, ar->bmi.cmd_buf, sizeof(*param));
        if (ret) {
                ath6kl_err("Unable to read from the device: %d\n", ret);
                return ret;
@@ -504,7 +501,7 @@ int ath6kl_bmi_reg_read(struct ath6kl *ar, u32 addr, u32 *param)
                return ret;
        }
 
-       ret = ath6kl_bmi_recv_buf(ar, ar->bmi.cmd_buf, sizeof(*param), true);
+       ret = ath6kl_bmi_recv_buf(ar, ar->bmi.cmd_buf, sizeof(*param));
        if (ret) {
                ath6kl_err("Unable to read from the device: %d\n", ret);
                return ret;
index 83546d76d97955fc2caa260efb2dcdea5ff1ab81..96851d5df24b604e6f6079728012a8c0dc7ef1cb 100644 (file)
  */
 
 #define TARGET_VERSION_SENTINAL 0xffffffff
-#define TARGET_TYPE_AR6003     3
-
+#define TARGET_TYPE_AR6003      3
+#define TARGET_TYPE_AR6004      5
 #define BMI_ROMPATCH_INSTALL               9
 /*
  * Semantics: Install a ROM Patch.
index 14559ffb1453bb019d6e4300ddd06d032759581a..8d9fbd4a62b79771e8d6d3910f3f682ce3700e48 100644 (file)
 #include "core.h"
 #include "cfg80211.h"
 #include "debug.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),                  \
@@ -152,8 +158,7 @@ static int ath6kl_set_auth_type(struct ath6kl *ar,
                break;
 
        case NL80211_AUTHTYPE_AUTOMATIC:
-               ar->dot11_auth_mode = OPEN_AUTH;
-               ar->auto_auth_stage = AUTH_OPEN_IN_PROGRESS;
+               ar->dot11_auth_mode = OPEN_AUTH | SHARED_AUTH;
                break;
 
        default:
@@ -167,7 +172,8 @@ static int ath6kl_set_auth_type(struct ath6kl *ar,
 static int ath6kl_set_cipher(struct ath6kl *ar, u32 cipher, bool ucast)
 {
        u8 *ar_cipher = ucast ? &ar->prwise_crypto : &ar->grp_crypto;
-       u8 *ar_cipher_len = ucast ? &ar->prwise_crypto_len : &ar->grp_crpto_len;
+       u8 *ar_cipher_len = ucast ? &ar->prwise_crypto_len :
+               &ar->grp_crypto_len;
 
        ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: cipher 0x%x, ucast %u\n",
                   __func__, cipher, ucast);
@@ -354,6 +360,7 @@ static int ath6kl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev,
        }
 
        if (!ar->usr_bss_filter) {
+               clear_bit(CLEAR_BSSFILTER_ON_BEACON, &ar->flag);
                if (ath6kl_wmi_bssfilter_cmd(ar->wmi, ALL_BSS_FILTER, 0) != 0) {
                        ath6kl_err("couldn't set bss filtering\n");
                        up(&ar->sem);
@@ -370,14 +377,14 @@ static int ath6kl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev,
                   __func__,
                   ar->auth_mode, ar->dot11_auth_mode, ar->prwise_crypto,
                   ar->prwise_crypto_len, ar->grp_crypto,
-                  ar->grp_crpto_len, ar->ch_hint);
+                  ar->grp_crypto_len, ar->ch_hint);
 
        ar->reconnect_flag = 0;
        status = ath6kl_wmi_connect_cmd(ar->wmi, ar->nw_type,
                                        ar->dot11_auth_mode, ar->auth_mode,
                                        ar->prwise_crypto,
                                        ar->prwise_crypto_len,
-                                       ar->grp_crypto, ar->grp_crpto_len,
+                                       ar->grp_crypto, ar->grp_crypto_len,
                                        ar->ssid_len, ar->ssid,
                                        ar->req_bssid, ar->ch_hint,
                                        ar->connect_ctrl_flags);
@@ -407,6 +414,53 @@ static int ath6kl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev,
        return 0;
 }
 
+static int ath6kl_add_bss_if_needed(struct ath6kl *ar, const u8 *bssid,
+                                   struct ieee80211_channel *chan,
+                                   const u8 *beacon_ie, size_t beacon_ie_len)
+{
+       struct cfg80211_bss *bss;
+       u8 *ie;
+
+       bss = cfg80211_get_bss(ar->wdev->wiphy, chan, bssid,
+                              ar->ssid, ar->ssid_len, WLAN_CAPABILITY_ESS,
+                              WLAN_CAPABILITY_ESS);
+       if (bss == NULL) {
+               /*
+                * Since cfg80211 may not yet know about the BSS,
+                * generate a partial entry until the first BSS info
+                * event becomes available.
+                *
+                * Prepend SSID element since it is not included in the Beacon
+                * IEs from the target.
+                */
+               ie = kmalloc(2 + ar->ssid_len + beacon_ie_len, GFP_KERNEL);
+               if (ie == NULL)
+                       return -ENOMEM;
+               ie[0] = WLAN_EID_SSID;
+               ie[1] = ar->ssid_len;
+               memcpy(ie + 2, ar->ssid, ar->ssid_len);
+               memcpy(ie + 2 + ar->ssid_len, beacon_ie, beacon_ie_len);
+               bss = cfg80211_inform_bss(ar->wdev->wiphy, chan,
+                                         bssid, 0, WLAN_CAPABILITY_ESS, 100,
+                                         ie, 2 + ar->ssid_len + beacon_ie_len,
+                                         0, GFP_KERNEL);
+               if (bss)
+                       ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "added dummy bss for "
+                                  "%pM prior to indicating connect/roamed "
+                                  "event\n", bssid);
+               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);
+
+       return 0;
+}
+
 void ath6kl_cfg80211_connect_event(struct ath6kl *ar, u16 channel,
                                   u8 *bssid, u16 listen_intvl,
                                   u16 beacon_intvl,
@@ -414,19 +468,7 @@ void ath6kl_cfg80211_connect_event(struct ath6kl *ar, u16 channel,
                                   u8 beacon_ie_len, u8 assoc_req_len,
                                   u8 assoc_resp_len, u8 *assoc_info)
 {
-       u16 size = 0;
-       u16 capability = 0;
-       struct cfg80211_bss *bss = NULL;
-       struct ieee80211_mgmt *mgmt = NULL;
-       struct ieee80211_channel *ibss_ch = NULL;
-       s32 signal = 50 * 100;
-       u8 ie_buf_len = 0;
-       unsigned char ie_buf[256];
-       unsigned char *ptr_ie_buf = ie_buf;
-       unsigned char *ieeemgmtbuf = NULL;
-       u8 source_mac[ETH_ALEN];
-       u16 capa_mask;
-       u16 capa_val;
+       struct ieee80211_channel *chan;
 
        /* capinfo + listen interval */
        u8 assoc_req_ie_offset = sizeof(u16) + sizeof(u16);
@@ -441,7 +483,12 @@ void ath6kl_cfg80211_connect_event(struct ath6kl *ar, u16 channel,
        assoc_req_len -= assoc_req_ie_offset;
        assoc_resp_len -= assoc_resp_ie_offset;
 
-       ar->auto_auth_stage = AUTH_IDLE;
+       /*
+        * Store Beacon interval here; DTIM period will be available only once
+        * a Beacon frame from the AP is seen.
+        */
+       ar->assoc_bss_beacon_int = beacon_intvl;
+       clear_bit(DTIM_PERIOD_AVAIL, &ar->flag);
 
        if (nw_type & ADHOC_NETWORK) {
                if (ar->wdev->iftype != NL80211_IFTYPE_ADHOC) {
@@ -452,110 +499,26 @@ void ath6kl_cfg80211_connect_event(struct ath6kl *ar, u16 channel,
        }
 
        if (nw_type & INFRA_NETWORK) {
-               if (ar->wdev->iftype != NL80211_IFTYPE_STATION) {
+               if (ar->wdev->iftype != NL80211_IFTYPE_STATION &&
+                   ar->wdev->iftype != NL80211_IFTYPE_P2P_CLIENT) {
                        ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
                                   "%s: ath6k not in station mode\n", __func__);
                        return;
                }
        }
 
-       if (nw_type & ADHOC_NETWORK) {
-               capa_mask = WLAN_CAPABILITY_IBSS;
-               capa_val = WLAN_CAPABILITY_IBSS;
-       } else {
-               capa_mask = WLAN_CAPABILITY_ESS;
-               capa_val = WLAN_CAPABILITY_ESS;
-       }
+       chan = ieee80211_get_channel(ar->wdev->wiphy, (int) channel);
 
-       /* Before informing the join/connect event, make sure that
-        * bss entry is present in scan list, if it not present
-        * construct and insert into scan list, otherwise that
-        * event will be dropped on the way by cfg80211, due to
-        * this keys will not be plumbed in case of WEP and
-        * application will not be aware of join/connect status. */
-       bss = cfg80211_get_bss(ar->wdev->wiphy, NULL, bssid,
-                              ar->wdev->ssid, ar->wdev->ssid_len,
-                              capa_mask, capa_val);
-
-       /*
-        * Earlier we were updating the cfg about bss by making a beacon frame
-        * only if the entry for bss is not there. This can have some issue if
-        * ROAM event is generated and a heavy traffic is ongoing. The ROAM
-        * event is handled through a work queue and by the time it really gets
-        * handled, BSS would have been aged out. So it is better to update the
-        * cfg about BSS irrespective of its entry being present right now or
-        * not.
-        */
 
        if (nw_type & ADHOC_NETWORK) {
-               /* construct 802.11 mgmt beacon */
-               if (ptr_ie_buf) {
-                       *ptr_ie_buf++ = WLAN_EID_SSID;
-                       *ptr_ie_buf++ = ar->ssid_len;
-                       memcpy(ptr_ie_buf, ar->ssid, ar->ssid_len);
-                       ptr_ie_buf += ar->ssid_len;
-
-                       *ptr_ie_buf++ = WLAN_EID_IBSS_PARAMS;
-                       *ptr_ie_buf++ = 2;      /* length */
-                       *ptr_ie_buf++ = 0;      /* ATIM window */
-                       *ptr_ie_buf++ = 0;      /* ATIM window */
-
-                       /* TODO: update ibss params and include supported rates,
-                        * DS param set, extened support rates, wmm. */
-
-                       ie_buf_len = ptr_ie_buf - ie_buf;
-               }
-
-               capability |= WLAN_CAPABILITY_IBSS;
-
-               if (ar->prwise_crypto == WEP_CRYPT)
-                       capability |= WLAN_CAPABILITY_PRIVACY;
-
-               memcpy(source_mac, ar->net_dev->dev_addr, ETH_ALEN);
-               ptr_ie_buf = ie_buf;
-       } else {
-               capability = *(u16 *) (&assoc_info[beacon_ie_len]);
-               memcpy(source_mac, bssid, ETH_ALEN);
-               ptr_ie_buf = assoc_req_ie;
-               ie_buf_len = assoc_req_len;
-       }
-
-       size = offsetof(struct ieee80211_mgmt, u)
-       + sizeof(mgmt->u.beacon)
-       + ie_buf_len;
-
-       ieeemgmtbuf = kzalloc(size, GFP_ATOMIC);
-       if (!ieeemgmtbuf) {
-               ath6kl_err("ieee mgmt buf alloc error\n");
-               cfg80211_put_bss(bss);
+               cfg80211_ibss_joined(ar->net_dev, bssid, GFP_KERNEL);
                return;
        }
 
-       mgmt = (struct ieee80211_mgmt *)ieeemgmtbuf;
-       mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
-                                         IEEE80211_STYPE_BEACON);
-       memset(mgmt->da, 0xff, ETH_ALEN);       /* broadcast addr */
-       memcpy(mgmt->sa, source_mac, ETH_ALEN);
-       memcpy(mgmt->bssid, bssid, ETH_ALEN);
-       mgmt->u.beacon.beacon_int = cpu_to_le16(beacon_intvl);
-       mgmt->u.beacon.capab_info = cpu_to_le16(capability);
-       memcpy(mgmt->u.beacon.variable, ptr_ie_buf, ie_buf_len);
-
-       ibss_ch = ieee80211_get_channel(ar->wdev->wiphy, (int)channel);
-
-       ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
-                  "%s: inform bss with bssid %pM channel %d beacon_intvl %d capability 0x%x\n",
-                  __func__, mgmt->bssid, ibss_ch->hw_value,
-                  beacon_intvl, capability);
-
-       bss = cfg80211_inform_bss_frame(ar->wdev->wiphy,
-                                       ibss_ch, mgmt,
-                                       size, signal, GFP_KERNEL);
-       kfree(ieeemgmtbuf);
-       cfg80211_put_bss(bss);
-
-       if (nw_type & ADHOC_NETWORK) {
-               cfg80211_ibss_joined(ar->net_dev, bssid, GFP_KERNEL);
+       if (ath6kl_add_bss_if_needed(ar, bssid, chan, assoc_info,
+                                    beacon_ie_len) < 0) {
+               ath6kl_err("could not add cfg80211 bss entry for "
+                          "connect/roamed notification\n");
                return;
        }
 
@@ -568,7 +531,7 @@ void ath6kl_cfg80211_connect_event(struct ath6kl *ar, u16 channel,
                                        WLAN_STATUS_SUCCESS, GFP_KERNEL);
        } else if (ar->sme_state == SME_CONNECTED) {
                /* inform roam event to cfg80211 */
-               cfg80211_roamed(ar->net_dev, ibss_ch, bssid,
+               cfg80211_roamed(ar->net_dev, chan, bssid,
                                assoc_req_ie, assoc_req_len,
                                assoc_resp_ie, assoc_resp_len, GFP_KERNEL);
        }
@@ -605,6 +568,8 @@ static int ath6kl_cfg80211_disconnect(struct wiphy *wiphy,
 
        up(&ar->sem);
 
+       ar->sme_state = SME_DISCONNECTED;
+
        return 0;
 }
 
@@ -612,9 +577,6 @@ void ath6kl_cfg80211_disconnect_event(struct ath6kl *ar, u8 reason,
                                      u8 *bssid, u8 assoc_resp_len,
                                      u8 *assoc_info, u16 proto_reason)
 {
-       struct ath6kl_key *key = NULL;
-       u16 status;
-
        if (ar->scan_req) {
                cfg80211_scan_done(ar->scan_req, true);
                ar->scan_req = NULL;
@@ -632,164 +594,64 @@ void ath6kl_cfg80211_disconnect_event(struct ath6kl *ar, u8 reason,
        }
 
        if (ar->nw_type & INFRA_NETWORK) {
-               if (ar->wdev->iftype != NL80211_IFTYPE_STATION) {
+               if (ar->wdev->iftype != NL80211_IFTYPE_STATION &&
+                   ar->wdev->iftype != NL80211_IFTYPE_P2P_CLIENT) {
                        ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
                                   "%s: ath6k not in station mode\n", __func__);
                        return;
                }
        }
 
-       if (!test_bit(CONNECT_PEND, &ar->flag)) {
-               if (reason != DISCONNECT_CMD)
-                       ath6kl_wmi_disconnect_cmd(ar->wmi);
-
-               return;
-       }
-
-       if (reason == NO_NETWORK_AVAIL) {
-               /* connect cmd failed */
-               ath6kl_wmi_disconnect_cmd(ar->wmi);
-               return;
-       }
-
-       if (reason != DISCONNECT_CMD)
-               return;
-
-       if (!ar->auto_auth_stage) {
-               clear_bit(CONNECT_PEND, &ar->flag);
-
-               if (ar->sme_state == SME_CONNECTING) {
-                       cfg80211_connect_result(ar->net_dev,
-                                               bssid, NULL, 0,
-                                               NULL, 0,
-                                               WLAN_STATUS_UNSPECIFIED_FAILURE,
-                                               GFP_KERNEL);
-               } else {
-                       cfg80211_disconnected(ar->net_dev, reason,
-                                             NULL, 0, GFP_KERNEL);
-               }
-
-               ar->sme_state = SME_DISCONNECTED;
-               return;
-       }
-
-       if (ar->dot11_auth_mode != OPEN_AUTH)
-               return;
-
        /*
-        * If the current auth algorithm is open, try shared and
-        * make autoAuthStage idle. We do not make it leap for now
-        * being.
+        * Send a disconnect command to target when a disconnect event is
+        * received with reason code other than 3 (DISCONNECT_CMD - disconnect
+        * request from host) to make the firmware stop trying to connect even
+        * after giving disconnect event. There will be one more disconnect
+        * event for this disconnect command with reason code DISCONNECT_CMD
+        * which will be notified to cfg80211.
         */
-       key = &ar->keys[ar->def_txkey_index];
-       if (down_interruptible(&ar->sem)) {
-               ath6kl_err("busy, couldn't get access\n");
-               return;
-       }
-
-       ar->dot11_auth_mode = SHARED_AUTH;
-       ar->auto_auth_stage = AUTH_IDLE;
 
-       ath6kl_wmi_addkey_cmd(ar->wmi,
-                             ar->def_txkey_index,
-                             ar->prwise_crypto,
-                             GROUP_USAGE | TX_USAGE,
-                             key->key_len, NULL,
-                             key->key,
-                             KEY_OP_INIT_VAL, NULL,
-                             NO_SYNC_WMIFLAG);
-
-       status = ath6kl_wmi_connect_cmd(ar->wmi,
-                                       ar->nw_type,
-                                       ar->dot11_auth_mode,
-                                       ar->auth_mode,
-                                       ar->prwise_crypto,
-                                       ar->prwise_crypto_len,
-                                       ar->grp_crypto,
-                                       ar->grp_crpto_len,
-                                       ar->ssid_len,
-                                       ar->ssid,
-                                       ar->req_bssid,
-                                       ar->ch_hint,
-                                       ar->connect_ctrl_flags);
-       up(&ar->sem);
-}
-
-static inline bool is_ch_11a(u16 ch)
-{
-       return (!((ch >= 2412) && (ch <= 2484)));
-}
-
-/* struct ath6kl_node_table::nt_nodelock is locked when calling this */
-void ath6kl_cfg80211_scan_node(struct wiphy *wiphy, struct bss *ni)
-{
-       u16 size;
-       unsigned char *ieeemgmtbuf = NULL;
-       struct ieee80211_mgmt *mgmt;
-       struct ieee80211_channel *channel;
-       struct ieee80211_supported_band *band;
-       struct ath6kl_common_ie *cie;
-       s32 signal;
-       int freq;
-
-       cie = &ni->ni_cie;
-
-       if (is_ch_11a(cie->ie_chan))
-               band = wiphy->bands[IEEE80211_BAND_5GHZ]; /* 11a */
-       else if ((cie->ie_erp) || (cie->ie_xrates))
-               band = wiphy->bands[IEEE80211_BAND_2GHZ]; /* 11g */
-       else
-               band = wiphy->bands[IEEE80211_BAND_2GHZ]; /* 11b */
-
-       size = ni->ni_framelen + offsetof(struct ieee80211_mgmt, u);
-       ieeemgmtbuf = kmalloc(size, GFP_ATOMIC);
-       if (!ieeemgmtbuf) {
-               ath6kl_err("ieee mgmt buf alloc error\n");
+       if (reason != DISCONNECT_CMD) {
+               ath6kl_wmi_disconnect_cmd(ar->wmi);
                return;
        }
 
-       /*
-        * TODO: Update target to include 802.11 mac header while sending
-        * bss info. Target removes 802.11 mac header while sending the bss
-        * info to host, cfg80211 needs it, for time being just filling the
-        * da, sa and bssid fields alone.
-        */
-       mgmt = (struct ieee80211_mgmt *)ieeemgmtbuf;
-       memset(mgmt->da, 0xff, ETH_ALEN);       /*broadcast addr */
-       memcpy(mgmt->sa, ni->ni_macaddr, ETH_ALEN);
-       memcpy(mgmt->bssid, ni->ni_macaddr, ETH_ALEN);
-       memcpy(ieeemgmtbuf + offsetof(struct ieee80211_mgmt, u),
-              ni->ni_buf, ni->ni_framelen);
-
-       freq = cie->ie_chan;
-       channel = ieee80211_get_channel(wiphy, freq);
-       signal = ni->ni_snr * 100;
+       clear_bit(CONNECT_PEND, &ar->flag);
 
-       ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
-                  "%s: bssid %pM ch %d freq %d size %d\n", __func__,
-                  mgmt->bssid, channel->hw_value, freq, size);
-       cfg80211_inform_bss_frame(wiphy, channel, mgmt,
-                                 size, signal, GFP_ATOMIC);
+       if (ar->sme_state == SME_CONNECTING) {
+               cfg80211_connect_result(ar->net_dev,
+                               bssid, NULL, 0,
+                               NULL, 0,
+                               WLAN_STATUS_UNSPECIFIED_FAILURE,
+                               GFP_KERNEL);
+       } else if (ar->sme_state == SME_CONNECTED) {
+               cfg80211_disconnected(ar->net_dev, reason,
+                               NULL, 0, GFP_KERNEL);
+       }
 
-       kfree(ieeemgmtbuf);
+       ar->sme_state = SME_DISCONNECTED;
 }
 
 static int ath6kl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev,
                                struct cfg80211_scan_request *request)
 {
        struct ath6kl *ar = (struct ath6kl *)ath6kl_priv(ndev);
+       s8 n_channels = 0;
+       u16 *channels = NULL;
        int ret = 0;
 
        if (!ath6kl_cfg80211_ready(ar))
                return -EIO;
 
        if (!ar->usr_bss_filter) {
-               if (ath6kl_wmi_bssfilter_cmd(ar->wmi,
-                                            (test_bit(CONNECTED, &ar->flag) ?
-                                            ALL_BUT_BSS_FILTER :
-                                            ALL_BSS_FILTER), 0) != 0) {
+               clear_bit(CLEAR_BSSFILTER_ON_BEACON, &ar->flag);
+               ret = ath6kl_wmi_bssfilter_cmd(
+                       ar->wmi,
+                       (test_bit(CONNECTED, &ar->flag) ?
+                        ALL_BUT_BSS_FILTER : ALL_BSS_FILTER), 0);
+               if (ret) {
                        ath6kl_err("couldn't set bss filtering\n");
-                       return -EIO;
+                       return ret;
                }
        }
 
@@ -806,13 +668,46 @@ static int ath6kl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev,
                                                  request->ssids[i].ssid);
        }
 
-       if (ath6kl_wmi_startscan_cmd(ar->wmi, WMI_LONG_SCAN, 0,
-                                    false, 0, 0, 0, NULL) != 0) {
-               ath6kl_err("wmi_startscan_cmd failed\n");
-               ret = -EIO;
+       if (request->ie) {
+               ret = ath6kl_wmi_set_appie_cmd(ar->wmi, WMI_FRAME_PROBE_REQ,
+                                              request->ie, request->ie_len);
+               if (ret) {
+                       ath6kl_err("failed to set Probe Request appie for "
+                                  "scan");
+                       return ret;
+               }
        }
 
-       ar->scan_req = request;
+       /*
+        * Scan only the requested channels if the request specifies a set of
+        * channels. If the list is longer than the target supports, do not
+        * configure the list and instead, scan all available channels.
+        */
+       if (request->n_channels > 0 &&
+           request->n_channels <= WMI_MAX_CHANNELS) {
+               u8 i;
+
+               n_channels = request->n_channels;
+
+               channels = kzalloc(n_channels * sizeof(u16), GFP_KERNEL);
+               if (channels == NULL) {
+                       ath6kl_warn("failed to set scan channels, "
+                                   "scan all channels");
+                       n_channels = 0;
+               }
+
+               for (i = 0; i < n_channels; i++)
+                       channels[i] = request->channels[i]->center_freq;
+       }
+
+       ret = ath6kl_wmi_startscan_cmd(ar->wmi, WMI_LONG_SCAN, 0,
+                                      false, 0, 0, n_channels, channels);
+       if (ret)
+               ath6kl_err("wmi_startscan_cmd failed\n");
+       else
+               ar->scan_req = request;
+
+       kfree(channels);
 
        return ret;
 }
@@ -831,9 +726,6 @@ void ath6kl_cfg80211_scan_complete_event(struct ath6kl *ar, int status)
                goto out;
        }
 
-       /* Translate data to cfg80211 mgmt format */
-       wlan_iterate_nodes(&ar->scan_table, ar->wdev->wiphy);
-
        cfg80211_scan_done(ar->scan_req, false);
 
        if (ar->scan_req->n_ssids && ar->scan_req->ssids[0].ssid_len) {
@@ -918,6 +810,40 @@ static int ath6kl_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev,
                   key_usage, key->seq_len);
 
        ar->def_txkey_index = key_index;
+
+       if (ar->nw_type == AP_NETWORK && !pairwise &&
+           (key_type == TKIP_CRYPT || key_type == AES_CRYPT) && params) {
+               ar->ap_mode_bkey.valid = true;
+               ar->ap_mode_bkey.key_index = key_index;
+               ar->ap_mode_bkey.key_type = key_type;
+               ar->ap_mode_bkey.key_len = key->key_len;
+               memcpy(ar->ap_mode_bkey.key, key->key, key->key_len);
+               if (!test_bit(CONNECTED, &ar->flag)) {
+                       ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "Delay initial group "
+                                  "key configuration until AP mode has been "
+                                  "started\n");
+                       /*
+                        * The key will be set in ath6kl_connect_ap_mode() once
+                        * the connected event is received from the target.
+                        */
+                       return 0;
+               }
+       }
+
+       if (ar->next_mode == AP_NETWORK && key_type == WEP_CRYPT &&
+           !test_bit(CONNECTED, &ar->flag)) {
+               /*
+                * Store the key locally so that it can be re-configured after
+                * the AP mode has properly started
+                * (ath6kl_install_statioc_wep_keys).
+                */
+               ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "Delay WEP key configuration "
+                          "until AP mode has been started\n");
+               ar->wep_key_list[key_index].key_len = key->key_len;
+               memcpy(ar->wep_key_list[key_index].key, key->key, key->key_len);
+               return 0;
+       }
+
        status = ath6kl_wmi_addkey_cmd(ar->wmi, ar->def_txkey_index,
                                       key_type, key_usage, key->key_len,
                                       key->seq, key->key, KEY_OP_INIT_VAL,
@@ -1002,6 +928,7 @@ static int ath6kl_cfg80211_set_default_key(struct wiphy *wiphy,
        struct ath6kl_key *key = NULL;
        int status = 0;
        u8 key_usage;
+       enum crypto_type key_type = NONE_CRYPT;
 
        ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: index %d\n", __func__, key_index);
 
@@ -1026,9 +953,16 @@ static int ath6kl_cfg80211_set_default_key(struct wiphy *wiphy,
        key_usage = GROUP_USAGE;
        if (ar->prwise_crypto == WEP_CRYPT)
                key_usage |= TX_USAGE;
+       if (unicast)
+               key_type = ar->prwise_crypto;
+       if (multicast)
+               key_type = ar->grp_crypto;
+
+       if (ar->next_mode == AP_NETWORK && !test_bit(CONNECTED, &ar->flag))
+               return 0; /* Delay until AP mode has been started */
 
        status = ath6kl_wmi_addkey_cmd(ar->wmi, ar->def_txkey_index,
-                                      ar->prwise_crypto, key_usage,
+                                      key_type, key_usage,
                                       key->key_len, key->seq, key->key,
                                       KEY_OP_INIT_VAL, NULL,
                                       SYNC_BOTH_WMIFLAG);
@@ -1183,6 +1117,15 @@ static int ath6kl_cfg80211_change_iface(struct wiphy *wiphy,
        case NL80211_IFTYPE_ADHOC:
                ar->next_mode = ADHOC_NETWORK;
                break;
+       case NL80211_IFTYPE_AP:
+               ar->next_mode = AP_NETWORK;
+               break;
+       case NL80211_IFTYPE_P2P_CLIENT:
+               ar->next_mode = INFRA_NETWORK;
+               break;
+       case NL80211_IFTYPE_P2P_GO:
+               ar->next_mode = AP_NETWORK;
+               break;
        default:
                ath6kl_err("invalid interface type %u\n", type);
                return -EOPNOTSUPP;
@@ -1246,13 +1189,13 @@ static int ath6kl_cfg80211_join_ibss(struct wiphy *wiphy,
                   __func__,
                   ar->auth_mode, ar->dot11_auth_mode, ar->prwise_crypto,
                   ar->prwise_crypto_len, ar->grp_crypto,
-                  ar->grp_crpto_len, ar->ch_hint);
+                  ar->grp_crypto_len, ar->ch_hint);
 
        status = ath6kl_wmi_connect_cmd(ar->wmi, ar->nw_type,
                                        ar->dot11_auth_mode, ar->auth_mode,
                                        ar->prwise_crypto,
                                        ar->prwise_crypto_len,
-                                       ar->grp_crypto, ar->grp_crpto_len,
+                                       ar->grp_crypto, ar->grp_crypto_len,
                                        ar->ssid_len, ar->ssid,
                                        ar->req_bssid, ar->ch_hint,
                                        ar->connect_ctrl_flags);
@@ -1422,12 +1365,23 @@ static int ath6kl_get_station(struct wiphy *wiphy, struct net_device *dev,
                sinfo->txrate.flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH;
                sinfo->txrate.flags |= RATE_INFO_FLAGS_MCS;
        } else {
-               ath6kl_warn("invalid rate: %d\n", rate);
+               ath6kl_dbg(ATH6KL_DBG_WLAN_CFG,
+                          "invalid rate from stats: %d\n", rate);
+               ath6kl_debug_war(ar, ATH6KL_WAR_INVALID_RATE);
                return 0;
        }
 
        sinfo->filled |= STATION_INFO_TX_BITRATE;
 
+       if (test_bit(CONNECTED, &ar->flag) &&
+           test_bit(DTIM_PERIOD_AVAIL, &ar->flag) &&
+           ar->nw_type == INFRA_NETWORK) {
+               sinfo->filled |= STATION_INFO_BSS_PARAM;
+               sinfo->bss_param.flags = 0;
+               sinfo->bss_param.dtim_period = ar->assoc_bss_dtim_period;
+               sinfo->bss_param.beacon_interval = ar->assoc_bss_beacon_int;
+       }
+
        return 0;
 }
 
@@ -1455,6 +1409,402 @@ static int ath6kl_flush_pmksa(struct wiphy *wiphy, struct net_device *netdev)
        return 0;
 }
 
+#ifdef CONFIG_PM
+static int ar6k_cfg80211_suspend(struct wiphy *wiphy,
+                                struct cfg80211_wowlan *wow)
+{
+       struct ath6kl *ar = wiphy_priv(wiphy);
+
+       return ath6kl_hif_suspend(ar);
+}
+#endif
+
+static int ath6kl_set_channel(struct wiphy *wiphy, struct net_device *dev,
+                             struct ieee80211_channel *chan,
+                             enum nl80211_channel_type channel_type)
+{
+       struct ath6kl *ar = ath6kl_priv(dev);
+
+       if (!ath6kl_cfg80211_ready(ar))
+               return -EIO;
+
+       ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: center_freq=%u hw_value=%u\n",
+                  __func__, chan->center_freq, chan->hw_value);
+       ar->next_chan = chan->center_freq;
+
+       return 0;
+}
+
+static bool ath6kl_is_p2p_ie(const u8 *pos)
+{
+       return pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
+               pos[2] == 0x50 && pos[3] == 0x6f &&
+               pos[4] == 0x9a && pos[5] == 0x09;
+}
+
+static int ath6kl_set_ap_probe_resp_ies(struct ath6kl *ar, const u8 *ies,
+                                       size_t ies_len)
+{
+       const u8 *pos;
+       u8 *buf = NULL;
+       size_t len = 0;
+       int ret;
+
+       /*
+        * Filter out P2P IE(s) since they will be included depending on
+        * the Probe Request frame in ath6kl_send_go_probe_resp().
+        */
+
+       if (ies && ies_len) {
+               buf = kmalloc(ies_len, GFP_KERNEL);
+               if (buf == NULL)
+                       return -ENOMEM;
+               pos = ies;
+               while (pos + 1 < ies + ies_len) {
+                       if (pos + 2 + pos[1] > ies + ies_len)
+                               break;
+                       if (!ath6kl_is_p2p_ie(pos)) {
+                               memcpy(buf + len, pos, 2 + pos[1]);
+                               len += 2 + pos[1];
+                       }
+                       pos += 2 + pos[1];
+               }
+       }
+
+       ret = ath6kl_wmi_set_appie_cmd(ar->wmi, WMI_FRAME_PROBE_RESP,
+                                      buf, len);
+       kfree(buf);
+       return ret;
+}
+
+static int ath6kl_ap_beacon(struct wiphy *wiphy, struct net_device *dev,
+                           struct beacon_parameters *info, bool add)
+{
+       struct ath6kl *ar = ath6kl_priv(dev);
+       struct ieee80211_mgmt *mgmt;
+       u8 *ies;
+       int ies_len;
+       struct wmi_connect_cmd p;
+       int res;
+       int i;
+
+       ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: add=%d\n", __func__, add);
+
+       if (!ath6kl_cfg80211_ready(ar))
+               return -EIO;
+
+       if (ar->next_mode != AP_NETWORK)
+               return -EOPNOTSUPP;
+
+       if (info->beacon_ies) {
+               res = ath6kl_wmi_set_appie_cmd(ar->wmi, WMI_FRAME_BEACON,
+                                              info->beacon_ies,
+                                              info->beacon_ies_len);
+               if (res)
+                       return res;
+       }
+       if (info->proberesp_ies) {
+               res = ath6kl_set_ap_probe_resp_ies(ar, info->proberesp_ies,
+                                                  info->proberesp_ies_len);
+               if (res)
+                       return res;
+       }
+       if (info->assocresp_ies) {
+               res = ath6kl_wmi_set_appie_cmd(ar->wmi, WMI_FRAME_ASSOC_RESP,
+                                              info->assocresp_ies,
+                                              info->assocresp_ies_len);
+               if (res)
+                       return res;
+       }
+
+       if (!add)
+               return 0;
+
+       ar->ap_mode_bkey.valid = false;
+
+       /* TODO:
+        * info->interval
+        * info->dtim_period
+        */
+
+       if (info->head == NULL)
+               return -EINVAL;
+       mgmt = (struct ieee80211_mgmt *) info->head;
+       ies = mgmt->u.beacon.variable;
+       if (ies > info->head + info->head_len)
+               return -EINVAL;
+       ies_len = info->head + info->head_len - ies;
+
+       if (info->ssid == NULL)
+               return -EINVAL;
+       memcpy(ar->ssid, info->ssid, info->ssid_len);
+       ar->ssid_len = info->ssid_len;
+       if (info->hidden_ssid != NL80211_HIDDEN_SSID_NOT_IN_USE)
+               return -EOPNOTSUPP; /* TODO */
+
+       ar->dot11_auth_mode = OPEN_AUTH;
+
+       memset(&p, 0, sizeof(p));
+
+       for (i = 0; i < info->crypto.n_akm_suites; i++) {
+               switch (info->crypto.akm_suites[i]) {
+               case WLAN_AKM_SUITE_8021X:
+                       if (info->crypto.wpa_versions & NL80211_WPA_VERSION_1)
+                               p.auth_mode |= WPA_AUTH;
+                       if (info->crypto.wpa_versions & NL80211_WPA_VERSION_2)
+                               p.auth_mode |= WPA2_AUTH;
+                       break;
+               case WLAN_AKM_SUITE_PSK:
+                       if (info->crypto.wpa_versions & NL80211_WPA_VERSION_1)
+                               p.auth_mode |= WPA_PSK_AUTH;
+                       if (info->crypto.wpa_versions & NL80211_WPA_VERSION_2)
+                               p.auth_mode |= WPA2_PSK_AUTH;
+                       break;
+               }
+       }
+       if (p.auth_mode == 0)
+               p.auth_mode = NONE_AUTH;
+       ar->auth_mode = p.auth_mode;
+
+       for (i = 0; i < info->crypto.n_ciphers_pairwise; i++) {
+               switch (info->crypto.ciphers_pairwise[i]) {
+               case WLAN_CIPHER_SUITE_WEP40:
+               case WLAN_CIPHER_SUITE_WEP104:
+                       p.prwise_crypto_type |= WEP_CRYPT;
+                       break;
+               case WLAN_CIPHER_SUITE_TKIP:
+                       p.prwise_crypto_type |= TKIP_CRYPT;
+                       break;
+               case WLAN_CIPHER_SUITE_CCMP:
+                       p.prwise_crypto_type |= AES_CRYPT;
+                       break;
+               }
+       }
+       if (p.prwise_crypto_type == 0) {
+               p.prwise_crypto_type = NONE_CRYPT;
+               ath6kl_set_cipher(ar, 0, true);
+       } else if (info->crypto.n_ciphers_pairwise == 1)
+               ath6kl_set_cipher(ar, info->crypto.ciphers_pairwise[0], true);
+
+       switch (info->crypto.cipher_group) {
+       case WLAN_CIPHER_SUITE_WEP40:
+       case WLAN_CIPHER_SUITE_WEP104:
+               p.grp_crypto_type = WEP_CRYPT;
+               break;
+       case WLAN_CIPHER_SUITE_TKIP:
+               p.grp_crypto_type = TKIP_CRYPT;
+               break;
+       case WLAN_CIPHER_SUITE_CCMP:
+               p.grp_crypto_type = AES_CRYPT;
+               break;
+       default:
+               p.grp_crypto_type = NONE_CRYPT;
+               break;
+       }
+       ath6kl_set_cipher(ar, info->crypto.cipher_group, false);
+
+       p.nw_type = AP_NETWORK;
+       ar->nw_type = ar->next_mode;
+
+       p.ssid_len = ar->ssid_len;
+       memcpy(p.ssid, ar->ssid, ar->ssid_len);
+       p.dot11_auth_mode = ar->dot11_auth_mode;
+       p.ch = cpu_to_le16(ar->next_chan);
+
+       res = ath6kl_wmi_ap_profile_commit(ar->wmi, &p);
+       if (res < 0)
+               return res;
+
+       return 0;
+}
+
+static int ath6kl_add_beacon(struct wiphy *wiphy, struct net_device *dev,
+                            struct beacon_parameters *info)
+{
+       return ath6kl_ap_beacon(wiphy, dev, info, true);
+}
+
+static int ath6kl_set_beacon(struct wiphy *wiphy, struct net_device *dev,
+                            struct beacon_parameters *info)
+{
+       return ath6kl_ap_beacon(wiphy, dev, info, false);
+}
+
+static int ath6kl_del_beacon(struct wiphy *wiphy, struct net_device *dev)
+{
+       struct ath6kl *ar = ath6kl_priv(dev);
+
+       if (ar->nw_type != AP_NETWORK)
+               return -EOPNOTSUPP;
+       if (!test_bit(CONNECTED, &ar->flag))
+               return -ENOTCONN;
+
+       ath6kl_wmi_disconnect_cmd(ar->wmi);
+       clear_bit(CONNECTED, &ar->flag);
+
+       return 0;
+}
+
+static int ath6kl_change_station(struct wiphy *wiphy, struct net_device *dev,
+                                u8 *mac, struct station_parameters *params)
+{
+       struct ath6kl *ar = ath6kl_priv(dev);
+
+       if (ar->nw_type != AP_NETWORK)
+               return -EOPNOTSUPP;
+
+       /* Use this only for authorizing/unauthorizing a station */
+       if (!(params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED)))
+               return -EOPNOTSUPP;
+
+       if (params->sta_flags_set & BIT(NL80211_STA_FLAG_AUTHORIZED))
+               return ath6kl_wmi_ap_set_mlme(ar->wmi, WMI_AP_MLME_AUTHORIZE,
+                                             mac, 0);
+       return ath6kl_wmi_ap_set_mlme(ar->wmi, WMI_AP_MLME_UNAUTHORIZE, mac,
+                                     0);
+}
+
+static int ath6kl_remain_on_channel(struct wiphy *wiphy,
+                                   struct net_device *dev,
+                                   struct ieee80211_channel *chan,
+                                   enum nl80211_channel_type channel_type,
+                                   unsigned int duration,
+                                   u64 *cookie)
+{
+       struct ath6kl *ar = ath6kl_priv(dev);
+
+       /* TODO: if already pending or ongoing remain-on-channel,
+        * return -EBUSY */
+       *cookie = 1; /* only a single pending request is supported */
+
+       return ath6kl_wmi_remain_on_chnl_cmd(ar->wmi, chan->center_freq,
+                                            duration);
+}
+
+static int ath6kl_cancel_remain_on_channel(struct wiphy *wiphy,
+                                          struct net_device *dev,
+                                          u64 cookie)
+{
+       struct ath6kl *ar = ath6kl_priv(dev);
+
+       if (cookie != 1)
+               return -ENOENT;
+
+       return ath6kl_wmi_cancel_remain_on_chnl_cmd(ar->wmi);
+}
+
+static int ath6kl_send_go_probe_resp(struct ath6kl *ar, const u8 *buf,
+                                    size_t len, unsigned int freq)
+{
+       const u8 *pos;
+       u8 *p2p;
+       int p2p_len;
+       int ret;
+       const struct ieee80211_mgmt *mgmt;
+
+       mgmt = (const struct ieee80211_mgmt *) buf;
+
+       /* Include P2P IE(s) from the frame generated in user space. */
+
+       p2p = kmalloc(len, GFP_KERNEL);
+       if (p2p == NULL)
+               return -ENOMEM;
+       p2p_len = 0;
+
+       pos = mgmt->u.probe_resp.variable;
+       while (pos + 1 < buf + len) {
+               if (pos + 2 + pos[1] > buf + len)
+                       break;
+               if (ath6kl_is_p2p_ie(pos)) {
+                       memcpy(p2p + p2p_len, pos, 2 + pos[1]);
+                       p2p_len += 2 + pos[1];
+               }
+               pos += 2 + pos[1];
+       }
+
+       ret = ath6kl_wmi_send_probe_response_cmd(ar->wmi, freq, mgmt->da,
+                                                p2p, p2p_len);
+       kfree(p2p);
+       return ret;
+}
+
+static int ath6kl_mgmt_tx(struct wiphy *wiphy, struct net_device *dev,
+                         struct ieee80211_channel *chan, bool offchan,
+                         enum nl80211_channel_type channel_type,
+                         bool channel_type_valid, unsigned int wait,
+                         const u8 *buf, size_t len, u64 *cookie)
+{
+       struct ath6kl *ar = ath6kl_priv(dev);
+       u32 id;
+       const struct ieee80211_mgmt *mgmt;
+
+       mgmt = (const struct ieee80211_mgmt *) buf;
+       if (buf + len >= mgmt->u.probe_resp.variable &&
+           ar->nw_type == AP_NETWORK && test_bit(CONNECTED, &ar->flag) &&
+           ieee80211_is_probe_resp(mgmt->frame_control)) {
+               /*
+                * Send Probe Response frame in AP mode using a separate WMI
+                * command to allow the target to fill in the generic IEs.
+                */
+               *cookie = 0; /* TX status not supported */
+               return ath6kl_send_go_probe_resp(ar, buf, len,
+                                                chan->center_freq);
+       }
+
+       id = ar->send_action_id++;
+       if (id == 0) {
+               /*
+                * 0 is a reserved value in the WMI command and shall not be
+                * used for the command.
+                */
+               id = ar->send_action_id++;
+       }
+
+       *cookie = id;
+       return ath6kl_wmi_send_action_cmd(ar->wmi, id, chan->center_freq, wait,
+                                         buf, len);
+}
+
+static void ath6kl_mgmt_frame_register(struct wiphy *wiphy,
+                                      struct net_device *dev,
+                                      u16 frame_type, bool reg)
+{
+       struct ath6kl *ar = ath6kl_priv(dev);
+
+       ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: frame_type=0x%x reg=%d\n",
+                  __func__, frame_type, reg);
+       if (frame_type == IEEE80211_STYPE_PROBE_REQ) {
+               /*
+                * Note: This notification callback is not allowed to sleep, so
+                * we cannot send WMI_PROBE_REQ_REPORT_CMD here. Instead, we
+                * hardcode target to report Probe Request frames all the time.
+                */
+               ar->probe_req_report = reg;
+       }
+}
+
+static const struct ieee80211_txrx_stypes
+ath6kl_mgmt_stypes[NUM_NL80211_IFTYPES] = {
+       [NL80211_IFTYPE_STATION] = {
+               .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),
+               .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
+               BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
+       },
+       [NL80211_IFTYPE_P2P_GO] = {
+               .tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
+               BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
+               .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
+               BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
+       },
+};
+
 static struct cfg80211_ops ath6kl_cfg80211_ops = {
        .change_virtual_intf = ath6kl_cfg80211_change_iface,
        .scan = ath6kl_cfg80211_scan,
@@ -1474,12 +1824,26 @@ static struct cfg80211_ops ath6kl_cfg80211_ops = {
        .set_pmksa = ath6kl_set_pmksa,
        .del_pmksa = ath6kl_del_pmksa,
        .flush_pmksa = ath6kl_flush_pmksa,
+       CFG80211_TESTMODE_CMD(ath6kl_tm_cmd)
+#ifdef CONFIG_PM
+       .suspend = ar6k_cfg80211_suspend,
+#endif
+       .set_channel = ath6kl_set_channel,
+       .add_beacon = ath6kl_add_beacon,
+       .set_beacon = ath6kl_set_beacon,
+       .del_beacon = ath6kl_del_beacon,
+       .change_station = ath6kl_change_station,
+       .remain_on_channel = ath6kl_remain_on_channel,
+       .cancel_remain_on_channel = ath6kl_cancel_remain_on_channel,
+       .mgmt_tx = ath6kl_mgmt_tx,
+       .mgmt_frame_register = ath6kl_mgmt_frame_register,
 };
 
 struct wireless_dev *ath6kl_cfg80211_init(struct device *dev)
 {
        int ret = 0;
        struct wireless_dev *wdev;
+       struct ath6kl *ar;
 
        wdev = kzalloc(sizeof(struct wireless_dev), GFP_KERNEL);
        if (!wdev) {
@@ -1495,13 +1859,25 @@ struct wireless_dev *ath6kl_cfg80211_init(struct device *dev)
                return NULL;
        }
 
+       ar = wiphy_priv(wdev->wiphy);
+       ar->p2p = !!ath6kl_p2p;
+
+       wdev->wiphy->mgmt_stypes = ath6kl_mgmt_stypes;
+
+       wdev->wiphy->max_remain_on_channel_duration = 5000;
+
        /* set device pointer for wiphy */
        set_wiphy_dev(wdev->wiphy, dev);
 
        wdev->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
-           BIT(NL80211_IFTYPE_ADHOC);
+               BIT(NL80211_IFTYPE_ADHOC) | BIT(NL80211_IFTYPE_AP);
+       if (ar->p2p) {
+               wdev->wiphy->interface_modes |= BIT(NL80211_IFTYPE_P2P_GO) |
+                       BIT(NL80211_IFTYPE_P2P_CLIENT);
+       }
        /* max num of ssids that can be probed during scanning */
        wdev->wiphy->max_scan_ssids = MAX_PROBED_SSID_INDEX;
+       wdev->wiphy->max_scan_ie_len = 1000; /* FIX: what is correct limit? */
        wdev->wiphy->bands[IEEE80211_BAND_2GHZ] = &ath6kl_band_2ghz;
        wdev->wiphy->bands[IEEE80211_BAND_5GHZ] = &ath6kl_band_5ghz;
        wdev->wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
index 6b0d45642fe361df1751a127943733e4ef27ea9c..b92f0e5d233631290cb262ca99a375827d4dc452 100644 (file)
@@ -75,94 +75,11 @@ enum crypto_type {
        AES_CRYPT           = 0x08,
 };
 
-#define ATH6KL_NODE_HASHSIZE 32
-/* simple hash is enough for variation of macaddr */
-#define ATH6KL_NODE_HASH(addr)   \
-       (((const u8 *)(addr))[ETH_ALEN - 1] % \
-        ATH6KL_NODE_HASHSIZE)
-
-/*
- * Table of ath6kl_node instances.  Each ieee80211com
- * has at least one for holding the scan candidates.
- * When operating as an access point or in ibss mode there
- * is a second table for associated stations or neighbors.
- */
-struct ath6kl_node_table {
-       spinlock_t nt_nodelock; /* on node table */
-       struct bss *nt_node_first;      /* information of all nodes */
-       struct bss *nt_node_last;       /* information of all nodes */
-       struct bss *nt_hash[ATH6KL_NODE_HASHSIZE];
-       const char *nt_name;    /* for debugging */
-       u32 nt_node_age;                /* node aging time */
-};
-
-#define WLAN_NODE_INACT_TIMEOUT_MSEC    120000
-#define WLAN_NODE_INACT_CNT            4
-
-struct ath6kl_common_ie {
-       u16 ie_chan;
-       u8 *ie_tstamp;
-       u8 *ie_ssid;
-       u8 *ie_rates;
-       u8 *ie_xrates;
-       u8 *ie_country;
-       u8 *ie_wpa;
-       u8 *ie_rsn;
-       u8 *ie_wmm;
-       u8 *ie_ath;
-       u16 ie_capInfo;
-       u16 ie_beaconInt;
-       u8 *ie_tim;
-       u8 *ie_chswitch;
-       u8 ie_erp;
-       u8 *ie_wsc;
-       u8 *ie_htcap;
-       u8 *ie_htop;
-};
-
-struct bss {
-       u8 ni_macaddr[ETH_ALEN];
-       u8 ni_snr;
-       s16 ni_rssi;
-       struct bss *ni_list_next;
-       struct bss *ni_list_prev;
-       struct bss *ni_hash_next;
-       struct bss *ni_hash_prev;
-       struct ath6kl_common_ie ni_cie;
-       u8 *ni_buf;
-       u16 ni_framelen;
-       struct ath6kl_node_table *ni_table;
-       u32 ni_refcnt;
-
-       u32 ni_tstamp;
-       u32 ni_actcnt;
-};
-
 struct htc_endpoint_credit_dist;
 struct ath6kl;
 enum htc_credit_dist_reason;
 struct htc_credit_state_info;
 
-struct bss *wlan_node_alloc(int wh_size);
-void wlan_node_free(struct bss *ni);
-void wlan_setup_node(struct ath6kl_node_table *nt, struct bss *ni,
-                    const u8 *mac_addr);
-struct bss *wlan_find_node(struct ath6kl_node_table *nt,
-                          const u8 *mac_addr);
-void wlan_node_reclaim(struct ath6kl_node_table *nt, struct bss *ni);
-void wlan_free_allnodes(struct ath6kl_node_table *nt);
-void wlan_iterate_nodes(struct ath6kl_node_table *nt, void *arg);
-
-void wlan_node_table_init(struct ath6kl_node_table *nt);
-void wlan_node_table_cleanup(struct ath6kl_node_table *nt);
-
-void wlan_refresh_inactive_nodes(struct ath6kl *ar);
-
-struct bss *wlan_find_ssid_node(struct ath6kl_node_table *nt, u8 *ssid,
-                                 u32 ssid_len, bool is_wpa2, bool match_ssid);
-
-void wlan_node_return(struct ath6kl_node_table *nt, struct bss *ni);
-
 int ath6k_setup_credit_dist(void *htc_handle,
                            struct htc_credit_state_info *cred_info);
 void ath6k_credit_distribute(struct htc_credit_state_info *cred_inf,
index 74170229523f0d350900e71da9d43bb7e777ddbe..6d8a4845baafe48b0ac3fa6dcb3dac7a4a63dbfb 100644 (file)
 #include <linux/rtnetlink.h>
 #include <linux/firmware.h>
 #include <linux/sched.h>
+#include <linux/circ_buf.h>
 #include <net/cfg80211.h>
 #include "htc.h"
 #include "wmi.h"
 #include "bmi.h"
+#include "target.h"
 
 #define MAX_ATH6KL                        1
 #define ATH6KL_MAX_RX_BUFFERS             16
@@ -42,6 +44,9 @@
 #define ATH6KL_MAX_ENDPOINTS   4
 #define MAX_NODE_NUM           15
 
+/* Extra bytes for htc header alignment */
+#define ATH6KL_HTC_ALIGN_BYTES 3
+
 /* MAX_HI_COOKIE_NUM are reserved for high priority traffic */
 #define MAX_DEF_COOKIE_NUM                180
 #define MAX_HI_COOKIE_NUM                 18   /* 10% of MAX_COOKIE_NUM */
 #define A_DEFAULT_LISTEN_INTERVAL         100
 #define A_MAX_WOW_LISTEN_INTERVAL         1000
 
+/* includes also the null byte */
+#define ATH6KL_FIRMWARE_MAGIC               "QCA-ATH6KL"
+
+enum ath6kl_fw_ie_type {
+       ATH6KL_FW_IE_FW_VERSION = 0,
+       ATH6KL_FW_IE_TIMESTAMP = 1,
+       ATH6KL_FW_IE_OTP_IMAGE = 2,
+       ATH6KL_FW_IE_FW_IMAGE = 3,
+       ATH6KL_FW_IE_PATCH_IMAGE = 4,
+       ATH6KL_FW_IE_RESERVED_RAM_SIZE = 5,
+       ATH6KL_FW_IE_CAPABILITIES = 6,
+       ATH6KL_FW_IE_PATCH_ADDR = 7,
+};
+
+enum ath6kl_fw_capability {
+       ATH6KL_FW_CAPABILITY_HOST_P2P = 0,
+
+       /* this needs to be last */
+       ATH6KL_FW_CAPABILITY_MAX,
+};
+
+#define ATH6KL_CAPABILITY_LEN (ALIGN(ATH6KL_FW_CAPABILITY_MAX, 32) / 32)
+
+struct ath6kl_fw_ie {
+       __le32 id;
+       __le32 len;
+       u8 data[0];
+};
+
 /* AR6003 1.0 definitions */
 #define AR6003_REV1_VERSION                 0x300002ba
 
@@ -61,7 +95,9 @@
 #define AR6003_REV2_PATCH_DOWNLOAD_ADDRESS  0x57e910
 #define AR6003_REV2_OTP_FILE                "ath6k/AR6003/hw2.0/otp.bin.z77"
 #define AR6003_REV2_FIRMWARE_FILE           "ath6k/AR6003/hw2.0/athwlan.bin.z77"
+#define AR6003_REV2_TCMD_FIRMWARE_FILE      "ath6k/AR6003/hw2.0/athtcmd_ram.bin"
 #define AR6003_REV2_PATCH_FILE              "ath6k/AR6003/hw2.0/data.patch.bin"
+#define AR6003_REV2_FIRMWARE_2_FILE         "ath6k/AR6003/hw2.0/fw-2.bin"
 #define AR6003_REV2_BOARD_DATA_FILE         "ath6k/AR6003/hw2.0/bdata.bin"
 #define AR6003_REV2_DEFAULT_BOARD_DATA_FILE "ath6k/AR6003/hw2.0/bdata.SD31.bin"
 
 #define AR6003_REV3_VERSION                 0x30000582
 #define AR6003_REV3_OTP_FILE                "ath6k/AR6003/hw2.1.1/otp.bin"
 #define AR6003_REV3_FIRMWARE_FILE           "ath6k/AR6003/hw2.1.1/athwlan.bin"
+#define AR6003_REV3_TCMD_FIRMWARE_FILE    "ath6k/AR6003/hw2.1.1/athtcmd_ram.bin"
 #define AR6003_REV3_PATCH_FILE            "ath6k/AR6003/hw2.1.1/data.patch.bin"
+#define AR6003_REV3_FIRMWARE_2_FILE           "ath6k/AR6003/hw2.1.1/fw-2.bin"
 #define AR6003_REV3_BOARD_DATA_FILE       "ath6k/AR6003/hw2.1.1/bdata.bin"
 #define AR6003_REV3_DEFAULT_BOARD_DATA_FILE    \
        "ath6k/AR6003/hw2.1.1/bdata.SD31.bin"
 
+/* AR6004 1.0 definitions */
+#define AR6004_REV1_VERSION                 0x30000623
+#define AR6004_REV1_FIRMWARE_FILE           "ath6k/AR6004/hw6.1/fw.ram.bin"
+#define AR6004_REV1_FIRMWARE_2_FILE         "ath6k/AR6004/hw6.1/fw-2.bin"
+#define AR6004_REV1_BOARD_DATA_FILE         "ath6k/AR6004/hw6.1/bdata.bin"
+#define AR6004_REV1_DEFAULT_BOARD_DATA_FILE "ath6k/AR6004/hw6.1/bdata.DB132.bin"
+#define AR6004_REV1_EPPING_FIRMWARE_FILE "ath6k/AR6004/hw6.1/endpointping.bin"
+
 /* Per STA data, used in AP mode */
 #define STA_PS_AWAKE           BIT(0)
 #define        STA_PS_SLEEP            BIT(1)
@@ -325,26 +371,13 @@ struct ath6kl_mbox_info {
 #define ATH6KL_KEY_RECV  0x02
 #define ATH6KL_KEY_DEFAULT   0x80      /* default xmit key */
 
-/*
- * WPA/RSN get/set key request.  Specify the key/cipher
- * type and whether the key is to be used for sending and/or
- * receiving.  The key index should be set only when working
- * with global keys (use IEEE80211_KEYIX_NONE for ``no index'').
- * Otherwise a unicast/pairwise key is specified by the bssid
- * (on a station) or mac address (on an ap).  They key length
- * must include any MIC key data; otherwise it should be no
- * more than ATH6KL_KEYBUF_SIZE.
- */
+/* Initial group key for AP mode */
 struct ath6kl_req_key {
-       u8 ik_type;     /* key/cipher type */
-       u8 ik_pad;
-       u16 ik_keyix;   /* key index */
-       u8 ik_keylen;   /* key length in bytes */
-       u8 ik_flags;
-       u8 ik_macaddr[ETH_ALEN];
-       u64 ik_keyrsc;  /* key receive sequence counter */
-       u64 ik_keytsc;  /* key transmit sequence counter */
-       u8 ik_keydata[ATH6KL_KEYBUF_SIZE + ATH6KL_MICBUF_SIZE];
+       bool valid;
+       u8 key_index;
+       int key_type;
+       u8 key[WLAN_MAX_KEY_LEN];
+       u8 key_len;
 };
 
 /* Flag info */
@@ -361,6 +394,9 @@ struct ath6kl_req_key {
 #define NETDEV_REGISTERED    10
 #define SKIP_SCAN           11
 #define WLAN_ENABLED        12
+#define TESTMODE            13
+#define CLEAR_BSSFILTER_ON_BEACON 14
+#define DTIM_PERIOD_AVAIL    15
 
 struct ath6kl {
        struct device *dev;
@@ -383,7 +419,7 @@ struct ath6kl {
        u8 prwise_crypto;
        u8 prwise_crypto_len;
        u8 grp_crypto;
-       u8 grp_crpto_len;
+       u8 grp_crypto_len;
        u8 def_txkey_index;
        struct ath6kl_wep_key wep_key_list[WMI_MAX_KEY_INDEX + 1];
        u8 bssid[ETH_ALEN];
@@ -392,6 +428,7 @@ struct ath6kl {
        u16 bss_ch;
        u16 listen_intvl_b;
        u16 listen_intvl_t;
+       u8 lrssi_roam_threshold;
        struct ath6kl_version version;
        u32 target_type;
        u8 tx_pwr;
@@ -432,7 +469,18 @@ struct ath6kl {
        enum wlan_low_pwr_state wlan_pwr_state;
        struct wmi_scan_params_cmd sc_params;
 #define AR_MCAST_FILTER_MAC_ADDR_SIZE  4
-       u8 auto_auth_stage;
+       struct {
+               void *rx_report;
+               size_t rx_report_len;
+       } tm;
+
+       struct {
+               u32 dataset_patch_addr;
+               u32 app_load_addr;
+               u32 app_start_override_addr;
+               u32 board_ext_data_addr;
+               u32 reserved_ram_size;
+       } hw;
 
        u16 conf_flags;
        wait_queue_head_t event_wq;
@@ -454,9 +502,35 @@ struct ath6kl {
        u8 *fw_patch;
        size_t fw_patch_len;
 
+       unsigned long fw_capabilities[ATH6KL_CAPABILITY_LEN];
+
        struct workqueue_struct *ath6kl_wq;
 
-       struct ath6kl_node_table scan_table;
+       struct dentry *debugfs_phy;
+
+       u32 send_action_id;
+       bool probe_req_report;
+       u16 next_chan;
+
+       bool p2p;
+       u16 assoc_bss_beacon_int;
+       u8 assoc_bss_dtim_period;
+
+#ifdef CONFIG_ATH6KL_DEBUG
+       struct {
+               struct circ_buf fwlog_buf;
+               spinlock_t fwlog_lock;
+               void *fwlog_tmp;
+               u32 fwlog_mask;
+               unsigned int dbgfs_diag_reg;
+               u32 diag_reg_addr_wr;
+               u32 diag_reg_val_wr;
+
+               struct {
+                       unsigned int invalid_rate;
+               } war_stats;
+       } debug;
+#endif /* CONFIG_ATH6KL_DEBUG */
 };
 
 static inline void *ath6kl_priv(struct net_device *dev)
@@ -474,6 +548,19 @@ static inline void ath6kl_deposit_credit_to_ep(struct htc_credit_state_info
        cred_info->cur_free_credits -= credits;
 }
 
+static inline u32 ath6kl_get_hi_item_addr(struct ath6kl *ar,
+                                         u32 item_offset)
+{
+       u32 addr = 0;
+
+       if (ar->target_type == TARGET_TYPE_AR6003)
+               addr = ATH6KL_AR6003_HI_START_ADDR + item_offset;
+       else if (ar->target_type == TARGET_TYPE_AR6004)
+               addr = ATH6KL_AR6004_HI_START_ADDR + item_offset;
+
+       return addr;
+}
+
 void ath6kl_destroy(struct net_device *dev, unsigned int unregister);
 int ath6kl_configure_target(struct ath6kl *ar);
 void ath6kl_detect_error(unsigned long ptr);
@@ -487,9 +574,11 @@ enum htc_send_full_action ath6kl_tx_queue_full(struct htc_target *target,
                                               struct htc_packet *packet);
 void ath6kl_stop_txrx(struct ath6kl *ar);
 void ath6kl_cleanup_amsdu_rxbufs(struct ath6kl *ar);
-int ath6kl_access_datadiag(struct ath6kl *ar, u32 address,
-                          u8 *data, u32 length, bool read);
-int ath6kl_read_reg_diag(struct ath6kl *ar, u32 *address, u32 *data);
+int ath6kl_diag_write32(struct ath6kl *ar, u32 address, __le32 value);
+int ath6kl_diag_write(struct ath6kl *ar, u32 address, void *data, u32 length);
+int ath6kl_diag_read32(struct ath6kl *ar, u32 address, u32 *value);
+int ath6kl_diag_read(struct ath6kl *ar, u32 address, void *data, u32 length);
+int ath6kl_read_fwlogs(struct ath6kl *ar);
 void ath6kl_init_profile_info(struct ath6kl *ar);
 void ath6kl_tx_data_cleanup(struct ath6kl *ar);
 void ath6kl_stop_endpoint(struct net_device *dev, bool keep_profile,
@@ -520,6 +609,10 @@ void ath6kl_connect_event(struct ath6kl *ar, u16 channel,
                          u16 beacon_int, enum network_type net_type,
                          u8 beacon_ie_len, u8 assoc_req_len,
                          u8 assoc_resp_len, u8 *assoc_info);
+void ath6kl_connect_ap_mode_bss(struct ath6kl *ar, u16 channel);
+void ath6kl_connect_ap_mode_sta(struct ath6kl *ar, u16 aid, u8 *mac_addr,
+                               u8 keymgmt, u8 ucipher, u8 auth,
+                               u8 assoc_req_len, u8 *assoc_info);
 void ath6kl_disconnect_event(struct ath6kl *ar, u8 reason,
                             u8 *bssid, u8 assoc_resp_len,
                             u8 *assoc_info, u16 prot_reason_status);
@@ -534,11 +627,11 @@ void ath6kl_pspoll_event(struct ath6kl *ar, u8 aid);
 
 void ath6kl_dtimexpiry_event(struct ath6kl *ar);
 void ath6kl_disconnect(struct ath6kl *ar);
+void ath6kl_deep_sleep_enable(struct ath6kl *ar);
 void aggr_recv_delba_req_evt(struct ath6kl *ar, u8 tid);
 void aggr_recv_addba_req_evt(struct ath6kl *ar, u8 tid, u16 seq_no,
                             u8 win_sz);
 void ath6kl_wakeup_event(void *dev);
 void ath6kl_target_failure(struct ath6kl *ar);
 
-void ath6kl_cfg80211_scan_node(struct wiphy *wiphy, struct bss *ni);
 #endif /* CORE_H */
index 316136c8b9036713a6dbcfade8aac76f79dec552..ba3f23d71150ee0bf0054efbc42530cac4ac3569 100644 (file)
  */
 
 #include "core.h"
+
+#include <linux/circ_buf.h>
+#include <linux/fs.h>
+#include <linux/vmalloc.h>
+
 #include "debug.h"
+#include "target.h"
+
+struct ath6kl_fwlog_slot {
+       __le32 timestamp;
+       __le32 length;
+
+       /* max ATH6KL_FWLOG_PAYLOAD_SIZE bytes */
+       u8 payload[0];
+};
+
+#define ATH6KL_FWLOG_SIZE 32768
+#define ATH6KL_FWLOG_SLOT_SIZE (sizeof(struct ath6kl_fwlog_slot) + \
+                               ATH6KL_FWLOG_PAYLOAD_SIZE)
+#define ATH6KL_FWLOG_VALID_MASK 0x1ffff
 
 int ath6kl_printk(const char *level, const char *fmt, ...)
 {
@@ -36,6 +55,27 @@ int ath6kl_printk(const char *level, const char *fmt, ...)
 }
 
 #ifdef CONFIG_ATH6KL_DEBUG
+
+#define REG_OUTPUT_LEN_PER_LINE        25
+#define REGTYPE_STR_LEN                100
+
+struct ath6kl_diag_reg_info {
+       u32 reg_start;
+       u32 reg_end;
+       const char *reg_info;
+};
+
+static const struct ath6kl_diag_reg_info diag_reg[] = {
+       { 0x20000, 0x200fc, "General DMA and Rx registers" },
+       { 0x28000, 0x28900, "MAC PCU register & keycache" },
+       { 0x20800, 0x20a40, "QCU" },
+       { 0x21000, 0x212f0, "DCU" },
+       { 0x4000,  0x42e4, "RTC" },
+       { 0x540000, 0x540000 + (256 * 1024), "RAM" },
+       { 0x29800, 0x2B210, "Base Band" },
+       { 0x1C000, 0x1C748, "Analog" },
+};
+
 void ath6kl_dump_registers(struct ath6kl_device *dev,
                           struct ath6kl_irq_proc_registers *irq_proc_reg,
                           struct ath6kl_irq_enable_reg *irq_enable_reg)
@@ -147,4 +187,748 @@ void dump_cred_dist_stats(struct htc_target *target)
                   target->cred_dist_cntxt->cur_free_credits);
 }
 
+static int ath6kl_debugfs_open(struct inode *inode, struct file *file)
+{
+       file->private_data = inode->i_private;
+       return 0;
+}
+
+void ath6kl_debug_war(struct ath6kl *ar, enum ath6kl_war war)
+{
+       switch (war) {
+       case ATH6KL_WAR_INVALID_RATE:
+               ar->debug.war_stats.invalid_rate++;
+               break;
+       }
+}
+
+static ssize_t read_file_war_stats(struct file *file, char __user *user_buf,
+                                  size_t count, loff_t *ppos)
+{
+       struct ath6kl *ar = file->private_data;
+       char *buf;
+       unsigned int len = 0, buf_len = 1500;
+       ssize_t ret_cnt;
+
+       buf = kzalloc(buf_len, GFP_KERNEL);
+       if (!buf)
+               return -ENOMEM;
+
+       len += scnprintf(buf + len, buf_len - len, "\n");
+       len += scnprintf(buf + len, buf_len - len, "%25s\n",
+                        "Workaround stats");
+       len += scnprintf(buf + len, buf_len - len, "%25s\n\n",
+                        "=================");
+       len += scnprintf(buf + len, buf_len - len, "%20s %10u\n",
+                        "Invalid rates", ar->debug.war_stats.invalid_rate);
+
+       if (WARN_ON(len > buf_len))
+               len = buf_len;
+
+       ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len);
+
+       kfree(buf);
+       return ret_cnt;
+}
+
+static const struct file_operations fops_war_stats = {
+       .read = read_file_war_stats,
+       .open = ath6kl_debugfs_open,
+       .owner = THIS_MODULE,
+       .llseek = default_llseek,
+};
+
+static void ath6kl_debug_fwlog_add(struct ath6kl *ar, const void *buf,
+                                  size_t buf_len)
+{
+       struct circ_buf *fwlog = &ar->debug.fwlog_buf;
+       size_t space;
+       int i;
+
+       /* entries must all be equal size */
+       if (WARN_ON(buf_len != ATH6KL_FWLOG_SLOT_SIZE))
+               return;
+
+       space = CIRC_SPACE(fwlog->head, fwlog->tail, ATH6KL_FWLOG_SIZE);
+       if (space < buf_len)
+               /* discard oldest slot */
+               fwlog->tail = (fwlog->tail + ATH6KL_FWLOG_SLOT_SIZE) &
+                       (ATH6KL_FWLOG_SIZE - 1);
+
+       for (i = 0; i < buf_len; i += space) {
+               space = CIRC_SPACE_TO_END(fwlog->head, fwlog->tail,
+                                         ATH6KL_FWLOG_SIZE);
+
+               if ((size_t) space > buf_len - i)
+                       space = buf_len - i;
+
+               memcpy(&fwlog->buf[fwlog->head], buf, space);
+               fwlog->head = (fwlog->head + space) & (ATH6KL_FWLOG_SIZE - 1);
+       }
+
+}
+
+void ath6kl_debug_fwlog_event(struct ath6kl *ar, const void *buf, size_t len)
+{
+       struct ath6kl_fwlog_slot *slot = ar->debug.fwlog_tmp;
+       size_t slot_len;
+
+       if (WARN_ON(len > ATH6KL_FWLOG_PAYLOAD_SIZE))
+               return;
+
+       spin_lock_bh(&ar->debug.fwlog_lock);
+
+       slot->timestamp = cpu_to_le32(jiffies);
+       slot->length = cpu_to_le32(len);
+       memcpy(slot->payload, buf, len);
+
+       slot_len = sizeof(*slot) + len;
+
+       if (slot_len < ATH6KL_FWLOG_SLOT_SIZE)
+               memset(slot->payload + len, 0,
+                      ATH6KL_FWLOG_SLOT_SIZE - slot_len);
+
+       ath6kl_debug_fwlog_add(ar, slot, ATH6KL_FWLOG_SLOT_SIZE);
+
+       spin_unlock_bh(&ar->debug.fwlog_lock);
+}
+
+static bool ath6kl_debug_fwlog_empty(struct ath6kl *ar)
+{
+       return CIRC_CNT(ar->debug.fwlog_buf.head,
+                       ar->debug.fwlog_buf.tail,
+                       ATH6KL_FWLOG_SLOT_SIZE) == 0;
+}
+
+static ssize_t ath6kl_fwlog_read(struct file *file, char __user *user_buf,
+                                size_t count, loff_t *ppos)
+{
+       struct ath6kl *ar = file->private_data;
+       struct circ_buf *fwlog = &ar->debug.fwlog_buf;
+       size_t len = 0, buf_len = count;
+       ssize_t ret_cnt;
+       char *buf;
+       int ccnt;
+
+       buf = vmalloc(buf_len);
+       if (!buf)
+               return -ENOMEM;
+
+       /* read undelivered logs from firmware */
+       ath6kl_read_fwlogs(ar);
+
+       spin_lock_bh(&ar->debug.fwlog_lock);
+
+       while (len < buf_len && !ath6kl_debug_fwlog_empty(ar)) {
+               ccnt = CIRC_CNT_TO_END(fwlog->head, fwlog->tail,
+                                      ATH6KL_FWLOG_SIZE);
+
+               if ((size_t) ccnt > buf_len - len)
+                       ccnt = buf_len - len;
+
+               memcpy(buf + len, &fwlog->buf[fwlog->tail], ccnt);
+               len += ccnt;
+
+               fwlog->tail = (fwlog->tail + ccnt) &
+                       (ATH6KL_FWLOG_SIZE - 1);
+       }
+
+       spin_unlock_bh(&ar->debug.fwlog_lock);
+
+       if (WARN_ON(len > buf_len))
+               len = buf_len;
+
+       ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len);
+
+       vfree(buf);
+
+       return ret_cnt;
+}
+
+static const struct file_operations fops_fwlog = {
+       .open = ath6kl_debugfs_open,
+       .read = ath6kl_fwlog_read,
+       .owner = THIS_MODULE,
+       .llseek = default_llseek,
+};
+
+static ssize_t ath6kl_fwlog_mask_read(struct file *file, char __user *user_buf,
+                                     size_t count, loff_t *ppos)
+{
+       struct ath6kl *ar = file->private_data;
+       char buf[16];
+       int len;
+
+       len = snprintf(buf, sizeof(buf), "0x%x\n", ar->debug.fwlog_mask);
+
+       return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
+
+static ssize_t ath6kl_fwlog_mask_write(struct file *file,
+                                      const char __user *user_buf,
+                                      size_t count, loff_t *ppos)
+{
+       struct ath6kl *ar = file->private_data;
+       int ret;
+
+       ret = kstrtou32_from_user(user_buf, count, 0, &ar->debug.fwlog_mask);
+       if (ret)
+               return ret;
+
+       ret = ath6kl_wmi_config_debug_module_cmd(ar->wmi,
+                                                ATH6KL_FWLOG_VALID_MASK,
+                                                ar->debug.fwlog_mask);
+       if (ret)
+               return ret;
+
+       return count;
+}
+
+static const struct file_operations fops_fwlog_mask = {
+       .open = ath6kl_debugfs_open,
+       .read = ath6kl_fwlog_mask_read,
+       .write = ath6kl_fwlog_mask_write,
+       .owner = THIS_MODULE,
+       .llseek = default_llseek,
+};
+
+static ssize_t read_file_tgt_stats(struct file *file, char __user *user_buf,
+                                  size_t count, loff_t *ppos)
+{
+       struct ath6kl *ar = file->private_data;
+       struct target_stats *tgt_stats = &ar->target_stats;
+       char *buf;
+       unsigned int len = 0, buf_len = 1500;
+       int i;
+       long left;
+       ssize_t ret_cnt;
+
+       buf = kzalloc(buf_len, GFP_KERNEL);
+       if (!buf)
+               return -ENOMEM;
+
+       if (down_interruptible(&ar->sem)) {
+               kfree(buf);
+               return -EBUSY;
+       }
+
+       set_bit(STATS_UPDATE_PEND, &ar->flag);
+
+       if (ath6kl_wmi_get_stats_cmd(ar->wmi)) {
+               up(&ar->sem);
+               kfree(buf);
+               return -EIO;
+       }
+
+       left = wait_event_interruptible_timeout(ar->event_wq,
+                                               !test_bit(STATS_UPDATE_PEND,
+                                               &ar->flag), WMI_TIMEOUT);
+
+       up(&ar->sem);
+
+       if (left <= 0) {
+               kfree(buf);
+               return -ETIMEDOUT;
+       }
+
+       len += scnprintf(buf + len, buf_len - len, "\n");
+       len += scnprintf(buf + len, buf_len - len, "%25s\n",
+                        "Target Tx stats");
+       len += scnprintf(buf + len, buf_len - len, "%25s\n\n",
+                        "=================");
+       len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
+                        "Ucast packets", tgt_stats->tx_ucast_pkt);
+       len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
+                        "Bcast packets", tgt_stats->tx_bcast_pkt);
+       len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
+                        "Ucast byte", tgt_stats->tx_ucast_byte);
+       len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
+                        "Bcast byte", tgt_stats->tx_bcast_byte);
+       len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
+                        "Rts success cnt", tgt_stats->tx_rts_success_cnt);
+       for (i = 0; i < 4; i++)
+               len += scnprintf(buf + len, buf_len - len,
+                                "%18s %d %10llu\n", "PER on ac",
+                                i, tgt_stats->tx_pkt_per_ac[i]);
+       len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
+                        "Error", tgt_stats->tx_err);
+       len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
+                        "Fail count", tgt_stats->tx_fail_cnt);
+       len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
+                        "Retry count", tgt_stats->tx_retry_cnt);
+       len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
+                        "Multi retry cnt", tgt_stats->tx_mult_retry_cnt);
+       len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
+                        "Rts fail cnt", tgt_stats->tx_rts_fail_cnt);
+       len += scnprintf(buf + len, buf_len - len, "%25s %10llu\n\n",
+                        "TKIP counter measure used",
+                        tgt_stats->tkip_cnter_measures_invoked);
+
+       len += scnprintf(buf + len, buf_len - len, "%25s\n",
+                        "Target Rx stats");
+       len += scnprintf(buf + len, buf_len - len, "%25s\n",
+                        "=================");
+
+       len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
+                        "Ucast packets", tgt_stats->rx_ucast_pkt);
+       len += scnprintf(buf + len, buf_len - len, "%20s %10d\n",
+                        "Ucast Rate", tgt_stats->rx_ucast_rate);
+       len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
+                        "Bcast packets", tgt_stats->rx_bcast_pkt);
+       len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
+                        "Ucast byte", tgt_stats->rx_ucast_byte);
+       len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
+                        "Bcast byte", tgt_stats->rx_bcast_byte);
+       len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
+                        "Fragmented pkt", tgt_stats->rx_frgment_pkt);
+       len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
+                        "Error", tgt_stats->rx_err);
+       len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
+                        "CRC Err", tgt_stats->rx_crc_err);
+       len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
+                        "Key chache miss", tgt_stats->rx_key_cache_miss);
+       len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
+                        "Decrypt Err", tgt_stats->rx_decrypt_err);
+       len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
+                        "Duplicate frame", tgt_stats->rx_dupl_frame);
+       len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
+                        "Tkip Mic failure", tgt_stats->tkip_local_mic_fail);
+       len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
+                        "TKIP format err", tgt_stats->tkip_fmt_err);
+       len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
+                        "CCMP format Err", tgt_stats->ccmp_fmt_err);
+       len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n\n",
+                        "CCMP Replay Err", tgt_stats->ccmp_replays);
+
+       len += scnprintf(buf + len, buf_len - len, "%25s\n",
+                        "Misc Target stats");
+       len += scnprintf(buf + len, buf_len - len, "%25s\n",
+                        "=================");
+       len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
+                        "Beacon Miss count", tgt_stats->cs_bmiss_cnt);
+       len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
+                        "Num Connects", tgt_stats->cs_connect_cnt);
+       len += scnprintf(buf + len, buf_len - len, "%20s %10llu\n",
+                        "Num disconnects", tgt_stats->cs_discon_cnt);
+       len += scnprintf(buf + len, buf_len - len, "%20s %10d\n",
+                        "Beacon avg rssi", tgt_stats->cs_ave_beacon_rssi);
+
+       if (len > buf_len)
+               len = buf_len;
+
+       ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len);
+
+       kfree(buf);
+       return ret_cnt;
+}
+
+static const struct file_operations fops_tgt_stats = {
+       .read = read_file_tgt_stats,
+       .open = ath6kl_debugfs_open,
+       .owner = THIS_MODULE,
+       .llseek = default_llseek,
+};
+
+#define print_credit_info(fmt_str, ep_list_field)              \
+       (len += scnprintf(buf + len, buf_len - len, fmt_str,    \
+                        ep_list->ep_list_field))
+#define CREDIT_INFO_DISPLAY_STRING_LEN 200
+#define CREDIT_INFO_LEN        128
+
+static ssize_t read_file_credit_dist_stats(struct file *file,
+                                          char __user *user_buf,
+                                          size_t count, loff_t *ppos)
+{
+       struct ath6kl *ar = file->private_data;
+       struct htc_target *target = ar->htc_target;
+       struct htc_endpoint_credit_dist *ep_list;
+       char *buf;
+       unsigned int buf_len, len = 0;
+       ssize_t ret_cnt;
+
+       buf_len = CREDIT_INFO_DISPLAY_STRING_LEN +
+                 get_queue_depth(&target->cred_dist_list) * CREDIT_INFO_LEN;
+       buf = kzalloc(buf_len, GFP_KERNEL);
+       if (!buf)
+               return -ENOMEM;
+
+       len += scnprintf(buf + len, buf_len - len, "%25s%5d\n",
+                        "Total Avail Credits: ",
+                        target->cred_dist_cntxt->total_avail_credits);
+       len += scnprintf(buf + len, buf_len - len, "%25s%5d\n",
+                        "Free credits :",
+                        target->cred_dist_cntxt->cur_free_credits);
+
+       len += scnprintf(buf + len, buf_len - len,
+                        " Epid  Flags    Cred_norm  Cred_min  Credits  Cred_assngd"
+                        "  Seek_cred  Cred_sz  Cred_per_msg  Cred_to_dist"
+                        "  qdepth\n");
+
+       list_for_each_entry(ep_list, &target->cred_dist_list, list) {
+               print_credit_info("  %2d", endpoint);
+               print_credit_info("%10x", dist_flags);
+               print_credit_info("%8d", cred_norm);
+               print_credit_info("%9d", cred_min);
+               print_credit_info("%9d", credits);
+               print_credit_info("%10d", cred_assngd);
+               print_credit_info("%13d", seek_cred);
+               print_credit_info("%12d", cred_sz);
+               print_credit_info("%9d", cred_per_msg);
+               print_credit_info("%14d", cred_to_dist);
+               len += scnprintf(buf + len, buf_len - len, "%12d\n",
+                                get_queue_depth(&((struct htc_endpoint *)
+                                                ep_list->htc_rsvd)->txq));
+       }
+
+       if (len > buf_len)
+               len = buf_len;
+
+       ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len);
+       kfree(buf);
+       return ret_cnt;
+}
+
+static const struct file_operations fops_credit_dist_stats = {
+       .read = read_file_credit_dist_stats,
+       .open = ath6kl_debugfs_open,
+       .owner = THIS_MODULE,
+       .llseek = default_llseek,
+};
+
+static unsigned long ath6kl_get_num_reg(void)
+{
+       int i;
+       unsigned long n_reg = 0;
+
+       for (i = 0; i < ARRAY_SIZE(diag_reg); i++)
+               n_reg = n_reg +
+                    (diag_reg[i].reg_end - diag_reg[i].reg_start) / 4 + 1;
+
+       return n_reg;
+}
+
+static bool ath6kl_dbg_is_diag_reg_valid(u32 reg_addr)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(diag_reg); i++) {
+               if (reg_addr >= diag_reg[i].reg_start &&
+                   reg_addr <= diag_reg[i].reg_end)
+                       return true;
+       }
+
+       return false;
+}
+
+static ssize_t ath6kl_regread_read(struct file *file, char __user *user_buf,
+                                   size_t count, loff_t *ppos)
+{
+       struct ath6kl *ar = file->private_data;
+       u8 buf[50];
+       unsigned int len = 0;
+
+       if (ar->debug.dbgfs_diag_reg)
+               len += scnprintf(buf + len, sizeof(buf) - len, "0x%x\n",
+                               ar->debug.dbgfs_diag_reg);
+       else
+               len += scnprintf(buf + len, sizeof(buf) - len,
+                                "All diag registers\n");
+
+       return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
+
+static ssize_t ath6kl_regread_write(struct file *file,
+                                   const char __user *user_buf,
+                                   size_t count, loff_t *ppos)
+{
+       struct ath6kl *ar = file->private_data;
+       u8 buf[50];
+       unsigned int len;
+       unsigned long reg_addr;
+
+       len = min(count, sizeof(buf) - 1);
+       if (copy_from_user(buf, user_buf, len))
+               return -EFAULT;
+
+       buf[len] = '\0';
+
+       if (strict_strtoul(buf, 0, &reg_addr))
+               return -EINVAL;
+
+       if ((reg_addr % 4) != 0)
+               return -EINVAL;
+
+       if (reg_addr && !ath6kl_dbg_is_diag_reg_valid(reg_addr))
+               return -EINVAL;
+
+       ar->debug.dbgfs_diag_reg = reg_addr;
+
+       return count;
+}
+
+static const struct file_operations fops_diag_reg_read = {
+       .read = ath6kl_regread_read,
+       .write = ath6kl_regread_write,
+       .open = ath6kl_debugfs_open,
+       .owner = THIS_MODULE,
+       .llseek = default_llseek,
+};
+
+static int ath6kl_regdump_open(struct inode *inode, struct file *file)
+{
+       struct ath6kl *ar = inode->i_private;
+       u8 *buf;
+       unsigned long int reg_len;
+       unsigned int len = 0, n_reg;
+       u32 addr;
+       __le32 reg_val;
+       int i, status;
+
+       /* Dump all the registers if no register is specified */
+       if (!ar->debug.dbgfs_diag_reg)
+               n_reg = ath6kl_get_num_reg();
+       else
+               n_reg = 1;
+
+       reg_len = n_reg * REG_OUTPUT_LEN_PER_LINE;
+       if (n_reg > 1)
+               reg_len += REGTYPE_STR_LEN;
+
+       buf = vmalloc(reg_len);
+       if (!buf)
+               return -ENOMEM;
+
+       if (n_reg == 1) {
+               addr = ar->debug.dbgfs_diag_reg;
+
+               status = ath6kl_diag_read32(ar,
+                               TARG_VTOP(ar->target_type, addr),
+                               (u32 *)&reg_val);
+               if (status)
+                       goto fail_reg_read;
+
+               len += scnprintf(buf + len, reg_len - len,
+                                "0x%06x 0x%08x\n", addr, le32_to_cpu(reg_val));
+               goto done;
+       }
+
+       for (i = 0; i < ARRAY_SIZE(diag_reg); i++) {
+               len += scnprintf(buf + len, reg_len - len,
+                               "%s\n", diag_reg[i].reg_info);
+               for (addr = diag_reg[i].reg_start;
+                    addr <= diag_reg[i].reg_end; addr += 4) {
+                       status = ath6kl_diag_read32(ar,
+                                       TARG_VTOP(ar->target_type, addr),
+                                       (u32 *)&reg_val);
+                       if (status)
+                               goto fail_reg_read;
+
+                       len += scnprintf(buf + len, reg_len - len,
+                                       "0x%06x 0x%08x\n",
+                                       addr, le32_to_cpu(reg_val));
+               }
+       }
+
+done:
+       file->private_data = buf;
+       return 0;
+
+fail_reg_read:
+       ath6kl_warn("Unable to read memory:%u\n", addr);
+       vfree(buf);
+       return -EIO;
+}
+
+static ssize_t ath6kl_regdump_read(struct file *file, char __user *user_buf,
+                                 size_t count, loff_t *ppos)
+{
+       u8 *buf = file->private_data;
+       return simple_read_from_buffer(user_buf, count, ppos, buf, strlen(buf));
+}
+
+static int ath6kl_regdump_release(struct inode *inode, struct file *file)
+{
+       vfree(file->private_data);
+       return 0;
+}
+
+static const struct file_operations fops_reg_dump = {
+       .open = ath6kl_regdump_open,
+       .read = ath6kl_regdump_read,
+       .release = ath6kl_regdump_release,
+       .owner = THIS_MODULE,
+       .llseek = default_llseek,
+};
+
+static ssize_t ath6kl_lrssi_roam_write(struct file *file,
+                                      const char __user *user_buf,
+                                      size_t count, loff_t *ppos)
+{
+       struct ath6kl *ar = file->private_data;
+       unsigned long lrssi_roam_threshold;
+       char buf[32];
+       ssize_t len;
+
+       len = min(count, sizeof(buf) - 1);
+       if (copy_from_user(buf, user_buf, len))
+               return -EFAULT;
+
+       buf[len] = '\0';
+       if (strict_strtoul(buf, 0, &lrssi_roam_threshold))
+               return -EINVAL;
+
+       ar->lrssi_roam_threshold = lrssi_roam_threshold;
+
+       ath6kl_wmi_set_roam_lrssi_cmd(ar->wmi, ar->lrssi_roam_threshold);
+
+       return count;
+}
+
+static ssize_t ath6kl_lrssi_roam_read(struct file *file,
+                                     char __user *user_buf,
+                                     size_t count, loff_t *ppos)
+{
+       struct ath6kl *ar = file->private_data;
+       char buf[32];
+       unsigned int len;
+
+       len = snprintf(buf, sizeof(buf), "%u\n", ar->lrssi_roam_threshold);
+
+       return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
+
+static const struct file_operations fops_lrssi_roam_threshold = {
+       .read = ath6kl_lrssi_roam_read,
+       .write = ath6kl_lrssi_roam_write,
+       .open = ath6kl_debugfs_open,
+       .owner = THIS_MODULE,
+       .llseek = default_llseek,
+};
+
+static ssize_t ath6kl_regwrite_read(struct file *file,
+                                   char __user *user_buf,
+                                   size_t count, loff_t *ppos)
+{
+       struct ath6kl *ar = file->private_data;
+       u8 buf[32];
+       unsigned int len = 0;
+
+       len = scnprintf(buf, sizeof(buf), "Addr: 0x%x Val: 0x%x\n",
+                       ar->debug.diag_reg_addr_wr, ar->debug.diag_reg_val_wr);
+
+       return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
+
+static ssize_t ath6kl_regwrite_write(struct file *file,
+                                    const char __user *user_buf,
+                                    size_t count, loff_t *ppos)
+{
+       struct ath6kl *ar = file->private_data;
+       char buf[32];
+       char *sptr, *token;
+       unsigned int len = 0;
+       u32 reg_addr, reg_val;
+
+       len = min(count, sizeof(buf) - 1);
+       if (copy_from_user(buf, user_buf, len))
+               return -EFAULT;
+
+       buf[len] = '\0';
+       sptr = buf;
+
+       token = strsep(&sptr, "=");
+       if (!token)
+               return -EINVAL;
+
+       if (kstrtou32(token, 0, &reg_addr))
+               return -EINVAL;
+
+       if (!ath6kl_dbg_is_diag_reg_valid(reg_addr))
+               return -EINVAL;
+
+       if (kstrtou32(sptr, 0, &reg_val))
+               return -EINVAL;
+
+       ar->debug.diag_reg_addr_wr = reg_addr;
+       ar->debug.diag_reg_val_wr = reg_val;
+
+       if (ath6kl_diag_write32(ar, ar->debug.diag_reg_addr_wr,
+                               cpu_to_le32(ar->debug.diag_reg_val_wr)))
+               return -EIO;
+
+       return count;
+}
+
+static const struct file_operations fops_diag_reg_write = {
+       .read = ath6kl_regwrite_read,
+       .write = ath6kl_regwrite_write,
+       .open = ath6kl_debugfs_open,
+       .owner = THIS_MODULE,
+       .llseek = default_llseek,
+};
+
+int ath6kl_debug_init(struct ath6kl *ar)
+{
+       ar->debug.fwlog_buf.buf = vmalloc(ATH6KL_FWLOG_SIZE);
+       if (ar->debug.fwlog_buf.buf == NULL)
+               return -ENOMEM;
+
+       ar->debug.fwlog_tmp = kmalloc(ATH6KL_FWLOG_SLOT_SIZE, GFP_KERNEL);
+       if (ar->debug.fwlog_tmp == NULL) {
+               vfree(ar->debug.fwlog_buf.buf);
+               return -ENOMEM;
+       }
+
+       spin_lock_init(&ar->debug.fwlog_lock);
+
+       /*
+        * Actually we are lying here but don't know how to read the mask
+        * value from the firmware.
+        */
+       ar->debug.fwlog_mask = 0;
+
+       ar->debugfs_phy = debugfs_create_dir("ath6kl",
+                                            ar->wdev->wiphy->debugfsdir);
+       if (!ar->debugfs_phy) {
+               vfree(ar->debug.fwlog_buf.buf);
+               kfree(ar->debug.fwlog_tmp);
+               return -ENOMEM;
+       }
+
+       debugfs_create_file("tgt_stats", S_IRUSR, ar->debugfs_phy, ar,
+                           &fops_tgt_stats);
+
+       debugfs_create_file("credit_dist_stats", S_IRUSR, ar->debugfs_phy, ar,
+                           &fops_credit_dist_stats);
+
+       debugfs_create_file("fwlog", S_IRUSR, ar->debugfs_phy, ar,
+                           &fops_fwlog);
+
+       debugfs_create_file("fwlog_mask", S_IRUSR | S_IWUSR, ar->debugfs_phy,
+                           ar, &fops_fwlog_mask);
+
+       debugfs_create_file("reg_addr", S_IRUSR | S_IWUSR, ar->debugfs_phy, ar,
+                           &fops_diag_reg_read);
+
+       debugfs_create_file("reg_dump", S_IRUSR, ar->debugfs_phy, ar,
+                           &fops_reg_dump);
+
+       debugfs_create_file("lrssi_roam_threshold", S_IRUSR | S_IWUSR,
+                           ar->debugfs_phy, ar, &fops_lrssi_roam_threshold);
+
+       debugfs_create_file("reg_write", S_IRUSR | S_IWUSR,
+                           ar->debugfs_phy, ar, &fops_diag_reg_write);
+
+       debugfs_create_file("war_stats", S_IRUSR, ar->debugfs_phy, ar,
+                           &fops_war_stats);
+
+       return 0;
+}
+
+void ath6kl_debug_cleanup(struct ath6kl *ar)
+{
+       vfree(ar->debug.fwlog_buf.buf);
+       kfree(ar->debug.fwlog_tmp);
+}
+
 #endif
index 66b399962f01f04d3e0304e8c3e402d0acdd076f..9288a3ce1e399446fada3406c5659e0dbe5d8a40 100644 (file)
@@ -34,8 +34,12 @@ enum ATH6K_DEBUG_MASK {
        ATH6KL_DBG_TRC          = BIT(11),    /* generic func tracing */
        ATH6KL_DBG_SCATTER      = BIT(12),    /* hif scatter tracing */
        ATH6KL_DBG_WLAN_CFG     = BIT(13),    /* cfg80211 i/f file tracing */
-       ATH6KL_DBG_RAW_BYTES    = BIT(14),    /* dump tx/rx and wmi frames */
+       ATH6KL_DBG_RAW_BYTES    = BIT(14),    /* dump tx/rx frames */
        ATH6KL_DBG_AGGR         = BIT(15),    /* aggregation */
+       ATH6KL_DBG_SDIO         = BIT(16),
+       ATH6KL_DBG_SDIO_DUMP    = BIT(17),
+       ATH6KL_DBG_BOOT         = BIT(18),    /* driver init and fw boot */
+       ATH6KL_DBG_WMI_DUMP     = BIT(19),
        ATH6KL_DBG_ANY          = 0xffffffff  /* enable all logs */
 };
 
@@ -52,6 +56,10 @@ extern int ath6kl_printk(const char *level, const char *fmt, ...)
 
 #define AR_DBG_LVL_CHECK(mask) (debug_mask & mask)
 
+enum ath6kl_war {
+       ATH6KL_WAR_INVALID_RATE,
+};
+
 #ifdef CONFIG_ATH6KL_DEBUG
 #define ath6kl_dbg(mask, fmt, ...)                                     \
        ({                                                              \
@@ -65,12 +73,14 @@ extern int ath6kl_printk(const char *level, const char *fmt, ...)
         })
 
 static inline void ath6kl_dbg_dump(enum ATH6K_DEBUG_MASK mask,
-                                  const char *msg, const void *buf,
-                                  size_t len)
+                                  const char *msg, const char *prefix,
+                                  const void *buf, size_t len)
 {
        if (debug_mask & mask) {
-               ath6kl_dbg(mask, "%s\n", msg);
-               print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, buf, len);
+               if (msg)
+                       ath6kl_dbg(mask, "%s\n", msg);
+
+               print_hex_dump_bytes(prefix, DUMP_PREFIX_OFFSET, buf, len);
        }
 }
 
@@ -78,6 +88,11 @@ void ath6kl_dump_registers(struct ath6kl_device *dev,
                           struct ath6kl_irq_proc_registers *irq_proc_reg,
                           struct ath6kl_irq_enable_reg *irq_en_reg);
 void dump_cred_dist_stats(struct htc_target *target);
+void ath6kl_debug_fwlog_event(struct ath6kl *ar, const void *buf, size_t len);
+void ath6kl_debug_war(struct ath6kl *ar, enum ath6kl_war war);
+int ath6kl_debug_init(struct ath6kl *ar);
+void ath6kl_debug_cleanup(struct ath6kl *ar);
+
 #else
 static inline int ath6kl_dbg(enum ATH6K_DEBUG_MASK dbg_mask,
                             const char *fmt, ...)
@@ -86,8 +101,8 @@ static inline int ath6kl_dbg(enum ATH6K_DEBUG_MASK dbg_mask,
 }
 
 static inline void ath6kl_dbg_dump(enum ATH6K_DEBUG_MASK mask,
-                                  const char *msg, const void *buf,
-                                  size_t len)
+                                  const char *msg, const char *prefix,
+                                  const void *buf, size_t len)
 {
 }
 
@@ -100,6 +115,24 @@ static inline void ath6kl_dump_registers(struct ath6kl_device *dev,
 static inline void dump_cred_dist_stats(struct htc_target *target)
 {
 }
-#endif
 
+static inline void ath6kl_debug_fwlog_event(struct ath6kl *ar,
+                                           const void *buf, size_t len)
+{
+}
+
+static inline void ath6kl_debug_war(struct ath6kl *ar, enum ath6kl_war war)
+{
+}
+
+static inline int ath6kl_debug_init(struct ath6kl *ar)
+{
+       return 0;
+}
+
+static inline void ath6kl_debug_cleanup(struct ath6kl *ar)
+{
+}
+
+#endif
 #endif
index c923979776a057fe59ce139396b17c90b094d850..d6c898f3d0b325a79ed968897f6cec9bdfd968a2 100644 (file)
@@ -69,4 +69,9 @@ static inline void ath6kl_hif_cleanup_scatter(struct ath6kl *ar)
        return ar->hif_ops->cleanup_scatter(ar);
 }
 
+static inline int ath6kl_hif_suspend(struct ath6kl *ar)
+{
+       return ar->hif_ops->suspend(ar);
+}
+
 #endif
index 5ceff54775a1deef0bafee7c51e4852bde00db7a..797e2d1d9bf9f41ede26891e1ac1eef0f673be5c 100644 (file)
@@ -202,6 +202,7 @@ struct ath6kl_hif_ops {
        int (*scat_req_rw) (struct ath6kl *ar,
                            struct hif_scatter_req *scat_req);
        void (*cleanup_scatter)(struct ath6kl *ar);
+       int (*suspend)(struct ath6kl *ar);
 };
 
 #endif
index a8dc5c3ea567b9cb42926139fa678879ec2bce42..f88a7c9e41485d6cb1806110c359e4d8cddce241 100644 (file)
 
 #define CALC_TXRX_PADDED_LEN(dev, len)  (__ALIGN_MASK((len), (dev)->block_mask))
 
-static void htc_prep_send_pkt(struct htc_packet *packet, u8 flags, int ctrl0,
-                             int ctrl1)
+static void ath6kl_htc_tx_buf_align(u8 **buf, unsigned long len)
+{
+       u8 *align_addr;
+
+       if (!IS_ALIGNED((unsigned long) *buf, 4)) {
+               align_addr = PTR_ALIGN(*buf - 4, 4);
+               memmove(align_addr, *buf, len);
+               *buf = align_addr;
+       }
+}
+
+static void ath6kl_htc_tx_prep_pkt(struct htc_packet *packet, u8 flags,
+                                  int ctrl0, int ctrl1)
 {
        struct htc_frame_hdr *hdr;
 
@@ -167,7 +178,8 @@ static void htc_async_tx_scat_complete(struct htc_target *target,
        htc_tx_complete(endpoint, &tx_compq);
 }
 
-static int htc_issue_send(struct htc_target *target, struct htc_packet *packet)
+static int ath6kl_htc_tx_issue(struct htc_target *target,
+                              struct htc_packet *packet)
 {
        int status;
        bool sync = false;
@@ -196,7 +208,7 @@ static int htc_issue_send(struct htc_target *target, struct htc_packet *packet)
                                 HIF_WR_SYNC_BLOCK_INC);
 
                packet->status = status;
-                packet->buf += HTC_HDR_LENGTH;
+               packet->buf += HTC_HDR_LENGTH;
        } else
                status = hif_write_async(target->dev->ar,
                                target->dev->ar->mbox_info.htc_addr,
@@ -265,9 +277,9 @@ static int htc_check_credits(struct htc_target *target,
        return 0;
 }
 
-static void htc_tx_pkts_get(struct htc_target *target,
-                           struct htc_endpoint *endpoint,
-                           struct list_head *queue)
+static void ath6kl_htc_tx_pkts_get(struct htc_target *target,
+                                  struct htc_endpoint *endpoint,
+                                  struct list_head *queue)
 {
        int req_cred;
        u8 flags;
@@ -346,11 +358,11 @@ static int htc_get_credit_padding(unsigned int cred_sz, int *len,
        return cred_pad;
 }
 
-static int htc_setup_send_scat_list(struct htc_target *target,
-                                   struct htc_endpoint *endpoint,
-                                   struct hif_scatter_req *scat_req,
-                                   int n_scat,
-                                   struct list_head *queue)
+static int ath6kl_htc_tx_setup_scat_list(struct htc_target *target,
+                                        struct htc_endpoint *endpoint,
+                                        struct hif_scatter_req *scat_req,
+                                        int n_scat,
+                                        struct list_head *queue)
 {
        struct htc_packet *packet;
        int i, len, rem_scat, cred_pad;
@@ -370,27 +382,23 @@ static int htc_setup_send_scat_list(struct htc_target *target,
 
                cred_pad = htc_get_credit_padding(target->tgt_cred_sz,
                                                  &len, endpoint);
-               if (cred_pad < 0) {
-                       status = -EINVAL;
-                       break;
-               }
-
-               if (rem_scat < len) {
-                       /* exceeds what we can transfer */
+               if (cred_pad < 0 || rem_scat < len) {
                        status = -ENOSPC;
                        break;
                }
 
                rem_scat -= len;
                /* now remove it from the queue */
-               packet = list_first_entry(queue, struct htc_packet, list);
                list_del(&packet->list);
 
                scat_req->scat_list[i].packet = packet;
                /* prepare packet and flag message as part of a send bundle */
-               htc_prep_send_pkt(packet,
+               ath6kl_htc_tx_prep_pkt(packet,
                                packet->info.tx.flags | HTC_FLAGS_SEND_BUNDLE,
                                cred_pad, packet->info.tx.seqno);
+               /* Make sure the buffer is 4-byte aligned */
+               ath6kl_htc_tx_buf_align(&packet->buf,
+                                       packet->act_len + HTC_HDR_LENGTH);
                scat_req->scat_list[i].buf = packet->buf;
                scat_req->scat_list[i].len = len;
 
@@ -402,7 +410,7 @@ static int htc_setup_send_scat_list(struct htc_target *target,
        }
 
        /* Roll back scatter setup in case of any failure */
-       if (status || (scat_req->scat_entries < HTC_MIN_HTC_MSGS_TO_BUNDLE)) {
+       if (scat_req->scat_entries < HTC_MIN_HTC_MSGS_TO_BUNDLE) {
                for (i = scat_req->scat_entries - 1; i >= 0; i--) {
                        packet = scat_req->scat_list[i].packet;
                        if (packet) {
@@ -410,31 +418,32 @@ static int htc_setup_send_scat_list(struct htc_target *target,
                                list_add(&packet->list, queue);
                        }
                }
-               return -EINVAL;
+               return -EAGAIN;
        }
 
-       return 0;
+       return status;
 }
 
 /*
- * htc_issue_send_bundle: drain a queue and send as bundles
- * this function may return without fully draining the queue
- * when
+ * Drain a queue and send as bundles this function may return without fully
+ * draining the queue when
  *
  *    1. scatter resources are exhausted
  *    2. a message that will consume a partial credit will stop the
  *    bundling process early
  *    3. we drop below the minimum number of messages for a bundle
  */
-static void htc_issue_send_bundle(struct htc_endpoint *endpoint,
-                                 struct list_head *queue,
-                                 int *sent_bundle, int *n_bundle_pkts)
+static void ath6kl_htc_tx_bundle(struct htc_endpoint *endpoint,
+                                struct list_head *queue,
+                                int *sent_bundle, int *n_bundle_pkts)
 {
        struct htc_target *target = endpoint->target;
        struct hif_scatter_req *scat_req = NULL;
        int n_scat, n_sent_bundle = 0, tot_pkts_bundle = 0;
+       int status;
 
        while (true) {
+               status = 0;
                n_scat = get_queue_depth(queue);
                n_scat = min(n_scat, target->msg_per_bndl_max);
 
@@ -457,8 +466,10 @@ static void htc_issue_send_bundle(struct htc_endpoint *endpoint,
                scat_req->len = 0;
                scat_req->scat_entries = 0;
 
-               if (htc_setup_send_scat_list(target, endpoint, scat_req,
-                                            n_scat, queue)) {
+               status = ath6kl_htc_tx_setup_scat_list(target, endpoint,
+                                                      scat_req, n_scat,
+                                                      queue);
+               if (status == -EAGAIN) {
                        hif_scatter_req_add(target->dev->ar, scat_req);
                        break;
                }
@@ -472,18 +483,21 @@ static void htc_issue_send_bundle(struct htc_endpoint *endpoint,
                           "send scatter total bytes: %d , entries: %d\n",
                           scat_req->len, scat_req->scat_entries);
                ath6kldev_submit_scat_req(target->dev, scat_req, false);
+
+               if (status)
+                       break;
        }
 
        *sent_bundle = n_sent_bundle;
        *n_bundle_pkts = tot_pkts_bundle;
-       ath6kl_dbg(ATH6KL_DBG_HTC_SEND, "htc_issue_send_bundle (sent:%d)\n",
-                  n_sent_bundle);
+       ath6kl_dbg(ATH6KL_DBG_HTC_SEND, "%s (sent:%d)\n",
+                  __func__, n_sent_bundle);
 
        return;
 }
 
-static void htc_tx_from_ep_txq(struct htc_target *target,
-                              struct htc_endpoint *endpoint)
+static void ath6kl_htc_tx_from_queue(struct htc_target *target,
+                                    struct htc_endpoint *endpoint)
 {
        struct list_head txq;
        struct htc_packet *packet;
@@ -511,7 +525,7 @@ static void htc_tx_from_ep_txq(struct htc_target *target,
                if (list_empty(&endpoint->txq))
                        break;
 
-               htc_tx_pkts_get(target, endpoint, &txq);
+               ath6kl_htc_tx_pkts_get(target, endpoint, &txq);
 
                if (list_empty(&txq))
                        break;
@@ -528,8 +542,8 @@ static void htc_tx_from_ep_txq(struct htc_target *target,
                            HTC_MIN_HTC_MSGS_TO_BUNDLE)) {
                                int temp1 = 0, temp2 = 0;
 
-                               htc_issue_send_bundle(endpoint, &txq,
-                                                     &temp1, &temp2);
+                               ath6kl_htc_tx_bundle(endpoint, &txq,
+                                                    &temp1, &temp2);
                                bundle_sent += temp1;
                                n_pkts_bundle += temp2;
                        }
@@ -541,9 +555,9 @@ static void htc_tx_from_ep_txq(struct htc_target *target,
                                                  list);
                        list_del(&packet->list);
 
-                       htc_prep_send_pkt(packet, packet->info.tx.flags,
-                                         0, packet->info.tx.seqno);
-                       htc_issue_send(target, packet);
+                       ath6kl_htc_tx_prep_pkt(packet, packet->info.tx.flags,
+                                              0, packet->info.tx.seqno);
+                       ath6kl_htc_tx_issue(target, packet);
                }
 
                spin_lock_bh(&target->tx_lock);
@@ -556,9 +570,9 @@ static void htc_tx_from_ep_txq(struct htc_target *target,
        spin_unlock_bh(&target->tx_lock);
 }
 
-static bool htc_try_send(struct htc_target *target,
-                        struct htc_endpoint *endpoint,
-                        struct htc_packet *tx_pkt)
+static bool ath6kl_htc_tx_try(struct htc_target *target,
+                             struct htc_endpoint *endpoint,
+                             struct htc_packet *tx_pkt)
 {
        struct htc_ep_callbacks ep_cb;
        int txq_depth;
@@ -594,7 +608,7 @@ static bool htc_try_send(struct htc_target *target,
        list_add_tail(&tx_pkt->list, &endpoint->txq);
        spin_unlock_bh(&target->tx_lock);
 
-       htc_tx_from_ep_txq(target, endpoint);
+       ath6kl_htc_tx_from_queue(target, endpoint);
 
        return true;
 }
@@ -628,7 +642,7 @@ static void htc_chk_ep_txq(struct htc_target *target)
                         * chance to reclaim credits from lower priority
                         * ones.
                         */
-                       htc_tx_from_ep_txq(target, endpoint);
+                       ath6kl_htc_tx_from_queue(target, endpoint);
                        spin_lock_bh(&target->tx_lock);
                }
                spin_unlock_bh(&target->tx_lock);
@@ -680,8 +694,8 @@ static int htc_setup_tx_complete(struct htc_target *target)
 
        /* we want synchronous operation */
        send_pkt->completion = NULL;
-       htc_prep_send_pkt(send_pkt, 0, 0, 0);
-       status = htc_issue_send(target, send_pkt);
+       ath6kl_htc_tx_prep_pkt(send_pkt, 0, 0, 0);
+       status = ath6kl_htc_tx_issue(target, send_pkt);
 
        if (send_pkt != NULL)
                htc_reclaim_txctrl_buf(target, send_pkt);
@@ -733,7 +747,7 @@ int ath6kl_htc_tx(struct htc_target *target, struct htc_packet *packet)
 
        endpoint = &target->endpoint[packet->endpoint];
 
-       if (!htc_try_send(target, endpoint, packet)) {
+       if (!ath6kl_htc_tx_try(target, endpoint, packet)) {
                packet->status = (target->htc_flags & HTC_OP_STATE_STOPPING) ?
                                 -ECANCELED : -ENOSPC;
                INIT_LIST_HEAD(&queue);
@@ -846,8 +860,8 @@ void ath6kl_htc_indicate_activity_change(struct htc_target *target,
 
 /* HTC Rx */
 
-static inline void htc_update_rx_stats(struct htc_endpoint *endpoint,
-                                      int n_look_ahds)
+static inline void ath6kl_htc_rx_update_stats(struct htc_endpoint *endpoint,
+                                             int n_look_ahds)
 {
        endpoint->ep_st.rx_pkts++;
        if (n_look_ahds == 1)
@@ -894,8 +908,9 @@ static void reclaim_rx_ctrl_buf(struct htc_target *target,
        spin_unlock_bh(&target->htc_lock);
 }
 
-static int dev_rx_pkt(struct htc_target *target, struct htc_packet *packet,
-                     u32 rx_len)
+static int ath6kl_htc_rx_packet(struct htc_target *target,
+                               struct htc_packet *packet,
+                               u32 rx_len)
 {
        struct ath6kl_device *dev = target->dev;
        u32 padded_len;
@@ -929,9 +944,9 @@ static int dev_rx_pkt(struct htc_target *target, struct htc_packet *packet,
  * "hint" that there are more  single-packets to fetch
  * on this endpoint.
  */
-static void set_rxpkt_indication_flag(u32 lk_ahd,
-                                     struct htc_endpoint *endpoint,
-                                     struct htc_packet *packet)
+static void ath6kl_htc_rx_set_indicate(u32 lk_ahd,
+                                      struct htc_endpoint *endpoint,
+                                      struct htc_packet *packet)
 {
        struct htc_frame_hdr *htc_hdr = (struct htc_frame_hdr *)&lk_ahd;
 
@@ -942,7 +957,7 @@ static void set_rxpkt_indication_flag(u32 lk_ahd,
        }
 }
 
-static void chk_rx_water_mark(struct htc_endpoint *endpoint)
+static void ath6kl_htc_rx_chk_water_mark(struct htc_endpoint *endpoint)
 {
        struct htc_ep_callbacks ep_cb = endpoint->ep_cb;
 
@@ -959,8 +974,9 @@ static void chk_rx_water_mark(struct htc_endpoint *endpoint)
 }
 
 /* This function is called with rx_lock held */
-static int htc_setup_rxpkts(struct htc_target *target, struct htc_endpoint *ep,
-                           u32 *lk_ahds, struct list_head *queue, int n_msg)
+static int ath6kl_htc_rx_setup(struct htc_target *target,
+                              struct htc_endpoint *ep,
+                              u32 *lk_ahds, struct list_head *queue, int n_msg)
 {
        struct htc_packet *packet;
        /* FIXME: type of lk_ahds can't be right */
@@ -1060,10 +1076,10 @@ static int htc_setup_rxpkts(struct htc_target *target, struct htc_endpoint *ep,
        return status;
 }
 
-static int alloc_and_prep_rxpkts(struct htc_target *target,
-                                u32 lk_ahds[], int msg,
-                                struct htc_endpoint *endpoint,
-                                struct list_head *queue)
+static int ath6kl_htc_rx_alloc(struct htc_target *target,
+                              u32 lk_ahds[], int msg,
+                              struct htc_endpoint *endpoint,
+                              struct list_head *queue)
 {
        int status = 0;
        struct htc_packet *packet, *tmp_pkt;
@@ -1129,8 +1145,8 @@ static int alloc_and_prep_rxpkts(struct htc_target *target,
                        n_msg = 1;
 
                /* Setup packet buffers for each message */
-               status = htc_setup_rxpkts(target, endpoint, &lk_ahds[i], queue,
-                                         n_msg);
+               status = ath6kl_htc_rx_setup(target, endpoint, &lk_ahds[i],
+                                            queue, n_msg);
 
                /*
                 * This is due to unavailabilty of buffers to rx entire data.
@@ -1176,9 +1192,9 @@ static void htc_ctrl_rx(struct htc_target *context, struct htc_packet *packets)
                        packets->act_len + HTC_HDR_LENGTH);
 
                ath6kl_dbg_dump(ATH6KL_DBG_RAW_BYTES,
-                            "Unexpected ENDPOINT 0 Message",
-                            packets->buf - HTC_HDR_LENGTH,
-                            packets->act_len + HTC_HDR_LENGTH);
+                               "Unexpected ENDPOINT 0 Message", "",
+                               packets->buf - HTC_HDR_LENGTH,
+                               packets->act_len + HTC_HDR_LENGTH);
        }
 
        htc_reclaim_rxbuf(context, packets, &context->endpoint[0]);
@@ -1312,7 +1328,7 @@ static int htc_parse_trailer(struct htc_target *target,
                        memcpy((u8 *)&next_lk_ahds[0], lk_ahd->lk_ahd, 4);
 
                        ath6kl_dbg_dump(ATH6KL_DBG_RAW_BYTES, "Next Look Ahead",
-                                       next_lk_ahds, 4);
+                                       "", next_lk_ahds, 4);
 
                        *n_lk_ahds = 1;
                }
@@ -1331,7 +1347,7 @@ static int htc_parse_trailer(struct htc_target *target,
                                (struct htc_bundle_lkahd_rpt *) record_buf;
 
                        ath6kl_dbg_dump(ATH6KL_DBG_RAW_BYTES, "Bundle lk_ahd",
-                                       record_buf, record->len);
+                                       "", record_buf, record->len);
 
                        for (i = 0; i < len; i++) {
                                memcpy((u8 *)&next_lk_ahds[i],
@@ -1364,7 +1380,8 @@ static int htc_proc_trailer(struct htc_target *target,
 
        ath6kl_dbg(ATH6KL_DBG_HTC_RECV, "+htc_proc_trailer (len:%d)\n", len);
 
-       ath6kl_dbg_dump(ATH6KL_DBG_RAW_BYTES, "Recv Trailer", buf, len);
+       ath6kl_dbg_dump(ATH6KL_DBG_RAW_BYTES, "Recv Trailer", "",
+                       buf, len);
 
        orig_buf = buf;
        orig_len = len;
@@ -1402,14 +1419,14 @@ static int htc_proc_trailer(struct htc_target *target,
 
        if (status)
                ath6kl_dbg_dump(ATH6KL_DBG_RAW_BYTES, "BAD Recv Trailer",
-                               orig_buf, orig_len);
+                               "", orig_buf, orig_len);
 
        return status;
 }
 
-static int htc_proc_rxhdr(struct htc_target *target,
-                         struct htc_packet *packet,
-                         u32 *next_lkahds, int *n_lkahds)
+static int ath6kl_htc_rx_process_hdr(struct htc_target *target,
+                                    struct htc_packet *packet,
+                                    u32 *next_lkahds, int *n_lkahds)
 {
        int status = 0;
        u16 payload_len;
@@ -1419,8 +1436,8 @@ static int htc_proc_rxhdr(struct htc_target *target,
        if (n_lkahds != NULL)
                *n_lkahds = 0;
 
-       ath6kl_dbg_dump(ATH6KL_DBG_RAW_BYTES, "HTC Recv PKT", packet->buf,
-                       packet->act_len);
+       ath6kl_dbg_dump(ATH6KL_DBG_RAW_BYTES, "HTC Recv PKT", "htc ",
+                       packet->buf, packet->act_len);
 
        /*
         * NOTE: we cannot assume the alignment of buf, so we use the safe
@@ -1461,12 +1478,12 @@ static int htc_proc_rxhdr(struct htc_target *target,
        }
 
        if (lk_ahd != packet->info.rx.exp_hdr) {
-               ath6kl_err("htc_proc_rxhdr, lk_ahd mismatch! (pPkt:0x%p flags:0x%X)\n",
-                          packet, packet->info.rx.rx_flags);
+               ath6kl_err("%s(): lk_ahd mismatch! (pPkt:0x%p flags:0x%X)\n",
+                          __func__, packet, packet->info.rx.rx_flags);
                ath6kl_dbg_dump(ATH6KL_DBG_RAW_BYTES, "Expected Message lk_ahd",
-                               &packet->info.rx.exp_hdr, 4);
+                               "", &packet->info.rx.exp_hdr, 4);
                ath6kl_dbg_dump(ATH6KL_DBG_RAW_BYTES, "Current Frame Header",
-                               (u8 *)&lk_ahd, sizeof(lk_ahd));
+                               "", (u8 *)&lk_ahd, sizeof(lk_ahd));
                status = -ENOMEM;
                goto fail_rx;
        }
@@ -1474,8 +1491,8 @@ static int htc_proc_rxhdr(struct htc_target *target,
        if (htc_hdr->flags & HTC_FLG_RX_TRAILER) {
                if (htc_hdr->ctrl[0] < sizeof(struct htc_record_hdr) ||
                    htc_hdr->ctrl[0] > payload_len) {
-                       ath6kl_err("htc_proc_rxhdr, invalid hdr (payload len should be :%d, CB[0] is:%d)\n",
-                                  payload_len, htc_hdr->ctrl[0]);
+                       ath6kl_err("%s(): invalid hdr (payload len should be :%d, CB[0] is:%d)\n",
+                                  __func__, payload_len, htc_hdr->ctrl[0]);
                        status = -ENOMEM;
                        goto fail_rx;
                }
@@ -1502,20 +1519,20 @@ static int htc_proc_rxhdr(struct htc_target *target,
 fail_rx:
        if (status)
                ath6kl_dbg_dump(ATH6KL_DBG_RAW_BYTES, "BAD HTC Recv PKT",
-                               packet->buf,
+                               "", packet->buf,
                                packet->act_len < 256 ? packet->act_len : 256);
        else {
                if (packet->act_len > 0)
                        ath6kl_dbg_dump(ATH6KL_DBG_RAW_BYTES,
-                                       "HTC - Application Msg",
+                                       "HTC - Application Msg", "",
                                        packet->buf, packet->act_len);
        }
 
        return status;
 }
 
-static void do_rx_completion(struct htc_endpoint *endpoint,
-                            struct htc_packet *packet)
+static void ath6kl_htc_rx_complete(struct htc_endpoint *endpoint,
+                                  struct htc_packet *packet)
 {
                ath6kl_dbg(ATH6KL_DBG_HTC_RECV,
                           "htc calling ep %d recv callback on packet 0x%p\n",
@@ -1523,10 +1540,10 @@ static void do_rx_completion(struct htc_endpoint *endpoint,
                endpoint->ep_cb.rx(endpoint->target, packet);
 }
 
-static int htc_issue_rxpkt_bundle(struct htc_target *target,
-                                 struct list_head *rxq,
-                                 struct list_head *sync_compq,
-                                 int *n_pkt_fetched, bool part_bundle)
+static int ath6kl_htc_rx_bundle(struct htc_target *target,
+                               struct list_head *rxq,
+                               struct list_head *sync_compq,
+                               int *n_pkt_fetched, bool part_bundle)
 {
        struct hif_scatter_req *scat_req;
        struct htc_packet *packet;
@@ -1548,15 +1565,15 @@ static int htc_issue_rxpkt_bundle(struct htc_target *target,
                 * This would only happen if the target ignored our max
                 * bundle limit.
                 */
-               ath6kl_warn("htc_issue_rxpkt_bundle : partial bundle detected num:%d , %d\n",
-                           get_queue_depth(rxq), n_scat_pkt);
+               ath6kl_warn("%s(): partial bundle detected num:%d , %d\n",
+                           __func__, get_queue_depth(rxq), n_scat_pkt);
        }
 
        len = 0;
 
        ath6kl_dbg(ATH6KL_DBG_HTC_RECV,
-               "htc_issue_rxpkt_bundle (numpackets: %d , actual : %d)\n",
-               get_queue_depth(rxq), n_scat_pkt);
+                  "%s(): (numpackets: %d , actual : %d)\n",
+                  __func__, get_queue_depth(rxq), n_scat_pkt);
 
        scat_req = hif_scatter_req_get(target->dev->ar);
 
@@ -1616,9 +1633,10 @@ fail_rx_pkt:
        return status;
 }
 
-static int htc_proc_fetched_rxpkts(struct htc_target *target,
-                                  struct list_head *comp_pktq, u32 lk_ahds[],
-                                  int *n_lk_ahd)
+static int ath6kl_htc_rx_process_packets(struct htc_target *target,
+                                        struct list_head *comp_pktq,
+                                        u32 lk_ahds[],
+                                        int *n_lk_ahd)
 {
        struct htc_packet *packet, *tmp_pkt;
        struct htc_endpoint *ep;
@@ -1629,7 +1647,8 @@ static int htc_proc_fetched_rxpkts(struct htc_target *target,
                ep = &target->endpoint[packet->endpoint];
 
                /* process header for each of the recv packet */
-               status = htc_proc_rxhdr(target, packet, lk_ahds, n_lk_ahd);
+               status = ath6kl_htc_rx_process_hdr(target, packet, lk_ahds,
+                                                  n_lk_ahd);
                if (status)
                        return status;
 
@@ -1639,8 +1658,8 @@ static int htc_proc_fetched_rxpkts(struct htc_target *target,
                         * based on the lookahead.
                         */
                        if (*n_lk_ahd > 0)
-                               set_rxpkt_indication_flag(lk_ahds[0],
-                                                         ep, packet);
+                               ath6kl_htc_rx_set_indicate(lk_ahds[0],
+                                                          ep, packet);
                } else
                        /*
                         * Packets in a bundle automatically have
@@ -1649,20 +1668,20 @@ static int htc_proc_fetched_rxpkts(struct htc_target *target,
                        packet->info.rx.indicat_flags |=
                                HTC_RX_FLAGS_INDICATE_MORE_PKTS;
 
-               htc_update_rx_stats(ep, *n_lk_ahd);
+               ath6kl_htc_rx_update_stats(ep, *n_lk_ahd);
 
                if (packet->info.rx.rx_flags & HTC_RX_PKT_PART_OF_BUNDLE)
                        ep->ep_st.rx_bundl += 1;
 
-               do_rx_completion(ep, packet);
+               ath6kl_htc_rx_complete(ep, packet);
        }
 
        return status;
 }
 
-static int htc_fetch_rxpkts(struct htc_target *target,
-                           struct list_head *rx_pktq,
-                           struct list_head *comp_pktq)
+static int ath6kl_htc_rx_fetch(struct htc_target *target,
+                              struct list_head *rx_pktq,
+                              struct list_head *comp_pktq)
 {
        int fetched_pkts;
        bool part_bundle = false;
@@ -1678,10 +1697,10 @@ static int htc_fetch_rxpkts(struct htc_target *target,
                         * bundle transfer and recv bundling is
                         * allowed.
                         */
-                       status = htc_issue_rxpkt_bundle(target, rx_pktq,
-                                                       comp_pktq,
-                                                       &fetched_pkts,
-                                                       part_bundle);
+                       status = ath6kl_htc_rx_bundle(target, rx_pktq,
+                                                     comp_pktq,
+                                                     &fetched_pkts,
+                                                     part_bundle);
                        if (status)
                                return status;
 
@@ -1710,7 +1729,8 @@ static int htc_fetch_rxpkts(struct htc_target *target,
                                        HTC_RX_PKT_IGNORE_LOOKAHEAD;
 
                        /* go fetch the packet */
-                       status = dev_rx_pkt(target, packet, packet->act_len);
+                       status = ath6kl_htc_rx_packet(target, packet,
+                                                     packet->act_len);
                        if (status)
                                return status;
 
@@ -1764,9 +1784,9 @@ int ath6kl_htc_rxmsg_pending_handler(struct htc_target *target,
                 * Try to allocate as many HTC RX packets indicated by the
                 * look_aheads.
                 */
-               status = alloc_and_prep_rxpkts(target, look_aheads,
-                                              num_look_ahead, endpoint,
-                                              &rx_pktq);
+               status = ath6kl_htc_rx_alloc(target, look_aheads,
+                                            num_look_ahead, endpoint,
+                                            &rx_pktq);
                if (status)
                        break;
 
@@ -1781,14 +1801,15 @@ int ath6kl_htc_rxmsg_pending_handler(struct htc_target *target,
 
                num_look_ahead = 0;
 
-               status = htc_fetch_rxpkts(target, &rx_pktq, &comp_pktq);
+               status = ath6kl_htc_rx_fetch(target, &rx_pktq, &comp_pktq);
 
                if (!status)
-                       chk_rx_water_mark(endpoint);
+                       ath6kl_htc_rx_chk_water_mark(endpoint);
 
                /* Process fetched packets */
-               status = htc_proc_fetched_rxpkts(target, &comp_pktq,
-                                                look_aheads, &num_look_ahead);
+               status = ath6kl_htc_rx_process_packets(target, &comp_pktq,
+                                                      look_aheads,
+                                                      &num_look_ahead);
 
                if (!num_look_ahead || status)
                        break;
@@ -1881,14 +1902,14 @@ static struct htc_packet *htc_wait_for_ctrl_msg(struct htc_target *target)
        packet->completion = NULL;
 
        /* get the message from the device, this will block */
-       if (dev_rx_pkt(target, packet, packet->act_len))
+       if (ath6kl_htc_rx_packet(target, packet, packet->act_len))
                goto fail_ctrl_rx;
 
        /* process receive header */
-       packet->status = htc_proc_rxhdr(target, packet, NULL, NULL);
+       packet->status = ath6kl_htc_rx_process_hdr(target, packet, NULL, NULL);
 
        if (packet->status) {
-               ath6kl_err("htc_wait_for_ctrl_msg, htc_proc_rxhdr failed (status = %d)\n",
+               ath6kl_err("htc_wait_for_ctrl_msg, ath6kl_htc_rx_process_hdr failed (status = %d)\n",
                           packet->status);
                goto fail_ctrl_rx;
        }
@@ -1935,7 +1956,7 @@ int ath6kl_htc_add_rxbuf_multiple(struct htc_target *target,
                list_for_each_entry_safe(packet, tmp_pkt, pkt_queue, list) {
                        packet->status = -ECANCELED;
                        list_del(&packet->list);
-                       do_rx_completion(endpoint, packet);
+                       ath6kl_htc_rx_complete(endpoint, packet);
                }
 
                return status;
@@ -2034,8 +2055,8 @@ int ath6kl_htc_conn_service(struct htc_target *target,
 
                /* we want synchronous operation */
                tx_pkt->completion = NULL;
-               htc_prep_send_pkt(tx_pkt, 0, 0, 0);
-               status = htc_issue_send(target, tx_pkt);
+               ath6kl_htc_tx_prep_pkt(tx_pkt, 0, 0, 0);
+               status = ath6kl_htc_tx_issue(target, tx_pkt);
 
                if (status)
                        goto fail_tx;
index 9d10322eac41abb1143ce52bc5e8d1799cd67206..c1d2366704b5d89ad4dbad2b2a90629e10e6c849 100644 (file)
@@ -15,6 +15,8 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
+#include <linux/moduleparam.h>
+#include <linux/of.h>
 #include <linux/mmc/sdio_func.h>
 #include "core.h"
 #include "cfg80211.h"
 #include "hif-ops.h"
 
 unsigned int debug_mask;
+static unsigned int testmode;
 
 module_param(debug_mask, uint, 0644);
+module_param(testmode, uint, 0644);
 
 /*
  * Include definitions here that can be used to tune the WLAN module
@@ -53,12 +57,6 @@ module_param(debug_mask, uint, 0644);
 
 #define CONFIG_AR600x_DEBUG_UART_TX_PIN 8
 
-enum addr_type {
-       DATASET_PATCH_ADDR,
-       APP_LOAD_ADDR,
-       APP_START_OVERRIDE_ADDR,
-};
-
 #define ATH6KL_DATA_OFFSET    64
 struct sk_buff *ath6kl_buf_alloc(int size)
 {
@@ -67,7 +65,7 @@ struct sk_buff *ath6kl_buf_alloc(int size)
 
        /* Add chacheline space at front and back of buffer */
        reserved = (2 * L1_CACHE_BYTES) + ATH6KL_DATA_OFFSET +
-                  sizeof(struct htc_packet);
+                  sizeof(struct htc_packet) + ATH6KL_HTC_ALIGN_BYTES;
        skb = dev_alloc_skb(size + reserved);
 
        if (skb)
@@ -85,7 +83,7 @@ void ath6kl_init_profile_info(struct ath6kl *ar)
        ar->prwise_crypto = NONE_CRYPT;
        ar->prwise_crypto_len = 0;
        ar->grp_crypto = NONE_CRYPT;
-       ar->grp_crpto_len = 0;
+       ar->grp_crypto_len = 0;
        memset(ar->wep_key_list, 0, sizeof(ar->wep_key_list));
        memset(ar->req_bssid, 0, sizeof(ar->req_bssid));
        memset(ar->bssid, 0, sizeof(ar->bssid));
@@ -108,17 +106,6 @@ static u8 ath6kl_get_fw_iftype(struct ath6kl *ar)
        }
 }
 
-static inline u32 ath6kl_get_hi_item_addr(struct ath6kl *ar,
-                                         u32 item_offset)
-{
-       u32 addr = 0;
-
-       if (ar->target_type == TARGET_TYPE_AR6003)
-               addr = ATH6KL_HI_START_ADDR + item_offset;
-
-       return addr;
-}
-
 static int ath6kl_set_host_app_area(struct ath6kl *ar)
 {
        u32 address, data;
@@ -127,16 +114,15 @@ static int ath6kl_set_host_app_area(struct ath6kl *ar)
        /* Fetch the address of the host_app_area_s
         * instance in the host interest area */
        address = ath6kl_get_hi_item_addr(ar, HI_ITEM(hi_app_host_interest));
-       address = TARG_VTOP(address);
+       address = TARG_VTOP(ar->target_type, address);
 
-       if (ath6kl_read_reg_diag(ar, &address, &data))
+       if (ath6kl_diag_read32(ar, address, &data))
                return -EIO;
 
-       address = TARG_VTOP(data);
+       address = TARG_VTOP(ar->target_type, data);
        host_app_area.wmi_protocol_ver = WMI_PROTOCOL_VERSION;
-       if (ath6kl_access_datadiag(ar, address,
-                               (u8 *)&host_app_area,
-                               sizeof(struct host_app_area), false))
+       if (ath6kl_diag_write(ar, address, (u8 *) &host_app_area,
+                             sizeof(struct host_app_area)))
                return -EIO;
 
        return 0;
@@ -290,6 +276,7 @@ static void ath6kl_init_control_info(struct ath6kl *ar)
        memset(&ar->sc_params, 0, sizeof(ar->sc_params));
        ar->sc_params.short_scan_ratio = WMI_SHORTSCANRATIO_DEFAULT;
        ar->sc_params.scan_ctrl_flags = DEFAULT_SCAN_CTRL_FLAGS;
+       ar->lrssi_roam_threshold = DEF_LRSSI_ROAM_THRESHOLD;
 
        memset((u8 *)ar->sta_list, 0,
               AP_MAX_NUM_STA * sizeof(struct ath6kl_sta));
@@ -370,10 +357,10 @@ static void ath6kl_dump_target_assert_info(struct ath6kl *ar)
 
        /* the reg dump pointer is copied to the host interest area */
        address = ath6kl_get_hi_item_addr(ar, HI_ITEM(hi_failure_state));
-       address = TARG_VTOP(address);
+       address = TARG_VTOP(ar->target_type, address);
 
        /* read RAM location through diagnostic window */
-       status = ath6kl_read_reg_diag(ar, &address, &regdump_loc);
+       status = ath6kl_diag_read32(ar, address, &regdump_loc);
 
        if (status || !regdump_loc) {
                ath6kl_err("failed to get ptr to register dump area\n");
@@ -382,15 +369,11 @@ static void ath6kl_dump_target_assert_info(struct ath6kl *ar)
 
        ath6kl_dbg(ATH6KL_DBG_TRC, "location of register dump data: 0x%X\n",
                regdump_loc);
-
-       regdump_loc = TARG_VTOP(regdump_loc);
+       regdump_loc = TARG_VTOP(ar->target_type, regdump_loc);
 
        /* fetch register dump data */
-       status = ath6kl_access_datadiag(ar,
-                                       regdump_loc,
-                                       (u8 *)&regdump_val[0],
-                                       REG_DUMP_COUNT_AR6003 * (sizeof(u32)),
-                                       true);
+       status = ath6kl_diag_read(ar, regdump_loc, (u8 *)&regdump_val[0],
+                                 REG_DUMP_COUNT_AR6003 * (sizeof(u32)));
 
        if (status) {
                ath6kl_err("failed to get register dump\n");
@@ -416,6 +399,7 @@ void ath6kl_target_failure(struct ath6kl *ar)
 static int ath6kl_target_config_wlan_params(struct ath6kl *ar)
 {
        int status = 0;
+       int ret;
 
        /*
         * Configure the device for rx dot11 header rules. "0,0" are the
@@ -460,6 +444,28 @@ static int ath6kl_target_config_wlan_params(struct ath6kl *ar)
                        status = -EIO;
                }
 
+       if (ar->p2p) {
+               ret = ath6kl_wmi_info_req_cmd(ar->wmi,
+                                             P2P_FLAG_CAPABILITIES_REQ |
+                                             P2P_FLAG_MACADDR_REQ |
+                                             P2P_FLAG_HMODEL_REQ);
+               if (ret) {
+                       ath6kl_dbg(ATH6KL_DBG_TRC, "failed to request P2P "
+                                  "capabilities (%d) - assuming P2P not "
+                                  "supported\n", ret);
+                       ar->p2p = 0;
+               }
+       }
+
+       if (ar->p2p) {
+               /* Enable Probe Request reporting for P2P */
+               ret = ath6kl_wmi_probe_report_req_cmd(ar->wmi, true);
+               if (ret) {
+                       ath6kl_dbg(ATH6KL_DBG_TRC, "failed to enable Probe "
+                                  "Request reporting (%d)\n", ret);
+               }
+       }
+
        return status;
 }
 
@@ -495,6 +501,10 @@ int ath6kl_configure_target(struct ath6kl *ar)
 
        param |= (1 << HI_OPTION_NUM_DEV_SHIFT);
        param |= (fw_iftype << HI_OPTION_FW_MODE_SHIFT);
+       if (ar->p2p && fw_iftype == HI_OPTION_FW_MODE_BSS_STA) {
+               param |= HI_OPTION_FW_SUBMODE_P2PDEV <<
+                       HI_OPTION_FW_SUBMODE_SHIFT;
+       }
        param |= (0 << HI_OPTION_MAC_ADDR_METHOD_SHIFT);
        param |= (0 << HI_OPTION_FW_BRIDGE_SHIFT);
 
@@ -518,29 +528,21 @@ int ath6kl_configure_target(struct ath6kl *ar)
         * but possible in theory.
         */
 
-       if (ar->target_type == TARGET_TYPE_AR6003) {
-               if (ar->version.target_ver == AR6003_REV2_VERSION) {
-                       param = AR6003_REV2_BOARD_EXT_DATA_ADDRESS;
-                       ram_reserved_size =  AR6003_REV2_RAM_RESERVE_SIZE;
-               } else {
-                       param = AR6003_REV3_BOARD_EXT_DATA_ADDRESS;
-                       ram_reserved_size =  AR6003_REV3_RAM_RESERVE_SIZE;
-               }
+       param = ar->hw.board_ext_data_addr;
+       ram_reserved_size = ar->hw.reserved_ram_size;
 
-               if (ath6kl_bmi_write(ar,
-                                    ath6kl_get_hi_item_addr(ar,
-                                    HI_ITEM(hi_board_ext_data)),
-                                    (u8 *)&param, 4) != 0) {
-                       ath6kl_err("bmi_write_memory for hi_board_ext_data failed\n");
-                       return -EIO;
-               }
-               if (ath6kl_bmi_write(ar,
-                                    ath6kl_get_hi_item_addr(ar,
-                                    HI_ITEM(hi_end_ram_reserve_sz)),
-                                    (u8 *)&ram_reserved_size, 4) != 0) {
-                       ath6kl_err("bmi_write_memory for hi_end_ram_reserve_sz failed\n");
-                       return -EIO;
-               }
+       if (ath6kl_bmi_write(ar, ath6kl_get_hi_item_addr(ar,
+                                       HI_ITEM(hi_board_ext_data)),
+                            (u8 *)&param, 4) != 0) {
+               ath6kl_err("bmi_write_memory for hi_board_ext_data failed\n");
+               return -EIO;
+       }
+
+       if (ath6kl_bmi_write(ar, ath6kl_get_hi_item_addr(ar,
+                                       HI_ITEM(hi_end_ram_reserve_sz)),
+                            (u8 *)&ram_reserved_size, 4) != 0) {
+               ath6kl_err("bmi_write_memory for hi_end_ram_reserve_sz failed\n");
+               return -EIO;
        }
 
        /* set the block size for the target */
@@ -568,6 +570,12 @@ struct ath6kl *ath6kl_core_alloc(struct device *sdev)
        ar->wdev = wdev;
        wdev->iftype = NL80211_IFTYPE_STATION;
 
+       if (ath6kl_debug_init(ar)) {
+               ath6kl_err("Failed to initialize debugfs\n");
+               ath6kl_cfg80211_deinit(ar);
+               return NULL;
+       }
+
        dev = alloc_netdev(0, "wlan%d", ether_setup);
        if (!dev) {
                ath6kl_err("no memory for network device instance\n");
@@ -579,7 +587,6 @@ struct ath6kl *ath6kl_core_alloc(struct device *sdev)
        SET_NETDEV_DEV(dev, wiphy_dev(wdev->wiphy));
        wdev->netdev = dev;
        ar->sme_state = SME_DISCONNECTED;
-       ar->auto_auth_stage = AUTH_IDLE;
 
        init_netdev(dev);
 
@@ -611,29 +618,6 @@ int ath6kl_unavail_ev(struct ath6kl *ar)
 }
 
 /* firmware upload */
-static u32 ath6kl_get_load_address(u32 target_ver, enum addr_type type)
-{
-       WARN_ON(target_ver != AR6003_REV2_VERSION &&
-               target_ver != AR6003_REV3_VERSION);
-
-       switch (type) {
-       case DATASET_PATCH_ADDR:
-               return (target_ver == AR6003_REV2_VERSION) ?
-                       AR6003_REV2_DATASET_PATCH_ADDRESS :
-                       AR6003_REV3_DATASET_PATCH_ADDRESS;
-       case APP_LOAD_ADDR:
-               return (target_ver == AR6003_REV2_VERSION) ?
-                       AR6003_REV2_APP_LOAD_ADDRESS :
-                       0x1234;
-       case APP_START_OVERRIDE_ADDR:
-               return (target_ver == AR6003_REV2_VERSION) ?
-                       AR6003_REV2_APP_START_OVERRIDE :
-                       AR6003_REV3_APP_START_OVERRIDE;
-       default:
-               return 0;
-       }
-}
-
 static int ath6kl_get_fw(struct ath6kl *ar, const char *filename,
                         u8 **fw, size_t *fw_len)
 {
@@ -655,15 +639,79 @@ static int ath6kl_get_fw(struct ath6kl *ar, const char *filename,
        return ret;
 }
 
+#ifdef CONFIG_OF
+static const char *get_target_ver_dir(const struct ath6kl *ar)
+{
+       switch (ar->version.target_ver) {
+       case AR6003_REV1_VERSION:
+               return "ath6k/AR6003/hw1.0";
+       case AR6003_REV2_VERSION:
+               return "ath6k/AR6003/hw2.0";
+       case AR6003_REV3_VERSION:
+               return "ath6k/AR6003/hw2.1.1";
+       }
+       ath6kl_warn("%s: unsupported target version 0x%x.\n", __func__,
+                   ar->version.target_ver);
+       return NULL;
+}
+
+/*
+ * Check the device tree for a board-id and use it to construct
+ * the pathname to the firmware file.  Used (for now) to find a
+ * fallback to the "bdata.bin" file--typically a symlink to the
+ * appropriate board-specific file.
+ */
+static bool check_device_tree(struct ath6kl *ar)
+{
+       static const char *board_id_prop = "atheros,board-id";
+       struct device_node *node;
+       char board_filename[64];
+       const char *board_id;
+       int ret;
+
+       for_each_compatible_node(node, NULL, "atheros,ath6kl") {
+               board_id = of_get_property(node, board_id_prop, NULL);
+               if (board_id == NULL) {
+                       ath6kl_warn("No \"%s\" property on %s node.\n",
+                                   board_id_prop, node->name);
+                       continue;
+               }
+               snprintf(board_filename, sizeof(board_filename),
+                        "%s/bdata.%s.bin", get_target_ver_dir(ar), board_id);
+
+               ret = ath6kl_get_fw(ar, board_filename, &ar->fw_board,
+                                   &ar->fw_board_len);
+               if (ret) {
+                       ath6kl_err("Failed to get DT board file %s: %d\n",
+                                  board_filename, ret);
+                       continue;
+               }
+               return true;
+       }
+       return false;
+}
+#else
+static bool check_device_tree(struct ath6kl *ar)
+{
+       return false;
+}
+#endif /* CONFIG_OF */
+
 static int ath6kl_fetch_board_file(struct ath6kl *ar)
 {
        const char *filename;
        int ret;
 
+       if (ar->fw_board != NULL)
+               return 0;
+
        switch (ar->version.target_ver) {
        case AR6003_REV2_VERSION:
                filename = AR6003_REV2_BOARD_DATA_FILE;
                break;
+       case AR6004_REV1_VERSION:
+               filename = AR6004_REV1_BOARD_DATA_FILE;
+               break;
        default:
                filename = AR6003_REV3_BOARD_DATA_FILE;
                break;
@@ -676,6 +724,11 @@ static int ath6kl_fetch_board_file(struct ath6kl *ar)
                return 0;
        }
 
+       if (check_device_tree(ar)) {
+               /* got board file from device tree */
+               return 0;
+       }
+
        /* there was no proper board file, try to use default instead */
        ath6kl_warn("Failed to get board file %s (%d), trying to find default board file.\n",
                    filename, ret);
@@ -684,6 +737,9 @@ static int ath6kl_fetch_board_file(struct ath6kl *ar)
        case AR6003_REV2_VERSION:
                filename = AR6003_REV2_DEFAULT_BOARD_DATA_FILE;
                break;
+       case AR6004_REV1_VERSION:
+               filename = AR6004_REV1_DEFAULT_BOARD_DATA_FILE;
+               break;
        default:
                filename = AR6003_REV3_DEFAULT_BOARD_DATA_FILE;
                break;
@@ -703,25 +759,346 @@ static int ath6kl_fetch_board_file(struct ath6kl *ar)
        return 0;
 }
 
+static int ath6kl_fetch_otp_file(struct ath6kl *ar)
+{
+       const char *filename;
+       int ret;
 
-static int ath6kl_upload_board_file(struct ath6kl *ar)
+       if (ar->fw_otp != NULL)
+               return 0;
+
+       switch (ar->version.target_ver) {
+       case AR6003_REV2_VERSION:
+               filename = AR6003_REV2_OTP_FILE;
+               break;
+       case AR6004_REV1_VERSION:
+               ath6kl_dbg(ATH6KL_DBG_TRC, "AR6004 doesn't need OTP file\n");
+               return 0;
+               break;
+       default:
+               filename = AR6003_REV3_OTP_FILE;
+               break;
+       }
+
+       ret = ath6kl_get_fw(ar, filename, &ar->fw_otp,
+                           &ar->fw_otp_len);
+       if (ret) {
+               ath6kl_err("Failed to get OTP file %s: %d\n",
+                          filename, ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+static int ath6kl_fetch_fw_file(struct ath6kl *ar)
 {
-       u32 board_address, board_ext_address, param;
+       const char *filename;
+       int ret;
+
+       if (ar->fw != NULL)
+               return 0;
+
+       if (testmode) {
+               switch (ar->version.target_ver) {
+               case AR6003_REV2_VERSION:
+                       filename = AR6003_REV2_TCMD_FIRMWARE_FILE;
+                       break;
+               case AR6003_REV3_VERSION:
+                       filename = AR6003_REV3_TCMD_FIRMWARE_FILE;
+                       break;
+               case AR6004_REV1_VERSION:
+                       ath6kl_warn("testmode not supported with ar6004\n");
+                       return -EOPNOTSUPP;
+               default:
+                       ath6kl_warn("unknown target version: 0x%x\n",
+                                      ar->version.target_ver);
+                       return -EINVAL;
+               }
+
+               set_bit(TESTMODE, &ar->flag);
+
+               goto get_fw;
+       }
+
+       switch (ar->version.target_ver) {
+       case AR6003_REV2_VERSION:
+               filename = AR6003_REV2_FIRMWARE_FILE;
+               break;
+       case AR6004_REV1_VERSION:
+               filename = AR6004_REV1_FIRMWARE_FILE;
+               break;
+       default:
+               filename = AR6003_REV3_FIRMWARE_FILE;
+               break;
+       }
+
+get_fw:
+       ret = ath6kl_get_fw(ar, filename, &ar->fw, &ar->fw_len);
+       if (ret) {
+               ath6kl_err("Failed to get firmware file %s: %d\n",
+                          filename, ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+static int ath6kl_fetch_patch_file(struct ath6kl *ar)
+{
+       const char *filename;
        int ret;
 
-       if (ar->fw_board == NULL) {
-               ret = ath6kl_fetch_board_file(ar);
-               if (ret)
+       switch (ar->version.target_ver) {
+       case AR6003_REV2_VERSION:
+               filename = AR6003_REV2_PATCH_FILE;
+               break;
+       case AR6004_REV1_VERSION:
+               /* FIXME: implement for AR6004 */
+               return 0;
+               break;
+       default:
+               filename = AR6003_REV3_PATCH_FILE;
+               break;
+       }
+
+       if (ar->fw_patch == NULL) {
+               ret = ath6kl_get_fw(ar, filename, &ar->fw_patch,
+                                   &ar->fw_patch_len);
+               if (ret) {
+                       ath6kl_err("Failed to get patch file %s: %d\n",
+                                  filename, ret);
                        return ret;
+               }
        }
 
-       /* Determine where in Target RAM to write Board Data */
-       ath6kl_bmi_read(ar,
-                       ath6kl_get_hi_item_addr(ar,
-                       HI_ITEM(hi_board_data)),
-                       (u8 *) &board_address, 4);
-       ath6kl_dbg(ATH6KL_DBG_TRC, "board data download addr: 0x%x\n",
-                  board_address);
+       return 0;
+}
+
+static int ath6kl_fetch_fw_api1(struct ath6kl *ar)
+{
+       int ret;
+
+       ret = ath6kl_fetch_otp_file(ar);
+       if (ret)
+               return ret;
+
+       ret = ath6kl_fetch_fw_file(ar);
+       if (ret)
+               return ret;
+
+       ret = ath6kl_fetch_patch_file(ar);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+static int ath6kl_fetch_fw_api2(struct ath6kl *ar)
+{
+       size_t magic_len, len, ie_len;
+       const struct firmware *fw;
+       struct ath6kl_fw_ie *hdr;
+       const char *filename;
+       const u8 *data;
+       int ret, ie_id, i, index, bit;
+       __le32 *val;
+
+       switch (ar->version.target_ver) {
+       case AR6003_REV2_VERSION:
+               filename = AR6003_REV2_FIRMWARE_2_FILE;
+               break;
+       case AR6003_REV3_VERSION:
+               filename = AR6003_REV3_FIRMWARE_2_FILE;
+               break;
+       case AR6004_REV1_VERSION:
+               filename = AR6004_REV1_FIRMWARE_2_FILE;
+               break;
+       default:
+               return -EOPNOTSUPP;
+       }
+
+       ret = request_firmware(&fw, filename, ar->dev);
+       if (ret)
+               return ret;
+
+       data = fw->data;
+       len = fw->size;
+
+       /* magic also includes the null byte, check that as well */
+       magic_len = strlen(ATH6KL_FIRMWARE_MAGIC) + 1;
+
+       if (len < magic_len) {
+               ret = -EINVAL;
+               goto out;
+       }
+
+       if (memcmp(data, ATH6KL_FIRMWARE_MAGIC, magic_len) != 0) {
+               ret = -EINVAL;
+               goto out;
+       }
+
+       len -= magic_len;
+       data += magic_len;
+
+       /* loop elements */
+       while (len > sizeof(struct ath6kl_fw_ie)) {
+               /* hdr is unaligned! */
+               hdr = (struct ath6kl_fw_ie *) data;
+
+               ie_id = le32_to_cpup(&hdr->id);
+               ie_len = le32_to_cpup(&hdr->len);
+
+               len -= sizeof(*hdr);
+               data += sizeof(*hdr);
+
+               if (len < ie_len) {
+                       ret = -EINVAL;
+                       goto out;
+               }
+
+               switch (ie_id) {
+               case ATH6KL_FW_IE_OTP_IMAGE:
+                       ath6kl_dbg(ATH6KL_DBG_BOOT, "found otp image ie (%zd B)\n",
+                               ie_len);
+
+                       ar->fw_otp = kmemdup(data, ie_len, GFP_KERNEL);
+
+                       if (ar->fw_otp == NULL) {
+                               ret = -ENOMEM;
+                               goto out;
+                       }
+
+                       ar->fw_otp_len = ie_len;
+                       break;
+               case ATH6KL_FW_IE_FW_IMAGE:
+                       ath6kl_dbg(ATH6KL_DBG_BOOT, "found fw image ie (%zd B)\n",
+                               ie_len);
+
+                       ar->fw = kmemdup(data, ie_len, GFP_KERNEL);
+
+                       if (ar->fw == NULL) {
+                               ret = -ENOMEM;
+                               goto out;
+                       }
+
+                       ar->fw_len = ie_len;
+                       break;
+               case ATH6KL_FW_IE_PATCH_IMAGE:
+                       ath6kl_dbg(ATH6KL_DBG_BOOT, "found patch image ie (%zd B)\n",
+                               ie_len);
+
+                       ar->fw_patch = kmemdup(data, ie_len, GFP_KERNEL);
+
+                       if (ar->fw_patch == NULL) {
+                               ret = -ENOMEM;
+                               goto out;
+                       }
+
+                       ar->fw_patch_len = ie_len;
+                       break;
+               case ATH6KL_FW_IE_RESERVED_RAM_SIZE:
+                       val = (__le32 *) data;
+                       ar->hw.reserved_ram_size = le32_to_cpup(val);
+
+                       ath6kl_dbg(ATH6KL_DBG_BOOT,
+                                  "found reserved ram size ie 0x%d\n",
+                                  ar->hw.reserved_ram_size);
+                       break;
+               case ATH6KL_FW_IE_CAPABILITIES:
+                       ath6kl_dbg(ATH6KL_DBG_BOOT,
+                                  "found firmware capabilities ie (%zd B)\n",
+                                  ie_len);
+
+                       for (i = 0; i < ATH6KL_FW_CAPABILITY_MAX; i++) {
+                               index = ALIGN(i, 8) / 8;
+                               bit = i % 8;
+
+                               if (data[index] & (1 << bit))
+                                       __set_bit(i, ar->fw_capabilities);
+                       }
+
+                       ath6kl_dbg_dump(ATH6KL_DBG_BOOT, "capabilities", "",
+                                       ar->fw_capabilities,
+                                       sizeof(ar->fw_capabilities));
+                       break;
+               case ATH6KL_FW_IE_PATCH_ADDR:
+                       if (ie_len != sizeof(*val))
+                               break;
+
+                       val = (__le32 *) data;
+                       ar->hw.dataset_patch_addr = le32_to_cpup(val);
+
+                       ath6kl_dbg(ATH6KL_DBG_BOOT,
+                                  "found patch address ie 0x%d\n",
+                                  ar->hw.dataset_patch_addr);
+                       break;
+               default:
+                       ath6kl_dbg(ATH6KL_DBG_BOOT, "Unknown fw ie: %u\n",
+                                  le32_to_cpup(&hdr->id));
+                       break;
+               }
+
+               len -= ie_len;
+               data += ie_len;
+       };
+
+       ret = 0;
+out:
+       release_firmware(fw);
+
+       return ret;
+}
+
+static int ath6kl_fetch_firmwares(struct ath6kl *ar)
+{
+       int ret;
+
+       ret = ath6kl_fetch_board_file(ar);
+       if (ret)
+               return ret;
+
+       ret = ath6kl_fetch_fw_api2(ar);
+       if (ret == 0) {
+               ath6kl_dbg(ATH6KL_DBG_BOOT, "using fw api 2\n");
+               return 0;
+       }
+
+       ret = ath6kl_fetch_fw_api1(ar);
+       if (ret)
+               return ret;
+
+       ath6kl_dbg(ATH6KL_DBG_BOOT, "using fw api 1\n");
+
+       return 0;
+}
+
+static int ath6kl_upload_board_file(struct ath6kl *ar)
+{
+       u32 board_address, board_ext_address, param;
+       u32 board_data_size, board_ext_data_size;
+       int ret;
+
+       if (WARN_ON(ar->fw_board == NULL))
+               return -ENOENT;
+
+       /*
+        * Determine where in Target RAM to write Board Data.
+        * For AR6004, host determine Target RAM address for
+        * writing board data.
+        */
+       if (ar->target_type == TARGET_TYPE_AR6004) {
+               board_address = AR6004_REV1_BOARD_DATA_ADDRESS;
+               ath6kl_bmi_write(ar,
+                               ath6kl_get_hi_item_addr(ar,
+                               HI_ITEM(hi_board_data)),
+                               (u8 *) &board_address, 4);
+       } else {
+               ath6kl_bmi_read(ar,
+                               ath6kl_get_hi_item_addr(ar,
+                               HI_ITEM(hi_board_data)),
+                               (u8 *) &board_address, 4);
+       }
 
        /* determine where in target ram to write extended board data */
        ath6kl_bmi_read(ar,
@@ -729,21 +1106,37 @@ static int ath6kl_upload_board_file(struct ath6kl *ar)
                        HI_ITEM(hi_board_ext_data)),
                        (u8 *) &board_ext_address, 4);
 
-       ath6kl_dbg(ATH6KL_DBG_TRC, "board file download addr: 0x%x\n",
-                  board_ext_address);
-
        if (board_ext_address == 0) {
                ath6kl_err("Failed to get board file target address.\n");
                return -EINVAL;
        }
 
-       if (ar->fw_board_len == (AR6003_BOARD_DATA_SZ +
-                                AR6003_BOARD_EXT_DATA_SZ)) {
+       switch (ar->target_type) {
+       case TARGET_TYPE_AR6003:
+               board_data_size = AR6003_BOARD_DATA_SZ;
+               board_ext_data_size = AR6003_BOARD_EXT_DATA_SZ;
+               break;
+       case TARGET_TYPE_AR6004:
+               board_data_size = AR6004_BOARD_DATA_SZ;
+               board_ext_data_size = AR6004_BOARD_EXT_DATA_SZ;
+               break;
+       default:
+               WARN_ON(1);
+               return -EINVAL;
+               break;
+       }
+
+       if (ar->fw_board_len == (board_data_size +
+                                board_ext_data_size)) {
+
                /* write extended board data */
-               ret = ath6kl_bmi_write(ar, board_ext_address,
-                                      ar->fw_board + AR6003_BOARD_DATA_SZ,
-                                      AR6003_BOARD_EXT_DATA_SZ);
+               ath6kl_dbg(ATH6KL_DBG_BOOT,
+                          "writing extended board data to 0x%x (%d B)\n",
+                          board_ext_address, board_ext_data_size);
 
+               ret = ath6kl_bmi_write(ar, board_ext_address,
+                                      ar->fw_board + board_data_size,
+                                      board_ext_data_size);
                if (ret) {
                        ath6kl_err("Failed to write extended board data: %d\n",
                                   ret);
@@ -751,21 +1144,25 @@ static int ath6kl_upload_board_file(struct ath6kl *ar)
                }
 
                /* record that extended board data is initialized */
-               param = (AR6003_BOARD_EXT_DATA_SZ << 16) | 1;
+               param = (board_ext_data_size << 16) | 1;
+
                ath6kl_bmi_write(ar,
                                 ath6kl_get_hi_item_addr(ar,
                                 HI_ITEM(hi_board_ext_data_config)),
                                 (unsigned char *) &param, 4);
        }
 
-       if (ar->fw_board_len < AR6003_BOARD_DATA_SZ) {
+       if (ar->fw_board_len < board_data_size) {
                ath6kl_err("Too small board file: %zu\n", ar->fw_board_len);
                ret = -EINVAL;
                return ret;
        }
 
+       ath6kl_dbg(ATH6KL_DBG_BOOT, "writing board file to 0x%x (%d B)\n",
+                  board_address, board_data_size);
+
        ret = ath6kl_bmi_write(ar, board_address, ar->fw_board,
-                              AR6003_BOARD_DATA_SZ);
+                              board_data_size);
 
        if (ret) {
                ath6kl_err("Board file bmi write failed: %d\n", ret);
@@ -784,31 +1181,16 @@ static int ath6kl_upload_board_file(struct ath6kl *ar)
 
 static int ath6kl_upload_otp(struct ath6kl *ar)
 {
-       const char *filename;
        u32 address, param;
        int ret;
 
-       switch (ar->version.target_ver) {
-       case AR6003_REV2_VERSION:
-               filename = AR6003_REV2_OTP_FILE;
-               break;
-       default:
-               filename = AR6003_REV3_OTP_FILE;
-               break;
-       }
+       if (WARN_ON(ar->fw_otp == NULL))
+               return -ENOENT;
 
-       if (ar->fw_otp == NULL) {
-               ret = ath6kl_get_fw(ar, filename, &ar->fw_otp,
-                                   &ar->fw_otp_len);
-               if (ret) {
-                       ath6kl_err("Failed to get OTP file %s: %d\n",
-                                  filename, ret);
-                       return ret;
-               }
-       }
+       address = ar->hw.app_load_addr;
 
-       address = ath6kl_get_load_address(ar->version.target_ver,
-                                         APP_LOAD_ADDR);
+       ath6kl_dbg(ATH6KL_DBG_BOOT, "writing otp to 0x%x (%zd B)\n", address,
+                  ar->fw_otp_len);
 
        ret = ath6kl_bmi_fast_download(ar, address, ar->fw_otp,
                                       ar->fw_otp_len);
@@ -817,10 +1199,25 @@ static int ath6kl_upload_otp(struct ath6kl *ar)
                return ret;
        }
 
+       /* read firmware start address */
+       ret = ath6kl_bmi_read(ar,
+                             ath6kl_get_hi_item_addr(ar,
+                                                     HI_ITEM(hi_app_start)),
+                             (u8 *) &address, sizeof(address));
+
+       if (ret) {
+               ath6kl_err("Failed to read hi_app_start: %d\n", ret);
+               return ret;
+       }
+
+       ar->hw.app_start_override_addr = address;
+
+       ath6kl_dbg(ATH6KL_DBG_BOOT, "app_start_override_addr 0x%x\n",
+                  ar->hw.app_start_override_addr);
+
        /* execute the OTP code */
+       ath6kl_dbg(ATH6KL_DBG_BOOT, "executing OTP at 0x%x\n", address);
        param = 0;
-       address = ath6kl_get_load_address(ar->version.target_ver,
-                                         APP_START_OVERRIDE_ADDR);
        ath6kl_bmi_execute(ar, address, &param);
 
        return ret;
@@ -828,30 +1225,16 @@ static int ath6kl_upload_otp(struct ath6kl *ar)
 
 static int ath6kl_upload_firmware(struct ath6kl *ar)
 {
-       const char *filename;
        u32 address;
        int ret;
 
-       switch (ar->version.target_ver) {
-       case AR6003_REV2_VERSION:
-               filename = AR6003_REV2_FIRMWARE_FILE;
-               break;
-       default:
-               filename = AR6003_REV3_FIRMWARE_FILE;
-               break;
-       }
+       if (WARN_ON(ar->fw == NULL))
+               return -ENOENT;
 
-       if (ar->fw == NULL) {
-               ret = ath6kl_get_fw(ar, filename, &ar->fw, &ar->fw_len);
-               if (ret) {
-                       ath6kl_err("Failed to get firmware file %s: %d\n",
-                                  filename, ret);
-                       return ret;
-               }
-       }
+       address = ar->hw.app_load_addr;
 
-       address = ath6kl_get_load_address(ar->version.target_ver,
-                                         APP_LOAD_ADDR);
+       ath6kl_dbg(ATH6KL_DBG_BOOT, "writing firmware to 0x%x (%zd B)\n",
+                  address, ar->fw_len);
 
        ret = ath6kl_bmi_fast_download(ar, address, ar->fw, ar->fw_len);
 
@@ -860,41 +1243,29 @@ static int ath6kl_upload_firmware(struct ath6kl *ar)
                return ret;
        }
 
-       /* Set starting address for firmware */
-       address = ath6kl_get_load_address(ar->version.target_ver,
-                                         APP_START_OVERRIDE_ADDR);
-       ath6kl_bmi_set_app_start(ar, address);
-
+       /*
+        * Set starting address for firmware
+        * Don't need to setup app_start override addr on AR6004
+        */
+       if (ar->target_type != TARGET_TYPE_AR6004) {
+               address = ar->hw.app_start_override_addr;
+               ath6kl_bmi_set_app_start(ar, address);
+       }
        return ret;
 }
 
 static int ath6kl_upload_patch(struct ath6kl *ar)
 {
-       const char *filename;
        u32 address, param;
        int ret;
 
-       switch (ar->version.target_ver) {
-       case AR6003_REV2_VERSION:
-               filename = AR6003_REV2_PATCH_FILE;
-               break;
-       default:
-               filename = AR6003_REV3_PATCH_FILE;
-               break;
-       }
+       if (WARN_ON(ar->fw_patch == NULL))
+               return -ENOENT;
 
-       if (ar->fw_patch == NULL) {
-               ret = ath6kl_get_fw(ar, filename, &ar->fw_patch,
-                                   &ar->fw_patch_len);
-               if (ret) {
-                       ath6kl_err("Failed to get patch file %s: %d\n",
-                                  filename, ret);
-                       return ret;
-               }
-       }
+       address = ar->hw.dataset_patch_addr;
 
-       address = ath6kl_get_load_address(ar->version.target_ver,
-                                         DATASET_PATCH_ADDR);
+       ath6kl_dbg(ATH6KL_DBG_BOOT, "writing patch to 0x%x (%zd B)\n",
+                  address, ar->fw_patch_len);
 
        ret = ath6kl_bmi_write(ar, address, ar->fw_patch, ar->fw_patch_len);
        if (ret) {
@@ -916,7 +1287,8 @@ static int ath6kl_init_upload(struct ath6kl *ar)
        u32 param, options, sleep, address;
        int status = 0;
 
-       if (ar->target_type != TARGET_TYPE_AR6003)
+       if (ar->target_type != TARGET_TYPE_AR6003 &&
+               ar->target_type != TARGET_TYPE_AR6004)
                return -EINVAL;
 
        /* temporarily disable system sleep */
@@ -948,18 +1320,22 @@ static int ath6kl_init_upload(struct ath6kl *ar)
                   options, sleep);
 
        /* program analog PLL register */
-       status = ath6kl_bmi_reg_write(ar, ATH6KL_ANALOG_PLL_REGISTER,
-                                     0xF9104001);
-       if (status)
-               return status;
+       /* no need to control 40/44MHz clock on AR6004 */
+       if (ar->target_type != TARGET_TYPE_AR6004) {
+               status = ath6kl_bmi_reg_write(ar, ATH6KL_ANALOG_PLL_REGISTER,
+                                             0xF9104001);
 
-       /* Run at 80/88MHz by default */
-       param = SM(CPU_CLOCK_STANDARD, 1);
+               if (status)
+                       return status;
 
-       address = RTC_BASE_ADDRESS + CPU_CLOCK_ADDRESS;
-       status = ath6kl_bmi_reg_write(ar, address, param);
-       if (status)
-               return status;
+               /* Run at 80/88MHz by default */
+               param = SM(CPU_CLOCK_STANDARD, 1);
+
+               address = RTC_BASE_ADDRESS + CPU_CLOCK_ADDRESS;
+               status = ath6kl_bmi_reg_write(ar, address, param);
+               if (status)
+                       return status;
+       }
 
        param = 0;
        address = RTC_BASE_ADDRESS + LPO_CAL_ADDRESS;
@@ -1036,6 +1412,45 @@ static int ath6kl_init_upload(struct ath6kl *ar)
        return status;
 }
 
+static int ath6kl_init_hw_params(struct ath6kl *ar)
+{
+       switch (ar->version.target_ver) {
+       case AR6003_REV2_VERSION:
+               ar->hw.dataset_patch_addr = AR6003_REV2_DATASET_PATCH_ADDRESS;
+               ar->hw.app_load_addr = AR6003_REV2_APP_LOAD_ADDRESS;
+               ar->hw.board_ext_data_addr = AR6003_REV2_BOARD_EXT_DATA_ADDRESS;
+               ar->hw.reserved_ram_size = AR6003_REV2_RAM_RESERVE_SIZE;
+               break;
+       case AR6003_REV3_VERSION:
+               ar->hw.dataset_patch_addr = AR6003_REV3_DATASET_PATCH_ADDRESS;
+               ar->hw.app_load_addr = 0x1234;
+               ar->hw.board_ext_data_addr = AR6003_REV3_BOARD_EXT_DATA_ADDRESS;
+               ar->hw.reserved_ram_size = AR6003_REV3_RAM_RESERVE_SIZE;
+               break;
+       case AR6004_REV1_VERSION:
+               ar->hw.dataset_patch_addr = AR6003_REV2_DATASET_PATCH_ADDRESS;
+               ar->hw.app_load_addr = AR6003_REV3_APP_LOAD_ADDRESS;
+               ar->hw.board_ext_data_addr = AR6004_REV1_BOARD_EXT_DATA_ADDRESS;
+               ar->hw.reserved_ram_size = AR6004_REV1_RAM_RESERVE_SIZE;
+               break;
+       default:
+               ath6kl_err("Unsupported hardware version: 0x%x\n",
+                          ar->version.target_ver);
+               return -EINVAL;
+       }
+
+       ath6kl_dbg(ATH6KL_DBG_BOOT,
+                  "target_ver 0x%x target_type 0x%x dataset_patch 0x%x app_load_addr 0x%x\n",
+                  ar->version.target_ver, ar->target_type,
+                  ar->hw.dataset_patch_addr, ar->hw.app_load_addr);
+       ath6kl_dbg(ATH6KL_DBG_BOOT,
+                  "app_start_override_addr 0x%x board_ext_data_addr 0x%x reserved_ram_size 0x%x",
+                  ar->hw.app_start_override_addr, ar->hw.board_ext_data_addr,
+                  ar->hw.reserved_ram_size);
+
+       return 0;
+}
+
 static int ath6kl_init(struct net_device *dev)
 {
        struct ath6kl *ar = ath6kl_priv(dev);
@@ -1062,8 +1477,6 @@ static int ath6kl_init(struct net_device *dev)
 
        ath6kl_dbg(ATH6KL_DBG_TRC, "%s: got wmi @ 0x%p.\n", __func__, ar->wmi);
 
-       wlan_node_table_init(&ar->scan_table);
-
        /*
         * The reason we have to wait for the target here is that the
         * driver layer has to init BMI in order to set the host block
@@ -1111,6 +1524,8 @@ static int ath6kl_init(struct net_device *dev)
                                                             &ar->flag),
                                                    WMI_TIMEOUT);
 
+       ath6kl_dbg(ATH6KL_DBG_BOOT, "firmware booted\n");
+
        if (ar->version.abi_ver != ATH6KL_ABI_VERSION) {
                ath6kl_err("abi version mismatch: host(0x%x), target(0x%x)\n",
                           ATH6KL_ABI_VERSION, ar->version.abi_ver);
@@ -1133,6 +1548,8 @@ static int ath6kl_init(struct net_device *dev)
        ar->conf_flags = ATH6KL_CONF_IGNORE_ERP_BARKER |
                         ATH6KL_CONF_ENABLE_11N | ATH6KL_CONF_ENABLE_TX_BURST;
 
+       ar->wdev->wiphy->flags |= WIPHY_FLAG_SUPPORTS_FW_ROAM;
+
        status = ath6kl_target_config_wlan_params(ar);
        if (!status)
                goto ath6kl_init_done;
@@ -1145,7 +1562,6 @@ err_rxbuf_cleanup:
 err_cleanup_scatter:
        ath6kl_hif_cleanup_scatter(ar);
 err_node_cleanup:
-       wlan_node_table_cleanup(&ar->scan_table);
        ath6kl_wmi_shutdown(ar->wmi);
        clear_bit(WMI_ENABLED, &ar->flag);
        ar->wmi = NULL;
@@ -1175,6 +1591,10 @@ int ath6kl_core_init(struct ath6kl *ar)
        ar->target_type = le32_to_cpu(targ_info.type);
        ar->wdev->wiphy->hw_version = le32_to_cpu(targ_info.version);
 
+       ret = ath6kl_init_hw_params(ar);
+       if (ret)
+               goto err_bmi_cleanup;
+
        ret = ath6kl_configure_target(ar);
        if (ret)
                goto err_bmi_cleanup;
@@ -1193,6 +1613,10 @@ int ath6kl_core_init(struct ath6kl *ar)
                goto err_htc_cleanup;
        }
 
+       ret = ath6kl_fetch_firmwares(ar);
+       if (ret)
+               goto err_htc_cleanup;
+
        ret = ath6kl_init_upload(ar);
        if (ret)
                goto err_htc_cleanup;
@@ -1285,6 +1709,8 @@ void ath6kl_destroy(struct net_device *dev, unsigned int unregister)
 
        ath6kl_bmi_cleanup(ar);
 
+       ath6kl_debug_cleanup(ar);
+
        if (unregister && test_bit(NETDEV_REGISTERED, &ar->flag)) {
                unregister_netdev(dev);
                clear_bit(NETDEV_REGISTERED, &ar->flag);
@@ -1292,8 +1718,6 @@ void ath6kl_destroy(struct net_device *dev, unsigned int unregister)
 
        free_netdev(dev);
 
-       wlan_node_table_cleanup(&ar->scan_table);
-
        kfree(ar->fw_board);
        kfree(ar->fw_otp);
        kfree(ar->fw);
index c336eae0cf48585755204a3c1f47ae5db5b82f14..30b5a53db9ed061dd6ba60a790a7ccde0ba8fc23 100644 (file)
@@ -61,7 +61,8 @@ static void ath6kl_add_new_sta(struct ath6kl *ar, u8 *mac, u16 aid, u8 *wpaie,
 
        sta = &ar->sta_list[free_slot];
        memcpy(sta->mac, mac, ETH_ALEN);
-       memcpy(sta->wpa_ie, wpaie, ielen);
+       if (ielen <= ATH6KL_MAX_IE)
+               memcpy(sta->wpa_ie, wpaie, ielen);
        sta->aid = aid;
        sta->keymgmt = keymgmt;
        sta->ucipher = ucipher;
@@ -177,8 +178,8 @@ void ath6kl_free_cookie(struct ath6kl *ar, struct ath6kl_cookie *cookie)
 static int ath6kl_set_addrwin_reg(struct ath6kl *ar, u32 reg_addr, u32 addr)
 {
        int status;
-       u8 addr_val[4];
        s32 i;
+       __le32 addr_val;
 
        /*
         * Write bytes 1,2,3 of the register to set the upper address bytes,
@@ -188,16 +189,18 @@ static int ath6kl_set_addrwin_reg(struct ath6kl *ar, u32 reg_addr, u32 addr)
        for (i = 1; i <= 3; i++) {
                /*
                 * Fill the buffer with the address byte value we want to
-                * hit 4 times.
+                * hit 4 times. No need to worry about endianness as the
+                * same byte is copied to all four bytes of addr_val at
+                * any time.
                 */
-               memset(addr_val, ((u8 *)&addr)[i], 4);
+               memset((u8 *)&addr_val, ((u8 *)&addr)[i], 4);
 
                /*
                 * Hit each byte of the register address with a 4-byte
                 * write operation to the same address, this is a harmless
                 * operation.
                 */
-               status = hif_read_write_sync(ar, reg_addr + i, addr_val,
+               status = hif_read_write_sync(ar, reg_addr + i, (u8 *)&addr_val,
                                             4, HIF_WR_SYNC_BYTE_FIX);
                if (status)
                        break;
@@ -215,7 +218,9 @@ static int ath6kl_set_addrwin_reg(struct ath6kl *ar, u32 reg_addr, u32 addr)
         * cycle to start, the extra 3 byte write to bytes 1,2,3 has no
         * effect since we are writing the same values again
         */
-       status = hif_read_write_sync(ar, reg_addr, (u8 *)(&addr),
+       addr_val = cpu_to_le32(addr);
+       status = hif_read_write_sync(ar, reg_addr,
+                                    (u8 *)&(addr_val),
                                     4, HIF_WR_SYNC_BYTE_INC);
 
        if (status) {
@@ -228,90 +233,193 @@ static int ath6kl_set_addrwin_reg(struct ath6kl *ar, u32 reg_addr, u32 addr)
 }
 
 /*
- * Read from the ATH6KL through its diagnostic window. No cooperation from
- * the Target is required for this.
+ * Read from the hardware through its diagnostic window. No cooperation
+ * from the firmware is required for this.
  */
-int ath6kl_read_reg_diag(struct ath6kl *ar, u32 *address, u32 *data)
+int ath6kl_diag_read32(struct ath6kl *ar, u32 address, u32 *value)
 {
-       int status;
+       int ret;
 
        /* set window register to start read cycle */
-       status = ath6kl_set_addrwin_reg(ar, WINDOW_READ_ADDR_ADDRESS,
-                                       *address);
-
-       if (status)
-               return status;
+       ret = ath6kl_set_addrwin_reg(ar, WINDOW_READ_ADDR_ADDRESS, address);
+       if (ret)
+               return ret;
 
        /* read the data */
-       status = hif_read_write_sync(ar, WINDOW_DATA_ADDRESS, (u8 *)data,
-                                    sizeof(u32), HIF_RD_SYNC_BYTE_INC);
-       if (status) {
-               ath6kl_err("failed to read from window data addr\n");
-               return status;
+       ret = hif_read_write_sync(ar, WINDOW_DATA_ADDRESS, (u8 *) value,
+                                 sizeof(*value), HIF_RD_SYNC_BYTE_INC);
+       if (ret) {
+               ath6kl_warn("failed to read32 through diagnose window: %d\n",
+                           ret);
+               return ret;
        }
 
-       return status;
+       return 0;
 }
 
-
 /*
  * Write to the ATH6KL through its diagnostic window. No cooperation from
  * the Target is required for this.
  */
-static int ath6kl_write_reg_diag(struct ath6kl *ar, u32 *address, u32 *data)
+int ath6kl_diag_write32(struct ath6kl *ar, u32 address, __le32 value)
 {
-       int status;
+       int ret;
 
        /* set write data */
-       status = hif_read_write_sync(ar, WINDOW_DATA_ADDRESS, (u8 *)data,
-                                    sizeof(u32), HIF_WR_SYNC_BYTE_INC);
-       if (status) {
-               ath6kl_err("failed to write 0x%x to window data addr\n", *data);
-               return status;
+       ret = hif_read_write_sync(ar, WINDOW_DATA_ADDRESS, (u8 *) &value,
+                                 sizeof(value), HIF_WR_SYNC_BYTE_INC);
+       if (ret) {
+               ath6kl_err("failed to write 0x%x during diagnose window to 0x%d\n",
+                          address, value);
+               return ret;
        }
 
        /* set window register, which starts the write cycle */
        return ath6kl_set_addrwin_reg(ar, WINDOW_WRITE_ADDR_ADDRESS,
-                                     *address);
+                                     address);
 }
 
-int ath6kl_access_datadiag(struct ath6kl *ar, u32 address,
-                          u8 *data, u32 length, bool read)
+int ath6kl_diag_read(struct ath6kl *ar, u32 address, void *data, u32 length)
+{
+       u32 count, *buf = data;
+       int ret;
+
+       if (WARN_ON(length % 4))
+               return -EINVAL;
+
+       for (count = 0; count < length / 4; count++, address += 4) {
+               ret = ath6kl_diag_read32(ar, address, &buf[count]);
+               if (ret)
+                       return ret;
+       }
+
+       return 0;
+}
+
+int ath6kl_diag_write(struct ath6kl *ar, u32 address, void *data, u32 length)
 {
        u32 count;
-       int status = 0;
+       __le32 *buf = data;
+       int ret;
 
-       for (count = 0; count < length; count += 4, address += 4) {
-               if (read) {
-                       status = ath6kl_read_reg_diag(ar, &address,
-                                                     (u32 *) &data[count]);
-                       if (status)
-                               break;
-               } else {
-                       status = ath6kl_write_reg_diag(ar, &address,
-                                                      (u32 *) &data[count]);
-                       if (status)
-                               break;
-               }
+       if (WARN_ON(length % 4))
+               return -EINVAL;
+
+       for (count = 0; count < length / 4; count++, address += 4) {
+               ret = ath6kl_diag_write32(ar, address, buf[count]);
+               if (ret)
+                       return ret;
+       }
+
+       return 0;
+}
+
+int ath6kl_read_fwlogs(struct ath6kl *ar)
+{
+       struct ath6kl_dbglog_hdr debug_hdr;
+       struct ath6kl_dbglog_buf debug_buf;
+       u32 address, length, dropped, firstbuf, debug_hdr_addr;
+       int ret = 0, loop;
+       u8 *buf;
+
+       buf = kmalloc(ATH6KL_FWLOG_PAYLOAD_SIZE, GFP_KERNEL);
+       if (!buf)
+               return -ENOMEM;
+
+       address = TARG_VTOP(ar->target_type,
+                           ath6kl_get_hi_item_addr(ar,
+                                                   HI_ITEM(hi_dbglog_hdr)));
+
+       ret = ath6kl_diag_read32(ar, address, &debug_hdr_addr);
+       if (ret)
+               goto out;
+
+       /* Get the contents of the ring buffer */
+       if (debug_hdr_addr == 0) {
+               ath6kl_warn("Invalid address for debug_hdr_addr\n");
+               ret = -EINVAL;
+               goto out;
        }
 
-       return status;
+       address = TARG_VTOP(ar->target_type, debug_hdr_addr);
+       ath6kl_diag_read(ar, address, &debug_hdr, sizeof(debug_hdr));
+
+       address = TARG_VTOP(ar->target_type,
+                           le32_to_cpu(debug_hdr.dbuf_addr));
+       firstbuf = address;
+       dropped = le32_to_cpu(debug_hdr.dropped);
+       ath6kl_diag_read(ar, address, &debug_buf, sizeof(debug_buf));
+
+       loop = 100;
+
+       do {
+               address = TARG_VTOP(ar->target_type,
+                                   le32_to_cpu(debug_buf.buffer_addr));
+               length = le32_to_cpu(debug_buf.length);
+
+               if (length != 0 && (le32_to_cpu(debug_buf.length) <=
+                                   le32_to_cpu(debug_buf.bufsize))) {
+                       length = ALIGN(length, 4);
+
+                       ret = ath6kl_diag_read(ar, address,
+                                              buf, length);
+                       if (ret)
+                               goto out;
+
+                       ath6kl_debug_fwlog_event(ar, buf, length);
+               }
+
+               address = TARG_VTOP(ar->target_type,
+                                   le32_to_cpu(debug_buf.next));
+               ath6kl_diag_read(ar, address, &debug_buf, sizeof(debug_buf));
+               if (ret)
+                       goto out;
+
+               loop--;
+
+               if (WARN_ON(loop == 0)) {
+                       ret = -ETIMEDOUT;
+                       goto out;
+               }
+       } while (address != firstbuf);
+
+out:
+       kfree(buf);
+
+       return ret;
 }
 
+/* FIXME: move to a better place, target.h? */
+#define AR6003_RESET_CONTROL_ADDRESS 0x00004000
+#define AR6004_RESET_CONTROL_ADDRESS 0x00004000
+
 static void ath6kl_reset_device(struct ath6kl *ar, u32 target_type,
                                bool wait_fot_compltn, bool cold_reset)
 {
        int status = 0;
        u32 address;
-       u32 data;
+       __le32 data;
 
-       if (target_type != TARGET_TYPE_AR6003)
+       if (target_type != TARGET_TYPE_AR6003 &&
+               target_type != TARGET_TYPE_AR6004)
                return;
 
-       data = cold_reset ? RESET_CONTROL_COLD_RST : RESET_CONTROL_MBOX_RST;
+       data = cold_reset ? cpu_to_le32(RESET_CONTROL_COLD_RST) :
+                           cpu_to_le32(RESET_CONTROL_MBOX_RST);
 
-       address = RTC_BASE_ADDRESS;
-       status = ath6kl_write_reg_diag(ar, &address, &data);
+       switch (target_type) {
+       case TARGET_TYPE_AR6003:
+               address = AR6003_RESET_CONTROL_ADDRESS;
+               break;
+       case TARGET_TYPE_AR6004:
+               address = AR6004_RESET_CONTROL_ADDRESS;
+               break;
+       default:
+               address = AR6003_RESET_CONTROL_ADDRESS;
+               break;
+       }
+
+       status = ath6kl_diag_write32(ar, address, data);
 
        if (status)
                ath6kl_err("failed to reset target\n");
@@ -411,68 +519,107 @@ static void ath6kl_install_static_wep_keys(struct ath6kl *ar)
        }
 }
 
-static void ath6kl_connect_ap_mode(struct ath6kl *ar, u16 channel, u8 *bssid,
-                                  u16 listen_int, u16 beacon_int,
-                                  u8 assoc_resp_len, u8 *assoc_info)
+void ath6kl_connect_ap_mode_bss(struct ath6kl *ar, u16 channel)
 {
-       struct net_device *dev = ar->net_dev;
-       struct station_info sinfo;
        struct ath6kl_req_key *ik;
-       enum crypto_type keyType = NONE_CRYPT;
+       int res;
+       u8 key_rsc[ATH6KL_KEY_SEQ_LEN];
 
-       if (memcmp(dev->dev_addr, bssid, ETH_ALEN) == 0) {
-               ik = &ar->ap_mode_bkey;
+       ik = &ar->ap_mode_bkey;
 
-               switch (ar->auth_mode) {
-               case NONE_AUTH:
-                       if (ar->prwise_crypto == WEP_CRYPT)
-                               ath6kl_install_static_wep_keys(ar);
-                       break;
-               case WPA_PSK_AUTH:
-               case WPA2_PSK_AUTH:
-               case (WPA_PSK_AUTH|WPA2_PSK_AUTH):
-                       switch (ik->ik_type) {
-                       case ATH6KL_CIPHER_TKIP:
-                               keyType = TKIP_CRYPT;
-                               break;
-                       case ATH6KL_CIPHER_AES_CCM:
-                               keyType = AES_CRYPT;
-                               break;
-                       default:
-                               goto skip_key;
-                       }
-                       ath6kl_wmi_addkey_cmd(ar->wmi, ik->ik_keyix, keyType,
-                                             GROUP_USAGE, ik->ik_keylen,
-                                             (u8 *)&ik->ik_keyrsc,
-                                             ik->ik_keydata,
-                                             KEY_OP_INIT_VAL, ik->ik_macaddr,
-                                             SYNC_BOTH_WMIFLAG);
+       ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "AP mode started on %u MHz\n", channel);
+
+       switch (ar->auth_mode) {
+       case NONE_AUTH:
+               if (ar->prwise_crypto == WEP_CRYPT)
+                       ath6kl_install_static_wep_keys(ar);
+               break;
+       case WPA_PSK_AUTH:
+       case WPA2_PSK_AUTH:
+       case (WPA_PSK_AUTH | WPA2_PSK_AUTH):
+               if (!ik->valid)
                        break;
+
+               ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "Delayed addkey for "
+                          "the initial group key for AP mode\n");
+               memset(key_rsc, 0, sizeof(key_rsc));
+               res = ath6kl_wmi_addkey_cmd(
+                       ar->wmi, ik->key_index, ik->key_type,
+                       GROUP_USAGE, ik->key_len, key_rsc, ik->key,
+                       KEY_OP_INIT_VAL, NULL, SYNC_BOTH_WMIFLAG);
+               if (res) {
+                       ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "Delayed "
+                                  "addkey failed: %d\n", res);
                }
-skip_key:
-               set_bit(CONNECTED, &ar->flag);
-               return;
+               break;
        }
 
-       ath6kl_dbg(ATH6KL_DBG_TRC, "new station %pM aid=%d\n",
-                  bssid, channel);
+       ath6kl_wmi_bssfilter_cmd(ar->wmi, NONE_BSS_FILTER, 0);
+       set_bit(CONNECTED, &ar->flag);
+       netif_carrier_on(ar->net_dev);
+}
+
+void ath6kl_connect_ap_mode_sta(struct ath6kl *ar, u16 aid, u8 *mac_addr,
+                               u8 keymgmt, u8 ucipher, u8 auth,
+                               u8 assoc_req_len, u8 *assoc_info)
+{
+       u8 *ies = NULL, *wpa_ie = NULL, *pos;
+       size_t ies_len = 0;
+       struct station_info sinfo;
 
-       ath6kl_add_new_sta(ar, bssid, channel, assoc_info, assoc_resp_len,
-                          listen_int & 0xFF, beacon_int,
-                          (listen_int >> 8) & 0xFF);
+       ath6kl_dbg(ATH6KL_DBG_TRC, "new station %pM aid=%d\n", mac_addr, aid);
+
+       if (assoc_req_len > sizeof(struct ieee80211_hdr_3addr)) {
+               struct ieee80211_mgmt *mgmt =
+                       (struct ieee80211_mgmt *) assoc_info;
+               if (ieee80211_is_assoc_req(mgmt->frame_control) &&
+                   assoc_req_len >= sizeof(struct ieee80211_hdr_3addr) +
+                   sizeof(mgmt->u.assoc_req)) {
+                       ies = mgmt->u.assoc_req.variable;
+                       ies_len = assoc_info + assoc_req_len - ies;
+               } else if (ieee80211_is_reassoc_req(mgmt->frame_control) &&
+                          assoc_req_len >= sizeof(struct ieee80211_hdr_3addr)
+                          + sizeof(mgmt->u.reassoc_req)) {
+                       ies = mgmt->u.reassoc_req.variable;
+                       ies_len = assoc_info + assoc_req_len - ies;
+               }
+       }
+
+       pos = ies;
+       while (pos && pos + 1 < ies + ies_len) {
+               if (pos + 2 + pos[1] > ies + ies_len)
+                       break;
+               if (pos[0] == WLAN_EID_RSN)
+                       wpa_ie = pos; /* RSN IE */
+               else if (pos[0] == WLAN_EID_VENDOR_SPECIFIC &&
+                        pos[1] >= 4 &&
+                        pos[2] == 0x00 && pos[3] == 0x50 && pos[4] == 0xf2) {
+                       if (pos[5] == 0x01)
+                               wpa_ie = pos; /* WPA IE */
+                       else if (pos[5] == 0x04) {
+                               wpa_ie = pos; /* WPS IE */
+                               break; /* overrides WPA/RSN IE */
+                       }
+               }
+               pos += 2 + pos[1];
+       }
+
+       ath6kl_add_new_sta(ar, mac_addr, aid, wpa_ie,
+                          wpa_ie ? 2 + wpa_ie[1] : 0,
+                          keymgmt, ucipher, auth);
 
        /* send event to application */
        memset(&sinfo, 0, sizeof(sinfo));
 
        /* TODO: sinfo.generation */
-       /* TODO: need to deliver (Re)AssocReq IEs somehow.. change in
-        * cfg80211 needed, e.g., by adding those into sinfo
-        */
-       cfg80211_new_sta(ar->net_dev, bssid, &sinfo, GFP_KERNEL);
 
-       netif_wake_queue(ar->net_dev);
+       sinfo.assoc_req_ies = ies;
+       sinfo.assoc_req_ies_len = ies_len;
+       sinfo.filled |= STATION_INFO_ASSOC_REQ_IES;
 
-       return;
+       cfg80211_new_sta(ar->net_dev, mac_addr, &sinfo, GFP_KERNEL);
+
+       netif_wake_queue(ar->net_dev);
 }
 
 /* Functions for Tx credit handling */
@@ -779,6 +926,41 @@ void ath6kl_disconnect(struct ath6kl *ar)
        }
 }
 
+void ath6kl_deep_sleep_enable(struct ath6kl *ar)
+{
+       switch (ar->sme_state) {
+       case SME_CONNECTING:
+               cfg80211_connect_result(ar->net_dev, ar->bssid, NULL, 0,
+                                       NULL, 0,
+                                       WLAN_STATUS_UNSPECIFIED_FAILURE,
+                                       GFP_KERNEL);
+               break;
+       case SME_CONNECTED:
+       default:
+               /*
+                * FIXME: oddly enough smeState is in DISCONNECTED during
+                * suspend, why? Need to send disconnected event in that
+                * state.
+                */
+               cfg80211_disconnected(ar->net_dev, 0, NULL, 0, GFP_KERNEL);
+               break;
+       }
+
+       if (test_bit(CONNECTED, &ar->flag) ||
+           test_bit(CONNECT_PEND, &ar->flag))
+               ath6kl_wmi_disconnect_cmd(ar->wmi);
+
+       ar->sme_state = SME_DISCONNECTED;
+
+       /* disable scanning */
+       if (ath6kl_wmi_scanparams_cmd(ar->wmi, 0xFFFF, 0, 0, 0, 0, 0, 0, 0,
+                                     0, 0) != 0)
+               printk(KERN_WARNING "ath6kl: failed to disable scan "
+                      "during suspend\n");
+
+       ath6kl_cfg80211_scan_complete_event(ar, -ECANCELED);
+}
+
 /* WMI Event handlers */
 
 static const char *get_hw_id_string(u32 id)
@@ -819,17 +1001,20 @@ void ath6kl_ready_event(void *devt, u8 *datap, u32 sw_ver, u32 abi_ver)
        set_bit(WMI_READY, &ar->flag);
        wake_up(&ar->event_wq);
 
-       ath6kl_info("hw %s fw %s\n",
+       ath6kl_info("hw %s fw %s%s\n",
                    get_hw_id_string(ar->wdev->wiphy->hw_version),
-                   ar->wdev->wiphy->fw_version);
+                   ar->wdev->wiphy->fw_version,
+                   test_bit(TESTMODE, &ar->flag) ? " testmode" : "");
 }
 
 void ath6kl_scan_complete_evt(struct ath6kl *ar, int status)
 {
        ath6kl_cfg80211_scan_complete_event(ar, status);
 
-       if (!ar->usr_bss_filter)
+       if (!ar->usr_bss_filter) {
+               clear_bit(CLEAR_BSSFILTER_ON_BEACON, &ar->flag);
                ath6kl_wmi_bssfilter_cmd(ar->wmi, NONE_BSS_FILTER, 0);
+       }
 
        ath6kl_dbg(ATH6KL_DBG_WLAN_SCAN, "scan complete: %d\n", status);
 }
@@ -842,13 +1027,6 @@ void ath6kl_connect_event(struct ath6kl *ar, u16 channel, u8 *bssid,
 {
        unsigned long flags;
 
-       if (ar->nw_type == AP_NETWORK) {
-               ath6kl_connect_ap_mode(ar, channel, bssid, listen_int,
-                                      beacon_int, assoc_resp_len,
-                                      assoc_info);
-               return;
-       }
-
        ath6kl_cfg80211_connect_event(ar, channel, bssid,
                                      listen_int, beacon_int,
                                      net_type, beacon_ie_len,
@@ -880,8 +1058,10 @@ void ath6kl_connect_event(struct ath6kl *ar, u16 channel, u8 *bssid,
                ar->next_ep_id = ENDPOINT_2;
        }
 
-       if (!ar->usr_bss_filter)
-               ath6kl_wmi_bssfilter_cmd(ar->wmi, NONE_BSS_FILTER, 0);
+       if (!ar->usr_bss_filter) {
+               set_bit(CLEAR_BSSFILTER_ON_BEACON, &ar->flag);
+               ath6kl_wmi_bssfilter_cmd(ar->wmi, CURRENT_BSS_FILTER, 0);
+       }
 }
 
 void ath6kl_tkip_micerr_event(struct ath6kl *ar, u8 keyid, bool ismcast)
@@ -915,26 +1095,11 @@ static void ath6kl_update_target_stats(struct ath6kl *ar, u8 *ptr, u32 len)
                (struct wmi_target_stats *) ptr;
        struct target_stats *stats = &ar->target_stats;
        struct tkip_ccmp_stats *ccmp_stats;
-       struct bss *conn_bss = NULL;
-       struct cserv_stats *c_stats;
        u8 ac;
 
        if (len < sizeof(*tgt_stats))
                return;
 
-       /* update the RSSI of the connected bss */
-       if (test_bit(CONNECTED, &ar->flag)) {
-               conn_bss = ath6kl_wmi_find_node(ar->wmi, ar->bssid);
-               if (conn_bss) {
-                       c_stats = &tgt_stats->cserv_stats;
-                       conn_bss->ni_rssi =
-                               a_sle16_to_cpu(c_stats->cs_ave_beacon_rssi);
-                       conn_bss->ni_snr =
-                               tgt_stats->cserv_stats.cs_ave_beacon_snr;
-                       ath6kl_wmi_node_return(ar->wmi, conn_bss);
-               }
-       }
-
        ath6kl_dbg(ATH6KL_DBG_TRC, "updating target stats\n");
 
        stats->tx_pkt += le32_to_cpu(tgt_stats->stats.tx.pkt);
@@ -1165,7 +1330,6 @@ void ath6kl_disconnect_event(struct ath6kl *ar, u8 reason, u8 *bssid,
                             u8 assoc_resp_len, u8 *assoc_info,
                             u16 prot_reason_status)
 {
-       struct bss *wmi_ssid_node = NULL;
        unsigned long flags;
 
        if (ar->nw_type == AP_NETWORK) {
@@ -1188,7 +1352,10 @@ void ath6kl_disconnect_event(struct ath6kl *ar, u8 reason, u8 *bssid,
                        cfg80211_del_sta(ar->net_dev, bssid, GFP_KERNEL);
                }
 
-               clear_bit(CONNECTED, &ar->flag);
+               if (memcmp(ar->net_dev->dev_addr, bssid, ETH_ALEN) == 0) {
+                       memset(ar->wep_key_list, 0, sizeof(ar->wep_key_list));
+                       clear_bit(CONNECTED, &ar->flag);
+               }
                return;
        }
 
@@ -1222,33 +1389,6 @@ void ath6kl_disconnect_event(struct ath6kl *ar, u8 reason, u8 *bssid,
                }
        }
 
-       if ((reason == NO_NETWORK_AVAIL) && test_bit(WMI_READY, &ar->flag))  {
-               ath6kl_wmi_node_free(ar->wmi, bssid);
-
-               /*
-                * In case any other same SSID nodes are present remove it,
-                * since those nodes also not available now.
-                */
-               do {
-                       /*
-                        * Find the nodes based on SSID and remove it
-                        *
-                        * Note: This case will not work out for
-                        * Hidden-SSID
-                        */
-                       wmi_ssid_node = ath6kl_wmi_find_ssid_node(ar->wmi,
-                                                                 ar->ssid,
-                                                                 ar->ssid_len,
-                                                                 false,
-                                                                 true);
-
-                       if (wmi_ssid_node)
-                               ath6kl_wmi_node_free(ar->wmi,
-                                                    wmi_ssid_node->ni_macaddr);
-
-               } while (wmi_ssid_node);
-       }
-
        /* update connect & link status atomically */
        spin_lock_irqsave(&ar->lock, flags);
        clear_bit(CONNECTED, &ar->flag);
@@ -1331,7 +1471,7 @@ void init_netdev(struct net_device *dev)
        dev->needed_headroom = ETH_HLEN;
        dev->needed_headroom += sizeof(struct ath6kl_llc_snap_hdr) +
                                sizeof(struct wmi_data_hdr) + HTC_HDR_LENGTH
-                               + WMI_MAX_TX_META_SZ;
+                               + WMI_MAX_TX_META_SZ + ATH6KL_HTC_ALIGN_BYTES;
 
        return;
 }
diff --git a/drivers/net/wireless/ath/ath6kl/node.c b/drivers/net/wireless/ath/ath6kl/node.c
deleted file mode 100644 (file)
index 131205c..0000000
+++ /dev/null
@@ -1,234 +0,0 @@
-/*
- * Copyright (c) 2004-2011 Atheros Communications 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
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include "htc.h"
-#include "wmi.h"
-#include "debug.h"
-
-struct bss *wlan_node_alloc(int wh_size)
-{
-       struct bss *ni;
-
-       ni = kzalloc(sizeof(struct bss), GFP_ATOMIC);
-
-       if ((ni != NULL) && wh_size) {
-               ni->ni_buf = kmalloc(wh_size, GFP_ATOMIC);
-               if (ni->ni_buf == NULL) {
-                       kfree(ni);
-                       return NULL;
-               }
-       }
-
-       return ni;
-}
-
-void wlan_node_free(struct bss *ni)
-{
-       kfree(ni->ni_buf);
-       kfree(ni);
-}
-
-void wlan_setup_node(struct ath6kl_node_table *nt, struct bss *ni,
-                    const u8 *mac_addr)
-{
-       int hash;
-
-       memcpy(ni->ni_macaddr, mac_addr, ETH_ALEN);
-       hash = ATH6KL_NODE_HASH(mac_addr);
-       ni->ni_refcnt = 1;
-
-       ni->ni_tstamp = jiffies_to_msecs(jiffies);
-       ni->ni_actcnt = WLAN_NODE_INACT_CNT;
-
-       spin_lock_bh(&nt->nt_nodelock);
-
-       /* insert at the end of the node list */
-       ni->ni_list_next = NULL;
-       ni->ni_list_prev = nt->nt_node_last;
-       if (nt->nt_node_last != NULL)
-               nt->nt_node_last->ni_list_next = ni;
-
-       nt->nt_node_last = ni;
-       if (nt->nt_node_first == NULL)
-               nt->nt_node_first = ni;
-
-       /* insert into the hash list */
-       ni->ni_hash_next = nt->nt_hash[hash];
-       if (ni->ni_hash_next != NULL)
-               nt->nt_hash[hash]->ni_hash_prev = ni;
-
-       ni->ni_hash_prev = NULL;
-       nt->nt_hash[hash] = ni;
-
-       spin_unlock_bh(&nt->nt_nodelock);
-}
-
-struct bss *wlan_find_node(struct ath6kl_node_table *nt,
-                          const u8 *mac_addr)
-{
-       struct bss *ni, *found_ni = NULL;
-       int hash;
-
-       spin_lock_bh(&nt->nt_nodelock);
-
-       hash = ATH6KL_NODE_HASH(mac_addr);
-       for (ni = nt->nt_hash[hash]; ni; ni = ni->ni_hash_next) {
-               if (memcmp(ni->ni_macaddr, mac_addr, ETH_ALEN) == 0) {
-                       ni->ni_refcnt++;
-                       found_ni = ni;
-                       break;
-               }
-       }
-
-       spin_unlock_bh(&nt->nt_nodelock);
-
-       return found_ni;
-}
-
-void wlan_node_reclaim(struct ath6kl_node_table *nt, struct bss *ni)
-{
-       int hash;
-
-       spin_lock_bh(&nt->nt_nodelock);
-
-       if (ni->ni_list_prev == NULL)
-               /* fix list head */
-               nt->nt_node_first = ni->ni_list_next;
-       else
-               ni->ni_list_prev->ni_list_next = ni->ni_list_next;
-
-       if (ni->ni_list_next == NULL)
-               /* fix list tail */
-               nt->nt_node_last = ni->ni_list_prev;
-       else
-               ni->ni_list_next->ni_list_prev = ni->ni_list_prev;
-
-       if (ni->ni_hash_prev == NULL) {
-               /* first in list so fix the list head */
-               hash = ATH6KL_NODE_HASH(ni->ni_macaddr);
-               nt->nt_hash[hash] = ni->ni_hash_next;
-       } else {
-               ni->ni_hash_prev->ni_hash_next = ni->ni_hash_next;
-       }
-
-       if (ni->ni_hash_next != NULL)
-               ni->ni_hash_next->ni_hash_prev = ni->ni_hash_prev;
-
-       wlan_node_free(ni);
-
-       spin_unlock_bh(&nt->nt_nodelock);
-}
-
-static void wlan_node_dec_free(struct bss *ni)
-{
-       if ((ni->ni_refcnt--) == 1)
-               wlan_node_free(ni);
-}
-
-void wlan_free_allnodes(struct ath6kl_node_table *nt)
-{
-       struct bss *ni;
-
-       while ((ni = nt->nt_node_first) != NULL)
-               wlan_node_reclaim(nt, ni);
-}
-
-void wlan_iterate_nodes(struct ath6kl_node_table *nt, void *arg)
-{
-       struct bss *ni;
-
-       spin_lock_bh(&nt->nt_nodelock);
-       for (ni = nt->nt_node_first; ni; ni = ni->ni_list_next) {
-                       ni->ni_refcnt++;
-                       ath6kl_cfg80211_scan_node(arg, ni);
-                       wlan_node_dec_free(ni);
-       }
-       spin_unlock_bh(&nt->nt_nodelock);
-}
-
-void wlan_node_table_init(struct ath6kl_node_table *nt)
-{
-       ath6kl_dbg(ATH6KL_DBG_WLAN_NODE, "node table = 0x%lx\n",
-                  (unsigned long)nt);
-
-       memset(nt, 0, sizeof(struct ath6kl_node_table));
-
-       spin_lock_init(&nt->nt_nodelock);
-
-       nt->nt_node_age = WLAN_NODE_INACT_TIMEOUT_MSEC;
-}
-
-void wlan_refresh_inactive_nodes(struct ath6kl *ar)
-{
-       struct ath6kl_node_table *nt = &ar->scan_table;
-       struct bss *bss;
-       u32 now;
-
-       now = jiffies_to_msecs(jiffies);
-       bss = nt->nt_node_first;
-       while (bss != NULL) {
-               /* refresh all nodes except the current bss */
-               if (memcmp(ar->bssid, bss->ni_macaddr, ETH_ALEN) != 0) {
-                       if (((now - bss->ni_tstamp) > nt->nt_node_age)
-                           || --bss->ni_actcnt == 0) {
-                               wlan_node_reclaim(nt, bss);
-                       }
-               }
-               bss = bss->ni_list_next;
-       }
-}
-
-void wlan_node_table_cleanup(struct ath6kl_node_table *nt)
-{
-       wlan_free_allnodes(nt);
-}
-
-struct bss *wlan_find_ssid_node(struct ath6kl_node_table *nt, u8 * ssid,
-                               u32 ssid_len, bool is_wpa2, bool match_ssid)
-{
-       struct bss *ni, *found_ni = NULL;
-       u8 *ie_ssid;
-
-       spin_lock_bh(&nt->nt_nodelock);
-
-       for (ni = nt->nt_node_first; ni; ni = ni->ni_list_next) {
-
-               ie_ssid = ni->ni_cie.ie_ssid;
-
-               if ((ie_ssid[1] <= IEEE80211_MAX_SSID_LEN) &&
-                   (memcmp(ssid, &ie_ssid[2], ssid_len) == 0)) {
-
-                               if (match_ssid ||
-                                   (is_wpa2 && ni->ni_cie.ie_rsn != NULL) ||
-                                   (!is_wpa2 && ni->ni_cie.ie_wpa != NULL)) {
-                                       ni->ni_refcnt++;
-                                       found_ni = ni;
-                                       break;
-                               }
-               }
-       }
-
-       spin_unlock_bh(&nt->nt_nodelock);
-
-       return found_ni;
-}
-
-void wlan_node_return(struct ath6kl_node_table *nt, struct bss *ni)
-{
-       spin_lock_bh(&nt->nt_nodelock);
-       wlan_node_dec_free(ni);
-       spin_unlock_bh(&nt->nt_nodelock);
-}
index 34171604cbe409e65f4a8389a5149443b76f5b80..f1dc311ee0c7b041bdbcac42b5bc29b867e7cdd4 100644 (file)
@@ -25,6 +25,7 @@
 #include "hif-ops.h"
 #include "target.h"
 #include "debug.h"
+#include "cfg80211.h"
 
 struct ath6kl_sdio {
        struct sdio_func *func;
@@ -134,10 +135,12 @@ static int ath6kl_sdio_io(struct sdio_func *func, u32 request, u32 addr,
        int ret = 0;
 
        if (request & HIF_WRITE) {
+               /* FIXME: looks like ugly workaround for something */
                if (addr >= HIF_MBOX_BASE_ADDR &&
                    addr <= HIF_MBOX_END_ADDR)
                        addr += (HIF_MBOX_WIDTH - len);
 
+               /* FIXME: this also looks like ugly workaround */
                if (addr == HIF_MBOX0_EXT_BASE_ADDR)
                        addr += HIF_MBOX0_EXT_WIDTH - len;
 
@@ -152,6 +155,11 @@ static int ath6kl_sdio_io(struct sdio_func *func, u32 request, u32 addr,
                        ret = sdio_memcpy_fromio(func, buf, addr, len);
        }
 
+       ath6kl_dbg(ATH6KL_DBG_SDIO, "%s addr 0x%x%s buf 0x%p len %d\n",
+                  request & HIF_WRITE ? "wr" : "rd", addr,
+                  request & HIF_FIXED_ADDRESS ? " (fixed)" : "", buf, len);
+       ath6kl_dbg_dump(ATH6KL_DBG_SDIO_DUMP, NULL, "sdio ", buf, len);
+
        return ret;
 }
 
@@ -172,7 +180,8 @@ static struct bus_request *ath6kl_sdio_alloc_busreq(struct ath6kl_sdio *ar_sdio)
        list_del(&bus_req->list);
 
        spin_unlock_irqrestore(&ar_sdio->lock, flag);
-       ath6kl_dbg(ATH6KL_DBG_TRC, "%s: bus request 0x%p\n", __func__, bus_req);
+       ath6kl_dbg(ATH6KL_DBG_SCATTER, "%s: bus request 0x%p\n",
+                  __func__, bus_req);
 
        return bus_req;
 }
@@ -182,7 +191,8 @@ static void ath6kl_sdio_free_bus_req(struct ath6kl_sdio *ar_sdio,
 {
        unsigned long flag;
 
-       ath6kl_dbg(ATH6KL_DBG_TRC, "%s: bus request 0x%p\n", __func__, bus_req);
+       ath6kl_dbg(ATH6KL_DBG_SCATTER, "%s: bus request 0x%p\n",
+                  __func__, bus_req);
 
        spin_lock_irqsave(&ar_sdio->lock, flag);
        list_add_tail(&bus_req->list, &ar_sdio->bus_req_freeq);
@@ -213,16 +223,6 @@ static void ath6kl_sdio_setup_scat_data(struct hif_scatter_req *scat_req,
 
        /* assemble SG list */
        for (i = 0; i < scat_req->scat_entries; i++, sg++) {
-               if ((unsigned long)scat_req->scat_list[i].buf & 0x3)
-                       /*
-                        * Some scatter engines can handle unaligned
-                        * buffers, print this as informational only.
-                        */
-                       ath6kl_dbg(ATH6KL_DBG_SCATTER,
-                                  "(%s) scatter buffer is unaligned 0x%p\n",
-                                  scat_req->req & HIF_WRITE ? "WR" : "RD",
-                                  scat_req->scat_list[i].buf);
-
                ath6kl_dbg(ATH6KL_DBG_SCATTER, "%d: addr:0x%p, len:%d\n",
                           i, scat_req->scat_list[i].buf,
                           scat_req->scat_list[i].len);
@@ -447,6 +447,8 @@ static void ath6kl_sdio_irq_handler(struct sdio_func *func)
        int status;
        struct ath6kl_sdio *ar_sdio;
 
+       ath6kl_dbg(ATH6KL_DBG_SDIO, "irq\n");
+
        ar_sdio = sdio_get_drvdata(func);
        atomic_set(&ar_sdio->irq_handling, 1);
 
@@ -684,7 +686,7 @@ static int ath6kl_sdio_enable_scatter(struct ath6kl *ar)
                                MAX_SCATTER_REQUESTS, virt_scat);
 
                if (!ret) {
-                       ath6kl_dbg(ATH6KL_DBG_ANY,
+                       ath6kl_dbg(ATH6KL_DBG_SCATTER,
                                   "hif-scatter enabled: max scatter req : %d entries: %d\n",
                                   MAX_SCATTER_REQUESTS,
                                   MAX_SCATTER_ENTRIES_PER_REQ);
@@ -709,7 +711,7 @@ static int ath6kl_sdio_enable_scatter(struct ath6kl *ar)
                        return ret;
                }
 
-               ath6kl_dbg(ATH6KL_DBG_ANY,
+               ath6kl_dbg(ATH6KL_DBG_SCATTER,
                           "Vitual scatter enabled, max_scat_req:%d, entries:%d\n",
                           ATH6KL_SCATTER_REQS, ATH6KL_SCATTER_ENTRIES_PER_REQ);
 
@@ -721,6 +723,34 @@ static int ath6kl_sdio_enable_scatter(struct ath6kl *ar)
        return 0;
 }
 
+static int ath6kl_sdio_suspend(struct ath6kl *ar)
+{
+       struct ath6kl_sdio *ar_sdio = ath6kl_sdio_priv(ar);
+       struct sdio_func *func = ar_sdio->func;
+       mmc_pm_flag_t flags;
+       int ret;
+
+       flags = sdio_get_host_pm_caps(func);
+
+       if (!(flags & MMC_PM_KEEP_POWER))
+               /* as host doesn't support keep power we need to bail out */
+               ath6kl_dbg(ATH6KL_DBG_SDIO,
+                          "func %d doesn't support MMC_PM_KEEP_POWER\n",
+                          func->num);
+               return -EINVAL;
+
+       ret = sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER);
+       if (ret) {
+               printk(KERN_ERR "ath6kl: set sdio pm flags failed: %d\n",
+                      ret);
+               return ret;
+       }
+
+       ath6kl_deep_sleep_enable(ar);
+
+       return 0;
+}
+
 static const struct ath6kl_hif_ops ath6kl_sdio_ops = {
        .read_write_sync = ath6kl_sdio_read_write_sync,
        .write_async = ath6kl_sdio_write_async,
@@ -731,6 +761,7 @@ static const struct ath6kl_hif_ops ath6kl_sdio_ops = {
        .enable_scatter = ath6kl_sdio_enable_scatter,
        .scat_req_rw = ath6kl_sdio_async_rw_scatter,
        .cleanup_scatter = ath6kl_sdio_cleanup_scatter,
+       .suspend = ath6kl_sdio_suspend,
 };
 
 static int ath6kl_sdio_probe(struct sdio_func *func,
@@ -741,10 +772,10 @@ static int ath6kl_sdio_probe(struct sdio_func *func,
        struct ath6kl *ar;
        int count;
 
-       ath6kl_dbg(ATH6KL_DBG_TRC,
-                  "%s: func: 0x%X, vendor id: 0x%X, dev id: 0x%X, block size: 0x%X/0x%X\n",
-                  __func__, func->num, func->vendor,
-                  func->device, func->max_blksize, func->cur_blksize);
+       ath6kl_dbg(ATH6KL_DBG_SDIO,
+                  "new func %d vendor 0x%x device 0x%x block 0x%x/0x%x\n",
+                  func->num, func->vendor, func->device,
+                  func->max_blksize, func->cur_blksize);
 
        ar_sdio = kzalloc(sizeof(struct ath6kl_sdio), GFP_KERNEL);
        if (!ar_sdio)
@@ -800,10 +831,10 @@ static int ath6kl_sdio_probe(struct sdio_func *func,
                        ath6kl_err("Failed to enable 4-bit async irq mode %d\n",
                                   ret);
                        sdio_release_host(func);
-                       goto err_dma;
+                       goto err_cfg80211;
                }
 
-               ath6kl_dbg(ATH6KL_DBG_TRC, "4-bit async irq mode enabled\n");
+               ath6kl_dbg(ATH6KL_DBG_SDIO, "4-bit async irq mode enabled\n");
        }
 
        /* give us some time to enable, in ms */
@@ -813,7 +844,7 @@ static int ath6kl_sdio_probe(struct sdio_func *func,
 
        ret = ath6kl_sdio_power_on(ar_sdio);
        if (ret)
-               goto err_dma;
+               goto err_cfg80211;
 
        sdio_claim_host(func);
 
@@ -837,6 +868,8 @@ static int ath6kl_sdio_probe(struct sdio_func *func,
 
 err_off:
        ath6kl_sdio_power_off(ar_sdio);
+err_cfg80211:
+       ath6kl_cfg80211_deinit(ar_sdio->ar);
 err_dma:
        kfree(ar_sdio->dma_buffer);
 err_hif:
@@ -849,6 +882,10 @@ static void ath6kl_sdio_remove(struct sdio_func *func)
 {
        struct ath6kl_sdio *ar_sdio;
 
+       ath6kl_dbg(ATH6KL_DBG_SDIO,
+                  "removed func %d vendor 0x%x device 0x%x\n",
+                  func->num, func->vendor, func->device);
+
        ar_sdio = sdio_get_drvdata(func);
 
        ath6kl_stop_txrx(ar_sdio->ar);
index 519a013c99914be8334fb15b4c9f10b6b71da577..c9a76051f0424fd6f9a139a6a15b1776fe63a768 100644 (file)
@@ -20,6 +20,9 @@
 #define AR6003_BOARD_DATA_SZ           1024
 #define AR6003_BOARD_EXT_DATA_SZ       768
 
+#define AR6004_BOARD_DATA_SZ     7168
+#define AR6004_BOARD_EXT_DATA_SZ 0
+
 #define RESET_CONTROL_ADDRESS          0x00000000
 #define RESET_CONTROL_COLD_RST         0x00000100
 #define RESET_CONTROL_MBOX_RST         0x00000004
  * between the two, and is intended to remain constant (with additions only
  * at the end).
  */
-#define ATH6KL_HI_START_ADDR           0x00540600
+#define ATH6KL_AR6003_HI_START_ADDR           0x00540600
+#define ATH6KL_AR6004_HI_START_ADDR           0x00400800
 
 /*
  * These are items that the Host may need to access
@@ -300,6 +304,11 @@ struct host_interest {
 #define HI_OPTION_FW_MODE_BSS_STA 0x1
 #define HI_OPTION_FW_MODE_AP      0x2
 
+#define HI_OPTION_FW_SUBMODE_NONE      0x0
+#define HI_OPTION_FW_SUBMODE_P2PDEV    0x1
+#define HI_OPTION_FW_SUBMODE_P2PCLIENT 0x2
+#define HI_OPTION_FW_SUBMODE_P2PGO     0x3
+
 #define HI_OPTION_NUM_DEV_SHIFT   0x9
 
 #define HI_OPTION_FW_BRIDGE_SHIFT 0x04
@@ -312,20 +321,44 @@ struct host_interest {
 |------------------------------------------------------------------------------|
 */
 #define HI_OPTION_FW_MODE_SHIFT        0xC
+#define HI_OPTION_FW_SUBMODE_SHIFT     0x14
 
 /* Convert a Target virtual address into a Target physical address */
-#define TARG_VTOP(vaddr)   (vaddr & 0x001fffff)
+#define AR6003_VTOP(vaddr) ((vaddr) & 0x001fffff)
+#define AR6004_VTOP(vaddr) (vaddr)
+
+#define TARG_VTOP(target_type, vaddr) \
+       (((target_type) == TARGET_TYPE_AR6003) ? AR6003_VTOP(vaddr) : \
+       (((target_type) == TARGET_TYPE_AR6004) ? AR6004_VTOP(vaddr) : 0))
 
-#define AR6003_REV2_APP_START_OVERRIDE          0x944C00
 #define AR6003_REV2_APP_LOAD_ADDRESS            0x543180
 #define AR6003_REV2_BOARD_EXT_DATA_ADDRESS      0x57E500
 #define AR6003_REV2_DATASET_PATCH_ADDRESS       0x57e884
 #define AR6003_REV2_RAM_RESERVE_SIZE            6912
 
-#define AR6003_REV3_APP_START_OVERRIDE          0x945d00
 #define AR6003_REV3_APP_LOAD_ADDRESS            0x545000
 #define AR6003_REV3_BOARD_EXT_DATA_ADDRESS      0x542330
 #define AR6003_REV3_DATASET_PATCH_ADDRESS       0x57FF74
 #define AR6003_REV3_RAM_RESERVE_SIZE            512
 
+#define AR6004_REV1_BOARD_DATA_ADDRESS          0x435400
+#define AR6004_REV1_BOARD_EXT_DATA_ADDRESS      0x437000
+#define AR6004_REV1_RAM_RESERVE_SIZE            11264
+
+#define ATH6KL_FWLOG_PAYLOAD_SIZE              1500
+
+struct ath6kl_dbglog_buf {
+       __le32 next;
+       __le32 buffer_addr;
+       __le32 bufsize;
+       __le32 length;
+       __le32 count;
+       __le32 free;
+} __packed;
+
+struct ath6kl_dbglog_hdr {
+       __le32 dbuf_addr;
+       __le32 dropped;
+} __packed;
+
 #endif
diff --git a/drivers/net/wireless/ath/ath6kl/testmode.c b/drivers/net/wireless/ath/ath6kl/testmode.c
new file mode 100644 (file)
index 0000000..381eb66
--- /dev/null
@@ -0,0 +1,167 @@
+/*
+ * Copyright (c) 2010-2011 Atheros Communications 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
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "testmode.h"
+
+#include <net/netlink.h>
+
+enum ath6kl_tm_attr {
+       __ATH6KL_TM_ATTR_INVALID        = 0,
+       ATH6KL_TM_ATTR_CMD              = 1,
+       ATH6KL_TM_ATTR_DATA             = 2,
+
+       /* keep last */
+       __ATH6KL_TM_ATTR_AFTER_LAST,
+       ATH6KL_TM_ATTR_MAX              = __ATH6KL_TM_ATTR_AFTER_LAST - 1,
+};
+
+enum ath6kl_tm_cmd {
+       ATH6KL_TM_CMD_TCMD              = 0,
+       ATH6KL_TM_CMD_RX_REPORT         = 1,
+};
+
+#define ATH6KL_TM_DATA_MAX_LEN         5000
+
+static const struct nla_policy ath6kl_tm_policy[ATH6KL_TM_ATTR_MAX + 1] = {
+       [ATH6KL_TM_ATTR_CMD]            = { .type = NLA_U32 },
+       [ATH6KL_TM_ATTR_DATA]           = { .type = NLA_BINARY,
+                                           .len = ATH6KL_TM_DATA_MAX_LEN },
+};
+
+void ath6kl_tm_rx_report_event(struct ath6kl *ar, void *buf, size_t buf_len)
+{
+       if (down_interruptible(&ar->sem))
+               return;
+
+       kfree(ar->tm.rx_report);
+
+       ar->tm.rx_report = kmemdup(buf, buf_len, GFP_KERNEL);
+       ar->tm.rx_report_len = buf_len;
+
+       up(&ar->sem);
+
+       wake_up(&ar->event_wq);
+}
+
+static int ath6kl_tm_rx_report(struct ath6kl *ar, void *buf, size_t buf_len,
+                              struct sk_buff *skb)
+{
+       int ret = 0;
+       long left;
+
+       if (down_interruptible(&ar->sem))
+               return -ERESTARTSYS;
+
+       if (!test_bit(WMI_READY, &ar->flag)) {
+               ret = -EIO;
+               goto out;
+       }
+
+       if (test_bit(DESTROY_IN_PROGRESS, &ar->flag)) {
+               ret = -EBUSY;
+               goto out;
+       }
+
+       if (ath6kl_wmi_test_cmd(ar->wmi, buf, buf_len) < 0) {
+               up(&ar->sem);
+               return -EIO;
+       }
+
+       left = wait_event_interruptible_timeout(ar->event_wq,
+                                               ar->tm.rx_report != NULL,
+                                               WMI_TIMEOUT);
+
+       if (left == 0) {
+               ret = -ETIMEDOUT;
+               goto out;
+       } else if (left < 0) {
+               ret = left;
+               goto out;
+       }
+
+       if (ar->tm.rx_report == NULL || ar->tm.rx_report_len == 0) {
+               ret = -EINVAL;
+               goto out;
+       }
+
+       NLA_PUT(skb, ATH6KL_TM_ATTR_DATA, ar->tm.rx_report_len,
+               ar->tm.rx_report);
+
+       kfree(ar->tm.rx_report);
+       ar->tm.rx_report = NULL;
+
+out:
+       up(&ar->sem);
+
+       return ret;
+
+nla_put_failure:
+       ret = -ENOBUFS;
+       goto out;
+}
+
+int ath6kl_tm_cmd(struct wiphy *wiphy, void *data, int len)
+{
+       struct ath6kl *ar = wiphy_priv(wiphy);
+       struct nlattr *tb[ATH6KL_TM_ATTR_MAX + 1];
+       int err, buf_len, reply_len;
+       struct sk_buff *skb;
+       void *buf;
+
+       err = nla_parse(tb, ATH6KL_TM_ATTR_MAX, data, len,
+                       ath6kl_tm_policy);
+       if (err)
+               return err;
+
+       if (!tb[ATH6KL_TM_ATTR_CMD])
+               return -EINVAL;
+
+       switch (nla_get_u32(tb[ATH6KL_TM_ATTR_CMD])) {
+       case ATH6KL_TM_CMD_TCMD:
+               if (!tb[ATH6KL_TM_ATTR_DATA])
+                       return -EINVAL;
+
+               buf = nla_data(tb[ATH6KL_TM_ATTR_DATA]);
+               buf_len = nla_len(tb[ATH6KL_TM_ATTR_DATA]);
+
+               ath6kl_wmi_test_cmd(ar->wmi, buf, buf_len);
+
+               return 0;
+
+               break;
+       case ATH6KL_TM_CMD_RX_REPORT:
+               if (!tb[ATH6KL_TM_ATTR_DATA])
+                       return -EINVAL;
+
+               buf = nla_data(tb[ATH6KL_TM_ATTR_DATA]);
+               buf_len = nla_len(tb[ATH6KL_TM_ATTR_DATA]);
+
+               reply_len = nla_total_size(ATH6KL_TM_DATA_MAX_LEN);
+               skb = cfg80211_testmode_alloc_reply_skb(wiphy, reply_len);
+               if (!skb)
+                       return -ENOMEM;
+
+               err = ath6kl_tm_rx_report(ar, buf, buf_len, skb);
+               if (err < 0) {
+                       kfree_skb(skb);
+                       return err;
+               }
+
+               return cfg80211_testmode_reply(skb);
+       default:
+               return -EOPNOTSUPP;
+       }
+}
diff --git a/drivers/net/wireless/ath/ath6kl/testmode.h b/drivers/net/wireless/ath/ath6kl/testmode.h
new file mode 100644 (file)
index 0000000..43dffcc
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2010-2011 Atheros Communications 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
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "core.h"
+
+#ifdef CONFIG_NL80211_TESTMODE
+
+void ath6kl_tm_rx_report_event(struct ath6kl *ar, void *buf, size_t buf_len);
+int ath6kl_tm_cmd(struct wiphy *wiphy, void *data, int len);
+
+#else
+
+static inline void ath6kl_tm_rx_report_event(struct ath6kl *ar, void *buf,
+                                            size_t buf_len)
+{
+}
+
+static inline int ath6kl_tm_cmd(struct wiphy *wiphy, void *data, int len)
+{
+       return 0;
+}
+
+#endif
index 167bdb9cf68daecc6ea909bed8d5f6d0b32c8467..a7117074f81c033776bc89808c8897e1146c4953 100644 (file)
@@ -239,7 +239,6 @@ int ath6kl_data_tx(struct sk_buff *skb, struct net_device *dev)
        u16 htc_tag = ATH6KL_DATA_PKT_TAG;
        u8 ac = 99 ; /* initialize to unmapped ac */
        bool chk_adhoc_ps_mapping = false, more_data = false;
-       struct wmi_tx_meta_v2 meta_v2;
        int ret;
 
        ath6kl_dbg(ATH6KL_DBG_WLAN_TX,
@@ -262,8 +261,6 @@ int ath6kl_data_tx(struct sk_buff *skb, struct net_device *dev)
        }
 
        if (test_bit(WMI_ENABLED, &ar->flag)) {
-               memset(&meta_v2, 0, sizeof(meta_v2));
-
                if (skb_headroom(skb) < dev->needed_headroom) {
                        WARN_ON(1);
                        goto fail_tx;
@@ -320,12 +317,31 @@ int ath6kl_data_tx(struct sk_buff *skb, struct net_device *dev)
 
        spin_unlock_bh(&ar->lock);
 
+       if (!IS_ALIGNED((unsigned long) skb->data - HTC_HDR_LENGTH, 4) &&
+           skb_cloned(skb)) {
+               /*
+                * We will touch (move the buffer data to align it. Since the
+                * skb buffer is cloned and not only the header is changed, we
+                * have to copy it to allow the changes. Since we are copying
+                * the data here, we may as well align it by reserving suitable
+                * headroom to avoid the memmove in ath6kl_htc_tx_buf_align().
+                */
+               struct sk_buff *nskb;
+
+               nskb = skb_copy_expand(skb, HTC_HDR_LENGTH, 0, GFP_ATOMIC);
+               if (nskb == NULL)
+                       goto fail_tx;
+               kfree_skb(skb);
+               skb = nskb;
+       }
+
        cookie->skb = skb;
        cookie->map_no = map_no;
        set_htc_pkt_info(&cookie->htc_pkt, cookie, skb->data, skb->len,
                         eid, htc_tag);
 
-       ath6kl_dbg_dump(ATH6KL_DBG_RAW_BYTES, __func__, skb->data, skb->len);
+       ath6kl_dbg_dump(ATH6KL_DBG_RAW_BYTES, __func__, "tx ",
+                       skb->data, skb->len);
 
        /*
         * HTC interface is asynchronous, if this fails, cleanup will
@@ -689,6 +705,8 @@ void ath6kl_rx_refill(struct htc_target *target, enum htc_endpoint_id endpoint)
                        break;
 
                packet = (struct htc_packet *) skb->head;
+               if (!IS_ALIGNED((unsigned long) skb->data, 4))
+                       skb->data = PTR_ALIGN(skb->data - 4, 4);
                set_htc_rxpkt_info(packet, skb, skb->data,
                                ATH6KL_BUFFER_SIZE, endpoint);
                list_add_tail(&packet->list, &queue);
@@ -709,6 +727,8 @@ void ath6kl_refill_amsdu_rxbufs(struct ath6kl *ar, int count)
                        return;
 
                packet = (struct htc_packet *) skb->head;
+               if (!IS_ALIGNED((unsigned long) skb->data, 4))
+                       skb->data = PTR_ALIGN(skb->data - 4, 4);
                set_htc_rxpkt_info(packet, skb, skb->data,
                                   ATH6KL_AMSDU_BUFFER_SIZE, 0);
                spin_lock_bh(&ar->lock);
@@ -812,7 +832,7 @@ static void aggr_slice_amsdu(struct aggr_info *p_aggr,
                /* Add the length of A-MSDU subframe padding bytes -
                 * Round to nearest word.
                 */
-               frame_8023_len = ALIGN(frame_8023_len + 3, 3);
+               frame_8023_len = ALIGN(frame_8023_len, 4);
 
                framep += frame_8023_len;
                amsdu_len -= frame_8023_len;
@@ -1044,12 +1064,13 @@ void ath6kl_rx(struct htc_target *target, struct htc_packet *packet)
        ar->net_stats.rx_packets++;
        ar->net_stats.rx_bytes += packet->act_len;
 
+       spin_unlock_bh(&ar->lock);
+
        skb_put(skb, packet->act_len + HTC_HDR_LENGTH);
        skb_pull(skb, HTC_HDR_LENGTH);
 
-       ath6kl_dbg_dump(ATH6KL_DBG_RAW_BYTES, __func__, skb->data, skb->len);
-
-       spin_unlock_bh(&ar->lock);
+       ath6kl_dbg_dump(ATH6KL_DBG_RAW_BYTES, __func__, "rx ",
+                       skb->data, skb->len);
 
        skb->dev = ar->net_dev;
 
@@ -1065,9 +1086,8 @@ void ath6kl_rx(struct htc_target *target, struct htc_packet *packet)
                return;
        }
 
-       min_hdr_len = sizeof(struct ethhdr);
-       min_hdr_len += sizeof(struct wmi_data_hdr) +
-                      sizeof(struct ath6kl_llc_snap_hdr);
+       min_hdr_len = sizeof(struct ethhdr) + sizeof(struct wmi_data_hdr) +
+                     sizeof(struct ath6kl_llc_snap_hdr);
 
        dhdr = (struct wmi_data_hdr *) skb->data;
 
@@ -1163,8 +1183,7 @@ void ath6kl_rx(struct htc_target *target, struct htc_packet *packet)
        seq_no = wmi_data_hdr_get_seqno(dhdr);
        meta_type = wmi_data_hdr_get_meta(dhdr);
        dot11_hdr = wmi_data_hdr_get_dot11(dhdr);
-
-       ath6kl_wmi_data_hdr_remove(ar->wmi, skb);
+       skb_pull(skb, sizeof(struct wmi_data_hdr));
 
        switch (meta_type) {
        case WMI_META_VERSION_1:
@@ -1231,9 +1250,15 @@ void ath6kl_rx(struct htc_target *target, struct htc_packet *packet)
                        ath6kl_data_tx(skb1, ar->net_dev);
        }
 
-       if (!aggr_process_recv_frm(ar->aggr_cntxt, tid, seq_no,
-                                  is_amsdu, skb))
-               ath6kl_deliver_frames_to_nw_stack(ar->net_dev, skb);
+       datap = (struct ethhdr *) skb->data;
+
+       if (is_unicast_ether_addr(datap->h_dest) &&
+           aggr_process_recv_frm(ar->aggr_cntxt, tid, seq_no,
+                                 is_amsdu, skb))
+               /* aggregation code will handle the skb */
+               return;
+
+       ath6kl_deliver_frames_to_nw_stack(ar->net_dev, skb);
 }
 
 static void aggr_timeout(unsigned long arg)
@@ -1250,10 +1275,6 @@ static void aggr_timeout(unsigned long arg)
                if (!rxtid->aggr || !rxtid->timer_mon || rxtid->progress)
                        continue;
 
-               /*
-                * FIXME: these timeouts happen quite fruently, something
-                * line once within 60 seconds. Investigate why.
-                */
                stats->num_timeouts++;
                ath6kl_dbg(ATH6KL_DBG_AGGR,
                           "aggr timeout (st %d end %d)\n",
index f5aa33dd4c42da1127b462d75bb718336005aa1b..a7de23cbd2c74db92a5a3348efe847cb88374c14 100644 (file)
@@ -17,6 +17,9 @@
 #include <linux/ip.h>
 #include "core.h"
 #include "debug.h"
+#include "testmode.h"
+#include "../regd.h"
+#include "../regd_common.h"
 
 static int ath6kl_wmi_sync_point(struct wmi *wmi);
 
@@ -167,9 +170,11 @@ int ath6kl_wmi_data_hdr_add(struct wmi *wmi, struct sk_buff *skb,
        if (WARN_ON(skb == NULL))
                return -EINVAL;
 
-       ret = ath6kl_wmi_meta_add(wmi, skb, &meta_ver, tx_meta_info);
-       if (ret)
-               return ret;
+       if (tx_meta_info) {
+               ret = ath6kl_wmi_meta_add(wmi, skb, &meta_ver, tx_meta_info);
+               if (ret)
+                       return ret;
+       }
 
        skb_push(skb, sizeof(struct wmi_data_hdr));
 
@@ -376,35 +381,6 @@ int ath6kl_wmi_dot3_2_dix(struct sk_buff *skb)
        return 0;
 }
 
-int ath6kl_wmi_data_hdr_remove(struct wmi *wmi, struct sk_buff *skb)
-{
-       if (WARN_ON(skb == NULL))
-               return -EINVAL;
-
-       skb_pull(skb, sizeof(struct wmi_data_hdr));
-
-       return 0;
-}
-
-static void ath6kl_wmi_convert_bssinfo_hdr2_to_hdr(struct sk_buff *skb,
-                                                  u8 *datap)
-{
-       struct wmi_bss_info_hdr2 bih2;
-       struct wmi_bss_info_hdr *bih;
-
-       memcpy(&bih2, datap, sizeof(struct wmi_bss_info_hdr2));
-
-       skb_push(skb, 4);
-       bih = (struct wmi_bss_info_hdr *) skb->data;
-
-       bih->ch = bih2.ch;
-       bih->frame_type = bih2.frame_type;
-       bih->snr = bih2.snr;
-       bih->rssi = a_cpu_to_sle16(bih2.snr - 95);
-       bih->ie_mask = cpu_to_le32(le16_to_cpu(bih2.ie_mask));
-       memcpy(bih->bssid, bih2.bssid, ETH_ALEN);
-}
-
 static int ath6kl_wmi_tx_complete_event_rx(u8 *datap, int len)
 {
        struct tx_complete_msg_v1 *msg_v1;
@@ -433,6 +409,201 @@ static int ath6kl_wmi_tx_complete_event_rx(u8 *datap, int len)
        return 0;
 }
 
+static int ath6kl_wmi_remain_on_chnl_event_rx(struct wmi *wmi, u8 *datap,
+                                             int len)
+{
+       struct wmi_remain_on_chnl_event *ev;
+       u32 freq;
+       u32 dur;
+       struct ieee80211_channel *chan;
+       struct ath6kl *ar = wmi->parent_dev;
+
+       if (len < sizeof(*ev))
+               return -EINVAL;
+
+       ev = (struct wmi_remain_on_chnl_event *) datap;
+       freq = le32_to_cpu(ev->freq);
+       dur = le32_to_cpu(ev->duration);
+       ath6kl_dbg(ATH6KL_DBG_WMI, "remain_on_chnl: freq=%u dur=%u\n",
+                  freq, dur);
+       chan = ieee80211_get_channel(ar->wdev->wiphy, freq);
+       if (!chan) {
+               ath6kl_dbg(ATH6KL_DBG_WMI, "remain_on_chnl: Unknown channel "
+                          "(freq=%u)\n", freq);
+               return -EINVAL;
+       }
+       cfg80211_ready_on_channel(ar->net_dev, 1, chan, NL80211_CHAN_NO_HT,
+                                 dur, GFP_ATOMIC);
+
+       return 0;
+}
+
+static int ath6kl_wmi_cancel_remain_on_chnl_event_rx(struct wmi *wmi,
+                                                    u8 *datap, int len)
+{
+       struct wmi_cancel_remain_on_chnl_event *ev;
+       u32 freq;
+       u32 dur;
+       struct ieee80211_channel *chan;
+       struct ath6kl *ar = wmi->parent_dev;
+
+       if (len < sizeof(*ev))
+               return -EINVAL;
+
+       ev = (struct wmi_cancel_remain_on_chnl_event *) datap;
+       freq = le32_to_cpu(ev->freq);
+       dur = le32_to_cpu(ev->duration);
+       ath6kl_dbg(ATH6KL_DBG_WMI, "cancel_remain_on_chnl: freq=%u dur=%u "
+                  "status=%u\n", freq, dur, ev->status);
+       chan = ieee80211_get_channel(ar->wdev->wiphy, freq);
+       if (!chan) {
+               ath6kl_dbg(ATH6KL_DBG_WMI, "cancel_remain_on_chnl: Unknown "
+                          "channel (freq=%u)\n", freq);
+               return -EINVAL;
+       }
+       cfg80211_remain_on_channel_expired(ar->net_dev, 1, chan,
+                                          NL80211_CHAN_NO_HT, GFP_ATOMIC);
+
+       return 0;
+}
+
+static int ath6kl_wmi_tx_status_event_rx(struct wmi *wmi, u8 *datap, int len)
+{
+       struct wmi_tx_status_event *ev;
+       u32 id;
+       struct ath6kl *ar = wmi->parent_dev;
+
+       if (len < sizeof(*ev))
+               return -EINVAL;
+
+       ev = (struct wmi_tx_status_event *) datap;
+       id = le32_to_cpu(ev->id);
+       ath6kl_dbg(ATH6KL_DBG_WMI, "tx_status: id=%x ack_status=%u\n",
+                  id, ev->ack_status);
+       if (wmi->last_mgmt_tx_frame) {
+               cfg80211_mgmt_tx_status(ar->net_dev, id,
+                                       wmi->last_mgmt_tx_frame,
+                                       wmi->last_mgmt_tx_frame_len,
+                                       !!ev->ack_status, GFP_ATOMIC);
+               kfree(wmi->last_mgmt_tx_frame);
+               wmi->last_mgmt_tx_frame = NULL;
+               wmi->last_mgmt_tx_frame_len = 0;
+       }
+
+       return 0;
+}
+
+static int ath6kl_wmi_rx_probe_req_event_rx(struct wmi *wmi, u8 *datap, int len)
+{
+       struct wmi_p2p_rx_probe_req_event *ev;
+       u32 freq;
+       u16 dlen;
+       struct ath6kl *ar = wmi->parent_dev;
+
+       if (len < sizeof(*ev))
+               return -EINVAL;
+
+       ev = (struct wmi_p2p_rx_probe_req_event *) datap;
+       freq = le32_to_cpu(ev->freq);
+       dlen = le16_to_cpu(ev->len);
+       if (datap + len < ev->data + dlen) {
+               ath6kl_err("invalid wmi_p2p_rx_probe_req_event: "
+                          "len=%d dlen=%u\n", len, dlen);
+               return -EINVAL;
+       }
+       ath6kl_dbg(ATH6KL_DBG_WMI, "rx_probe_req: len=%u freq=%u "
+                  "probe_req_report=%d\n",
+                  dlen, freq, ar->probe_req_report);
+
+       if (ar->probe_req_report || ar->nw_type == AP_NETWORK)
+               cfg80211_rx_mgmt(ar->net_dev, freq, ev->data, dlen, GFP_ATOMIC);
+
+       return 0;
+}
+
+static int ath6kl_wmi_p2p_capabilities_event_rx(u8 *datap, int len)
+{
+       struct wmi_p2p_capabilities_event *ev;
+       u16 dlen;
+
+       if (len < sizeof(*ev))
+               return -EINVAL;
+
+       ev = (struct wmi_p2p_capabilities_event *) datap;
+       dlen = le16_to_cpu(ev->len);
+       ath6kl_dbg(ATH6KL_DBG_WMI, "p2p_capab: len=%u\n", dlen);
+
+       return 0;
+}
+
+static int ath6kl_wmi_rx_action_event_rx(struct wmi *wmi, u8 *datap, int len)
+{
+       struct wmi_rx_action_event *ev;
+       u32 freq;
+       u16 dlen;
+       struct ath6kl *ar = wmi->parent_dev;
+
+       if (len < sizeof(*ev))
+               return -EINVAL;
+
+       ev = (struct wmi_rx_action_event *) datap;
+       freq = le32_to_cpu(ev->freq);
+       dlen = le16_to_cpu(ev->len);
+       if (datap + len < ev->data + dlen) {
+               ath6kl_err("invalid wmi_rx_action_event: "
+                          "len=%d dlen=%u\n", len, dlen);
+               return -EINVAL;
+       }
+       ath6kl_dbg(ATH6KL_DBG_WMI, "rx_action: len=%u freq=%u\n", dlen, freq);
+       cfg80211_rx_mgmt(ar->net_dev, freq, ev->data, dlen, GFP_ATOMIC);
+
+       return 0;
+}
+
+static int ath6kl_wmi_p2p_info_event_rx(u8 *datap, int len)
+{
+       struct wmi_p2p_info_event *ev;
+       u32 flags;
+       u16 dlen;
+
+       if (len < sizeof(*ev))
+               return -EINVAL;
+
+       ev = (struct wmi_p2p_info_event *) datap;
+       flags = le32_to_cpu(ev->info_req_flags);
+       dlen = le16_to_cpu(ev->len);
+       ath6kl_dbg(ATH6KL_DBG_WMI, "p2p_info: flags=%x len=%d\n", flags, dlen);
+
+       if (flags & P2P_FLAG_CAPABILITIES_REQ) {
+               struct wmi_p2p_capabilities *cap;
+               if (dlen < sizeof(*cap))
+                       return -EINVAL;
+               cap = (struct wmi_p2p_capabilities *) ev->data;
+               ath6kl_dbg(ATH6KL_DBG_WMI, "p2p_info: GO Power Save = %d\n",
+                          cap->go_power_save);
+       }
+
+       if (flags & P2P_FLAG_MACADDR_REQ) {
+               struct wmi_p2p_macaddr *mac;
+               if (dlen < sizeof(*mac))
+                       return -EINVAL;
+               mac = (struct wmi_p2p_macaddr *) ev->data;
+               ath6kl_dbg(ATH6KL_DBG_WMI, "p2p_info: MAC Address = %pM\n",
+                          mac->mac_addr);
+       }
+
+       if (flags & P2P_FLAG_HMODEL_REQ) {
+               struct wmi_p2p_hmodel *mod;
+               if (dlen < sizeof(*mod))
+                       return -EINVAL;
+               mod = (struct wmi_p2p_hmodel *) ev->data;
+               ath6kl_dbg(ATH6KL_DBG_WMI, "p2p_info: P2P Model = %d (%s)\n",
+                          mod->p2p_model,
+                          mod->p2p_model ? "host" : "firmware");
+       }
+       return 0;
+}
+
 static inline struct sk_buff *ath6kl_wmi_get_new_buf(u32 size)
 {
        struct sk_buff *skb;
@@ -478,18 +649,84 @@ static int ath6kl_wmi_ready_event_rx(struct wmi *wmi, u8 *datap, int len)
        return 0;
 }
 
+/*
+ * Mechanism to modify the roaming behavior in the firmware. The lower rssi
+ * at which the station has to roam can be passed with
+ * WMI_SET_LRSSI_SCAN_PARAMS. Subtract 96 from RSSI to get the signal level
+ * in dBm.
+ */
+int ath6kl_wmi_set_roam_lrssi_cmd(struct wmi *wmi, u8 lrssi)
+{
+       struct sk_buff *skb;
+       struct roam_ctrl_cmd *cmd;
+
+       skb = ath6kl_wmi_get_new_buf(sizeof(*cmd));
+       if (!skb)
+               return -ENOMEM;
+
+       cmd = (struct roam_ctrl_cmd *) skb->data;
+
+       cmd->info.params.lrssi_scan_period = cpu_to_le16(DEF_LRSSI_SCAN_PERIOD);
+       cmd->info.params.lrssi_scan_threshold = a_cpu_to_sle16(lrssi +
+                                                      DEF_SCAN_FOR_ROAM_INTVL);
+       cmd->info.params.lrssi_roam_threshold = a_cpu_to_sle16(lrssi);
+       cmd->info.params.roam_rssi_floor = DEF_LRSSI_ROAM_FLOOR;
+       cmd->roam_ctrl = WMI_SET_LRSSI_SCAN_PARAMS;
+
+       ath6kl_wmi_cmd_send(wmi, skb, WMI_SET_ROAM_CTRL_CMDID, NO_SYNC_WMIFLAG);
+
+       return 0;
+}
+
 static int ath6kl_wmi_connect_event_rx(struct wmi *wmi, u8 *datap, int len)
 {
        struct wmi_connect_event *ev;
        u8 *pie, *peie;
+       struct ath6kl *ar = wmi->parent_dev;
 
        if (len < sizeof(struct wmi_connect_event))
                return -EINVAL;
 
        ev = (struct wmi_connect_event *) datap;
 
-       ath6kl_dbg(ATH6KL_DBG_WMI, "%s: freq %d bssid %pM\n",
-                  __func__, ev->ch, ev->bssid);
+       if (ar->nw_type == AP_NETWORK) {
+               /* AP mode start/STA connected event */
+               struct net_device *dev = ar->net_dev;
+               if (memcmp(dev->dev_addr, ev->u.ap_bss.bssid, ETH_ALEN) == 0) {
+                       ath6kl_dbg(ATH6KL_DBG_WMI, "%s: freq %d bssid %pM "
+                                  "(AP started)\n",
+                                  __func__, le16_to_cpu(ev->u.ap_bss.ch),
+                                  ev->u.ap_bss.bssid);
+                       ath6kl_connect_ap_mode_bss(
+                               ar, le16_to_cpu(ev->u.ap_bss.ch));
+               } else {
+                       ath6kl_dbg(ATH6KL_DBG_WMI, "%s: aid %u mac_addr %pM "
+                                  "auth=%u keymgmt=%u cipher=%u apsd_info=%u "
+                                  "(STA connected)\n",
+                                  __func__, ev->u.ap_sta.aid,
+                                  ev->u.ap_sta.mac_addr,
+                                  ev->u.ap_sta.auth,
+                                  ev->u.ap_sta.keymgmt,
+                                  le16_to_cpu(ev->u.ap_sta.cipher),
+                                  ev->u.ap_sta.apsd_info);
+                       ath6kl_connect_ap_mode_sta(
+                               ar, ev->u.ap_sta.aid, ev->u.ap_sta.mac_addr,
+                               ev->u.ap_sta.keymgmt,
+                               le16_to_cpu(ev->u.ap_sta.cipher),
+                               ev->u.ap_sta.auth, ev->assoc_req_len,
+                               ev->assoc_info + ev->beacon_ie_len);
+               }
+               return 0;
+       }
+
+       /* STA/IBSS mode connection event */
+
+       ath6kl_dbg(ATH6KL_DBG_WMI,
+                  "wmi event connect freq %d bssid %pM listen_intvl %d beacon_intvl %d type %d\n",
+                  le16_to_cpu(ev->u.sta.ch), ev->u.sta.bssid,
+                  le16_to_cpu(ev->u.sta.listen_intvl),
+                  le16_to_cpu(ev->u.sta.beacon_intvl),
+                  le32_to_cpu(ev->u.sta.nw_type));
 
        /* Start of assoc rsp IEs */
        pie = ev->assoc_info + ev->beacon_ie_len +
@@ -518,16 +755,92 @@ static int ath6kl_wmi_connect_event_rx(struct wmi *wmi, u8 *datap, int len)
                pie += pie[1] + 2;
        }
 
-       ath6kl_connect_event(wmi->parent_dev, le16_to_cpu(ev->ch), ev->bssid,
-                            le16_to_cpu(ev->listen_intvl),
-                            le16_to_cpu(ev->beacon_intvl),
-                            le32_to_cpu(ev->nw_type),
+       ath6kl_connect_event(wmi->parent_dev, le16_to_cpu(ev->u.sta.ch),
+                            ev->u.sta.bssid,
+                            le16_to_cpu(ev->u.sta.listen_intvl),
+                            le16_to_cpu(ev->u.sta.beacon_intvl),
+                            le32_to_cpu(ev->u.sta.nw_type),
                             ev->beacon_ie_len, ev->assoc_req_len,
                             ev->assoc_resp_len, ev->assoc_info);
 
        return 0;
 }
 
+static struct country_code_to_enum_rd *
+ath6kl_regd_find_country(u16 countryCode)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(allCountries); i++) {
+               if (allCountries[i].countryCode == countryCode)
+                       return &allCountries[i];
+       }
+
+       return NULL;
+}
+
+static struct reg_dmn_pair_mapping *
+ath6kl_get_regpair(u16 regdmn)
+{
+       int i;
+
+       if (regdmn == NO_ENUMRD)
+               return NULL;
+
+       for (i = 0; i < ARRAY_SIZE(regDomainPairs); i++) {
+               if (regDomainPairs[i].regDmnEnum == regdmn)
+                       return &regDomainPairs[i];
+       }
+
+       return NULL;
+}
+
+static struct country_code_to_enum_rd *
+ath6kl_regd_find_country_by_rd(u16 regdmn)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(allCountries); i++) {
+               if (allCountries[i].regDmnEnum == regdmn)
+                       return &allCountries[i];
+       }
+
+       return NULL;
+}
+
+static void ath6kl_wmi_regdomain_event(struct wmi *wmi, u8 *datap, int len)
+{
+
+       struct ath6kl_wmi_regdomain *ev;
+       struct country_code_to_enum_rd *country = NULL;
+       struct reg_dmn_pair_mapping *regpair = NULL;
+       char alpha2[2];
+       u32 reg_code;
+
+       ev = (struct ath6kl_wmi_regdomain *) datap;
+       reg_code = le32_to_cpu(ev->reg_code);
+
+       if ((reg_code >> ATH6KL_COUNTRY_RD_SHIFT) & COUNTRY_ERD_FLAG)
+               country = ath6kl_regd_find_country((u16) reg_code);
+       else if (!(((u16) reg_code & WORLD_SKU_MASK) == WORLD_SKU_PREFIX)) {
+
+               regpair = ath6kl_get_regpair((u16) reg_code);
+               country = ath6kl_regd_find_country_by_rd((u16) reg_code);
+               ath6kl_dbg(ATH6KL_DBG_WMI, "Regpair used: 0x%0x\n",
+                               regpair->regDmnEnum);
+       }
+
+       if (country) {
+               alpha2[0] = country->isoName[0];
+               alpha2[1] = country->isoName[1];
+
+               regulatory_hint(wmi->parent_dev->wdev->wiphy, alpha2);
+
+               ath6kl_dbg(ATH6KL_DBG_WMI, "Country alpha2 being used: %c%c\n",
+                               alpha2[0], alpha2[1]);
+       }
+}
+
 static int ath6kl_wmi_disconnect_event_rx(struct wmi *wmi, u8 *datap, int len)
 {
        struct wmi_disconnect_event *ev;
@@ -538,6 +851,11 @@ static int ath6kl_wmi_disconnect_event_rx(struct wmi *wmi, u8 *datap, int len)
 
        ev = (struct wmi_disconnect_event *) datap;
 
+       ath6kl_dbg(ATH6KL_DBG_WMI,
+                  "wmi event disconnect proto_reason %d bssid %pM wmi_reason %d assoc_resp_len %d\n",
+                  le16_to_cpu(ev->proto_reason_status), ev->bssid,
+                  ev->disconn_reason, ev->assoc_resp_len);
+
        wmi->is_wmm_enabled = false;
        wmi->pair_crypto_type = NONE_CRYPT;
        wmi->grp_crypto_type = NONE_CRYPT;
@@ -582,315 +900,92 @@ static int ath6kl_wmi_tkip_micerr_event_rx(struct wmi *wmi, u8 *datap, int len)
        return 0;
 }
 
-static int ath6kl_wlan_parse_beacon(u8 *buf, int frame_len,
-                                   struct ath6kl_common_ie *cie)
-{
-       u8 *frm, *efrm;
-       u8 elemid_ssid = false;
-
-       frm = buf;
-       efrm = (u8 *) (frm + frame_len);
-
-       /*
-        * beacon/probe response frame format
-        *  [8] time stamp
-        *  [2] beacon interval
-        *  [2] capability information
-        *  [tlv] ssid
-        *  [tlv] supported rates
-        *  [tlv] country information
-        *  [tlv] parameter set (FH/DS)
-        *  [tlv] erp information
-        *  [tlv] extended supported rates
-        *  [tlv] WMM
-        *  [tlv] WPA or RSN
-        *  [tlv] Atheros Advanced Capabilities
-        */
-       if ((efrm - frm) < 12)
-               return -EINVAL;
-
-       memset(cie, 0, sizeof(*cie));
-
-       cie->ie_tstamp = frm;
-       frm += 8;
-       cie->ie_beaconInt = *(u16 *) frm;
-       frm += 2;
-       cie->ie_capInfo = *(u16 *) frm;
-       frm += 2;
-       cie->ie_chan = 0;
-
-       while (frm < efrm) {
-               switch (*frm) {
-               case WLAN_EID_SSID:
-                       if (!elemid_ssid) {
-                               cie->ie_ssid = frm;
-                               elemid_ssid = true;
-                       }
-                       break;
-               case WLAN_EID_SUPP_RATES:
-                       cie->ie_rates = frm;
-                       break;
-               case WLAN_EID_COUNTRY:
-                       cie->ie_country = frm;
-                       break;
-               case WLAN_EID_FH_PARAMS:
-                       break;
-               case WLAN_EID_DS_PARAMS:
-                       cie->ie_chan = frm[2];
-                       break;
-               case WLAN_EID_TIM:
-                       cie->ie_tim = frm;
-                       break;
-               case WLAN_EID_IBSS_PARAMS:
-                       break;
-               case WLAN_EID_EXT_SUPP_RATES:
-                       cie->ie_xrates = frm;
-                       break;
-               case WLAN_EID_ERP_INFO:
-                       if (frm[1] != 1)
-                               return -EINVAL;
-
-                       cie->ie_erp = frm[2];
-                       break;
-               case WLAN_EID_RSN:
-                       cie->ie_rsn = frm;
-                       break;
-               case WLAN_EID_HT_CAPABILITY:
-                       cie->ie_htcap = frm;
-                       break;
-               case WLAN_EID_HT_INFORMATION:
-                       cie->ie_htop = frm;
-                       break;
-               case WLAN_EID_VENDOR_SPECIFIC:
-                       if (frm[1] > 3 && frm[2] == 0x00 && frm[3] == 0x50 &&
-                           frm[4] == 0xf2) {
-                               /* OUT Type (00:50:F2) */
-
-                               if (frm[5] == WPA_OUI_TYPE) {
-                                       /* WPA OUT */
-                                       cie->ie_wpa = frm;
-                               } else if (frm[5] == WMM_OUI_TYPE) {
-                                       /* WMM OUT */
-                                       cie->ie_wmm = frm;
-                               } else if (frm[5] == WSC_OUT_TYPE) {
-                                       /* WSC OUT */
-                                       cie->ie_wsc = frm;
-                               }
-
-                       } else if (frm[1] > 3 && frm[2] == 0x00
-                                  && frm[3] == 0x03 && frm[4] == 0x7f
-                                  && frm[5] == ATH_OUI_TYPE) {
-                               /* Atheros OUI (00:03:7f) */
-                               cie->ie_ath = frm;
-                       }
-                       break;
-               default:
-                       break;
-               }
-               frm += frm[1] + 2;
-       }
-
-       if ((cie->ie_rates == NULL)
-           || (cie->ie_rates[1] > ATH6KL_RATE_MAXSIZE))
-               return -EINVAL;
-
-       if ((cie->ie_ssid == NULL)
-           || (cie->ie_ssid[1] > IEEE80211_MAX_SSID_LEN))
-               return -EINVAL;
-
-       return 0;
-}
-
 static int ath6kl_wmi_bssinfo_event_rx(struct wmi *wmi, u8 *datap, int len)
 {
-       struct bss *bss = NULL;
-       struct wmi_bss_info_hdr *bih;
-       u8 cached_ssid_len = 0;
-       u8 cached_ssid[IEEE80211_MAX_SSID_LEN] = { 0 };
-       u8 beacon_ssid_len = 0;
-       u8 *buf, *ie_ssid;
-       u8 *ni_buf;
-       int buf_len;
-
-       int ret;
+       struct wmi_bss_info_hdr2 *bih;
+       u8 *buf;
+       struct ieee80211_channel *channel;
+       struct ath6kl *ar = wmi->parent_dev;
+       struct ieee80211_mgmt *mgmt;
+       struct cfg80211_bss *bss;
 
-       if (len <= sizeof(struct wmi_bss_info_hdr))
+       if (len <= sizeof(struct wmi_bss_info_hdr2))
                return -EINVAL;
 
-       bih = (struct wmi_bss_info_hdr *) datap;
-       bss = wlan_find_node(&wmi->parent_dev->scan_table, bih->bssid);
-
-       if (a_sle16_to_cpu(bih->rssi) > 0) {
-               if (bss == NULL)
-                       return 0;
-               else
-                       bih->rssi = a_cpu_to_sle16(bss->ni_rssi);
-       }
-
-       buf = datap + sizeof(struct wmi_bss_info_hdr);
-       len -= sizeof(struct wmi_bss_info_hdr);
+       bih = (struct wmi_bss_info_hdr2 *) datap;
+       buf = datap + sizeof(struct wmi_bss_info_hdr2);
+       len -= sizeof(struct wmi_bss_info_hdr2);
 
        ath6kl_dbg(ATH6KL_DBG_WMI,
-                  "bss info evt - ch %u, rssi %02x, bssid \"%pM\"\n",
-                  bih->ch, a_sle16_to_cpu(bih->rssi), bih->bssid);
-
-       if (bss != NULL) {
-               /*
-                * Free up the node. We are about to allocate a new node.
-                * In case of hidden AP, beacon will not have ssid,
-                * but a directed probe response will have it,
-                * so cache the probe-resp-ssid if already present.
-                */
-               if (wmi->is_probe_ssid && (bih->frame_type == BEACON_FTYPE)) {
-                       ie_ssid = bss->ni_cie.ie_ssid;
-                       if (ie_ssid && (ie_ssid[1] <= IEEE80211_MAX_SSID_LEN) &&
-                           (ie_ssid[2] != 0)) {
-                               cached_ssid_len = ie_ssid[1];
-                               memcpy(cached_ssid, ie_ssid + 2,
-                                      cached_ssid_len);
-                       }
-               }
-
-               /*
-                * Use the current average rssi of associated AP base on
-                * assumption
-                *   1. Most os with GUI will update RSSI by
-                *      ath6kl_wmi_get_stats_cmd() periodically.
-                *   2. ath6kl_wmi_get_stats_cmd(..) will be called when calling
-                *      ath6kl_wmi_startscan_cmd(...)
-                * The average value of RSSI give end-user better feeling for
-                * instance value of scan result. It also sync up RSSI info
-                * in GUI between scan result and RSSI signal icon.
-                */
-               if (memcmp(wmi->parent_dev->bssid, bih->bssid, ETH_ALEN) == 0) {
-                       bih->rssi = a_cpu_to_sle16(bss->ni_rssi);
-                       bih->snr = bss->ni_snr;
-               }
-
-               wlan_node_reclaim(&wmi->parent_dev->scan_table, bss);
+                  "bss info evt - ch %u, snr %d, rssi %d, bssid \"%pM\" "
+                  "frame_type=%d\n",
+                  bih->ch, bih->snr, bih->snr - 95, bih->bssid,
+                  bih->frame_type);
+
+       if (bih->frame_type != BEACON_FTYPE &&
+           bih->frame_type != PROBERESP_FTYPE)
+               return 0; /* Only update BSS table for now */
+
+       if (bih->frame_type == BEACON_FTYPE &&
+           test_bit(CLEAR_BSSFILTER_ON_BEACON, &ar->flag)) {
+               clear_bit(CLEAR_BSSFILTER_ON_BEACON, &ar->flag);
+               ath6kl_wmi_bssfilter_cmd(ar->wmi, NONE_BSS_FILTER, 0);
        }
 
-       /*
-        * beacon/probe response frame format
-        *  [8] time stamp
-        *  [2] beacon interval
-        *  [2] capability information
-        *  [tlv] ssid
-        */
-       beacon_ssid_len = buf[SSID_IE_LEN_INDEX];
-
-       /*
-        * If ssid is cached for this hidden AP, then change
-        * buffer len accordingly.
-        */
-       if (wmi->is_probe_ssid && (bih->frame_type == BEACON_FTYPE) &&
-           (cached_ssid_len != 0) &&
-           (beacon_ssid_len == 0 || (cached_ssid_len > beacon_ssid_len &&
-                                     buf[SSID_IE_LEN_INDEX + 1] == 0))) {
-
-               len += (cached_ssid_len - beacon_ssid_len);
-       }
-
-       bss = wlan_node_alloc(len);
-       if (!bss)
-               return -ENOMEM;
-
-       bss->ni_snr = bih->snr;
-       bss->ni_rssi = a_sle16_to_cpu(bih->rssi);
-
-       if (WARN_ON(!bss->ni_buf))
+       channel = ieee80211_get_channel(ar->wdev->wiphy, le16_to_cpu(bih->ch));
+       if (channel == NULL)
                return -EINVAL;
 
-       /*
-        * In case of hidden AP, beacon will not have ssid,
-        * but a directed probe response will have it,
-        * so place the cached-ssid(probe-resp) in the bss info.
-        */
-       if (wmi->is_probe_ssid && (bih->frame_type == BEACON_FTYPE) &&
-           (cached_ssid_len != 0) &&
-           (beacon_ssid_len == 0 || (beacon_ssid_len &&
-                                     buf[SSID_IE_LEN_INDEX + 1] == 0))) {
-               ni_buf = bss->ni_buf;
-               buf_len = len;
-
-               /*
-                * Copy the first 14 bytes:
-                * time-stamp(8), beacon-interval(2),
-                * cap-info(2), ssid-id(1), ssid-len(1).
-                */
-               memcpy(ni_buf, buf, SSID_IE_LEN_INDEX + 1);
-
-               ni_buf[SSID_IE_LEN_INDEX] = cached_ssid_len;
-               ni_buf += (SSID_IE_LEN_INDEX + 1);
-
-               buf += (SSID_IE_LEN_INDEX + 1);
-               buf_len -= (SSID_IE_LEN_INDEX + 1);
-
-               memcpy(ni_buf, cached_ssid, cached_ssid_len);
-               ni_buf += cached_ssid_len;
-
-               buf += beacon_ssid_len;
-               buf_len -= beacon_ssid_len;
-
-               if (cached_ssid_len > beacon_ssid_len)
-                       buf_len -= (cached_ssid_len - beacon_ssid_len);
-
-               memcpy(ni_buf, buf, buf_len);
-       } else
-               memcpy(bss->ni_buf, buf, len);
-
-       bss->ni_framelen = len;
-
-       ret = ath6kl_wlan_parse_beacon(bss->ni_buf, len, &bss->ni_cie);
-       if (ret) {
-               wlan_node_free(bss);
+       if (len < 8 + 2 + 2)
                return -EINVAL;
+
+       if (bih->frame_type == BEACON_FTYPE && test_bit(CONNECTED, &ar->flag) &&
+           memcmp(bih->bssid, ar->bssid, ETH_ALEN) == 0) {
+               const u8 *tim;
+               tim = cfg80211_find_ie(WLAN_EID_TIM, buf + 8 + 2 + 2,
+                                      len - 8 - 2 - 2);
+               if (tim && tim[1] >= 2) {
+                       ar->assoc_bss_dtim_period = tim[3];
+                       set_bit(DTIM_PERIOD_AVAIL, &ar->flag);
+               }
        }
 
        /*
-        * Update the frequency in ie_chan, overwriting of channel number
-        * which is done in ath6kl_wlan_parse_beacon
+        * In theory, use of cfg80211_inform_bss() would be more natural here
+        * since we do not have the full frame. However, at least for now,
+        * cfg80211 can only distinguish Beacon and Probe Response frames from
+        * each other when using cfg80211_inform_bss_frame(), so let's build a
+        * fake IEEE 802.11 header to be able to take benefit of this.
         */
-       bss->ni_cie.ie_chan = le16_to_cpu(bih->ch);
-       wlan_setup_node(&wmi->parent_dev->scan_table, bss, bih->bssid);
-
-       return 0;
-}
-
-static int ath6kl_wmi_opt_frame_event_rx(struct wmi *wmi, u8 *datap, int len)
-{
-       struct bss *bss;
-       struct wmi_opt_rx_info_hdr *bih;
-       u8 *buf;
-
-       if (len <= sizeof(struct wmi_opt_rx_info_hdr))
+       mgmt = kmalloc(24 + len, GFP_ATOMIC);
+       if (mgmt == NULL)
                return -EINVAL;
 
-       bih = (struct wmi_opt_rx_info_hdr *) datap;
-       buf = datap + sizeof(struct wmi_opt_rx_info_hdr);
-       len -= sizeof(struct wmi_opt_rx_info_hdr);
-
-       ath6kl_dbg(ATH6KL_DBG_WMI, "opt frame event %2.2x:%2.2x\n",
-                  bih->bssid[4], bih->bssid[5]);
+       if (bih->frame_type == BEACON_FTYPE) {
+               mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
+                                                 IEEE80211_STYPE_BEACON);
+               memset(mgmt->da, 0xff, ETH_ALEN);
+       } else {
+               struct net_device *dev = ar->net_dev;
 
-       bss = wlan_find_node(&wmi->parent_dev->scan_table, bih->bssid);
-       if (bss != NULL) {
-               /* Free up the node. We are about to allocate a new node. */
-               wlan_node_reclaim(&wmi->parent_dev->scan_table, bss);
+               mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
+                                                 IEEE80211_STYPE_PROBE_RESP);
+               memcpy(mgmt->da, dev->dev_addr, ETH_ALEN);
        }
-
-       bss = wlan_node_alloc(len);
-       if (!bss)
+       mgmt->duration = cpu_to_le16(0);
+       memcpy(mgmt->sa, bih->bssid, ETH_ALEN);
+       memcpy(mgmt->bssid, bih->bssid, ETH_ALEN);
+       mgmt->seq_ctrl = cpu_to_le16(0);
+
+       memcpy(&mgmt->u.beacon, buf, len);
+
+       bss = cfg80211_inform_bss_frame(ar->wdev->wiphy, channel, mgmt,
+                                       24 + len, (bih->snr - 95) * 100,
+                                       GFP_ATOMIC);
+       kfree(mgmt);
+       if (bss == NULL)
                return -ENOMEM;
-
-       bss->ni_snr = bih->snr;
-       bss->ni_cie.ie_chan = le16_to_cpu(bih->ch);
-
-       if (WARN_ON(!bss->ni_buf))
-               return -EINVAL;
-
-       memcpy(bss->ni_buf, buf, len);
-       wlan_setup_node(&wmi->parent_dev->scan_table, bss, bih->bssid);
+       cfg80211_put_bss(bss);
 
        return 0;
 }
@@ -949,6 +1044,13 @@ static int ath6kl_wmi_bitrate_reply_rx(struct wmi *wmi, u8 *datap, int len)
        return 0;
 }
 
+static int ath6kl_wmi_tcmd_test_report_rx(struct wmi *wmi, u8 *datap, int len)
+{
+       ath6kl_tm_rx_report_event(wmi->parent_dev, datap, len);
+
+       return 0;
+}
+
 static int ath6kl_wmi_ratemask_reply_rx(struct wmi *wmi, u8 *datap, int len)
 {
        if (len < sizeof(struct wmi_fix_rates_reply))
@@ -998,15 +1100,41 @@ static int ath6kl_wmi_scan_complete_rx(struct wmi *wmi, u8 *datap, int len)
 
        ev = (struct wmi_scan_complete_event *) datap;
 
-       if (a_sle32_to_cpu(ev->status) == 0)
-               wlan_refresh_inactive_nodes(wmi->parent_dev);
-
        ath6kl_scan_complete_evt(wmi->parent_dev, a_sle32_to_cpu(ev->status));
        wmi->is_probe_ssid = false;
 
        return 0;
 }
 
+static int ath6kl_wmi_neighbor_report_event_rx(struct wmi *wmi, u8 *datap,
+                                              int len)
+{
+       struct wmi_neighbor_report_event *ev;
+       u8 i;
+
+       if (len < sizeof(*ev))
+               return -EINVAL;
+       ev = (struct wmi_neighbor_report_event *) datap;
+       if (sizeof(*ev) + ev->num_neighbors * sizeof(struct wmi_neighbor_info)
+           > len) {
+               ath6kl_dbg(ATH6KL_DBG_WMI, "truncated neighbor event "
+                          "(num=%d len=%d)\n", ev->num_neighbors, len);
+               return -EINVAL;
+       }
+       for (i = 0; i < ev->num_neighbors; i++) {
+               ath6kl_dbg(ATH6KL_DBG_WMI, "neighbor %d/%d - %pM 0x%x\n",
+                          i + 1, ev->num_neighbors, ev->neighbor[i].bssid,
+                          ev->neighbor[i].bss_flags);
+               cfg80211_pmksa_candidate_notify(wmi->parent_dev->net_dev, i,
+                                               ev->neighbor[i].bssid,
+                                               !!(ev->neighbor[i].bss_flags &
+                                                  WMI_PREAUTH_CAPABLE_BSS),
+                                               GFP_ATOMIC);
+       }
+
+       return 0;
+}
+
 /*
  * Target is reporting a programming error.  This is for
  * developer aid only.  Target only checks a few common violations
@@ -1410,6 +1538,11 @@ int ath6kl_wmi_cmd_send(struct wmi *wmi, struct sk_buff *skb,
        if (WARN_ON(skb == NULL))
                return -EINVAL;
 
+       ath6kl_dbg(ATH6KL_DBG_WMI, "wmi tx id %d len %d flag %d\n",
+                  cmd_id, skb->len, sync_flag);
+       ath6kl_dbg_dump(ATH6KL_DBG_WMI_DUMP, NULL, "wmi tx ",
+                       skb->data, skb->len);
+
        if (sync_flag >= END_WMIFLAG) {
                dev_kfree_skb(skb);
                return -EINVAL;
@@ -1468,6 +1601,13 @@ int ath6kl_wmi_connect_cmd(struct wmi *wmi, enum network_type nw_type,
        struct wmi_connect_cmd *cc;
        int ret;
 
+       ath6kl_dbg(ATH6KL_DBG_WMI,
+                  "wmi connect bssid %pM freq %d flags 0x%x ssid_len %d "
+                  "type %d dot11_auth %d auth %d pairwise %d group %d\n",
+                  bssid, channel, ctrl_flags, ssid_len, nw_type,
+                  dot11_auth_mode, auth_mode, pairwise_crypto, group_crypto);
+       ath6kl_dbg_dump(ATH6KL_DBG_WMI, NULL, "ssid ", ssid, ssid_len);
+
        wmi->traffic_class = 100;
 
        if ((pairwise_crypto == NONE_CRYPT) && (group_crypto != NONE_CRYPT))
@@ -1513,6 +1653,9 @@ int ath6kl_wmi_reconnect_cmd(struct wmi *wmi, u8 *bssid, u16 channel)
        struct wmi_reconnect_cmd *cc;
        int ret;
 
+       ath6kl_dbg(ATH6KL_DBG_WMI, "wmi reconnect bssid %pM freq %d\n",
+                  bssid, channel);
+
        wmi->traffic_class = 100;
 
        skb = ath6kl_wmi_get_new_buf(sizeof(struct wmi_reconnect_cmd));
@@ -1535,6 +1678,8 @@ int ath6kl_wmi_disconnect_cmd(struct wmi *wmi)
 {
        int ret;
 
+       ath6kl_dbg(ATH6KL_DBG_WMI, "wmi disconnect\n");
+
        wmi->traffic_class = 100;
 
        /* Disconnect command does not need to do a SYNC before. */
@@ -1551,7 +1696,7 @@ int ath6kl_wmi_startscan_cmd(struct wmi *wmi, enum wmi_scan_type scan_type,
        struct sk_buff *skb;
        struct wmi_start_scan_cmd *sc;
        s8 size;
-       int ret;
+       int i, ret;
 
        size = sizeof(struct wmi_start_scan_cmd);
 
@@ -1576,8 +1721,8 @@ int ath6kl_wmi_startscan_cmd(struct wmi *wmi, enum wmi_scan_type scan_type,
        sc->force_scan_intvl = cpu_to_le32(force_scan_interval);
        sc->num_ch = num_chan;
 
-       if (num_chan)
-               memcpy(sc->ch_list, ch_list, num_chan * sizeof(u16));
+       for (i = 0; i < num_chan; i++)
+               sc->ch_list[i] = cpu_to_le16(ch_list[i]);
 
        ret = ath6kl_wmi_cmd_send(wmi, skb, WMI_START_SCAN_CMDID,
                                  NO_SYNC_WMIFLAG);
@@ -1770,6 +1915,10 @@ int ath6kl_wmi_addkey_cmd(struct wmi *wmi, u8 key_index,
        struct wmi_add_cipher_key_cmd *cmd;
        int ret;
 
+       ath6kl_dbg(ATH6KL_DBG_WMI, "addkey cmd: key_index=%u key_type=%d "
+                  "key_usage=%d key_len=%d key_op_ctrl=%d\n",
+                  key_index, key_type, key_usage, key_len, key_op_ctrl);
+
        if ((key_index > WMI_MAX_KEY_INDEX) || (key_len > WMI_MAX_KEY_LEN) ||
            (key_material == NULL))
                return -EINVAL;
@@ -2211,6 +2360,25 @@ int ath6kl_wmi_get_challenge_resp_cmd(struct wmi *wmi, u32 cookie, u32 source)
        return ret;
 }
 
+int ath6kl_wmi_config_debug_module_cmd(struct wmi *wmi, u32 valid, u32 config)
+{
+       struct ath6kl_wmix_dbglog_cfg_module_cmd *cmd;
+       struct sk_buff *skb;
+       int ret;
+
+       skb = ath6kl_wmi_get_new_buf(sizeof(*cmd));
+       if (!skb)
+               return -ENOMEM;
+
+       cmd = (struct ath6kl_wmix_dbglog_cfg_module_cmd *) skb->data;
+       cmd->valid = cpu_to_le32(valid);
+       cmd->config = cpu_to_le32(config);
+
+       ret = ath6kl_wmi_cmd_send_xtnd(wmi, skb, WMIX_DBGLOG_CFG_MODULE_CMDID,
+                                      NO_SYNC_WMIFLAG);
+       return ret;
+}
+
 int ath6kl_wmi_get_stats_cmd(struct wmi *wmi)
 {
        return ath6kl_wmi_simple_cmd(wmi, WMI_GET_STATISTICS_CMDID);
@@ -2316,49 +2484,29 @@ int ath6kl_wmi_set_keepalive_cmd(struct wmi *wmi, u8 keep_alive_intvl)
        return ret;
 }
 
-s32 ath6kl_wmi_get_rate(s8 rate_index)
+int ath6kl_wmi_test_cmd(struct wmi *wmi, void *buf, size_t len)
 {
-       if (rate_index == RATE_AUTO)
-               return 0;
+       struct sk_buff *skb;
+       int ret;
 
-       return wmi_rate_tbl[(u32) rate_index][0];
-}
+       skb = ath6kl_wmi_get_new_buf(len);
+       if (!skb)
+               return -ENOMEM;
 
-void ath6kl_wmi_node_return(struct wmi *wmi, struct bss *bss)
-{
-       if (bss)
-               wlan_node_return(&wmi->parent_dev->scan_table, bss);
-}
+       memcpy(skb->data, buf, len);
 
-struct bss *ath6kl_wmi_find_ssid_node(struct wmi *wmi, u8 * ssid,
-                                     u32 ssid_len, bool is_wpa2,
-                                     bool match_ssid)
-{
-       struct bss *node = NULL;
+       ret = ath6kl_wmi_cmd_send(wmi, skb, WMI_TEST_CMDID, NO_SYNC_WMIFLAG);
 
-       node = wlan_find_ssid_node(&wmi->parent_dev->scan_table, ssid,
-                                 ssid_len, is_wpa2, match_ssid);
-       return node;
+       return ret;
 }
 
-struct bss *ath6kl_wmi_find_node(struct wmi *wmi, const u8 * mac_addr)
-{
-       struct bss *ni = NULL;
-
-       ni = wlan_find_node(&wmi->parent_dev->scan_table, mac_addr);
-
-       return ni;
-}
 
-void ath6kl_wmi_node_free(struct wmi *wmi, const u8 * mac_addr)
+s32 ath6kl_wmi_get_rate(s8 rate_index)
 {
-       struct bss *ni = NULL;
-
-       ni = wlan_find_node(&wmi->parent_dev->scan_table, mac_addr);
-       if (ni != NULL)
-               wlan_node_reclaim(&wmi->parent_dev->scan_table, ni);
+       if (rate_index == RATE_AUTO)
+               return 0;
 
-       return;
+       return wmi_rate_tbl[(u32) rate_index][0];
 }
 
 static int ath6kl_wmi_get_pmkid_list_event_rx(struct wmi *wmi, u8 *datap,
@@ -2400,6 +2548,47 @@ static int ath6kl_wmi_delba_req_event_rx(struct wmi *wmi, u8 *datap, int len)
 }
 
 /*  AP mode functions */
+
+int ath6kl_wmi_ap_profile_commit(struct wmi *wmip, struct wmi_connect_cmd *p)
+{
+       struct sk_buff *skb;
+       struct wmi_connect_cmd *cm;
+       int res;
+
+       skb = ath6kl_wmi_get_new_buf(sizeof(*cm));
+       if (!skb)
+               return -ENOMEM;
+
+       cm = (struct wmi_connect_cmd *) skb->data;
+       memcpy(cm, p, sizeof(*cm));
+
+       res = ath6kl_wmi_cmd_send(wmip, skb, WMI_AP_CONFIG_COMMIT_CMDID,
+                                 NO_SYNC_WMIFLAG);
+       ath6kl_dbg(ATH6KL_DBG_WMI, "%s: nw_type=%u auth_mode=%u ch=%u "
+                  "ctrl_flags=0x%x-> res=%d\n",
+                  __func__, p->nw_type, p->auth_mode, le16_to_cpu(p->ch),
+                  le32_to_cpu(p->ctrl_flags), res);
+       return res;
+}
+
+int ath6kl_wmi_ap_set_mlme(struct wmi *wmip, u8 cmd, const u8 *mac, u16 reason)
+{
+       struct sk_buff *skb;
+       struct wmi_ap_set_mlme_cmd *cm;
+
+       skb = ath6kl_wmi_get_new_buf(sizeof(*cm));
+       if (!skb)
+               return -ENOMEM;
+
+       cm = (struct wmi_ap_set_mlme_cmd *) skb->data;
+       memcpy(cm->mac, mac, ETH_ALEN);
+       cm->reason = cpu_to_le16(reason);
+       cm->cmd = cmd;
+
+       return ath6kl_wmi_cmd_send(wmip, skb, WMI_AP_SET_MLME_CMDID,
+                                  NO_SYNC_WMIFLAG);
+}
+
 static int ath6kl_wmi_pspoll_event_rx(struct wmi *wmi, u8 *datap, int len)
 {
        struct wmi_pspoll_event *ev;
@@ -2433,6 +2622,7 @@ int ath6kl_wmi_set_pvb_cmd(struct wmi *wmi, u16 aid, bool flag)
 
        cmd = (struct wmi_ap_set_pvb_cmd *) skb->data;
        cmd->aid = cpu_to_le16(aid);
+       cmd->rsvd = cpu_to_le16(0);
        cmd->flag = cpu_to_le32(flag);
 
        ret = ath6kl_wmi_cmd_send(wmi, skb, WMI_AP_SET_PVB_CMDID,
@@ -2464,6 +2654,160 @@ int ath6kl_wmi_set_rx_frame_format_cmd(struct wmi *wmi, u8 rx_meta_ver,
        return ret;
 }
 
+int ath6kl_wmi_set_appie_cmd(struct wmi *wmi, u8 mgmt_frm_type, const u8 *ie,
+                            u8 ie_len)
+{
+       struct sk_buff *skb;
+       struct wmi_set_appie_cmd *p;
+
+       skb = ath6kl_wmi_get_new_buf(sizeof(*p) + ie_len);
+       if (!skb)
+               return -ENOMEM;
+
+       ath6kl_dbg(ATH6KL_DBG_WMI, "set_appie_cmd: mgmt_frm_type=%u "
+                  "ie_len=%u\n", mgmt_frm_type, ie_len);
+       p = (struct wmi_set_appie_cmd *) skb->data;
+       p->mgmt_frm_type = mgmt_frm_type;
+       p->ie_len = ie_len;
+       memcpy(p->ie_info, ie, ie_len);
+       return ath6kl_wmi_cmd_send(wmi, skb, WMI_SET_APPIE_CMDID,
+                                  NO_SYNC_WMIFLAG);
+}
+
+int ath6kl_wmi_disable_11b_rates_cmd(struct wmi *wmi, bool disable)
+{
+       struct sk_buff *skb;
+       struct wmi_disable_11b_rates_cmd *cmd;
+
+       skb = ath6kl_wmi_get_new_buf(sizeof(*cmd));
+       if (!skb)
+               return -ENOMEM;
+
+       ath6kl_dbg(ATH6KL_DBG_WMI, "disable_11b_rates_cmd: disable=%u\n",
+                  disable);
+       cmd = (struct wmi_disable_11b_rates_cmd *) skb->data;
+       cmd->disable = disable ? 1 : 0;
+
+       return ath6kl_wmi_cmd_send(wmi, skb, WMI_DISABLE_11B_RATES_CMDID,
+                                  NO_SYNC_WMIFLAG);
+}
+
+int ath6kl_wmi_remain_on_chnl_cmd(struct wmi *wmi, u32 freq, u32 dur)
+{
+       struct sk_buff *skb;
+       struct wmi_remain_on_chnl_cmd *p;
+
+       skb = ath6kl_wmi_get_new_buf(sizeof(*p));
+       if (!skb)
+               return -ENOMEM;
+
+       ath6kl_dbg(ATH6KL_DBG_WMI, "remain_on_chnl_cmd: freq=%u dur=%u\n",
+                  freq, dur);
+       p = (struct wmi_remain_on_chnl_cmd *) skb->data;
+       p->freq = cpu_to_le32(freq);
+       p->duration = cpu_to_le32(dur);
+       return ath6kl_wmi_cmd_send(wmi, skb, WMI_REMAIN_ON_CHNL_CMDID,
+                                  NO_SYNC_WMIFLAG);
+}
+
+int ath6kl_wmi_send_action_cmd(struct wmi *wmi, u32 id, u32 freq, u32 wait,
+                              const u8 *data, u16 data_len)
+{
+       struct sk_buff *skb;
+       struct wmi_send_action_cmd *p;
+       u8 *buf;
+
+       if (wait)
+               return -EINVAL; /* Offload for wait not supported */
+
+       buf = kmalloc(data_len, GFP_KERNEL);
+       if (!buf)
+               return -ENOMEM;
+
+       skb = ath6kl_wmi_get_new_buf(sizeof(*p) + data_len);
+       if (!skb) {
+               kfree(buf);
+               return -ENOMEM;
+       }
+
+       kfree(wmi->last_mgmt_tx_frame);
+       wmi->last_mgmt_tx_frame = buf;
+       wmi->last_mgmt_tx_frame_len = data_len;
+
+       ath6kl_dbg(ATH6KL_DBG_WMI, "send_action_cmd: id=%u freq=%u wait=%u "
+                  "len=%u\n", id, freq, wait, data_len);
+       p = (struct wmi_send_action_cmd *) skb->data;
+       p->id = cpu_to_le32(id);
+       p->freq = cpu_to_le32(freq);
+       p->wait = cpu_to_le32(wait);
+       p->len = cpu_to_le16(data_len);
+       memcpy(p->data, data, data_len);
+       return ath6kl_wmi_cmd_send(wmi, skb, WMI_SEND_ACTION_CMDID,
+                                  NO_SYNC_WMIFLAG);
+}
+
+int ath6kl_wmi_send_probe_response_cmd(struct wmi *wmi, u32 freq,
+                                      const u8 *dst,
+                                      const u8 *data, u16 data_len)
+{
+       struct sk_buff *skb;
+       struct wmi_p2p_probe_response_cmd *p;
+
+       skb = ath6kl_wmi_get_new_buf(sizeof(*p) + data_len);
+       if (!skb)
+               return -ENOMEM;
+
+       ath6kl_dbg(ATH6KL_DBG_WMI, "send_probe_response_cmd: freq=%u dst=%pM "
+                  "len=%u\n", freq, dst, data_len);
+       p = (struct wmi_p2p_probe_response_cmd *) skb->data;
+       p->freq = cpu_to_le32(freq);
+       memcpy(p->destination_addr, dst, ETH_ALEN);
+       p->len = cpu_to_le16(data_len);
+       memcpy(p->data, data, data_len);
+       return ath6kl_wmi_cmd_send(wmi, skb, WMI_SEND_PROBE_RESPONSE_CMDID,
+                                  NO_SYNC_WMIFLAG);
+}
+
+int ath6kl_wmi_probe_report_req_cmd(struct wmi *wmi, bool enable)
+{
+       struct sk_buff *skb;
+       struct wmi_probe_req_report_cmd *p;
+
+       skb = ath6kl_wmi_get_new_buf(sizeof(*p));
+       if (!skb)
+               return -ENOMEM;
+
+       ath6kl_dbg(ATH6KL_DBG_WMI, "probe_report_req_cmd: enable=%u\n",
+                  enable);
+       p = (struct wmi_probe_req_report_cmd *) skb->data;
+       p->enable = enable ? 1 : 0;
+       return ath6kl_wmi_cmd_send(wmi, skb, WMI_PROBE_REQ_REPORT_CMDID,
+                                  NO_SYNC_WMIFLAG);
+}
+
+int ath6kl_wmi_info_req_cmd(struct wmi *wmi, u32 info_req_flags)
+{
+       struct sk_buff *skb;
+       struct wmi_get_p2p_info *p;
+
+       skb = ath6kl_wmi_get_new_buf(sizeof(*p));
+       if (!skb)
+               return -ENOMEM;
+
+       ath6kl_dbg(ATH6KL_DBG_WMI, "info_req_cmd: flags=%x\n",
+                  info_req_flags);
+       p = (struct wmi_get_p2p_info *) skb->data;
+       p->info_req_flags = cpu_to_le32(info_req_flags);
+       return ath6kl_wmi_cmd_send(wmi, skb, WMI_GET_P2P_INFO_CMDID,
+                                  NO_SYNC_WMIFLAG);
+}
+
+int ath6kl_wmi_cancel_remain_on_chnl_cmd(struct wmi *wmi)
+{
+       ath6kl_dbg(ATH6KL_DBG_WMI, "cancel_remain_on_chnl_cmd\n");
+       return ath6kl_wmi_simple_cmd(wmi, WMI_CANCEL_REMAIN_ON_CHNL_CMDID);
+}
+
 static int ath6kl_wmi_control_rx_xtnd(struct wmi *wmi, struct sk_buff *skb)
 {
        struct wmix_cmd_hdr *cmd;
@@ -2488,11 +2832,14 @@ static int ath6kl_wmi_control_rx_xtnd(struct wmi *wmi, struct sk_buff *skb)
 
        switch (id) {
        case WMIX_HB_CHALLENGE_RESP_EVENTID:
+               ath6kl_dbg(ATH6KL_DBG_WMI, "wmi event hb challenge resp\n");
                break;
        case WMIX_DBGLOG_EVENTID:
+               ath6kl_dbg(ATH6KL_DBG_WMI, "wmi event dbglog len %d\n", len);
+               ath6kl_debug_fwlog_event(wmi->parent_dev, datap, len);
                break;
        default:
-               ath6kl_err("unknown cmd id 0x%x\n", id);
+               ath6kl_warn("unknown cmd id 0x%x\n", id);
                wmi->stat.cmd_id_err++;
                ret = -EINVAL;
                break;
@@ -2528,8 +2875,9 @@ int ath6kl_wmi_control_rx(struct wmi *wmi, struct sk_buff *skb)
        datap = skb->data;
        len = skb->len;
 
-       ath6kl_dbg(ATH6KL_DBG_WMI, "%s: wmi id: %d\n", __func__, id);
-       ath6kl_dbg_dump(ATH6KL_DBG_RAW_BYTES, "msg payload ", datap, len);
+       ath6kl_dbg(ATH6KL_DBG_WMI, "wmi rx id %d len %d\n", id, len);
+       ath6kl_dbg_dump(ATH6KL_DBG_WMI_DUMP, NULL, "wmi rx ",
+                       datap, len);
 
        switch (id) {
        case WMI_GET_BITRATE_CMDID:
@@ -2566,11 +2914,11 @@ int ath6kl_wmi_control_rx(struct wmi *wmi, struct sk_buff *skb)
                break;
        case WMI_BSSINFO_EVENTID:
                ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_BSSINFO_EVENTID\n");
-               ath6kl_wmi_convert_bssinfo_hdr2_to_hdr(skb, datap);
-               ret = ath6kl_wmi_bssinfo_event_rx(wmi, skb->data, skb->len);
+               ret = ath6kl_wmi_bssinfo_event_rx(wmi, datap, len);
                break;
        case WMI_REGDOMAIN_EVENTID:
                ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_REGDOMAIN_EVENTID\n");
+               ath6kl_wmi_regdomain_event(wmi, datap, len);
                break;
        case WMI_PSTREAM_TIMEOUT_EVENTID:
                ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_PSTREAM_TIMEOUT_EVENTID\n");
@@ -2578,6 +2926,7 @@ int ath6kl_wmi_control_rx(struct wmi *wmi, struct sk_buff *skb)
                break;
        case WMI_NEIGHBOR_REPORT_EVENTID:
                ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_NEIGHBOR_REPORT_EVENTID\n");
+               ret = ath6kl_wmi_neighbor_report_event_rx(wmi, datap, len);
                break;
        case WMI_SCAN_COMPLETE_EVENTID:
                ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_SCAN_COMPLETE_EVENTID\n");
@@ -2600,7 +2949,7 @@ int ath6kl_wmi_control_rx(struct wmi *wmi, struct sk_buff *skb)
                break;
        case WMI_OPT_RX_FRAME_EVENTID:
                ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_OPT_RX_FRAME_EVENTID\n");
-               ret = ath6kl_wmi_opt_frame_event_rx(wmi, datap, len);
+               /* this event has been deprecated */
                break;
        case WMI_REPORT_ROAM_TBL_EVENTID:
                ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_REPORT_ROAM_TBL_EVENTID\n");
@@ -2619,6 +2968,10 @@ int ath6kl_wmi_control_rx(struct wmi *wmi, struct sk_buff *skb)
        case WMI_REPORT_ROAM_DATA_EVENTID:
                ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_REPORT_ROAM_DATA_EVENTID\n");
                break;
+       case WMI_TEST_EVENTID:
+               ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_TEST_EVENTID\n");
+               ret = ath6kl_wmi_tcmd_test_report_rx(wmi, datap, len);
+               break;
        case WMI_GET_FIXRATES_CMDID:
                ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_GET_FIXRATES_CMDID\n");
                ret = ath6kl_wmi_ratemask_reply_rx(wmi, datap, len);
@@ -2683,6 +3036,36 @@ int ath6kl_wmi_control_rx(struct wmi *wmi, struct sk_buff *skb)
                ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_TX_COMPLETE_EVENTID\n");
                ret = ath6kl_wmi_tx_complete_event_rx(datap, len);
                break;
+       case WMI_REMAIN_ON_CHNL_EVENTID:
+               ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_REMAIN_ON_CHNL_EVENTID\n");
+               ret = ath6kl_wmi_remain_on_chnl_event_rx(wmi, datap, len);
+               break;
+       case WMI_CANCEL_REMAIN_ON_CHNL_EVENTID:
+               ath6kl_dbg(ATH6KL_DBG_WMI,
+                          "WMI_CANCEL_REMAIN_ON_CHNL_EVENTID\n");
+               ret = ath6kl_wmi_cancel_remain_on_chnl_event_rx(wmi, datap,
+                                                               len);
+               break;
+       case WMI_TX_STATUS_EVENTID:
+               ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_TX_STATUS_EVENTID\n");
+               ret = ath6kl_wmi_tx_status_event_rx(wmi, datap, len);
+               break;
+       case WMI_RX_PROBE_REQ_EVENTID:
+               ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_RX_PROBE_REQ_EVENTID\n");
+               ret = ath6kl_wmi_rx_probe_req_event_rx(wmi, datap, len);
+               break;
+       case WMI_P2P_CAPABILITIES_EVENTID:
+               ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_P2P_CAPABILITIES_EVENTID\n");
+               ret = ath6kl_wmi_p2p_capabilities_event_rx(datap, len);
+               break;
+       case WMI_RX_ACTION_EVENTID:
+               ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_RX_ACTION_EVENTID\n");
+               ret = ath6kl_wmi_rx_action_event_rx(wmi, datap, len);
+               break;
+       case WMI_P2P_INFO_EVENTID:
+               ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_P2P_INFO_EVENTID\n");
+               ret = ath6kl_wmi_p2p_info_event_rx(datap, len);
+               break;
        default:
                ath6kl_dbg(ATH6KL_DBG_WMI, "unknown cmd id 0x%x\n", id);
                wmi->stat.cmd_id_err++;
@@ -2739,5 +3122,6 @@ void ath6kl_wmi_shutdown(struct wmi *wmi)
        if (!wmi)
                return;
 
+       kfree(wmi->last_mgmt_tx_frame);
        kfree(wmi);
 }
index fe3ddce6408793e541c8fcaa1b712daa3ccd10cc..f8e644d54aa78a042fab20328e33001fe7eea560 100644 (file)
@@ -129,6 +129,9 @@ struct wmi {
        u8 ht_allowed[A_NUM_BANDS];
        u8 traffic_class;
        bool is_probe_ssid;
+
+       u8 *last_mgmt_tx_frame;
+       size_t last_mgmt_tx_frame_len;
 };
 
 struct host_app_area {
@@ -490,17 +493,61 @@ enum wmi_cmd_id {
        WMI_SET_PASSPHRASE_CMDID,
        WMI_SEND_ASSOC_RES_CMDID,
        WMI_SET_ASSOC_REQ_RELAY_CMDID,
-       WMI_GET_RFKILL_MODE_CMDID,
 
        /* ACS command, consists of sub-commands */
        WMI_ACS_CTRL_CMDID,
+       WMI_SET_EXCESS_TX_RETRY_THRES_CMDID,
+       WMI_SET_TBD_TIME_CMDID, /*added for wmiconfig command for TBD */
+
+       /* Pktlog cmds */
+       WMI_PKTLOG_ENABLE_CMDID,
+       WMI_PKTLOG_DISABLE_CMDID,
+
+       /* More P2P Cmds */
+       WMI_P2P_GO_NEG_REQ_RSP_CMDID,
+       WMI_P2P_GRP_INIT_CMDID,
+       WMI_P2P_GRP_FORMATION_DONE_CMDID,
+       WMI_P2P_INVITE_CMDID,
+       WMI_P2P_INVITE_REQ_RSP_CMDID,
+       WMI_P2P_PROV_DISC_REQ_CMDID,
+       WMI_P2P_SET_CMDID,
+
+       WMI_GET_RFKILL_MODE_CMDID,
+       WMI_SET_RFKILL_MODE_CMDID,
+       WMI_AP_SET_APSD_CMDID,
+       WMI_AP_APSD_BUFFERED_TRAFFIC_CMDID,
 
+       WMI_P2P_SDPD_TX_CMDID, /* F05C */
+       WMI_P2P_STOP_SDPD_CMDID,
+       WMI_P2P_CANCEL_CMDID,
        /* Ultra low power store / recall commands */
        WMI_STORERECALL_CONFIGURE_CMDID,
        WMI_STORERECALL_RECALL_CMDID,
        WMI_STORERECALL_HOST_READY_CMDID,
        WMI_FORCE_TARGET_ASSERT_CMDID,
-       WMI_SET_EXCESS_TX_RETRY_THRES_CMDID,
+
+       WMI_SET_PROBED_SSID_EX_CMDID,
+       WMI_SET_NETWORK_LIST_OFFLOAD_CMDID,
+       WMI_SET_ARP_NS_OFFLOAD_CMDID,
+       WMI_ADD_WOW_EXT_PATTERN_CMDID,
+       WMI_GTK_OFFLOAD_OP_CMDID,
+       WMI_REMAIN_ON_CHNL_CMDID,
+       WMI_CANCEL_REMAIN_ON_CHNL_CMDID,
+       WMI_SEND_ACTION_CMDID,
+       WMI_PROBE_REQ_REPORT_CMDID,
+       WMI_DISABLE_11B_RATES_CMDID,
+       WMI_SEND_PROBE_RESPONSE_CMDID,
+       WMI_GET_P2P_INFO_CMDID,
+       WMI_AP_JOIN_BSS_CMDID,
+};
+
+enum wmi_mgmt_frame_type {
+       WMI_FRAME_BEACON = 0,
+       WMI_FRAME_PROBE_REQ,
+       WMI_FRAME_PROBE_RESP,
+       WMI_FRAME_ASSOC_REQ,
+       WMI_FRAME_ASSOC_RESP,
+       WMI_NUM_MGMT_FRAME
 };
 
 /* WMI_CONNECT_CMDID  */
@@ -519,11 +566,6 @@ enum dot11_auth_mode {
        LEAP_AUTH = 0x04,
 };
 
-enum {
-       AUTH_IDLE,
-       AUTH_OPEN_IN_PROGRESS,
-};
-
 enum auth_mode {
        NONE_AUTH = 0x01,
        WPA_AUTH = 0x02,
@@ -1179,15 +1221,26 @@ enum wmi_event_id {
        WMI_WAC_START_WPS_EVENTID,
        WMI_WAC_CTRL_REQ_REPLY_EVENTID,
 
+       WMI_REPORT_WMM_PARAMS_EVENTID,
+       WMI_WAC_REJECT_WPS_EVENTID,
+
+       /* More P2P Events */
+       WMI_P2P_GO_NEG_REQ_EVENTID,
+       WMI_P2P_INVITE_REQ_EVENTID,
+       WMI_P2P_INVITE_RCVD_RESULT_EVENTID,
+       WMI_P2P_INVITE_SENT_RESULT_EVENTID,
+       WMI_P2P_PROV_DISC_RESP_EVENTID,
+       WMI_P2P_PROV_DISC_REQ_EVENTID,
+
        /* RFKILL Events */
        WMI_RFKILL_STATE_CHANGE_EVENTID,
        WMI_RFKILL_GET_MODE_CMD_EVENTID,
-       WMI_THIN_RESERVED_START_EVENTID = 0x8000,
 
-       /*
-        * Events in this range are reserved for thinmode
-        * See wmi_thin.h for actual definitions
-        */
+       WMI_P2P_START_SDPD_EVENTID,
+       WMI_P2P_SDPD_RX_EVENTID,
+
+       WMI_THIN_RESERVED_START_EVENTID = 0x8000,
+       /* Events in this range are reserved for thinmode */
        WMI_THIN_RESERVED_END_EVENTID = 0x8fff,
 
        WMI_SET_CHANNEL_EVENTID,
@@ -1195,7 +1248,17 @@ enum wmi_event_id {
 
        /* Generic ACS event */
        WMI_ACS_EVENTID,
-       WMI_REPORT_WMM_PARAMS_EVENTID
+       WMI_STORERECALL_STORE_EVENTID,
+       WMI_WOW_EXT_WAKE_EVENTID,
+       WMI_GTK_OFFLOAD_STATUS_EVENTID,
+       WMI_NETWORK_LIST_OFFLOAD_EVENTID,
+       WMI_REMAIN_ON_CHNL_EVENTID,
+       WMI_CANCEL_REMAIN_ON_CHNL_EVENTID,
+       WMI_TX_STATUS_EVENTID,
+       WMI_RX_PROBE_REQ_EVENTID,
+       WMI_P2P_CAPABILITIES_EVENTID,
+       WMI_RX_ACTION_EVENTID,
+       WMI_P2P_INFO_EVENTID,
 };
 
 struct wmi_ready_event_2 {
@@ -1207,11 +1270,30 @@ struct wmi_ready_event_2 {
 
 /* Connect Event */
 struct wmi_connect_event {
-       __le16 ch;
-       u8 bssid[ETH_ALEN];
-       __le16 listen_intvl;
-       __le16 beacon_intvl;
-       __le32 nw_type;
+       union {
+               struct {
+                       __le16 ch;
+                       u8 bssid[ETH_ALEN];
+                       __le16 listen_intvl;
+                       __le16 beacon_intvl;
+                       __le32 nw_type;
+               } sta;
+               struct {
+                       u8 phymode;
+                       u8 aid;
+                       u8 mac_addr[ETH_ALEN];
+                       u8 auth;
+                       u8 keymgmt;
+                       __le16 cipher;
+                       u8 apsd_info;
+                       u8 unused[3];
+               } ap_sta;
+               struct {
+                       __le16 ch;
+                       u8 bssid[ETH_ALEN];
+                       u8 unused[8];
+               } ap_bss;
+       } u;
        u8 beacon_ie_len;
        u8 assoc_req_len;
        u8 assoc_resp_len;
@@ -1238,6 +1320,12 @@ enum wmi_disconnect_reason {
        IBSS_MERGE = 0xe,
 };
 
+#define ATH6KL_COUNTRY_RD_SHIFT        16
+
+struct ath6kl_wmi_regdomain {
+       __le32 reg_code;
+};
+
 struct wmi_disconnect_event {
        /* reason code, see 802.11 spec. */
        __le16 proto_reason_status;
@@ -1265,33 +1353,54 @@ enum wmi_bi_ftype {
        PROBEREQ_FTYPE,
 };
 
-struct wmi_bss_info_hdr {
-       __le16 ch;
+#define DEF_LRSSI_SCAN_PERIOD           5
+#define DEF_LRSSI_ROAM_THRESHOLD       20
+#define DEF_LRSSI_ROAM_FLOOR           60
+#define DEF_SCAN_FOR_ROAM_INTVL                 2
 
-       /* see, enum wmi_bi_ftype */
-       u8 frame_type;
+enum wmi_roam_ctrl {
+       WMI_FORCE_ROAM = 1,
+       WMI_SET_ROAM_MODE,
+       WMI_SET_HOST_BIAS,
+       WMI_SET_LRSSI_SCAN_PARAMS,
+};
 
-       u8 snr;
-       a_sle16 rssi;
+struct bss_bias {
        u8 bssid[ETH_ALEN];
-       __le32 ie_mask;
+       u8  bias;
 } __packed;
 
-/*
- * BSS INFO HDR version 2.0
- * With 6 bytes HTC header and 6 bytes of WMI header
- * WMI_BSS_INFO_HDR cannot be accommodated in the removed 802.11 management
- * header space.
- * - Reduce the ie_mask to 2 bytes as only two bit flags are used
- * - Remove rssi and compute it on the host. rssi = snr - 95
- */
+struct bss_bias_info {
+       u8 num_bss;
+       struct bss_bias bss_bias[1];
+} __packed;
+
+struct low_rssi_scan_params {
+       __le16 lrssi_scan_period;
+       a_sle16 lrssi_scan_threshold;
+       a_sle16 lrssi_roam_threshold;
+       u8 roam_rssi_floor;
+       u8 reserved[1];
+} __packed;
+
+struct roam_ctrl_cmd {
+       union {
+               u8 bssid[ETH_ALEN];
+               u8 roam_mode;
+               struct bss_bias_info bss;
+               struct low_rssi_scan_params params;
+       } __packed info;
+       u8 roam_ctrl;
+} __packed;
+
+/* BSS INFO HDR version 2.0 */
 struct wmi_bss_info_hdr2 {
-       __le16 ch;
+       __le16 ch; /* frequency in MHz */
 
        /* see, enum wmi_bi_ftype */
        u8 frame_type;
 
-       u8 snr;
+       u8 snr; /* note: rssi = snr - 95 dBm */
        u8 bssid[ETH_ALEN];
        __le16 ie_mask;
 } __packed;
@@ -1330,6 +1439,16 @@ enum wmi_bss_flags {
        WMI_PMKID_VALID_BSS = 0x02,
 };
 
+struct wmi_neighbor_info {
+       u8 bssid[ETH_ALEN];
+       u8 bss_flags; /* enum wmi_bss_flags */
+} __packed;
+
+struct wmi_neighbor_report_event {
+       u8 num_neighbors;
+       struct wmi_neighbor_info neighbor[0];
+} __packed;
+
 /* TKIP MIC Error Event */
 struct wmi_tkip_micerr_event {
        u8 key_id;
@@ -1642,6 +1761,12 @@ struct wmi_get_keepalive_cmd {
        u8 keep_alive_intvl;
 } __packed;
 
+struct wmi_set_appie_cmd {
+       u8 mgmt_frm_type; /* enum wmi_mgmt_frame_type */
+       u8 ie_len;
+       u8 ie_info[0];
+} __packed;
+
 /* Notify the WSC registration status to the target */
 #define WSC_REG_ACTIVE     1
 #define WSC_REG_INACTIVE   0
@@ -1789,8 +1914,26 @@ struct wmi_tx_complete_event {
 
 /* Used with WMI_AP_SET_NUM_STA_CMDID */
 
+/*
+ * Used with WMI_AP_SET_MLME_CMDID
+ */
+
+/* MLME Commands */
+#define WMI_AP_MLME_ASSOC       1   /* associate station */
+#define WMI_AP_DISASSOC         2   /* disassociate station */
+#define WMI_AP_DEAUTH           3   /* deauthenticate station */
+#define WMI_AP_MLME_AUTHORIZE   4   /* authorize station */
+#define WMI_AP_MLME_UNAUTHORIZE 5   /* unauthorize station */
+
+struct wmi_ap_set_mlme_cmd {
+       u8 mac[ETH_ALEN];
+       __le16 reason;          /* 802.11 reason code */
+       u8 cmd;                 /* operation to perform (WMI_AP_*) */
+} __packed;
+
 struct wmi_ap_set_pvb_cmd {
        __le32 flag;
+       __le16 rsvd;
        __le16 aid;
 } __packed;
 
@@ -1840,6 +1983,100 @@ struct wmi_ap_mode_stat {
 
 /* End of AP mode definitions */
 
+struct wmi_remain_on_chnl_cmd {
+       __le32 freq;
+       __le32 duration;
+} __packed;
+
+struct wmi_send_action_cmd {
+       __le32 id;
+       __le32 freq;
+       __le32 wait;
+       __le16 len;
+       u8 data[0];
+} __packed;
+
+struct wmi_tx_status_event {
+       __le32 id;
+       u8 ack_status;
+} __packed;
+
+struct wmi_probe_req_report_cmd {
+       u8 enable;
+} __packed;
+
+struct wmi_disable_11b_rates_cmd {
+       u8 disable;
+} __packed;
+
+struct wmi_set_appie_extended_cmd {
+       u8 role_id;
+       u8 mgmt_frm_type;
+       u8 ie_len;
+       u8 ie_info[0];
+} __packed;
+
+struct wmi_remain_on_chnl_event {
+       __le32 freq;
+       __le32 duration;
+} __packed;
+
+struct wmi_cancel_remain_on_chnl_event {
+       __le32 freq;
+       __le32 duration;
+       u8 status;
+} __packed;
+
+struct wmi_rx_action_event {
+       __le32 freq;
+       __le16 len;
+       u8 data[0];
+} __packed;
+
+struct wmi_p2p_capabilities_event {
+       __le16 len;
+       u8 data[0];
+} __packed;
+
+struct wmi_p2p_rx_probe_req_event {
+       __le32 freq;
+       __le16 len;
+       u8 data[0];
+} __packed;
+
+#define P2P_FLAG_CAPABILITIES_REQ   (0x00000001)
+#define P2P_FLAG_MACADDR_REQ        (0x00000002)
+#define P2P_FLAG_HMODEL_REQ         (0x00000002)
+
+struct wmi_get_p2p_info {
+       __le32 info_req_flags;
+} __packed;
+
+struct wmi_p2p_info_event {
+       __le32 info_req_flags;
+       __le16 len;
+       u8 data[0];
+} __packed;
+
+struct wmi_p2p_capabilities {
+       u8 go_power_save;
+} __packed;
+
+struct wmi_p2p_macaddr {
+       u8 mac_addr[ETH_ALEN];
+} __packed;
+
+struct wmi_p2p_hmodel {
+       u8 p2p_model;
+} __packed;
+
+struct wmi_p2p_probe_response_cmd {
+       __le32 freq;
+       u8 destination_addr[ETH_ALEN];
+       __le16 len;
+       u8 data[0];
+} __packed;
+
 /* Extended WMI (WMIX)
  *
  * Extended WMIX commands are encapsulated in a WMI message with
@@ -1898,6 +2135,11 @@ struct wmix_hb_challenge_resp_cmd {
        __le32 source;
 } __packed;
 
+struct ath6kl_wmix_dbglog_cfg_module_cmd {
+       __le32 valid;
+       __le32 config;
+} __packed;
+
 /* End of Extended WMI (WMIX) */
 
 enum wmi_sync_flag {
@@ -1925,14 +2167,11 @@ int ath6kl_wmi_data_hdr_add(struct wmi *wmi, struct sk_buff *skb,
 
 int ath6kl_wmi_dot11_hdr_remove(struct wmi *wmi, struct sk_buff *skb);
 int ath6kl_wmi_dot3_2_dix(struct sk_buff *skb);
-int ath6kl_wmi_data_hdr_remove(struct wmi *wmi, struct sk_buff *skb);
 int ath6kl_wmi_implicit_create_pstream(struct wmi *wmi, struct sk_buff *skb,
                                       u32 layer2_priority, bool wmm_enabled,
                                       u8 *ac);
 
 int ath6kl_wmi_control_rx(struct wmi *wmi, struct sk_buff *skb);
-struct bss *ath6kl_wmi_find_node(struct wmi *wmi, const u8 *mac_addr);
-void ath6kl_wmi_node_free(struct wmi *wmi, const u8 *mac_addr);
 
 int ath6kl_wmi_cmd_send(struct wmi *wmi, struct sk_buff *skb,
                        enum wmi_cmd_id cmd_id, enum wmi_sync_flag sync_flag);
@@ -1978,6 +2217,7 @@ int ath6kl_wmi_set_lpreamble_cmd(struct wmi *wmi, u8 status,
                                 u8 preamble_policy);
 
 int ath6kl_wmi_get_challenge_resp_cmd(struct wmi *wmi, u32 cookie, u32 source);
+int ath6kl_wmi_config_debug_module_cmd(struct wmi *wmi, u32 valid, u32 config);
 
 int ath6kl_wmi_get_stats_cmd(struct wmi *wmi);
 int ath6kl_wmi_addkey_cmd(struct wmi *wmi, u8 key_index,
@@ -1995,23 +2235,47 @@ int ath6kl_wmi_get_tx_pwr_cmd(struct wmi *wmi);
 
 int ath6kl_wmi_set_wmm_txop(struct wmi *wmi, enum wmi_txop_cfg cfg);
 int ath6kl_wmi_set_keepalive_cmd(struct wmi *wmi, u8 keep_alive_intvl);
+int ath6kl_wmi_test_cmd(struct wmi *wmi, void *buf, size_t len);
 
 s32 ath6kl_wmi_get_rate(s8 rate_index);
 
 int ath6kl_wmi_set_ip_cmd(struct wmi *wmi, struct wmi_set_ip_cmd *ip_cmd);
+int ath6kl_wmi_set_roam_lrssi_cmd(struct wmi *wmi, u8 lrssi);
 
-struct bss *ath6kl_wmi_find_ssid_node(struct wmi *wmi, u8 *ssid,
-                                     u32 ssid_len, bool is_wpa2,
-                                     bool match_ssid);
+/* AP mode */
+int ath6kl_wmi_ap_profile_commit(struct wmi *wmip, struct wmi_connect_cmd *p);
 
-void ath6kl_wmi_node_return(struct wmi *wmi, struct bss *bss);
+int ath6kl_wmi_ap_set_mlme(struct wmi *wmip, u8 cmd, const u8 *mac, u16 reason);
 
-/* AP mode */
 int ath6kl_wmi_set_pvb_cmd(struct wmi *wmi, u16 aid, bool flag);
 
 int ath6kl_wmi_set_rx_frame_format_cmd(struct wmi *wmi, u8 rx_meta_version,
                                       bool rx_dot11_hdr, bool defrag_on_host);
 
+int ath6kl_wmi_set_appie_cmd(struct wmi *wmi, u8 mgmt_frm_type, const u8 *ie,
+                            u8 ie_len);
+
+/* P2P */
+int ath6kl_wmi_disable_11b_rates_cmd(struct wmi *wmi, bool disable);
+
+int ath6kl_wmi_remain_on_chnl_cmd(struct wmi *wmi, u32 freq, u32 dur);
+
+int ath6kl_wmi_send_action_cmd(struct wmi *wmi, u32 id, u32 freq, u32 wait,
+                              const u8 *data, u16 data_len);
+
+int ath6kl_wmi_send_probe_response_cmd(struct wmi *wmi, u32 freq,
+                                      const u8 *dst,
+                                      const u8 *data, u16 data_len);
+
+int ath6kl_wmi_probe_report_req_cmd(struct wmi *wmi, bool enable);
+
+int ath6kl_wmi_info_req_cmd(struct wmi *wmi, u32 info_req_flags);
+
+int ath6kl_wmi_cancel_remain_on_chnl_cmd(struct wmi *wmi);
+
+int ath6kl_wmi_set_appie_cmd(struct wmi *wmi, u8 mgmt_frm_type, const u8 *ie,
+                            u8 ie_len);
+
 void *ath6kl_wmi_init(struct ath6kl *devt);
 void ath6kl_wmi_shutdown(struct wmi *wmi);
 
This page took 0.122182 seconds and 5 git commands to generate.