aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorNaveen Gangadharan <ngangadh@qca.qualcomm.com>2012-02-08 20:51:36 -0500
committerKalle Valo <kvalo@qca.qualcomm.com>2012-02-27 08:49:06 -0500
commitd0ff7383a3164adff7072719717d574436ec1677 (patch)
tree82608cf36a386bf7874f6e4c8fd43397c2467dc8 /drivers
parent3b96d49a79021ce4c1ca26aecd784d16aeb91a7c (diff)
ath6kl: Add unicast mgmt frame buffering
PS buffering of unicast Action frames that are sent in a context of a BSS. In AP mode when the recepient station goes to powersave and PS_POLL flag is not set, we would buffer the frames. Send out unicast mgmt bufferred frame when PS_POLL is received. This fixes a bug in P2P GO behavior when sending a GO Discoverability Request to a client that is in sleep mode. kvalo: indentation fixes Signed-off-by: Thirumalai Pachamuthu <tpachamu@qca.qualcomm.com> Signed-off-by: Naveen Gangadharan <ngangadh@qca.qualcomm.com> Signed-off-by: Aarthi Thiruvengadam <athiruve@qca.qualcomm.com> Signed-off-by: Kalle Valo <kvalo@qca.qualcomm.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/net/wireless/ath/ath6kl/cfg80211.c98
-rw-r--r--drivers/net/wireless/ath/ath6kl/core.c2
-rw-r--r--drivers/net/wireless/ath/ath6kl/core.h12
-rw-r--r--drivers/net/wireless/ath/ath6kl/main.c41
-rw-r--r--drivers/net/wireless/ath/ath6kl/txrx.c25
-rw-r--r--drivers/net/wireless/ath/ath6kl/wmi.c37
-rw-r--r--drivers/net/wireless/ath/ath6kl/wmi.h3
7 files changed, 188 insertions, 30 deletions
diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c
index a91f521c5227..86879896b1ab 100644
--- a/drivers/net/wireless/ath/ath6kl/cfg80211.c
+++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c
@@ -2573,6 +2573,76 @@ static int ath6kl_send_go_probe_resp(struct ath6kl_vif *vif,
2573 return ret; 2573 return ret;
2574} 2574}
2575 2575
2576static bool ath6kl_mgmt_powersave_ap(struct ath6kl_vif *vif,
2577 u32 id,
2578 u32 freq,
2579 u32 wait,
2580 const u8 *buf,
2581 size_t len,
2582 bool *more_data,
2583 bool no_cck)
2584{
2585 struct ieee80211_mgmt *mgmt;
2586 struct ath6kl_sta *conn;
2587 bool is_psq_empty = false;
2588 struct ath6kl_mgmt_buff *mgmt_buf;
2589 size_t mgmt_buf_size;
2590 struct ath6kl *ar = vif->ar;
2591
2592 mgmt = (struct ieee80211_mgmt *) buf;
2593 if (is_multicast_ether_addr(mgmt->da))
2594 return false;
2595
2596 conn = ath6kl_find_sta(vif, mgmt->da);
2597 if (!conn)
2598 return false;
2599
2600 if (conn->sta_flags & STA_PS_SLEEP) {
2601 if (!(conn->sta_flags & STA_PS_POLLED)) {
2602 /* Queue the frames if the STA is sleeping */
2603 mgmt_buf_size = len + sizeof(struct ath6kl_mgmt_buff);
2604 mgmt_buf = kmalloc(mgmt_buf_size, GFP_KERNEL);
2605 if (!mgmt_buf)
2606 return false;
2607
2608 INIT_LIST_HEAD(&mgmt_buf->list);
2609 mgmt_buf->id = id;
2610 mgmt_buf->freq = freq;
2611 mgmt_buf->wait = wait;
2612 mgmt_buf->len = len;
2613 mgmt_buf->no_cck = no_cck;
2614 memcpy(mgmt_buf->buf, buf, len);
2615 spin_lock_bh(&conn->psq_lock);
2616 is_psq_empty = skb_queue_empty(&conn->psq) &&
2617 (conn->mgmt_psq_len == 0);
2618 list_add_tail(&mgmt_buf->list, &conn->mgmt_psq);
2619 conn->mgmt_psq_len++;
2620 spin_unlock_bh(&conn->psq_lock);
2621
2622 /*
2623 * If this is the first pkt getting queued
2624 * for this STA, update the PVB for this
2625 * STA.
2626 */
2627 if (is_psq_empty)
2628 ath6kl_wmi_set_pvb_cmd(ar->wmi, vif->fw_vif_idx,
2629 conn->aid, 1);
2630 return true;
2631 }
2632
2633 /*
2634 * This tx is because of a PsPoll.
2635 * Determine if MoreData bit has to be set.
2636 */
2637 spin_lock_bh(&conn->psq_lock);
2638 if (!skb_queue_empty(&conn->psq) || (conn->mgmt_psq_len != 0))
2639 *more_data = true;
2640 spin_unlock_bh(&conn->psq_lock);
2641 }
2642
2643 return false;
2644}
2645
2576static int ath6kl_mgmt_tx(struct wiphy *wiphy, struct net_device *dev, 2646static int ath6kl_mgmt_tx(struct wiphy *wiphy, struct net_device *dev,
2577 struct ieee80211_channel *chan, bool offchan, 2647 struct ieee80211_channel *chan, bool offchan,
2578 enum nl80211_channel_type channel_type, 2648 enum nl80211_channel_type channel_type,
@@ -2584,6 +2654,7 @@ static int ath6kl_mgmt_tx(struct wiphy *wiphy, struct net_device *dev,
2584 struct ath6kl_vif *vif = netdev_priv(dev); 2654 struct ath6kl_vif *vif = netdev_priv(dev);
2585 u32 id; 2655 u32 id;
2586 const struct ieee80211_mgmt *mgmt; 2656 const struct ieee80211_mgmt *mgmt;
2657 bool more_data, queued;
2587 2658
2588 mgmt = (const struct ieee80211_mgmt *) buf; 2659 mgmt = (const struct ieee80211_mgmt *) buf;
2589 if (buf + len >= mgmt->u.probe_resp.variable && 2660 if (buf + len >= mgmt->u.probe_resp.variable &&
@@ -2609,22 +2680,19 @@ static int ath6kl_mgmt_tx(struct wiphy *wiphy, struct net_device *dev,
2609 2680
2610 *cookie = id; 2681 *cookie = id;
2611 2682
2612 if (test_bit(ATH6KL_FW_CAPABILITY_STA_P2PDEV_DUPLEX, 2683 /* AP mode Power saving processing */
2613 ar->fw_capabilities)) { 2684 if (vif->nw_type == AP_NETWORK) {
2614 /* 2685 queued = ath6kl_mgmt_powersave_ap(vif,
2615 * If capable of doing P2P mgmt operations using 2686 id, chan->center_freq,
2616 * station interface, send additional information like 2687 wait, buf,
2617 * supported rates to advertise and xmit rates for 2688 len, &more_data, no_cck);
2618 * probe requests 2689 if (queued)
2619 */ 2690 return 0;
2620 return ath6kl_wmi_send_mgmt_cmd(ar->wmi, vif->fw_vif_idx, id,
2621 chan->center_freq, wait,
2622 buf, len, no_cck);
2623 } else {
2624 return ath6kl_wmi_send_action_cmd(ar->wmi, vif->fw_vif_idx, id,
2625 chan->center_freq, wait,
2626 buf, len);
2627 } 2691 }
2692
2693 return ath6kl_wmi_send_mgmt_cmd(ar->wmi, vif->fw_vif_idx, id,
2694 chan->center_freq, wait,
2695 buf, len, no_cck);
2628} 2696}
2629 2697
2630static void ath6kl_mgmt_frame_register(struct wiphy *wiphy, 2698static void ath6kl_mgmt_frame_register(struct wiphy *wiphy,
diff --git a/drivers/net/wireless/ath/ath6kl/core.c b/drivers/net/wireless/ath/ath6kl/core.c
index c4926cf11213..6dec186d9ac7 100644
--- a/drivers/net/wireless/ath/ath6kl/core.c
+++ b/drivers/net/wireless/ath/ath6kl/core.c
@@ -262,6 +262,8 @@ struct ath6kl *ath6kl_core_create(struct device *dev)
262 spin_lock_init(&ar->sta_list[ctr].psq_lock); 262 spin_lock_init(&ar->sta_list[ctr].psq_lock);
263 skb_queue_head_init(&ar->sta_list[ctr].psq); 263 skb_queue_head_init(&ar->sta_list[ctr].psq);
264 skb_queue_head_init(&ar->sta_list[ctr].apsdq); 264 skb_queue_head_init(&ar->sta_list[ctr].apsdq);
265 ar->sta_list[ctr].mgmt_psq_len = 0;
266 INIT_LIST_HEAD(&ar->sta_list[ctr].mgmt_psq);
265 ar->sta_list[ctr].aggr_conn = 267 ar->sta_list[ctr].aggr_conn =
266 kzalloc(sizeof(struct aggr_info_conn), GFP_KERNEL); 268 kzalloc(sizeof(struct aggr_info_conn), GFP_KERNEL);
267 if (!ar->sta_list[ctr].aggr_conn) { 269 if (!ar->sta_list[ctr].aggr_conn) {
diff --git a/drivers/net/wireless/ath/ath6kl/core.h b/drivers/net/wireless/ath/ath6kl/core.h
index ec7d6b67b7ff..09efafa5fef1 100644
--- a/drivers/net/wireless/ath/ath6kl/core.h
+++ b/drivers/net/wireless/ath/ath6kl/core.h
@@ -286,6 +286,16 @@ struct ath6kl_cookie {
286 struct ath6kl_cookie *arc_list_next; 286 struct ath6kl_cookie *arc_list_next;
287}; 287};
288 288
289struct ath6kl_mgmt_buff {
290 struct list_head list;
291 u32 freq;
292 u32 wait;
293 u32 id;
294 bool no_cck;
295 size_t len;
296 u8 buf[0];
297};
298
289struct ath6kl_sta { 299struct ath6kl_sta {
290 u16 sta_flags; 300 u16 sta_flags;
291 u8 mac[ETH_ALEN]; 301 u8 mac[ETH_ALEN];
@@ -296,6 +306,8 @@ struct ath6kl_sta {
296 u8 wpa_ie[ATH6KL_MAX_IE]; 306 u8 wpa_ie[ATH6KL_MAX_IE];
297 struct sk_buff_head psq; 307 struct sk_buff_head psq;
298 spinlock_t psq_lock; 308 spinlock_t psq_lock;
309 struct list_head mgmt_psq;
310 size_t mgmt_psq_len;
299 u8 apsd_info; 311 u8 apsd_info;
300 struct sk_buff_head apsdq; 312 struct sk_buff_head apsdq;
301 struct aggr_info_conn *aggr_conn; 313 struct aggr_info_conn *aggr_conn;
diff --git a/drivers/net/wireless/ath/ath6kl/main.c b/drivers/net/wireless/ath/ath6kl/main.c
index d463a18332dc..0d6e352bfb17 100644
--- a/drivers/net/wireless/ath/ath6kl/main.c
+++ b/drivers/net/wireless/ath/ath6kl/main.c
@@ -81,11 +81,21 @@ static void ath6kl_add_new_sta(struct ath6kl_vif *vif, u8 *mac, u16 aid,
81static void ath6kl_sta_cleanup(struct ath6kl *ar, u8 i) 81static void ath6kl_sta_cleanup(struct ath6kl *ar, u8 i)
82{ 82{
83 struct ath6kl_sta *sta = &ar->sta_list[i]; 83 struct ath6kl_sta *sta = &ar->sta_list[i];
84 struct ath6kl_mgmt_buff *entry, *tmp;
84 85
85 /* empty the queued pkts in the PS queue if any */ 86 /* empty the queued pkts in the PS queue if any */
86 spin_lock_bh(&sta->psq_lock); 87 spin_lock_bh(&sta->psq_lock);
87 skb_queue_purge(&sta->psq); 88 skb_queue_purge(&sta->psq);
88 skb_queue_purge(&sta->apsdq); 89 skb_queue_purge(&sta->apsdq);
90
91 if (sta->mgmt_psq_len != 0) {
92 list_for_each_entry_safe(entry, tmp, &sta->mgmt_psq, list) {
93 kfree(entry);
94 }
95 INIT_LIST_HEAD(&sta->mgmt_psq);
96 sta->mgmt_psq_len = 0;
97 }
98
89 spin_unlock_bh(&sta->psq_lock); 99 spin_unlock_bh(&sta->psq_lock);
90 100
91 memset(&ar->ap_stats.sta[sta->aid - 1], 0, 101 memset(&ar->ap_stats.sta[sta->aid - 1], 0,
@@ -811,6 +821,7 @@ void ath6kl_pspoll_event(struct ath6kl_vif *vif, u8 aid)
811 struct sk_buff *skb; 821 struct sk_buff *skb;
812 bool psq_empty = false; 822 bool psq_empty = false;
813 struct ath6kl *ar = vif->ar; 823 struct ath6kl *ar = vif->ar;
824 struct ath6kl_mgmt_buff *mgmt_buf;
814 825
815 conn = ath6kl_find_sta_by_aid(ar, aid); 826 conn = ath6kl_find_sta_by_aid(ar, aid);
816 827
@@ -821,7 +832,7 @@ void ath6kl_pspoll_event(struct ath6kl_vif *vif, u8 aid)
821 * becomes empty update the PVB for this station. 832 * becomes empty update the PVB for this station.
822 */ 833 */
823 spin_lock_bh(&conn->psq_lock); 834 spin_lock_bh(&conn->psq_lock);
824 psq_empty = skb_queue_empty(&conn->psq); 835 psq_empty = skb_queue_empty(&conn->psq) && (conn->mgmt_psq_len == 0);
825 spin_unlock_bh(&conn->psq_lock); 836 spin_unlock_bh(&conn->psq_lock);
826 837
827 if (psq_empty) 838 if (psq_empty)
@@ -829,15 +840,31 @@ void ath6kl_pspoll_event(struct ath6kl_vif *vif, u8 aid)
829 return; 840 return;
830 841
831 spin_lock_bh(&conn->psq_lock); 842 spin_lock_bh(&conn->psq_lock);
832 skb = skb_dequeue(&conn->psq); 843 if (conn->mgmt_psq_len > 0) {
833 spin_unlock_bh(&conn->psq_lock); 844 mgmt_buf = list_first_entry(&conn->mgmt_psq,
845 struct ath6kl_mgmt_buff, list);
846 list_del(&mgmt_buf->list);
847 conn->mgmt_psq_len--;
848 spin_unlock_bh(&conn->psq_lock);
849
850 conn->sta_flags |= STA_PS_POLLED;
851 ath6kl_wmi_send_mgmt_cmd(ar->wmi, vif->fw_vif_idx,
852 mgmt_buf->id, mgmt_buf->freq,
853 mgmt_buf->wait, mgmt_buf->buf,
854 mgmt_buf->len, mgmt_buf->no_cck);
855 conn->sta_flags &= ~STA_PS_POLLED;
856 kfree(mgmt_buf);
857 } else {
858 skb = skb_dequeue(&conn->psq);
859 spin_unlock_bh(&conn->psq_lock);
834 860
835 conn->sta_flags |= STA_PS_POLLED; 861 conn->sta_flags |= STA_PS_POLLED;
836 ath6kl_data_tx(skb, vif->ndev); 862 ath6kl_data_tx(skb, vif->ndev);
837 conn->sta_flags &= ~STA_PS_POLLED; 863 conn->sta_flags &= ~STA_PS_POLLED;
864 }
838 865
839 spin_lock_bh(&conn->psq_lock); 866 spin_lock_bh(&conn->psq_lock);
840 psq_empty = skb_queue_empty(&conn->psq); 867 psq_empty = skb_queue_empty(&conn->psq) && (conn->mgmt_psq_len == 0);
841 spin_unlock_bh(&conn->psq_lock); 868 spin_unlock_bh(&conn->psq_lock);
842 869
843 if (psq_empty) 870 if (psq_empty)
diff --git a/drivers/net/wireless/ath/ath6kl/txrx.c b/drivers/net/wireless/ath/ath6kl/txrx.c
index 87d46460a524..633637ace661 100644
--- a/drivers/net/wireless/ath/ath6kl/txrx.c
+++ b/drivers/net/wireless/ath/ath6kl/txrx.c
@@ -1417,8 +1417,33 @@ void ath6kl_rx(struct htc_target *target, struct htc_packet *packet)
1417 if (!(conn->sta_flags & STA_PS_SLEEP)) { 1417 if (!(conn->sta_flags & STA_PS_SLEEP)) {
1418 struct sk_buff *skbuff = NULL; 1418 struct sk_buff *skbuff = NULL;
1419 bool is_apsdq_empty; 1419 bool is_apsdq_empty;
1420 struct ath6kl_mgmt_buff *mgmt;
1421 u8 idx;
1420 1422
1421 spin_lock_bh(&conn->psq_lock); 1423 spin_lock_bh(&conn->psq_lock);
1424 while (conn->mgmt_psq_len > 0) {
1425 mgmt = list_first_entry(
1426 &conn->mgmt_psq,
1427 struct ath6kl_mgmt_buff,
1428 list);
1429 list_del(&mgmt->list);
1430 conn->mgmt_psq_len--;
1431 spin_unlock_bh(&conn->psq_lock);
1432 idx = vif->fw_vif_idx;
1433
1434 ath6kl_wmi_send_mgmt_cmd(ar->wmi,
1435 idx,
1436 mgmt->id,
1437 mgmt->freq,
1438 mgmt->wait,
1439 mgmt->buf,
1440 mgmt->len,
1441 mgmt->no_cck);
1442
1443 kfree(mgmt);
1444 spin_lock_bh(&conn->psq_lock);
1445 }
1446 conn->mgmt_psq_len = 0;
1422 while ((skbuff = skb_dequeue(&conn->psq))) { 1447 while ((skbuff = skb_dequeue(&conn->psq))) {
1423 spin_unlock_bh(&conn->psq_lock); 1448 spin_unlock_bh(&conn->psq_lock);
1424 ath6kl_data_tx(skbuff, vif->ndev); 1449 ath6kl_data_tx(skbuff, vif->ndev);
diff --git a/drivers/net/wireless/ath/ath6kl/wmi.c b/drivers/net/wireless/ath/ath6kl/wmi.c
index bbbe0a74d3c3..fce29f7f2e5c 100644
--- a/drivers/net/wireless/ath/ath6kl/wmi.c
+++ b/drivers/net/wireless/ath/ath6kl/wmi.c
@@ -3182,8 +3182,9 @@ int ath6kl_wmi_remain_on_chnl_cmd(struct wmi *wmi, u8 if_idx, u32 freq, u32 dur)
3182 * ath6kl_wmi_send_mgmt_cmd instead. The new function supports P2P 3182 * ath6kl_wmi_send_mgmt_cmd instead. The new function supports P2P
3183 * mgmt operations using station interface. 3183 * mgmt operations using station interface.
3184 */ 3184 */
3185int ath6kl_wmi_send_action_cmd(struct wmi *wmi, u8 if_idx, u32 id, u32 freq, 3185static int ath6kl_wmi_send_action_cmd(struct wmi *wmi, u8 if_idx, u32 id,
3186 u32 wait, const u8 *data, u16 data_len) 3186 u32 freq, u32 wait, const u8 *data,
3187 u16 data_len)
3187{ 3188{
3188 struct sk_buff *skb; 3189 struct sk_buff *skb;
3189 struct wmi_send_action_cmd *p; 3190 struct wmi_send_action_cmd *p;
@@ -3219,9 +3220,9 @@ int ath6kl_wmi_send_action_cmd(struct wmi *wmi, u8 if_idx, u32 id, u32 freq,
3219 NO_SYNC_WMIFLAG); 3220 NO_SYNC_WMIFLAG);
3220} 3221}
3221 3222
3222int ath6kl_wmi_send_mgmt_cmd(struct wmi *wmi, u8 if_idx, u32 id, u32 freq, 3223static int __ath6kl_wmi_send_mgmt_cmd(struct wmi *wmi, u8 if_idx, u32 id,
3223 u32 wait, const u8 *data, u16 data_len, 3224 u32 freq, u32 wait, const u8 *data,
3224 u32 no_cck) 3225 u16 data_len, u32 no_cck)
3225{ 3226{
3226 struct sk_buff *skb; 3227 struct sk_buff *skb;
3227 struct wmi_send_mgmt_cmd *p; 3228 struct wmi_send_mgmt_cmd *p;
@@ -3258,6 +3259,32 @@ int ath6kl_wmi_send_mgmt_cmd(struct wmi *wmi, u8 if_idx, u32 id, u32 freq,
3258 NO_SYNC_WMIFLAG); 3259 NO_SYNC_WMIFLAG);
3259} 3260}
3260 3261
3262int ath6kl_wmi_send_mgmt_cmd(struct wmi *wmi, u8 if_idx, u32 id, u32 freq,
3263 u32 wait, const u8 *data, u16 data_len,
3264 u32 no_cck)
3265{
3266 int status;
3267 struct ath6kl *ar = wmi->parent_dev;
3268
3269 if (test_bit(ATH6KL_FW_CAPABILITY_STA_P2PDEV_DUPLEX,
3270 ar->fw_capabilities)) {
3271 /*
3272 * If capable of doing P2P mgmt operations using
3273 * station interface, send additional information like
3274 * supported rates to advertise and xmit rates for
3275 * probe requests
3276 */
3277 status = __ath6kl_wmi_send_mgmt_cmd(ar->wmi, if_idx, id, freq,
3278 wait, data, data_len,
3279 no_cck);
3280 } else {
3281 status = ath6kl_wmi_send_action_cmd(ar->wmi, if_idx, id, freq,
3282 wait, data, data_len);
3283 }
3284
3285 return status;
3286}
3287
3261int ath6kl_wmi_send_probe_response_cmd(struct wmi *wmi, u8 if_idx, u32 freq, 3288int ath6kl_wmi_send_probe_response_cmd(struct wmi *wmi, u8 if_idx, u32 freq,
3262 const u8 *dst, const u8 *data, 3289 const u8 *dst, const u8 *data,
3263 u16 data_len) 3290 u16 data_len)
diff --git a/drivers/net/wireless/ath/ath6kl/wmi.h b/drivers/net/wireless/ath/ath6kl/wmi.h
index e7d031b8451d..38907f411225 100644
--- a/drivers/net/wireless/ath/ath6kl/wmi.h
+++ b/drivers/net/wireless/ath/ath6kl/wmi.h
@@ -2506,9 +2506,6 @@ int ath6kl_wmi_disable_11b_rates_cmd(struct wmi *wmi, bool disable);
2506int ath6kl_wmi_remain_on_chnl_cmd(struct wmi *wmi, u8 if_idx, u32 freq, 2506int ath6kl_wmi_remain_on_chnl_cmd(struct wmi *wmi, u8 if_idx, u32 freq,
2507 u32 dur); 2507 u32 dur);
2508 2508
2509int ath6kl_wmi_send_action_cmd(struct wmi *wmi, u8 if_idx, u32 id, u32 freq,
2510 u32 wait, const u8 *data, u16 data_len);
2511
2512int ath6kl_wmi_send_mgmt_cmd(struct wmi *wmi, u8 if_idx, u32 id, u32 freq, 2509int ath6kl_wmi_send_mgmt_cmd(struct wmi *wmi, u8 if_idx, u32 id, u32 freq,
2513 u32 wait, const u8 *data, u16 data_len, 2510 u32 wait, const u8 *data, u16 data_len,
2514 u32 no_cck); 2511 u32 no_cck);