]> git.proxmox.com Git - mirror_ubuntu-zesty-kernel.git/commitdiff
Merge remote branch 'wireless-next/master' into ath6kl-next
authorKalle Valo <kvalo@qca.qualcomm.com>
Mon, 5 Mar 2012 17:32:58 +0000 (19:32 +0200)
committerKalle Valo <kvalo@qca.qualcomm.com>
Mon, 5 Mar 2012 17:32:58 +0000 (19:32 +0200)
Conflicts:
drivers/net/wireless/ath/ath6kl/cfg80211.c

25 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/cfg80211.h
drivers/net/wireless/ath/ath6kl/common.h
drivers/net/wireless/ath/ath6kl/core.c
drivers/net/wireless/ath/ath6kl/core.h
drivers/net/wireless/ath/ath6kl/debug.c [changed mode: 0644->0755]
drivers/net/wireless/ath/ath6kl/debug.h
drivers/net/wireless/ath/ath6kl/hif-ops.h
drivers/net/wireless/ath/ath6kl/hif.c
drivers/net/wireless/ath/ath6kl/hif.h
drivers/net/wireless/ath/ath6kl/htc.c
drivers/net/wireless/ath/ath6kl/htc.h
drivers/net/wireless/ath/ath6kl/init.c
drivers/net/wireless/ath/ath6kl/main.c
drivers/net/wireless/ath/ath6kl/sdio.c
drivers/net/wireless/ath/ath6kl/target.h
drivers/net/wireless/ath/ath6kl/testmode.c
drivers/net/wireless/ath/ath6kl/testmode.h
drivers/net/wireless/ath/ath6kl/txrx.c
drivers/net/wireless/ath/ath6kl/usb.c
drivers/net/wireless/ath/ath6kl/wmi.c
drivers/net/wireless/ath/ath6kl/wmi.h

index 9ba42fa049624b07772144a9f04b34999ad11065..85746c3eb02788c9109b07bbfda905d98b5a1daf 100644 (file)
@@ -1,5 +1,6 @@
 #------------------------------------------------------------------------------
-# Copyright (c) 2004-2010 Atheros Communications Inc.
+# Copyright (c) 2004-2011 Atheros Communications Inc.
+# Copyright (c) 2011-2012 Qualcomm Atheros, Inc.
 # All rights reserved.
 #
 #
index aef00d5a1438ce501408e8d1055cac00df449977..05d1d8941fd1e395349c6645ef5e45bc47968da1 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright (c) 2004-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2012 Qualcomm Atheros, Inc.
  *
  * Permission to use, copy, modify, and/or distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
index f1ca6812456df4d85c5bd85ef37c48c43b3c9c61..114f7b265a3a31f9ccc628c9729dd888c1afc66e 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright (c) 2004-2011 Atheros Communications Inc.
+ * Copyright (c) 2011 Qualcomm Atheros, Inc.
  *
  * Permission to use, copy, modify, and/or distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
index 5370333883e4632c03e9ca9958c43631d0072119..9abc7a6a69b13396bee96152bdeb5b5680769b69 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright (c) 2004-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2012 Qualcomm Atheros, Inc.
  *
  * Permission to use, copy, modify, and/or distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -68,6 +69,10 @@ static struct ieee80211_rate ath6kl_rates[] = {
 #define ath6kl_g_rates     (ath6kl_rates + 0)
 #define ath6kl_g_rates_size    12
 
+#define ath6kl_g_htcap (IEEE80211_HT_CAP_SUP_WIDTH_20_40 | \
+                       IEEE80211_HT_CAP_SGI_20          | \
+                       IEEE80211_HT_CAP_SGI_40)
+
 static struct ieee80211_channel ath6kl_2ghz_channels[] = {
        CHAN2G(1, 2412, 0),
        CHAN2G(2, 2417, 0),
@@ -112,6 +117,8 @@ static struct ieee80211_supported_band ath6kl_band_2ghz = {
        .channels = ath6kl_2ghz_channels,
        .n_bitrates = ath6kl_g_rates_size,
        .bitrates = ath6kl_g_rates,
+       .ht_cap.cap = ath6kl_g_htcap,
+       .ht_cap.ht_supported = true,
 };
 
 static struct ieee80211_supported_band ath6kl_band_5ghz = {
@@ -119,6 +126,8 @@ static struct ieee80211_supported_band ath6kl_band_5ghz = {
        .channels = ath6kl_5ghz_a_channels,
        .n_bitrates = ath6kl_a_rates_size,
        .bitrates = ath6kl_a_rates,
+       .ht_cap.cap = ath6kl_g_htcap,
+       .ht_cap.ht_supported = true,
 };
 
 #define CCKM_KRK_CIPHER_SUITE 0x004096ff /* use for KRK */
@@ -876,19 +885,14 @@ static int ath6kl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev,
                                                  request->ssids[i].ssid);
        }
 
-       /*
-        * FIXME: we should clear the IE in fw if it's not set so just
-        * remove the check altogether
-        */
-       if (request->ie) {
-               ret = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
-                                              WMI_FRAME_PROBE_REQ,
-                                              request->ie, request->ie_len);
-               if (ret) {
-                       ath6kl_err("failed to set Probe Request appie for "
-                                  "scan");
-                       return ret;
-               }
+       /* this also clears IE in fw if it's not set */
+       ret = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx,
+                                      WMI_FRAME_PROBE_REQ,
+                                      request->ie, request->ie_len);
+       if (ret) {
+               ath6kl_err("failed to set Probe Request appie for "
+                          "scan");
+               return ret;
        }
 
        /*
@@ -926,14 +930,17 @@ static int ath6kl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev,
                 */
                ret = ath6kl_wmi_beginscan_cmd(ar->wmi, vif->fw_vif_idx,
                                                WMI_LONG_SCAN, force_fg_scan,
-                                               false, 0, 0, n_channels,
-                                               channels, request->no_cck,
+                                               false, 0,
+                                               ATH6KL_FG_SCAN_INTERVAL,
+                                               n_channels, channels,
+                                               request->no_cck,
                                                request->rates);
        } else {
                ret = ath6kl_wmi_startscan_cmd(ar->wmi, vif->fw_vif_idx,
                                                WMI_LONG_SCAN, force_fg_scan,
-                                               false, 0, 0, n_channels,
-                                               channels);
+                                               false, 0,
+                                               ATH6KL_FG_SCAN_INTERVAL,
+                                               n_channels, channels);
        }
        if (ret)
                ath6kl_err("wmi_startscan_cmd failed\n");
@@ -1058,7 +1065,7 @@ static int ath6kl_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev,
 
        if (vif->nw_type == AP_NETWORK && !pairwise &&
            (key_type == TKIP_CRYPT || key_type == AES_CRYPT ||
-            key_type == WAPI_CRYPT) && params) {
+            key_type == WAPI_CRYPT)) {
                ar->ap_mode_bkey.valid = true;
                ar->ap_mode_bkey.key_index = key_index;
                ar->ap_mode_bkey.key_type = key_type;
@@ -2309,6 +2316,7 @@ static int ath6kl_start_ap(struct wiphy *wiphy, struct net_device *dev,
        struct ath6kl *ar = ath6kl_priv(dev);
        struct ath6kl_vif *vif = netdev_priv(dev);
        struct ieee80211_mgmt *mgmt;
+       bool hidden = false;
        u8 *ies;
        int ies_len;
        struct wmi_connect_cmd p;
@@ -2345,7 +2353,11 @@ static int ath6kl_start_ap(struct wiphy *wiphy, struct net_device *dev,
        memcpy(vif->ssid, info->ssid, info->ssid_len);
        vif->ssid_len = info->ssid_len;
        if (info->hidden_ssid != NL80211_HIDDEN_SSID_NOT_IN_USE)
-               return -EOPNOTSUPP; /* TODO */
+               hidden = true;
+
+       res = ath6kl_wmi_ap_hidden_ssid(ar->wmi, vif->fw_vif_idx, hidden);
+       if (res)
+               return res;
 
        ret = ath6kl_set_auth_type(vif, info->auth_type);
        if (ret)
@@ -2584,6 +2596,76 @@ static int ath6kl_send_go_probe_resp(struct ath6kl_vif *vif,
        return ret;
 }
 
+static bool ath6kl_mgmt_powersave_ap(struct ath6kl_vif *vif,
+                                    u32 id,
+                                    u32 freq,
+                                    u32 wait,
+                                    const u8 *buf,
+                                    size_t len,
+                                    bool *more_data,
+                                    bool no_cck)
+{
+       struct ieee80211_mgmt *mgmt;
+       struct ath6kl_sta *conn;
+       bool is_psq_empty = false;
+       struct ath6kl_mgmt_buff *mgmt_buf;
+       size_t mgmt_buf_size;
+       struct ath6kl *ar = vif->ar;
+
+       mgmt = (struct ieee80211_mgmt *) buf;
+       if (is_multicast_ether_addr(mgmt->da))
+               return false;
+
+       conn = ath6kl_find_sta(vif, mgmt->da);
+       if (!conn)
+               return false;
+
+       if (conn->sta_flags & STA_PS_SLEEP) {
+               if (!(conn->sta_flags & STA_PS_POLLED)) {
+                       /* Queue the frames if the STA is sleeping */
+                       mgmt_buf_size = len + sizeof(struct ath6kl_mgmt_buff);
+                       mgmt_buf = kmalloc(mgmt_buf_size, GFP_KERNEL);
+                       if (!mgmt_buf)
+                               return false;
+
+                       INIT_LIST_HEAD(&mgmt_buf->list);
+                       mgmt_buf->id = id;
+                       mgmt_buf->freq = freq;
+                       mgmt_buf->wait = wait;
+                       mgmt_buf->len = len;
+                       mgmt_buf->no_cck = no_cck;
+                       memcpy(mgmt_buf->buf, buf, len);
+                       spin_lock_bh(&conn->psq_lock);
+                       is_psq_empty = skb_queue_empty(&conn->psq) &&
+                                       (conn->mgmt_psq_len == 0);
+                       list_add_tail(&mgmt_buf->list, &conn->mgmt_psq);
+                       conn->mgmt_psq_len++;
+                       spin_unlock_bh(&conn->psq_lock);
+
+                       /*
+                        * If this is the first pkt getting queued
+                        * for this STA, update the PVB for this
+                        * STA.
+                        */
+                       if (is_psq_empty)
+                               ath6kl_wmi_set_pvb_cmd(ar->wmi, vif->fw_vif_idx,
+                                                      conn->aid, 1);
+                       return true;
+               }
+
+               /*
+                * This tx is because of a PsPoll.
+                * Determine if MoreData bit has to be set.
+                */
+               spin_lock_bh(&conn->psq_lock);
+               if (!skb_queue_empty(&conn->psq) || (conn->mgmt_psq_len != 0))
+                       *more_data = true;
+               spin_unlock_bh(&conn->psq_lock);
+       }
+
+       return false;
+}
+
 static int ath6kl_mgmt_tx(struct wiphy *wiphy, struct net_device *dev,
                          struct ieee80211_channel *chan, bool offchan,
                          enum nl80211_channel_type channel_type,
@@ -2595,6 +2677,7 @@ static int ath6kl_mgmt_tx(struct wiphy *wiphy, struct net_device *dev,
        struct ath6kl_vif *vif = netdev_priv(dev);
        u32 id;
        const struct ieee80211_mgmt *mgmt;
+       bool more_data, queued;
 
        mgmt = (const struct ieee80211_mgmt *) buf;
        if (buf + len >= mgmt->u.probe_resp.variable &&
@@ -2620,22 +2703,19 @@ static int ath6kl_mgmt_tx(struct wiphy *wiphy, struct net_device *dev,
 
        *cookie = id;
 
-       if (test_bit(ATH6KL_FW_CAPABILITY_STA_P2PDEV_DUPLEX,
-                   ar->fw_capabilities)) {
-               /*
-                * If capable of doing P2P mgmt operations using
-                * station interface, send additional information like
-                * supported rates to advertise and xmit rates for
-                * probe requests
-                */
-               return ath6kl_wmi_send_mgmt_cmd(ar->wmi, vif->fw_vif_idx, id,
-                                               chan->center_freq, wait,
-                                               buf, len, no_cck);
-       } else {
-               return ath6kl_wmi_send_action_cmd(ar->wmi, vif->fw_vif_idx, id,
-                                                 chan->center_freq, wait,
-                                                 buf, len);
+       /* AP mode Power saving processing */
+       if (vif->nw_type == AP_NETWORK) {
+               queued = ath6kl_mgmt_powersave_ap(vif,
+                                       id, chan->center_freq,
+                                       wait, buf,
+                                       len, &more_data, no_cck);
+               if (queued)
+                       return 0;
        }
+
+       return ath6kl_wmi_send_mgmt_cmd(ar->wmi, vif->fw_vif_idx, id,
+                                       chan->center_freq, wait,
+                                       buf, len, no_cck);
 }
 
 static void ath6kl_mgmt_frame_register(struct wiphy *wiphy,
@@ -3009,18 +3089,36 @@ int ath6kl_cfg80211_init(struct ath6kl *ar)
 
        wiphy->max_sched_scan_ssids = 10;
 
+       ar->wiphy->flags |= WIPHY_FLAG_SUPPORTS_FW_ROAM |
+                           WIPHY_FLAG_HAVE_AP_SME |
+                           WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL |
+                           WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD;
+
+       if (test_bit(ATH6KL_FW_CAPABILITY_SCHED_SCAN, ar->fw_capabilities))
+               ar->wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN;
+
+       ar->wiphy->probe_resp_offload =
+               NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS |
+               NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2 |
+               NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P |
+               NL80211_PROBE_RESP_OFFLOAD_SUPPORT_80211U;
+
        ret = wiphy_register(wiphy);
        if (ret < 0) {
                ath6kl_err("couldn't register wiphy device\n");
                return ret;
        }
 
+       ar->wiphy_registered = true;
+
        return 0;
 }
 
 void ath6kl_cfg80211_cleanup(struct ath6kl *ar)
 {
        wiphy_unregister(ar->wiphy);
+
+       ar->wiphy_registered = false;
 }
 
 struct ath6kl *ath6kl_cfg80211_create(void)
index 3c693b7c0efd317bec2fe2bbdea2611b65faef00..c5def436417f7cab79d832d64bf62138621cb20d 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright (c) 2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2012 Qualcomm Atheros, Inc.
  *
  * Permission to use, copy, modify, and/or distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
index f89f1e180da376b0181da9ec8a01cd0c176e819e..a60e78c0472ff0287714b8e114edb4c0d4fb0a3d 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright (c) 2010-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2012 Qualcomm Atheros, Inc.
  *
  * Permission to use, copy, modify, and/or distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
index 722ca59b88ce0b9c7ac6355a38639ae651dd5771..e66cf4399b61ed64e4f7b6ed475041048f844983 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright (c) 2004-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2012 Qualcomm Atheros, Inc.
  *
  * Permission to use, copy, modify, and/or distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -97,48 +98,12 @@ int ath6kl_core_init(struct ath6kl *ar)
 
        ath6kl_dbg(ATH6KL_DBG_TRC, "%s: got wmi @ 0x%p.\n", __func__, ar->wmi);
 
-       ret = ath6kl_cfg80211_init(ar);
-       if (ret)
-               goto err_node_cleanup;
-
-       ret = ath6kl_debug_init(ar);
-       if (ret) {
-               wiphy_unregister(ar->wiphy);
-               goto err_node_cleanup;
-       }
-
-       for (i = 0; i < ar->vif_max; i++)
-               ar->avail_idx_map |= BIT(i);
-
-       rtnl_lock();
-
-       /* Add an initial station interface */
-       ndev = ath6kl_interface_add(ar, "wlan%d", NL80211_IFTYPE_STATION, 0,
-                                   INFRA_NETWORK);
-
-       rtnl_unlock();
-
-       if (!ndev) {
-               ath6kl_err("Failed to instantiate a network device\n");
-               ret = -ENOMEM;
-               wiphy_unregister(ar->wiphy);
-               goto err_debug_init;
-       }
-
-
-       ath6kl_dbg(ATH6KL_DBG_TRC, "%s: name=%s dev=0x%p, ar=0x%p\n",
-                       __func__, ndev->name, ndev, ar);
-
        /* setup access class priority mappings */
        ar->ac_stream_pri_map[WMM_AC_BK] = 0; /* lowest  */
        ar->ac_stream_pri_map[WMM_AC_BE] = 1;
        ar->ac_stream_pri_map[WMM_AC_VI] = 2;
        ar->ac_stream_pri_map[WMM_AC_VO] = 3; /* highest */
 
-       /* give our connected endpoints some buffers */
-       ath6kl_rx_refill(ar->htc_target, ar->ctrl_ep);
-       ath6kl_rx_refill(ar->htc_target, ar->ac2ep_map[WMM_AC_BE]);
-
        /* allocate some buffers that handle larger AMSDU frames */
        ath6kl_refill_amsdu_rxbufs(ar, ATH6KL_MAX_AMSDU_RX_BUFFERS);
 
@@ -157,48 +122,56 @@ int ath6kl_core_init(struct ath6kl *ar)
        if (uart_debug)
                ar->conf_flags |= ATH6KL_CONF_UART_DEBUG;
 
-       ar->wiphy->flags |= WIPHY_FLAG_SUPPORTS_FW_ROAM |
-                           WIPHY_FLAG_HAVE_AP_SME |
-                           WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL |
-                           WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD;
-
-       if (test_bit(ATH6KL_FW_CAPABILITY_SCHED_SCAN, ar->fw_capabilities))
-               ar->wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN;
-
-       ar->wiphy->probe_resp_offload =
-               NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS |
-               NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2 |
-               NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P |
-               NL80211_PROBE_RESP_OFFLOAD_SUPPORT_80211U;
-
        set_bit(FIRST_BOOT, &ar->flag);
 
-       ndev->hw_features |= NETIF_F_IP_CSUM | NETIF_F_RXCSUM;
-
        ret = ath6kl_init_hw_start(ar);
        if (ret) {
                ath6kl_err("Failed to start hardware: %d\n", ret);
                goto err_rxbuf_cleanup;
        }
 
-       /*
-        * Set mac address which is received in ready event
-        * FIXME: Move to ath6kl_interface_add()
-        */
-       memcpy(ndev->dev_addr, ar->mac_addr, ETH_ALEN);
+       /* give our connected endpoints some buffers */
+       ath6kl_rx_refill(ar->htc_target, ar->ctrl_ep);
+       ath6kl_rx_refill(ar->htc_target, ar->ac2ep_map[WMM_AC_BE]);
 
-       return ret;
+       ret = ath6kl_cfg80211_init(ar);
+       if (ret)
+               goto err_rxbuf_cleanup;
+
+       ret = ath6kl_debug_init(ar);
+       if (ret) {
+               wiphy_unregister(ar->wiphy);
+               goto err_rxbuf_cleanup;
+       }
+
+       for (i = 0; i < ar->vif_max; i++)
+               ar->avail_idx_map |= BIT(i);
 
-err_rxbuf_cleanup:
-       ath6kl_htc_flush_rx_buf(ar->htc_target);
-       ath6kl_cleanup_amsdu_rxbufs(ar);
        rtnl_lock();
-       ath6kl_cfg80211_vif_cleanup(netdev_priv(ndev));
+
+       /* Add an initial station interface */
+       ndev = ath6kl_interface_add(ar, "wlan%d", NL80211_IFTYPE_STATION, 0,
+                                   INFRA_NETWORK);
+
        rtnl_unlock();
-       wiphy_unregister(ar->wiphy);
+
+       if (!ndev) {
+               ath6kl_err("Failed to instantiate a network device\n");
+               ret = -ENOMEM;
+               wiphy_unregister(ar->wiphy);
+               goto err_debug_init;
+       }
+
+       ath6kl_dbg(ATH6KL_DBG_TRC, "%s: name=%s dev=0x%p, ar=0x%p\n",
+                       __func__, ndev->name, ndev, ar);
+
+       return ret;
+
 err_debug_init:
        ath6kl_debug_cleanup(ar);
-err_node_cleanup:
+err_rxbuf_cleanup:
+       ath6kl_htc_flush_rx_buf(ar->htc_target);
+       ath6kl_cleanup_amsdu_rxbufs(ar);
        ath6kl_wmi_shutdown(ar->wmi);
        clear_bit(WMI_ENABLED, &ar->flag);
        ar->wmi = NULL;
@@ -261,6 +234,8 @@ struct ath6kl *ath6kl_core_create(struct device *dev)
                spin_lock_init(&ar->sta_list[ctr].psq_lock);
                skb_queue_head_init(&ar->sta_list[ctr].psq);
                skb_queue_head_init(&ar->sta_list[ctr].apsdq);
+               ar->sta_list[ctr].mgmt_psq_len = 0;
+               INIT_LIST_HEAD(&ar->sta_list[ctr].mgmt_psq);
                ar->sta_list[ctr].aggr_conn =
                        kzalloc(sizeof(struct aggr_info_conn), GFP_KERNEL);
                if (!ar->sta_list[ctr].aggr_conn) {
index c4d66e066dc9f293afeddcc449770fa0386a8d3e..b6bdece63361165b192a938147483befffac54ac 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright (c) 2010-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2012 Qualcomm Atheros, Inc.
  *
  * Permission to use, copy, modify, and/or distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -62,6 +63,9 @@
 #define A_DEFAULT_LISTEN_INTERVAL         1      /* beacon intervals */
 #define A_MAX_WOW_LISTEN_INTERVAL         1000
 
+/* Channel dwell time in fg scan */
+#define ATH6KL_FG_SCAN_INTERVAL                50 /* in ms */
+
 /* includes also the null byte */
 #define ATH6KL_FIRMWARE_MAGIC               "QCA-ATH6KL"
 
@@ -285,6 +289,16 @@ struct ath6kl_cookie {
        struct ath6kl_cookie *arc_list_next;
 };
 
+struct ath6kl_mgmt_buff {
+       struct list_head list;
+       u32 freq;
+       u32 wait;
+       u32 id;
+       bool no_cck;
+       size_t len;
+       u8 buf[0];
+};
+
 struct ath6kl_sta {
        u16 sta_flags;
        u8 mac[ETH_ALEN];
@@ -295,6 +309,8 @@ struct ath6kl_sta {
        u8 wpa_ie[ATH6KL_MAX_IE];
        struct sk_buff_head psq;
        spinlock_t psq_lock;
+       struct list_head mgmt_psq;
+       size_t mgmt_psq_len;
        u8 apsd_info;
        struct sk_buff_head apsdq;
        struct aggr_info_conn *aggr_conn;
@@ -650,12 +666,16 @@ struct ath6kl {
 
        bool p2p;
 
+       bool wiphy_registered;
+
 #ifdef CONFIG_ATH6KL_DEBUG
        struct {
-               struct circ_buf fwlog_buf;
-               spinlock_t fwlog_lock;
-               void *fwlog_tmp;
+               struct sk_buff_head fwlog_queue;
+               struct completion fwlog_completion;
+               bool fwlog_open;
+
                u32 fwlog_mask;
+
                unsigned int dbgfs_diag_reg;
                u32 diag_reg_addr_wr;
                u32 diag_reg_val_wr;
old mode 100644 (file)
new mode 100755 (executable)
index d832058..28b516f
@@ -1,5 +1,6 @@
 /*
  * Copyright (c) 2004-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2012 Qualcomm Atheros, Inc.
  *
  * Permission to use, copy, modify, and/or distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -16,7 +17,7 @@
 
 #include "core.h"
 
-#include <linux/circ_buf.h>
+#include <linux/skbuff.h>
 #include <linux/fs.h>
 #include <linux/vmalloc.h>
 #include <linux/export.h>
@@ -32,9 +33,8 @@ struct ath6kl_fwlog_slot {
        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_MAX_ENTRIES 20
+
 #define ATH6KL_FWLOG_VALID_MASK 0x1ffff
 
 int ath6kl_printk(const char *level, const char *fmt, ...)
@@ -268,105 +268,103 @@ static const struct file_operations fops_war_stats = {
        .llseek = default_llseek,
 };
 
-static void ath6kl_debug_fwlog_add(struct ath6kl *ar, const void *buf,
-                                  size_t buf_len)
+void ath6kl_debug_fwlog_event(struct ath6kl *ar, const void *buf, size_t len)
 {
-       struct circ_buf *fwlog = &ar->debug.fwlog_buf;
-       size_t space;
-       int i;
+       struct ath6kl_fwlog_slot *slot;
+       struct sk_buff *skb;
+       size_t slot_len;
 
-       /* entries must all be equal size */
-       if (WARN_ON(buf_len != ATH6KL_FWLOG_SLOT_SIZE))
+       if (WARN_ON(len > ATH6KL_FWLOG_PAYLOAD_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);
+       slot_len = sizeof(*slot) + ATH6KL_FWLOG_PAYLOAD_SIZE;
 
-       for (i = 0; i < buf_len; i += space) {
-               space = CIRC_SPACE_TO_END(fwlog->head, fwlog->tail,
-                                         ATH6KL_FWLOG_SIZE);
+       skb = alloc_skb(slot_len, GFP_KERNEL);
+       if (!skb)
+               return;
 
-               if ((size_t) space > buf_len - i)
-                       space = buf_len - i;
+       slot = (struct ath6kl_fwlog_slot *) skb_put(skb, slot_len);
+       slot->timestamp = cpu_to_le32(jiffies);
+       slot->length = cpu_to_le32(len);
+       memcpy(slot->payload, buf, len);
 
-               memcpy(&fwlog->buf[fwlog->head], buf, space);
-               fwlog->head = (fwlog->head + space) & (ATH6KL_FWLOG_SIZE - 1);
-       }
+       /* Need to pad each record to fixed length ATH6KL_FWLOG_PAYLOAD_SIZE */
+       memset(slot->payload + len, 0, ATH6KL_FWLOG_PAYLOAD_SIZE - len);
 
-}
+       spin_lock(&ar->debug.fwlog_queue.lock);
 
-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;
+       __skb_queue_tail(&ar->debug.fwlog_queue, skb);
+       complete(&ar->debug.fwlog_completion);
 
-       if (WARN_ON(len > ATH6KL_FWLOG_PAYLOAD_SIZE))
-               return;
+       /* drop oldest entries */
+       while (skb_queue_len(&ar->debug.fwlog_queue) >
+              ATH6KL_FWLOG_MAX_ENTRIES) {
+               skb = __skb_dequeue(&ar->debug.fwlog_queue);
+               kfree_skb(skb);
+       }
 
-       spin_lock_bh(&ar->debug.fwlog_lock);
+       spin_unlock(&ar->debug.fwlog_queue.lock);
 
-       slot->timestamp = cpu_to_le32(jiffies);
-       slot->length = cpu_to_le32(len);
-       memcpy(slot->payload, buf, len);
+       return;
+}
 
-       slot_len = sizeof(*slot) + len;
+static int ath6kl_fwlog_open(struct inode *inode, struct file *file)
+{
+       struct ath6kl *ar = inode->i_private;
 
-       if (slot_len < ATH6KL_FWLOG_SLOT_SIZE)
-               memset(slot->payload + len, 0,
-                      ATH6KL_FWLOG_SLOT_SIZE - slot_len);
+       if (ar->debug.fwlog_open)
+               return -EBUSY;
 
-       ath6kl_debug_fwlog_add(ar, slot, ATH6KL_FWLOG_SLOT_SIZE);
+       ar->debug.fwlog_open = true;
 
-       spin_unlock_bh(&ar->debug.fwlog_lock);
+       file->private_data = inode->i_private;
+       return 0;
 }
 
-static bool ath6kl_debug_fwlog_empty(struct ath6kl *ar)
+static int ath6kl_fwlog_release(struct inode *inode, struct file *file)
 {
-       return CIRC_CNT(ar->debug.fwlog_buf.head,
-                       ar->debug.fwlog_buf.tail,
-                       ATH6KL_FWLOG_SLOT_SIZE) == 0;
+       struct ath6kl *ar = inode->i_private;
+
+       ar->debug.fwlog_open = false;
+
+       return 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;
+       struct sk_buff *skb;
        ssize_t ret_cnt;
+       size_t len = 0;
        char *buf;
-       int ccnt;
 
-       buf = vmalloc(buf_len);
+       buf = vmalloc(count);
        if (!buf)
                return -ENOMEM;
 
        /* read undelivered logs from firmware */
        ath6kl_read_fwlogs(ar);
 
-       spin_lock_bh(&ar->debug.fwlog_lock);
+       spin_lock(&ar->debug.fwlog_queue.lock);
 
-       while (len < buf_len && !ath6kl_debug_fwlog_empty(ar)) {
-               ccnt = CIRC_CNT_TO_END(fwlog->head, fwlog->tail,
-                                      ATH6KL_FWLOG_SIZE);
+       while ((skb = __skb_dequeue(&ar->debug.fwlog_queue))) {
+               if (skb->len > count - len) {
+                       /* not enough space, put skb back and leave */
+                       __skb_queue_head(&ar->debug.fwlog_queue, skb);
+                       break;
+               }
 
-               if ((size_t) ccnt > buf_len - len)
-                       ccnt = buf_len - len;
 
-               memcpy(buf + len, &fwlog->buf[fwlog->tail], ccnt);
-               len += ccnt;
+               memcpy(buf + len, skb->data, skb->len);
+               len += skb->len;
 
-               fwlog->tail = (fwlog->tail + ccnt) &
-                       (ATH6KL_FWLOG_SIZE - 1);
+               kfree_skb(skb);
        }
 
-       spin_unlock_bh(&ar->debug.fwlog_lock);
+       spin_unlock(&ar->debug.fwlog_queue.lock);
 
-       if (WARN_ON(len > buf_len))
-               len = buf_len;
+       /* FIXME: what to do if len == 0? */
 
        ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len);
 
@@ -376,12 +374,87 @@ static ssize_t ath6kl_fwlog_read(struct file *file, char __user *user_buf,
 }
 
 static const struct file_operations fops_fwlog = {
-       .open = ath6kl_debugfs_open,
+       .open = ath6kl_fwlog_open,
+       .release = ath6kl_fwlog_release,
        .read = ath6kl_fwlog_read,
        .owner = THIS_MODULE,
        .llseek = default_llseek,
 };
 
+static ssize_t ath6kl_fwlog_block_read(struct file *file,
+                                      char __user *user_buf,
+                                      size_t count,
+                                      loff_t *ppos)
+{
+       struct ath6kl *ar = file->private_data;
+       struct sk_buff *skb;
+       ssize_t ret_cnt;
+       size_t len = 0, not_copied;
+       char *buf;
+       int ret;
+
+       buf = vmalloc(count);
+       if (!buf)
+               return -ENOMEM;
+
+       spin_lock(&ar->debug.fwlog_queue.lock);
+
+       if (skb_queue_len(&ar->debug.fwlog_queue) == 0) {
+               /* we must init under queue lock */
+               init_completion(&ar->debug.fwlog_completion);
+
+               spin_unlock(&ar->debug.fwlog_queue.lock);
+
+               ret = wait_for_completion_interruptible(
+                       &ar->debug.fwlog_completion);
+               if (ret == -ERESTARTSYS)
+                       return ret;
+
+               spin_lock(&ar->debug.fwlog_queue.lock);
+       }
+
+       while ((skb = __skb_dequeue(&ar->debug.fwlog_queue))) {
+               if (skb->len > count - len) {
+                       /* not enough space, put skb back and leave */
+                       __skb_queue_head(&ar->debug.fwlog_queue, skb);
+                       break;
+               }
+
+
+               memcpy(buf + len, skb->data, skb->len);
+               len += skb->len;
+
+               kfree_skb(skb);
+       }
+
+       spin_unlock(&ar->debug.fwlog_queue.lock);
+
+       /* FIXME: what to do if len == 0? */
+
+       not_copied = copy_to_user(user_buf, buf, len);
+       if (not_copied != 0) {
+               ret_cnt = -EFAULT;
+               goto out;
+       }
+
+       *ppos = *ppos + len;
+
+       ret_cnt = len;
+
+out:
+       vfree(buf);
+
+       return ret_cnt;
+}
+
+static const struct file_operations fops_fwlog_block = {
+       .open = ath6kl_fwlog_open,
+       .release = ath6kl_fwlog_release,
+       .read = ath6kl_fwlog_block_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)
 {
@@ -1651,17 +1724,8 @@ static const struct file_operations fops_power_params = {
 
 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);
+       skb_queue_head_init(&ar->debug.fwlog_queue);
+       init_completion(&ar->debug.fwlog_completion);
 
        /*
         * Actually we are lying here but don't know how to read the mask
@@ -1671,11 +1735,8 @@ int ath6kl_debug_init(struct ath6kl *ar)
 
        ar->debugfs_phy = debugfs_create_dir("ath6kl",
                                             ar->wiphy->debugfsdir);
-       if (!ar->debugfs_phy) {
-               vfree(ar->debug.fwlog_buf.buf);
-               kfree(ar->debug.fwlog_tmp);
+       if (!ar->debugfs_phy)
                return -ENOMEM;
-       }
 
        debugfs_create_file("tgt_stats", S_IRUSR, ar->debugfs_phy, ar,
                            &fops_tgt_stats);
@@ -1689,6 +1750,9 @@ int ath6kl_debug_init(struct ath6kl *ar)
        debugfs_create_file("fwlog", S_IRUSR, ar->debugfs_phy, ar,
                            &fops_fwlog);
 
+       debugfs_create_file("fwlog_block", S_IRUSR, ar->debugfs_phy, ar,
+                           &fops_fwlog_block);
+
        debugfs_create_file("fwlog_mask", S_IRUSR | S_IWUSR, ar->debugfs_phy,
                            ar, &fops_fwlog_mask);
 
@@ -1742,8 +1806,7 @@ int ath6kl_debug_init(struct ath6kl *ar)
 
 void ath6kl_debug_cleanup(struct ath6kl *ar)
 {
-       vfree(ar->debug.fwlog_buf.buf);
-       kfree(ar->debug.fwlog_tmp);
+       skb_queue_purge(&ar->debug.fwlog_queue);
        kfree(ar->debug.roam_tbl);
 }
 
index c4be6e50996b390a2e36dd242c9dc7de1e082239..c5d5e6c8259e54adeccd8515379246a8a7915e95 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright (c) 2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2012 Qualcomm Atheros, Inc.
  *
  * Permission to use, copy, modify, and/or distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
index 2fe1dadfc77a105d1cac911abc15fb770e8f7e32..fd84086638e35163e990b74dad6ec8cbb355208e 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright (c) 2004-2011 Atheros Communications Inc.
+ * Copyright (c) 2011 Qualcomm Atheros, Inc.
  *
  * Permission to use, copy, modify, and/or distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
index e911737ab3457ba47c4a757be2aa6354ea05017a..3e569b26533225cf7994d356f4687eea2745eb6d 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright (c) 2007-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2012 Qualcomm Atheros, Inc.
  *
  * Permission to use, copy, modify, and/or distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -106,9 +107,9 @@ static void ath6kl_hif_dump_fw_crash(struct ath6kl *ar)
 
        BUILD_BUG_ON(REG_DUMP_COUNT_AR6003 % 4);
 
-       for (i = 0; i < REG_DUMP_COUNT_AR6003 / 4; i++) {
+       for (i = 0; i < REG_DUMP_COUNT_AR6003; i += 4) {
                ath6kl_info("%d: 0x%8.8x 0x%8.8x 0x%8.8x 0x%8.8x\n",
-                           4 * i,
+                           i,
                            le32_to_cpu(regdump_val[i]),
                            le32_to_cpu(regdump_val[i + 1]),
                            le32_to_cpu(regdump_val[i + 2]),
@@ -134,6 +135,7 @@ static int ath6kl_hif_proc_dbg_intr(struct ath6kl_device *dev)
                ath6kl_warn("Failed to clear debug interrupt: %d\n", ret);
 
        ath6kl_hif_dump_fw_crash(dev->ar);
+       ath6kl_read_fwlogs(dev->ar);
 
        return ret;
 }
index 699a036f3a44eaad75ee9f81c4bca9e574c84d80..904458ab484b7ebc827f9d4df41067bec8c90916 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright (c) 2004-2011 Atheros Communications Inc.
+ * Copyright (c) 2011 Qualcomm Atheros, Inc.
  *
  * Permission to use, copy, modify, and/or distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -197,6 +198,8 @@ struct hif_scatter_req {
        u8 *virt_dma_buf;
 
        struct hif_scatter_item scat_list[1];
+
+       u32 scat_q_depth;
 };
 
 struct ath6kl_irq_proc_registers {
index 2d721903640b51015e2202773cb4ba9a65041a08..c6f45a744d5287e6ae46cb9f2daf76138fa3e0e7 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright (c) 2007-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2012 Qualcomm Atheros, Inc.
  *
  * Permission to use, copy, modify, and/or distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -22,6 +23,9 @@
 
 #define CALC_TXRX_PADDED_LEN(dev, len)  (__ALIGN_MASK((len), (dev)->block_mask))
 
+/* threshold to re-enable Tx bundling for an AC*/
+#define TX_RESUME_BUNDLE_THRESHOLD     1500
+
 /* Functions for Tx credit handling */
 static void ath6kl_credit_deposit(struct ath6kl_htc_credit_info *cred_info,
                                  struct htc_endpoint_credit_dist *ep_dist,
@@ -744,6 +748,12 @@ static void ath6kl_htc_tx_bundle(struct htc_endpoint *endpoint,
        struct hif_scatter_req *scat_req = NULL;
        int n_scat, n_sent_bundle = 0, tot_pkts_bundle = 0;
        int status;
+       u32 txb_mask;
+       u8 ac = WMM_NUM_AC;
+
+       if ((HTC_CTRL_RSVD_SVC != endpoint->svc_id) ||
+               (WMI_CONTROL_SVC != endpoint->svc_id))
+               ac = target->dev->ar->ep2ac_map[endpoint->eid];
 
        while (true) {
                status = 0;
@@ -763,6 +773,31 @@ static void ath6kl_htc_tx_bundle(struct htc_endpoint *endpoint,
                        break;
                }
 
+               if ((ac < WMM_NUM_AC) && (ac != WMM_AC_BK)) {
+                       if (WMM_AC_BE == ac)
+                               /*
+                                * BE, BK have priorities and bit
+                                * positions reversed
+                                */
+                               txb_mask = (1 << WMM_AC_BK);
+                       else
+                               /*
+                                * any AC with priority lower than
+                                * itself
+                                */
+                               txb_mask = ((1 << ac) - 1);
+               /*
+                * when the scatter request resources drop below a
+                * certain threshold, disable Tx bundling for all
+                * AC's with priority lower than the current requesting
+                * AC. Otherwise re-enable Tx bundling for them
+                */
+               if (scat_req->scat_q_depth < ATH6KL_SCATTER_REQS)
+                       target->tx_bndl_mask &= ~txb_mask;
+               else
+                       target->tx_bndl_mask |= txb_mask;
+               }
+
                ath6kl_dbg(ATH6KL_DBG_HTC, "htc tx pkts to scatter: %d\n",
                           n_scat);
 
@@ -806,6 +841,7 @@ static void ath6kl_htc_tx_from_queue(struct htc_target *target,
        struct htc_packet *packet;
        int bundle_sent;
        int n_pkts_bundle;
+       u8 ac = WMM_NUM_AC;
 
        spin_lock_bh(&target->tx_lock);
 
@@ -823,6 +859,10 @@ static void ath6kl_htc_tx_from_queue(struct htc_target *target,
         */
        INIT_LIST_HEAD(&txq);
 
+       if ((HTC_CTRL_RSVD_SVC != endpoint->svc_id) ||
+               (WMI_CONTROL_SVC != endpoint->svc_id))
+               ac = target->dev->ar->ep2ac_map[endpoint->eid];
+
        while (true) {
 
                if (list_empty(&endpoint->txq))
@@ -840,15 +880,18 @@ static void ath6kl_htc_tx_from_queue(struct htc_target *target,
 
                while (true) {
                        /* try to send a bundle on each pass */
-                       if ((target->tx_bndl_enable) &&
+                       if ((target->tx_bndl_mask) &&
                            (get_queue_depth(&txq) >=
                            HTC_MIN_HTC_MSGS_TO_BUNDLE)) {
                                int temp1 = 0, temp2 = 0;
 
-                               ath6kl_htc_tx_bundle(endpoint, &txq,
-                                                    &temp1, &temp2);
-                               bundle_sent += temp1;
-                               n_pkts_bundle += temp2;
+                               /* check if bundling is enabled for an AC */
+                               if (target->tx_bndl_mask & (1 << ac)) {
+                                       ath6kl_htc_tx_bundle(endpoint, &txq,
+                                                            &temp1, &temp2);
+                                       bundle_sent += temp1;
+                                       n_pkts_bundle += temp2;
+                               }
                        }
 
                        if (list_empty(&txq))
@@ -867,6 +910,26 @@ static void ath6kl_htc_tx_from_queue(struct htc_target *target,
 
                endpoint->ep_st.tx_bundles += bundle_sent;
                endpoint->ep_st.tx_pkt_bundled += n_pkts_bundle;
+
+               /*
+                * if an AC has bundling disabled and no tx bundling
+                * has occured continously for a certain number of TX,
+                * enable tx bundling for this AC
+                */
+               if (!bundle_sent) {
+                       if (!(target->tx_bndl_mask & (1 << ac)) &&
+                               (ac < WMM_NUM_AC)) {
+                               if (++target->ac_tx_count[ac] >=
+                                       TX_RESUME_BUNDLE_THRESHOLD) {
+                                       target->ac_tx_count[ac] = 0;
+                                       target->tx_bndl_mask |= (1 << ac);
+                               }
+                       }
+               } else {
+                       /* tx bundling will reset the counter */
+                       if (ac < WMM_NUM_AC)
+                               target->ac_tx_count[ac] = 0;
+               }
        }
 
        endpoint->tx_proc_cnt = 0;
@@ -2309,7 +2372,21 @@ void ath6kl_htc_flush_rx_buf(struct htc_target *target)
                                   "htc rx flush pkt 0x%p  len %d  ep %d\n",
                                   packet, packet->buf_len,
                                   packet->endpoint);
-                       dev_kfree_skb(packet->pkt_cntxt);
+                       /*
+                        * packets in rx_bufq of endpoint 0 have originally
+                        * been queued from target->free_ctrl_rxbuf where
+                        * packet and packet->buf_start are allocated
+                        * separately using kmalloc(). For other endpoint
+                        * rx_bufq, it is allocated as skb where packet is
+                        * skb->head. Take care of this difference while freeing
+                        * the memory.
+                        */
+                       if (packet->endpoint == ENDPOINT_0) {
+                               kfree(packet->buf_start);
+                               kfree(packet);
+                       } else {
+                               dev_kfree_skb(packet->pkt_cntxt);
+                       }
                        spin_lock_bh(&target->rx_lock);
                }
                spin_unlock_bh(&target->rx_lock);
@@ -2420,6 +2497,15 @@ int ath6kl_htc_conn_service(struct htc_target *target,
        endpoint->cred_dist.endpoint = assigned_ep;
        endpoint->cred_dist.cred_sz = target->tgt_cred_sz;
 
+       switch (endpoint->svc_id) {
+       case WMI_DATA_BK_SVC:
+               endpoint->tx_drop_packet_threshold = MAX_DEF_COOKIE_NUM / 3;
+               break;
+       default:
+               endpoint->tx_drop_packet_threshold = MAX_HI_COOKIE_NUM;
+               break;
+       }
+
        if (conn_req->max_rxmsg_sz) {
                /*
                 * Override cred_per_msg calculation, this optimizes
@@ -2517,7 +2603,8 @@ static void htc_setup_msg_bndl(struct htc_target *target)
                   target->max_rx_bndl_sz, target->max_tx_bndl_sz);
 
        if (target->max_tx_bndl_sz)
-               target->tx_bndl_enable = true;
+               /* tx_bndl_mask is enabled per AC, each has 1 bit */
+               target->tx_bndl_mask = (1 << WMM_NUM_AC) - 1;
 
        if (target->max_rx_bndl_sz)
                target->rx_bndl_enable = true;
@@ -2532,7 +2619,7 @@ static void htc_setup_msg_bndl(struct htc_target *target)
                 * padding will spill into the next credit buffer
                 * which is fatal.
                 */
-               target->tx_bndl_enable = false;
+               target->tx_bndl_mask = 0;
        }
 }
 
index 57672e1ed1a68480fd18ee6cf174766007ef4dd8..2de4d6fc1e32b41d7a68d8da2f56b5832824c8a8 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright (c) 2004-2011 Atheros Communications Inc.
+ * Copyright (c) 2011 Qualcomm Atheros, Inc.
  *
  * Permission to use, copy, modify, and/or distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -87,6 +88,8 @@
 #define WMI_DATA_VO_SVC   MAKE_SERVICE_ID(WMI_SERVICE_GROUP, 4)
 #define WMI_MAX_SERVICES  5
 
+#define WMM_NUM_AC  4
+
 /* reserved and used to flush ALL packets */
 #define HTC_TX_PACKET_TAG_ALL          0
 #define HTC_SERVICE_TX_PACKET_TAG      1
@@ -498,6 +501,7 @@ struct htc_endpoint {
        u8 seqno;
        u32 conn_flags;
        struct htc_endpoint_stats ep_st;
+       u16 tx_drop_packet_threshold;
 };
 
 struct htc_control_buffer {
@@ -531,7 +535,7 @@ struct htc_target {
        /* max messages per bundle for HTC */
        int msg_per_bndl_max;
 
-       bool tx_bndl_enable;
+       u32 tx_bndl_mask;
        int rx_bndl_enable;
        int max_rx_bndl_sz;
        int max_tx_bndl_sz;
@@ -543,6 +547,9 @@ struct htc_target {
        int max_xfer_szper_scatreq;
 
        int chk_irq_status_cnt;
+
+       /* counts the number of Tx without bundling continously per AC */
+       u32 ac_tx_count[WMM_NUM_AC];
 };
 
 void *ath6kl_htc_create(struct ath6kl *ar);
index 0d76c3778106b161f4d3195d62763de10e0a36ee..f3b5502362d82cfe3cdd2d223c98c85737a88090 100644 (file)
@@ -1,6 +1,7 @@
 
 /*
  * Copyright (c) 2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2012 Qualcomm Atheros, Inc.
  *
  * Permission to use, copy, modify, and/or distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -1123,6 +1124,8 @@ static int ath6kl_upload_board_file(struct ath6kl *ar)
        case TARGET_TYPE_AR6003:
                board_data_size = AR6003_BOARD_DATA_SZ;
                board_ext_data_size = AR6003_BOARD_EXT_DATA_SZ;
+               if (ar->fw_board_len > (board_data_size + board_ext_data_size))
+                       board_ext_data_size = AR6003_BOARD_EXT_DATA_SZ_V2;
                break;
        case TARGET_TYPE_AR6004:
                board_data_size = AR6004_BOARD_DATA_SZ;
@@ -1403,7 +1406,8 @@ static int ath6kl_init_upload(struct ath6kl *ar)
                return status;
 
        /* WAR to avoid SDIO CRC err */
-       if (ar->version.target_ver == AR6003_HW_2_0_VERSION) {
+       if (ar->version.target_ver == AR6003_HW_2_0_VERSION ||
+           ar->version.target_ver == AR6003_HW_2_1_1_VERSION) {
                ath6kl_err("temporary war to avoid sdio crc error\n");
 
                param = 0x20;
@@ -1730,5 +1734,7 @@ void ath6kl_stop_txrx(struct ath6kl *ar)
        ath6kl_reset_device(ar, ar->target_type, true, true);
 
        clear_bit(WLAN_ENABLED, &ar->flag);
+
+       up(&ar->sem);
 }
 EXPORT_SYMBOL(ath6kl_stop_txrx);
index b96d01a7919b9294f212ef2f4301aaf3052d650f..f804cf135a607f865ad76a961f4b68643b60f4f8 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright (c) 2004-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2012 Qualcomm Atheros, Inc.
  *
  * Permission to use, copy, modify, and/or distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -80,11 +81,21 @@ static void ath6kl_add_new_sta(struct ath6kl_vif *vif, u8 *mac, u16 aid,
 static void ath6kl_sta_cleanup(struct ath6kl *ar, u8 i)
 {
        struct ath6kl_sta *sta = &ar->sta_list[i];
+       struct ath6kl_mgmt_buff *entry, *tmp;
 
        /* empty the queued pkts in the PS queue if any */
        spin_lock_bh(&sta->psq_lock);
        skb_queue_purge(&sta->psq);
        skb_queue_purge(&sta->apsdq);
+
+       if (sta->mgmt_psq_len != 0) {
+               list_for_each_entry_safe(entry, tmp, &sta->mgmt_psq, list) {
+                       kfree(entry);
+               }
+               INIT_LIST_HEAD(&sta->mgmt_psq);
+               sta->mgmt_psq_len = 0;
+       }
+
        spin_unlock_bh(&sta->psq_lock);
 
        memset(&ar->ap_stats.sta[sta->aid - 1], 0,
@@ -810,6 +821,7 @@ void ath6kl_pspoll_event(struct ath6kl_vif *vif, u8 aid)
        struct sk_buff *skb;
        bool psq_empty = false;
        struct ath6kl *ar = vif->ar;
+       struct ath6kl_mgmt_buff *mgmt_buf;
 
        conn = ath6kl_find_sta_by_aid(ar, aid);
 
@@ -820,7 +832,7 @@ void ath6kl_pspoll_event(struct ath6kl_vif *vif, u8 aid)
         * becomes empty update the PVB for this station.
         */
        spin_lock_bh(&conn->psq_lock);
-       psq_empty  = skb_queue_empty(&conn->psq);
+       psq_empty  = skb_queue_empty(&conn->psq) && (conn->mgmt_psq_len == 0);
        spin_unlock_bh(&conn->psq_lock);
 
        if (psq_empty)
@@ -828,15 +840,31 @@ void ath6kl_pspoll_event(struct ath6kl_vif *vif, u8 aid)
                return;
 
        spin_lock_bh(&conn->psq_lock);
-       skb = skb_dequeue(&conn->psq);
-       spin_unlock_bh(&conn->psq_lock);
+       if (conn->mgmt_psq_len > 0) {
+               mgmt_buf = list_first_entry(&conn->mgmt_psq,
+                                       struct ath6kl_mgmt_buff, list);
+               list_del(&mgmt_buf->list);
+               conn->mgmt_psq_len--;
+               spin_unlock_bh(&conn->psq_lock);
+
+               conn->sta_flags |= STA_PS_POLLED;
+               ath6kl_wmi_send_mgmt_cmd(ar->wmi, vif->fw_vif_idx,
+                                        mgmt_buf->id, mgmt_buf->freq,
+                                        mgmt_buf->wait, mgmt_buf->buf,
+                                        mgmt_buf->len, mgmt_buf->no_cck);
+               conn->sta_flags &= ~STA_PS_POLLED;
+               kfree(mgmt_buf);
+       } else {
+               skb = skb_dequeue(&conn->psq);
+               spin_unlock_bh(&conn->psq_lock);
 
-       conn->sta_flags |= STA_PS_POLLED;
-       ath6kl_data_tx(skb, vif->ndev);
-       conn->sta_flags &= ~STA_PS_POLLED;
+               conn->sta_flags |= STA_PS_POLLED;
+               ath6kl_data_tx(skb, vif->ndev);
+               conn->sta_flags &= ~STA_PS_POLLED;
+       }
 
        spin_lock_bh(&conn->psq_lock);
-       psq_empty  = skb_queue_empty(&conn->psq);
+       psq_empty  = skb_queue_empty(&conn->psq) && (conn->mgmt_psq_len == 0);
        spin_unlock_bh(&conn->psq_lock);
 
        if (psq_empty)
@@ -1184,5 +1212,7 @@ void init_netdev(struct net_device *dev)
                                sizeof(struct wmi_data_hdr) + HTC_HDR_LENGTH
                                + WMI_MAX_TX_META_SZ + ATH6KL_HTC_ALIGN_BYTES;
 
+       dev->hw_features |= NETIF_F_IP_CSUM | NETIF_F_RXCSUM;
+
        return;
 }
index 4febee723495c672f6a4f86e10117b1c81097381..e2f42a13395249d96a8406a6599ff5921804cf74 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright (c) 2004-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2012 Qualcomm Atheros, Inc.
  *
  * Permission to use, copy, modify, and/or distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -49,8 +50,8 @@ struct ath6kl_sdio {
        /* scatter request list head */
        struct list_head scat_req;
 
-       /* Avoids disabling irq while the interrupts being handled */
-       struct mutex mtx_irq;
+       atomic_t irq_handling;
+       wait_queue_head_t irq_wq;
 
        spinlock_t scat_lock;
        bool scatter_enabled;
@@ -404,7 +405,10 @@ static int ath6kl_sdio_read_write_sync(struct ath6kl *ar, u32 addr, u8 *buf,
                        return -ENOMEM;
                mutex_lock(&ar_sdio->dma_buffer_mutex);
                tbuf = ar_sdio->dma_buffer;
-               memcpy(tbuf, buf, len);
+
+               if (request & HIF_WRITE)
+                       memcpy(tbuf, buf, len);
+
                bounced = true;
        } else
                tbuf = buf;
@@ -462,7 +466,7 @@ static void ath6kl_sdio_irq_handler(struct sdio_func *func)
        ath6kl_dbg(ATH6KL_DBG_SDIO, "irq\n");
 
        ar_sdio = sdio_get_drvdata(func);
-       mutex_lock(&ar_sdio->mtx_irq);
+       atomic_set(&ar_sdio->irq_handling, 1);
        /*
         * Release the host during interrups so we can pick it back up when
         * we process commands.
@@ -471,7 +475,10 @@ static void ath6kl_sdio_irq_handler(struct sdio_func *func)
 
        status = ath6kl_hif_intr_bh_handler(ar_sdio->ar);
        sdio_claim_host(ar_sdio->func);
-       mutex_unlock(&ar_sdio->mtx_irq);
+
+       atomic_set(&ar_sdio->irq_handling, 0);
+       wake_up(&ar_sdio->irq_wq);
+
        WARN_ON(status && status != -ECANCELED);
 }
 
@@ -572,6 +579,13 @@ static void ath6kl_sdio_irq_enable(struct ath6kl *ar)
        sdio_release_host(ar_sdio->func);
 }
 
+static bool ath6kl_sdio_is_on_irq(struct ath6kl *ar)
+{
+       struct ath6kl_sdio *ar_sdio = ath6kl_sdio_priv(ar);
+
+       return !atomic_read(&ar_sdio->irq_handling);
+}
+
 static void ath6kl_sdio_irq_disable(struct ath6kl *ar)
 {
        struct ath6kl_sdio *ar_sdio = ath6kl_sdio_priv(ar);
@@ -579,14 +593,21 @@ static void ath6kl_sdio_irq_disable(struct ath6kl *ar)
 
        sdio_claim_host(ar_sdio->func);
 
-       mutex_lock(&ar_sdio->mtx_irq);
+       if (atomic_read(&ar_sdio->irq_handling)) {
+               sdio_release_host(ar_sdio->func);
+
+               ret = wait_event_interruptible(ar_sdio->irq_wq,
+                                              ath6kl_sdio_is_on_irq(ar));
+               if (ret)
+                       return;
+
+               sdio_claim_host(ar_sdio->func);
+       }
 
        ret = sdio_release_irq(ar_sdio->func);
        if (ret)
                ath6kl_err("Failed to release sdio irq: %d\n", ret);
 
-       mutex_unlock(&ar_sdio->mtx_irq);
-
        sdio_release_host(ar_sdio->func);
 }
 
@@ -601,6 +622,8 @@ static struct hif_scatter_req *ath6kl_sdio_scatter_req_get(struct ath6kl *ar)
                node = list_first_entry(&ar_sdio->scat_req,
                                        struct hif_scatter_req, list);
                list_del(&node->list);
+
+               node->scat_q_depth = get_queue_depth(&ar_sdio->scat_req);
        }
 
        spin_unlock_bh(&ar_sdio->scat_lock);
@@ -1285,7 +1308,6 @@ static int ath6kl_sdio_probe(struct sdio_func *func,
        spin_lock_init(&ar_sdio->scat_lock);
        spin_lock_init(&ar_sdio->wr_async_lock);
        mutex_init(&ar_sdio->dma_buffer_mutex);
-       mutex_init(&ar_sdio->mtx_irq);
 
        INIT_LIST_HEAD(&ar_sdio->scat_req);
        INIT_LIST_HEAD(&ar_sdio->bus_req_freeq);
@@ -1293,6 +1315,8 @@ static int ath6kl_sdio_probe(struct sdio_func *func,
 
        INIT_WORK(&ar_sdio->wr_async_work, ath6kl_sdio_write_async_work);
 
+       init_waitqueue_head(&ar_sdio->irq_wq);
+
        for (count = 0; count < BUS_REQUEST_MAX_NUM; count++)
                ath6kl_sdio_free_bus_req(ar_sdio, &ar_sdio->bus_req[count]);
 
index 108a723a1085ad1cb9f7d879498dae8a4eda73d3..78e0ef4567a5287d4755f2812d7e41b97e1e6ccf 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright (c) 2004-2010 Atheros Communications Inc.
+ * Copyright (c) 2011 Qualcomm Atheros, Inc.
  *
  * Permission to use, copy, modify, and/or distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -19,6 +20,7 @@
 
 #define AR6003_BOARD_DATA_SZ           1024
 #define AR6003_BOARD_EXT_DATA_SZ       768
+#define AR6003_BOARD_EXT_DATA_SZ_V2    1024
 
 #define AR6004_BOARD_DATA_SZ     6144
 #define AR6004_BOARD_EXT_DATA_SZ 0
index f0cd61d6188af39cd24741cd70479acf1d8d0542..6675c92b542b4fca19c4af6d1ebd96e6586f046b 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright (c) 2010-2011 Atheros Communications Inc.
+ * Copyright (c) 2011 Qualcomm Atheros, Inc.
  *
  * Permission to use, copy, modify, and/or distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
index 7fd47a62d078ef0f240a98a9dafd3680ffc8190f..fe651d6707df4ed36fc08b69a63b554565582fb5 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright (c) 2010-2011 Atheros Communications Inc.
+ * Copyright (c) 2011 Qualcomm Atheros, Inc.
  *
  * Permission to use, copy, modify, and/or distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
index a3dc6943c7f74a27ef9090d3e00dd55bcdfa1f0a..6754441105efd336ca5df6236612c6d6159dd492 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright (c) 2004-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2012 Qualcomm Atheros, Inc.
  *
  * Permission to use, copy, modify, and/or distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -593,7 +594,8 @@ enum htc_send_full_action ath6kl_tx_queue_full(struct htc_target *target,
         */
        if (ar->ac_stream_pri_map[ar->ep2ac_map[endpoint]] <
            ar->hiac_stream_active_pri &&
-           ar->cookie_count <= MAX_HI_COOKIE_NUM)
+           ar->cookie_count <=
+                       target->endpoint[endpoint].tx_drop_packet_threshold)
                /*
                 * Give preference to the highest priority stream by
                 * dropping the packets which overflowed.
@@ -1296,7 +1298,15 @@ void ath6kl_rx(struct htc_target *target, struct htc_packet *packet)
        skb_put(skb, packet->act_len + HTC_HDR_LENGTH);
        skb_pull(skb, HTC_HDR_LENGTH);
 
+       ath6kl_dbg_dump(ATH6KL_DBG_RAW_BYTES, __func__, "rx ",
+                       skb->data, skb->len);
+
        if (ept == ar->ctrl_ep) {
+               if (test_bit(WMI_ENABLED, &ar->flag)) {
+                       ath6kl_check_wow_status(ar);
+                       ath6kl_wmi_control_rx(ar->wmi, skb);
+                       return;
+               }
                if_idx =
                wmi_cmd_hdr_get_if_idx((struct wmi_cmd_hdr *) skb->data);
        } else {
@@ -1321,10 +1331,6 @@ void ath6kl_rx(struct htc_target *target, struct htc_packet *packet)
 
        spin_unlock_bh(&vif->if_lock);
 
-
-       ath6kl_dbg_dump(ATH6KL_DBG_RAW_BYTES, __func__, "rx ",
-                       skb->data, skb->len);
-
        skb->dev = vif->ndev;
 
        if (!test_bit(WMI_ENABLED, &ar->flag)) {
@@ -1336,11 +1342,6 @@ void ath6kl_rx(struct htc_target *target, struct htc_packet *packet)
 
        ath6kl_check_wow_status(ar);
 
-       if (ept == ar->ctrl_ep) {
-               ath6kl_wmi_control_rx(ar->wmi, skb);
-               return;
-       }
-
        min_hdr_len = sizeof(struct ethhdr) + sizeof(struct wmi_data_hdr) +
                      sizeof(struct ath6kl_llc_snap_hdr);
 
@@ -1416,8 +1417,33 @@ void ath6kl_rx(struct htc_target *target, struct htc_packet *packet)
                        if (!(conn->sta_flags & STA_PS_SLEEP)) {
                                struct sk_buff *skbuff = NULL;
                                bool is_apsdq_empty;
+                               struct ath6kl_mgmt_buff *mgmt;
+                               u8 idx;
 
                                spin_lock_bh(&conn->psq_lock);
+                               while (conn->mgmt_psq_len > 0) {
+                                       mgmt = list_first_entry(
+                                                       &conn->mgmt_psq,
+                                                       struct ath6kl_mgmt_buff,
+                                                       list);
+                                       list_del(&mgmt->list);
+                                       conn->mgmt_psq_len--;
+                                       spin_unlock_bh(&conn->psq_lock);
+                                       idx = vif->fw_vif_idx;
+
+                                       ath6kl_wmi_send_mgmt_cmd(ar->wmi,
+                                                                idx,
+                                                                mgmt->id,
+                                                                mgmt->freq,
+                                                                mgmt->wait,
+                                                                mgmt->buf,
+                                                                mgmt->len,
+                                                                mgmt->no_cck);
+
+                                       kfree(mgmt);
+                                       spin_lock_bh(&conn->psq_lock);
+                               }
+                               conn->mgmt_psq_len = 0;
                                while ((skbuff = skb_dequeue(&conn->psq))) {
                                        spin_unlock_bh(&conn->psq_lock);
                                        ath6kl_data_tx(skbuff, vif->ndev);
index c72567c6d338a07a3e08bb4f2ce849b347cd9469..325b1224c2b13b6bf9aae818acb01e3aeac0862e 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright (c) 2007-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2012 Qualcomm Atheros, Inc.
  *
  * Permission to use, copy, modify, and/or distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
index 18fa9aa8af92464a4888f37b68d9904293ed2573..0a57dcc6041a7e3464cb0fe27ec76dd6695c48d1 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright (c) 2004-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2012 Qualcomm Atheros, Inc.
  *
  * Permission to use, copy, modify, and/or distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -913,7 +914,7 @@ static void ath6kl_wmi_regdomain_event(struct wmi *wmi, u8 *datap, int len)
                                regpair->regDmnEnum);
        }
 
-       if (country) {
+       if (country && wmi->parent_dev->wiphy_registered) {
                alpha2[0] = country->isoName[0];
                alpha2[1] = country->isoName[1];
 
@@ -3012,6 +3013,22 @@ int ath6kl_wmi_ap_set_mlme(struct wmi *wmip, u8 if_idx, u8 cmd, const u8 *mac,
                                   NO_SYNC_WMIFLAG);
 }
 
+int ath6kl_wmi_ap_hidden_ssid(struct wmi *wmi, u8 if_idx, bool enable)
+{
+       struct sk_buff *skb;
+       struct wmi_ap_hidden_ssid_cmd *cmd;
+
+       skb = ath6kl_wmi_get_new_buf(sizeof(*cmd));
+       if (!skb)
+               return -ENOMEM;
+
+       cmd = (struct wmi_ap_hidden_ssid_cmd *) skb->data;
+       cmd->hidden_ssid = enable ? 1 : 0;
+
+       return ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_AP_HIDDEN_SSID_CMDID,
+                                  NO_SYNC_WMIFLAG);
+}
+
 /* This command will be used to enable/disable AP uAPSD feature */
 int ath6kl_wmi_ap_set_apsd(struct wmi *wmi, u8 if_idx, u8 enable)
 {
@@ -3181,8 +3198,9 @@ int ath6kl_wmi_remain_on_chnl_cmd(struct wmi *wmi, u8 if_idx, u32 freq, u32 dur)
  * ath6kl_wmi_send_mgmt_cmd instead. The new function supports P2P
  * mgmt operations using station interface.
  */
-int ath6kl_wmi_send_action_cmd(struct wmi *wmi, u8 if_idx, u32 id, u32 freq,
-                              u32 wait, const u8 *data, u16 data_len)
+static int ath6kl_wmi_send_action_cmd(struct wmi *wmi, u8 if_idx, u32 id,
+                                     u32 freq, u32 wait, const u8 *data,
+                                     u16 data_len)
 {
        struct sk_buff *skb;
        struct wmi_send_action_cmd *p;
@@ -3218,9 +3236,9 @@ int ath6kl_wmi_send_action_cmd(struct wmi *wmi, u8 if_idx, u32 id, u32 freq,
                                   NO_SYNC_WMIFLAG);
 }
 
-int ath6kl_wmi_send_mgmt_cmd(struct wmi *wmi, u8 if_idx, u32 id, u32 freq,
-                              u32 wait, const u8 *data, u16 data_len,
-                              u32 no_cck)
+static int __ath6kl_wmi_send_mgmt_cmd(struct wmi *wmi, u8 if_idx, u32 id,
+                                     u32 freq, u32 wait, const u8 *data,
+                                     u16 data_len, u32 no_cck)
 {
        struct sk_buff *skb;
        struct wmi_send_mgmt_cmd *p;
@@ -3257,6 +3275,32 @@ int ath6kl_wmi_send_mgmt_cmd(struct wmi *wmi, u8 if_idx, u32 id, u32 freq,
                                   NO_SYNC_WMIFLAG);
 }
 
+int ath6kl_wmi_send_mgmt_cmd(struct wmi *wmi, u8 if_idx, u32 id, u32 freq,
+                               u32 wait, const u8 *data, u16 data_len,
+                               u32 no_cck)
+{
+       int status;
+       struct ath6kl *ar = wmi->parent_dev;
+
+       if (test_bit(ATH6KL_FW_CAPABILITY_STA_P2PDEV_DUPLEX,
+                    ar->fw_capabilities)) {
+               /*
+                * If capable of doing P2P mgmt operations using
+                * station interface, send additional information like
+                * supported rates to advertise and xmit rates for
+                * probe requests
+                */
+               status = __ath6kl_wmi_send_mgmt_cmd(ar->wmi, if_idx, id, freq,
+                                                   wait, data, data_len,
+                                                   no_cck);
+       } else {
+               status = ath6kl_wmi_send_action_cmd(ar->wmi, if_idx, id, freq,
+                                                   wait, data, data_len);
+       }
+
+       return status;
+}
+
 int ath6kl_wmi_send_probe_response_cmd(struct wmi *wmi, u8 if_idx, u32 freq,
                                       const u8 *dst, const u8 *data,
                                       u16 data_len)
@@ -3368,32 +3412,101 @@ static int ath6kl_wmi_roam_tbl_event_rx(struct wmi *wmi, u8 *datap, int len)
        return ath6kl_debug_roam_tbl_event(wmi->parent_dev, datap, len);
 }
 
-/* Control Path */
-int ath6kl_wmi_control_rx(struct wmi *wmi, struct sk_buff *skb)
+/* Process interface specific wmi events, caller would free the datap */
+static int ath6kl_wmi_proc_events_vif(struct wmi *wmi, u16 if_idx, u16 cmd_id,
+                                       u8 *datap, u32 len)
 {
-       struct wmi_cmd_hdr *cmd;
        struct ath6kl_vif *vif;
-       u32 len;
-       u16 id;
-       u8 if_idx;
-       u8 *datap;
-       int ret = 0;
 
-       if (WARN_ON(skb == NULL))
+       vif = ath6kl_get_vif_by_index(wmi->parent_dev, if_idx);
+       if (!vif) {
+               ath6kl_dbg(ATH6KL_DBG_WMI,
+                          "Wmi event for unavailable vif, vif_index:%d\n",
+                           if_idx);
                return -EINVAL;
+       }
 
-       if (skb->len < sizeof(struct wmi_cmd_hdr)) {
-               ath6kl_err("bad packet 1\n");
-               dev_kfree_skb(skb);
+       switch (cmd_id) {
+       case WMI_CONNECT_EVENTID:
+               ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_CONNECT_EVENTID\n");
+               return ath6kl_wmi_connect_event_rx(wmi, datap, len, vif);
+       case WMI_DISCONNECT_EVENTID:
+               ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_DISCONNECT_EVENTID\n");
+               return ath6kl_wmi_disconnect_event_rx(wmi, datap, len, vif);
+       case WMI_TKIP_MICERR_EVENTID:
+               ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_TKIP_MICERR_EVENTID\n");
+               return ath6kl_wmi_tkip_micerr_event_rx(wmi, datap, len, vif);
+       case WMI_BSSINFO_EVENTID:
+               ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_BSSINFO_EVENTID\n");
+               return ath6kl_wmi_bssinfo_event_rx(wmi, datap, len, vif);
+       case WMI_NEIGHBOR_REPORT_EVENTID:
+               ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_NEIGHBOR_REPORT_EVENTID\n");
+               return ath6kl_wmi_neighbor_report_event_rx(wmi, datap, len,
+                                                          vif);
+       case WMI_SCAN_COMPLETE_EVENTID:
+               ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_SCAN_COMPLETE_EVENTID\n");
+               return ath6kl_wmi_scan_complete_rx(wmi, datap, len, vif);
+       case WMI_REPORT_STATISTICS_EVENTID:
+               ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_REPORT_STATISTICS_EVENTID\n");
+               return ath6kl_wmi_stats_event_rx(wmi, datap, len, vif);
+       case WMI_CAC_EVENTID:
+               ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_CAC_EVENTID\n");
+               return ath6kl_wmi_cac_event_rx(wmi, datap, len, vif);
+       case WMI_PSPOLL_EVENTID:
+               ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_PSPOLL_EVENTID\n");
+               return ath6kl_wmi_pspoll_event_rx(wmi, datap, len, vif);
+       case WMI_DTIMEXPIRY_EVENTID:
+               ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_DTIMEXPIRY_EVENTID\n");
+               return ath6kl_wmi_dtimexpiry_event_rx(wmi, datap, len, vif);
+       case WMI_ADDBA_REQ_EVENTID:
+               ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_ADDBA_REQ_EVENTID\n");
+               return ath6kl_wmi_addba_req_event_rx(wmi, datap, len, vif);
+       case WMI_DELBA_REQ_EVENTID:
+               ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_DELBA_REQ_EVENTID\n");
+               return ath6kl_wmi_delba_req_event_rx(wmi, datap, len, vif);
+       case WMI_SET_HOST_SLEEP_MODE_CMD_PROCESSED_EVENTID:
+               ath6kl_dbg(ATH6KL_DBG_WMI,
+                          "WMI_SET_HOST_SLEEP_MODE_CMD_PROCESSED_EVENTID");
+               return ath6kl_wmi_host_sleep_mode_cmd_prcd_evt_rx(wmi, vif);
+       case WMI_REMAIN_ON_CHNL_EVENTID:
+               ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_REMAIN_ON_CHNL_EVENTID\n");
+               return ath6kl_wmi_remain_on_chnl_event_rx(wmi, datap, len, vif);
+       case WMI_CANCEL_REMAIN_ON_CHNL_EVENTID:
+               ath6kl_dbg(ATH6KL_DBG_WMI,
+                          "WMI_CANCEL_REMAIN_ON_CHNL_EVENTID\n");
+               return ath6kl_wmi_cancel_remain_on_chnl_event_rx(wmi, datap,
+                                                                len, vif);
+       case WMI_TX_STATUS_EVENTID:
+               ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_TX_STATUS_EVENTID\n");
+               return ath6kl_wmi_tx_status_event_rx(wmi, datap, len, vif);
+       case WMI_RX_PROBE_REQ_EVENTID:
+               ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_RX_PROBE_REQ_EVENTID\n");
+               return ath6kl_wmi_rx_probe_req_event_rx(wmi, datap, len, vif);
+       case WMI_RX_ACTION_EVENTID:
+               ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_RX_ACTION_EVENTID\n");
+               return ath6kl_wmi_rx_action_event_rx(wmi, datap, len, vif);
+       default:
+               ath6kl_dbg(ATH6KL_DBG_WMI, "unknown cmd id 0x%x\n", cmd_id);
                return -EINVAL;
        }
 
+       return 0;
+}
+
+static int ath6kl_wmi_proc_events(struct wmi *wmi, struct sk_buff *skb)
+{
+       struct wmi_cmd_hdr *cmd;
+       int ret = 0;
+       u32 len;
+       u16 id;
+       u8 if_idx;
+       u8 *datap;
+
        cmd = (struct wmi_cmd_hdr *) skb->data;
        id = le16_to_cpu(cmd->cmd_id);
        if_idx = le16_to_cpu(cmd->info1) & WMI_CMD_HDR_IF_ID_MASK;
 
        skb_pull(skb, sizeof(struct wmi_cmd_hdr));
-
        datap = skb->data;
        len = skb->len;
 
@@ -3401,15 +3514,6 @@ int ath6kl_wmi_control_rx(struct wmi *wmi, struct sk_buff *skb)
        ath6kl_dbg_dump(ATH6KL_DBG_WMI_DUMP, NULL, "wmi rx ",
                        datap, len);
 
-       vif = ath6kl_get_vif_by_index(wmi->parent_dev, if_idx);
-       if (!vif) {
-               ath6kl_dbg(ATH6KL_DBG_WMI,
-                          "Wmi event for unavailable vif, vif_index:%d\n",
-                           if_idx);
-               dev_kfree_skb(skb);
-               return -EINVAL;
-       }
-
        switch (id) {
        case WMI_GET_BITRATE_CMDID:
                ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_GET_BITRATE_CMDID\n");
@@ -3427,26 +3531,10 @@ int ath6kl_wmi_control_rx(struct wmi *wmi, struct sk_buff *skb)
                ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_READY_EVENTID\n");
                ret = ath6kl_wmi_ready_event_rx(wmi, datap, len);
                break;
-       case WMI_CONNECT_EVENTID:
-               ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_CONNECT_EVENTID\n");
-               ret = ath6kl_wmi_connect_event_rx(wmi, datap, len, vif);
-               break;
-       case WMI_DISCONNECT_EVENTID:
-               ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_DISCONNECT_EVENTID\n");
-               ret = ath6kl_wmi_disconnect_event_rx(wmi, datap, len, vif);
-               break;
        case WMI_PEER_NODE_EVENTID:
                ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_PEER_NODE_EVENTID\n");
                ret = ath6kl_wmi_peer_node_event_rx(wmi, datap, len);
                break;
-       case WMI_TKIP_MICERR_EVENTID:
-               ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_TKIP_MICERR_EVENTID\n");
-               ret = ath6kl_wmi_tkip_micerr_event_rx(wmi, datap, len, vif);
-               break;
-       case WMI_BSSINFO_EVENTID:
-               ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_BSSINFO_EVENTID\n");
-               ret = ath6kl_wmi_bssinfo_event_rx(wmi, datap, len, vif);
-               break;
        case WMI_REGDOMAIN_EVENTID:
                ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_REGDOMAIN_EVENTID\n");
                ath6kl_wmi_regdomain_event(wmi, datap, len);
@@ -3455,23 +3543,10 @@ int ath6kl_wmi_control_rx(struct wmi *wmi, struct sk_buff *skb)
                ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_PSTREAM_TIMEOUT_EVENTID\n");
                ret = ath6kl_wmi_pstream_timeout_event_rx(wmi, datap, len);
                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,
-                                                         vif);
-               break;
-       case WMI_SCAN_COMPLETE_EVENTID:
-               ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_SCAN_COMPLETE_EVENTID\n");
-               ret = ath6kl_wmi_scan_complete_rx(wmi, datap, len, vif);
-               break;
        case WMI_CMDERROR_EVENTID:
                ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_CMDERROR_EVENTID\n");
                ret = ath6kl_wmi_error_event_rx(wmi, datap, len);
                break;
-       case WMI_REPORT_STATISTICS_EVENTID:
-               ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_REPORT_STATISTICS_EVENTID\n");
-               ret = ath6kl_wmi_stats_event_rx(wmi, datap, len, vif);
-               break;
        case WMI_RSSI_THRESHOLD_EVENTID:
                ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_RSSI_THRESHOLD_EVENTID\n");
                ret = ath6kl_wmi_rssi_threshold_event_rx(wmi, datap, len);
@@ -3491,10 +3566,6 @@ int ath6kl_wmi_control_rx(struct wmi *wmi, struct sk_buff *skb)
                ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_EXTENSION_EVENTID\n");
                ret = ath6kl_wmi_control_rx_xtnd(wmi, skb);
                break;
-       case WMI_CAC_EVENTID:
-               ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_CAC_EVENTID\n");
-               ret = ath6kl_wmi_cac_event_rx(wmi, datap, len, vif);
-               break;
        case WMI_CHANNEL_CHANGE_EVENTID:
                ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_CHANNEL_CHANGE_EVENTID\n");
                break;
@@ -3534,28 +3605,12 @@ int ath6kl_wmi_control_rx(struct wmi *wmi, struct sk_buff *skb)
                ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_GET_PMKID_LIST_EVENTID\n");
                ret = ath6kl_wmi_get_pmkid_list_event_rx(wmi, datap, len);
                break;
-       case WMI_PSPOLL_EVENTID:
-               ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_PSPOLL_EVENTID\n");
-               ret = ath6kl_wmi_pspoll_event_rx(wmi, datap, len, vif);
-               break;
-       case WMI_DTIMEXPIRY_EVENTID:
-               ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_DTIMEXPIRY_EVENTID\n");
-               ret = ath6kl_wmi_dtimexpiry_event_rx(wmi, datap, len, vif);
-               break;
        case WMI_SET_PARAMS_REPLY_EVENTID:
                ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_SET_PARAMS_REPLY_EVENTID\n");
                break;
-       case WMI_ADDBA_REQ_EVENTID:
-               ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_ADDBA_REQ_EVENTID\n");
-               ret = ath6kl_wmi_addba_req_event_rx(wmi, datap, len, vif);
-               break;
        case WMI_ADDBA_RESP_EVENTID:
                ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_ADDBA_RESP_EVENTID\n");
                break;
-       case WMI_DELBA_REQ_EVENTID:
-               ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_DELBA_REQ_EVENTID\n");
-               ret = ath6kl_wmi_delba_req_event_rx(wmi, datap, len, vif);
-               break;
        case WMI_REPORT_BTCOEX_CONFIG_EVENTID:
                ath6kl_dbg(ATH6KL_DBG_WMI,
                           "WMI_REPORT_BTCOEX_CONFIG_EVENTID\n");
@@ -3568,52 +3623,39 @@ 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_SET_HOST_SLEEP_MODE_CMD_PROCESSED_EVENTID:
-               ath6kl_dbg(ATH6KL_DBG_WMI,
-                          "WMI_SET_HOST_SLEEP_MODE_CMD_PROCESSED_EVENTID");
-               ret = ath6kl_wmi_host_sleep_mode_cmd_prcd_evt_rx(wmi, vif);
-               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, vif);
-               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, vif);
-               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, vif);
-               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, vif);
-               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, vif);
-               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);
-               ret = -EINVAL;
+               /* may be the event is interface specific */
+               ret = ath6kl_wmi_proc_events_vif(wmi, if_idx, id, datap, len);
                break;
        }
 
        dev_kfree_skb(skb);
-
        return ret;
 }
 
+/* Control Path */
+int ath6kl_wmi_control_rx(struct wmi *wmi, struct sk_buff *skb)
+{
+       if (WARN_ON(skb == NULL))
+               return -EINVAL;
+
+       if (skb->len < sizeof(struct wmi_cmd_hdr)) {
+               ath6kl_err("bad packet 1\n");
+               dev_kfree_skb(skb);
+               return -EINVAL;
+       }
+
+       return ath6kl_wmi_proc_events(wmi, skb);
+}
+
 void ath6kl_wmi_reset(struct wmi *wmi)
 {
        spin_lock_bh(&wmi->lock);
index e7919869725e07b9d6300a259c522f3bd2b59ce3..cea7429a8a0b2c13b42b12582f1cee3eee71b7c1 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright (c) 2010-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2012 Qualcomm Atheros, Inc.
  *
  * Permission to use, copy, modify, and/or distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -2128,6 +2129,10 @@ struct wmi_rx_frame_format_cmd {
        u8 reserved[1];
 } __packed;
 
+struct wmi_ap_hidden_ssid_cmd {
+       u8 hidden_ssid;
+} __packed;
+
 /* AP mode events */
 struct wmi_ap_set_apsd_cmd {
        u8 enable;
@@ -2484,6 +2489,7 @@ u8 ath6kl_wmi_get_traffic_class(u8 user_priority);
 
 u8 ath6kl_wmi_determine_user_priority(u8 *pkt, u32 layer2_pri);
 /* AP mode */
+int ath6kl_wmi_ap_hidden_ssid(struct wmi *wmi, u8 if_idx, bool enable);
 int ath6kl_wmi_ap_profile_commit(struct wmi *wmip, u8 if_idx,
                                 struct wmi_connect_cmd *p);
 
@@ -2505,9 +2511,6 @@ int ath6kl_wmi_disable_11b_rates_cmd(struct wmi *wmi, bool disable);
 int ath6kl_wmi_remain_on_chnl_cmd(struct wmi *wmi, u8 if_idx, u32 freq,
                                  u32 dur);
 
-int ath6kl_wmi_send_action_cmd(struct wmi *wmi, u8 if_idx, u32 id, u32 freq,
-                              u32 wait, const u8 *data, u16 data_len);
-
 int ath6kl_wmi_send_mgmt_cmd(struct wmi *wmi, u8 if_idx, u32 id, u32 freq,
                               u32 wait, const u8 *data, u16 data_len,
                               u32 no_cck);