aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net
diff options
context:
space:
mode:
authorArik Nemtsov <arik@wizery.com>2011-02-22 17:22:31 -0500
committerLuciano Coelho <coelho@ti.com>2011-02-23 04:18:46 -0500
commitb622d992c21a85ce590afe2c18977ed28b457e0e (patch)
treeb5a5552141932de9628cc569d2f2dee4df9870dc /drivers/net
parent09039f42a24084c10e7761ab28ef22932c62a46f (diff)
wl12xx: AP-mode - management of links in PS-mode
Update the PS mode of each link according to a bitmap polled from fw_status. Manually notify mac80211 about PS mode changes in connected stations. mac80211 will only be notified about PS start when the station is in PS and there is a small number of TX blocks from this link ready in HW. This is required for waking up the remote station since the TIM is updated entirely by FW. When a station enters mac80211-PS-mode, we drop all the skbs in the low-level TX queues belonging to this sta with STAT_TX_FILTERED to keep our queues clean. Signed-off-by: Arik Nemtsov <arik@wizery.com> Signed-off-by: Luciano Coelho <coelho@ti.com>
Diffstat (limited to 'drivers/net')
-rw-r--r--drivers/net/wireless/wl12xx/main.c73
-rw-r--r--drivers/net/wireless/wl12xx/ps.c80
-rw-r--r--drivers/net/wireless/wl12xx/ps.h2
-rw-r--r--drivers/net/wireless/wl12xx/tx.c24
-rw-r--r--drivers/net/wireless/wl12xx/wl12xx.h19
5 files changed, 186 insertions, 12 deletions
diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c
index 5772a33d79ec..95aa19ae84e5 100644
--- a/drivers/net/wireless/wl12xx/main.c
+++ b/drivers/net/wireless/wl12xx/main.c
@@ -537,6 +537,57 @@ static int wl1271_plt_init(struct wl1271 *wl)
537 return ret; 537 return ret;
538} 538}
539 539
540static void wl1271_irq_ps_regulate_link(struct wl1271 *wl, u8 hlid, u8 tx_blks)
541{
542 bool fw_ps;
543
544 /* only regulate station links */
545 if (hlid < WL1271_AP_STA_HLID_START)
546 return;
547
548 fw_ps = test_bit(hlid, (unsigned long *)&wl->ap_fw_ps_map);
549
550 /*
551 * Wake up from high level PS if the STA is asleep with too little
552 * blocks in FW or if the STA is awake.
553 */
554 if (!fw_ps || tx_blks < WL1271_PS_STA_MAX_BLOCKS)
555 wl1271_ps_link_end(wl, hlid);
556
557 /* Start high-level PS if the STA is asleep with enough blocks in FW */
558 else if (fw_ps && tx_blks >= WL1271_PS_STA_MAX_BLOCKS)
559 wl1271_ps_link_start(wl, hlid, true);
560}
561
562static void wl1271_irq_update_links_status(struct wl1271 *wl,
563 struct wl1271_fw_ap_status *status)
564{
565 u32 cur_fw_ps_map;
566 u8 hlid;
567
568 cur_fw_ps_map = le32_to_cpu(status->link_ps_bitmap);
569 if (wl->ap_fw_ps_map != cur_fw_ps_map) {
570 wl1271_debug(DEBUG_PSM,
571 "link ps prev 0x%x cur 0x%x changed 0x%x",
572 wl->ap_fw_ps_map, cur_fw_ps_map,
573 wl->ap_fw_ps_map ^ cur_fw_ps_map);
574
575 wl->ap_fw_ps_map = cur_fw_ps_map;
576 }
577
578 for (hlid = WL1271_AP_STA_HLID_START; hlid < AP_MAX_LINKS; hlid++) {
579 u8 cnt = status->tx_lnk_free_blks[hlid] -
580 wl->links[hlid].prev_freed_blks;
581
582 wl->links[hlid].prev_freed_blks =
583 status->tx_lnk_free_blks[hlid];
584 wl->links[hlid].allocated_blks -= cnt;
585
586 wl1271_irq_ps_regulate_link(wl, hlid,
587 wl->links[hlid].allocated_blks);
588 }
589}
590
540static void wl1271_fw_status(struct wl1271 *wl, 591static void wl1271_fw_status(struct wl1271 *wl,
541 struct wl1271_fw_full_status *full_status) 592 struct wl1271_fw_full_status *full_status)
542{ 593{
@@ -574,16 +625,9 @@ static void wl1271_fw_status(struct wl1271 *wl,
574 if (total) 625 if (total)
575 clear_bit(WL1271_FLAG_FW_TX_BUSY, &wl->flags); 626 clear_bit(WL1271_FLAG_FW_TX_BUSY, &wl->flags);
576 627
577 if (wl->bss_type == BSS_TYPE_AP_BSS) { 628 /* for AP update num of allocated TX blocks per link and ps status */
578 for (i = 0; i < AP_MAX_LINKS; i++) { 629 if (wl->bss_type == BSS_TYPE_AP_BSS)
579 u8 cnt = status->tx_lnk_free_blks[i] - 630 wl1271_irq_update_links_status(wl, &full_status->ap);
580 wl->links[i].prev_freed_blks;
581
582 wl->links[i].prev_freed_blks =
583 status->tx_lnk_free_blks[i];
584 wl->links[i].allocated_blks -= cnt;
585 }
586 }
587 631
588 /* update the host-chipset time offset */ 632 /* update the host-chipset time offset */
589 getnstimeofday(&ts); 633 getnstimeofday(&ts);
@@ -1241,6 +1285,8 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl)
1241 wl->filters = 0; 1285 wl->filters = 0;
1242 wl1271_free_ap_keys(wl); 1286 wl1271_free_ap_keys(wl);
1243 memset(wl->ap_hlid_map, 0, sizeof(wl->ap_hlid_map)); 1287 memset(wl->ap_hlid_map, 0, sizeof(wl->ap_hlid_map));
1288 wl->ap_fw_ps_map = 0;
1289 wl->ap_ps_map = 0;
1244 1290
1245 for (i = 0; i < NUM_TX_QUEUES; i++) 1291 for (i = 0; i < NUM_TX_QUEUES; i++)
1246 wl->tx_blocks_freed[i] = 0; 1292 wl->tx_blocks_freed[i] = 0;
@@ -2649,10 +2695,10 @@ static int wl1271_allocate_sta(struct wl1271 *wl,
2649 } 2695 }
2650 2696
2651 wl_sta = (struct wl1271_station *)sta->drv_priv; 2697 wl_sta = (struct wl1271_station *)sta->drv_priv;
2652
2653 __set_bit(id, wl->ap_hlid_map); 2698 __set_bit(id, wl->ap_hlid_map);
2654 wl_sta->hlid = WL1271_AP_STA_HLID_START + id; 2699 wl_sta->hlid = WL1271_AP_STA_HLID_START + id;
2655 *hlid = wl_sta->hlid; 2700 *hlid = wl_sta->hlid;
2701 memcpy(wl->links[wl_sta->hlid].addr, sta->addr, ETH_ALEN);
2656 return 0; 2702 return 0;
2657} 2703}
2658 2704
@@ -2664,7 +2710,10 @@ static void wl1271_free_sta(struct wl1271 *wl, u8 hlid)
2664 return; 2710 return;
2665 2711
2666 __clear_bit(id, wl->ap_hlid_map); 2712 __clear_bit(id, wl->ap_hlid_map);
2713 memset(wl->links[hlid].addr, 0, ETH_ALEN);
2667 wl1271_tx_reset_link_queues(wl, hlid); 2714 wl1271_tx_reset_link_queues(wl, hlid);
2715 __clear_bit(hlid, &wl->ap_ps_map);
2716 __clear_bit(hlid, (unsigned long *)&wl->ap_fw_ps_map);
2668} 2717}
2669 2718
2670static int wl1271_op_sta_add(struct ieee80211_hw *hw, 2719static int wl1271_op_sta_add(struct ieee80211_hw *hw,
@@ -3355,6 +3404,8 @@ struct ieee80211_hw *wl1271_alloc_hw(void)
3355 wl->set_bss_type = MAX_BSS_TYPE; 3404 wl->set_bss_type = MAX_BSS_TYPE;
3356 wl->fw_bss_type = MAX_BSS_TYPE; 3405 wl->fw_bss_type = MAX_BSS_TYPE;
3357 wl->last_tx_hlid = 0; 3406 wl->last_tx_hlid = 0;
3407 wl->ap_ps_map = 0;
3408 wl->ap_fw_ps_map = 0;
3358 3409
3359 memset(wl->tx_frames_map, 0, sizeof(wl->tx_frames_map)); 3410 memset(wl->tx_frames_map, 0, sizeof(wl->tx_frames_map));
3360 for (i = 0; i < ACX_TX_DESCRIPTORS; i++) 3411 for (i = 0; i < ACX_TX_DESCRIPTORS; i++)
diff --git a/drivers/net/wireless/wl12xx/ps.c b/drivers/net/wireless/wl12xx/ps.c
index b7b3139e00fb..5c347b1bd17f 100644
--- a/drivers/net/wireless/wl12xx/ps.c
+++ b/drivers/net/wireless/wl12xx/ps.c
@@ -24,6 +24,7 @@
24#include "reg.h" 24#include "reg.h"
25#include "ps.h" 25#include "ps.h"
26#include "io.h" 26#include "io.h"
27#include "tx.h"
27 28
28#define WL1271_WAKEUP_TIMEOUT 500 29#define WL1271_WAKEUP_TIMEOUT 500
29 30
@@ -172,3 +173,82 @@ int wl1271_ps_set_mode(struct wl1271 *wl, enum wl1271_cmd_ps_mode mode,
172 173
173 return ret; 174 return ret;
174} 175}
176
177static void wl1271_ps_filter_frames(struct wl1271 *wl, u8 hlid)
178{
179 int i, filtered = 0;
180 struct sk_buff *skb;
181 struct ieee80211_tx_info *info;
182 unsigned long flags;
183
184 /* filter all frames currently the low level queus for this hlid */
185 for (i = 0; i < NUM_TX_QUEUES; i++) {
186 while ((skb = skb_dequeue(&wl->links[hlid].tx_queue[i]))) {
187 info = IEEE80211_SKB_CB(skb);
188 info->flags |= IEEE80211_TX_STAT_TX_FILTERED;
189 info->status.rates[0].idx = -1;
190 ieee80211_tx_status(wl->hw, skb);
191 filtered++;
192 }
193 }
194
195 spin_lock_irqsave(&wl->wl_lock, flags);
196 wl->tx_queue_count -= filtered;
197 spin_unlock_irqrestore(&wl->wl_lock, flags);
198
199 wl1271_handle_tx_low_watermark(wl);
200}
201
202void wl1271_ps_link_start(struct wl1271 *wl, u8 hlid, bool clean_queues)
203{
204 struct ieee80211_sta *sta;
205
206 if (test_bit(hlid, &wl->ap_ps_map))
207 return;
208
209 wl1271_debug(DEBUG_PSM, "start mac80211 PSM on hlid %d blks %d "
210 "clean_queues %d", hlid, wl->links[hlid].allocated_blks,
211 clean_queues);
212
213 rcu_read_lock();
214 sta = ieee80211_find_sta(wl->vif, wl->links[hlid].addr);
215 if (!sta) {
216 wl1271_error("could not find sta %pM for starting ps",
217 wl->links[hlid].addr);
218 rcu_read_unlock();
219 return;
220 }
221
222 ieee80211_sta_ps_transition_ni(sta, true);
223 rcu_read_unlock();
224
225 /* do we want to filter all frames from this link's queues? */
226 if (clean_queues)
227 wl1271_ps_filter_frames(wl, hlid);
228
229 __set_bit(hlid, &wl->ap_ps_map);
230}
231
232void wl1271_ps_link_end(struct wl1271 *wl, u8 hlid)
233{
234 struct ieee80211_sta *sta;
235
236 if (!test_bit(hlid, &wl->ap_ps_map))
237 return;
238
239 wl1271_debug(DEBUG_PSM, "end mac80211 PSM on hlid %d", hlid);
240
241 __clear_bit(hlid, &wl->ap_ps_map);
242
243 rcu_read_lock();
244 sta = ieee80211_find_sta(wl->vif, wl->links[hlid].addr);
245 if (!sta) {
246 wl1271_error("could not find sta %pM for ending ps",
247 wl->links[hlid].addr);
248 goto end;
249 }
250
251 ieee80211_sta_ps_transition_ni(sta, false);
252end:
253 rcu_read_unlock();
254}
diff --git a/drivers/net/wireless/wl12xx/ps.h b/drivers/net/wireless/wl12xx/ps.h
index 8415060f08e5..fc1f4c193593 100644
--- a/drivers/net/wireless/wl12xx/ps.h
+++ b/drivers/net/wireless/wl12xx/ps.h
@@ -32,5 +32,7 @@ int wl1271_ps_set_mode(struct wl1271 *wl, enum wl1271_cmd_ps_mode mode,
32void wl1271_ps_elp_sleep(struct wl1271 *wl); 32void wl1271_ps_elp_sleep(struct wl1271 *wl);
33int wl1271_ps_elp_wakeup(struct wl1271 *wl, bool chip_awake); 33int wl1271_ps_elp_wakeup(struct wl1271 *wl, bool chip_awake);
34void wl1271_elp_work(struct work_struct *work); 34void wl1271_elp_work(struct work_struct *work);
35void wl1271_ps_link_start(struct wl1271 *wl, u8 hlid, bool clean_queues);
36void wl1271_ps_link_end(struct wl1271 *wl, u8 hlid);
35 37
36#endif /* __WL1271_PS_H__ */ 38#endif /* __WL1271_PS_H__ */
diff --git a/drivers/net/wireless/wl12xx/tx.c b/drivers/net/wireless/wl12xx/tx.c
index ea1382bd38f4..ac60d577319f 100644
--- a/drivers/net/wireless/wl12xx/tx.c
+++ b/drivers/net/wireless/wl12xx/tx.c
@@ -86,6 +86,26 @@ static void wl1271_tx_ap_update_inconnection_sta(struct wl1271 *wl,
86 wl1271_acx_set_inconnection_sta(wl, hdr->addr1); 86 wl1271_acx_set_inconnection_sta(wl, hdr->addr1);
87} 87}
88 88
89static void wl1271_tx_regulate_link(struct wl1271 *wl, u8 hlid)
90{
91 bool fw_ps;
92 u8 tx_blks;
93
94 /* only regulate station links */
95 if (hlid < WL1271_AP_STA_HLID_START)
96 return;
97
98 fw_ps = test_bit(hlid, (unsigned long *)&wl->ap_fw_ps_map);
99 tx_blks = wl->links[hlid].allocated_blks;
100
101 /*
102 * if in FW PS and there is enough data in FW we can put the link
103 * into high-level PS and clean out its TX queues.
104 */
105 if (fw_ps && tx_blks >= WL1271_PS_STA_MAX_BLOCKS)
106 wl1271_ps_link_start(wl, hlid, true);
107}
108
89u8 wl1271_tx_get_hlid(struct sk_buff *skb) 109u8 wl1271_tx_get_hlid(struct sk_buff *skb)
90{ 110{
91 struct ieee80211_tx_info *control = IEEE80211_SKB_CB(skb); 111 struct ieee80211_tx_info *control = IEEE80211_SKB_CB(skb);
@@ -277,8 +297,10 @@ static int wl1271_prepare_tx_frame(struct wl1271 *wl, struct sk_buff *skb,
277 if (ret < 0) 297 if (ret < 0)
278 return ret; 298 return ret;
279 299
280 if (wl->bss_type == BSS_TYPE_AP_BSS) 300 if (wl->bss_type == BSS_TYPE_AP_BSS) {
281 wl1271_tx_ap_update_inconnection_sta(wl, skb); 301 wl1271_tx_ap_update_inconnection_sta(wl, skb);
302 wl1271_tx_regulate_link(wl, hlid);
303 }
282 304
283 wl1271_tx_fill_hdr(wl, skb, extra, info, hlid); 305 wl1271_tx_fill_hdr(wl, skb, extra, info, hlid);
284 306
diff --git a/drivers/net/wireless/wl12xx/wl12xx.h b/drivers/net/wireless/wl12xx/wl12xx.h
index 0e00c5be99d3..338acc9f60b3 100644
--- a/drivers/net/wireless/wl12xx/wl12xx.h
+++ b/drivers/net/wireless/wl12xx/wl12xx.h
@@ -153,6 +153,17 @@ extern u32 wl12xx_debug_level;
153#define WL1271_AP_BROADCAST_HLID 1 153#define WL1271_AP_BROADCAST_HLID 1
154#define WL1271_AP_STA_HLID_START 2 154#define WL1271_AP_STA_HLID_START 2
155 155
156/*
157 * When in AP-mode, we allow (at least) this number of mem-blocks
158 * to be transmitted to FW for a STA in PS-mode. Only when packets are
159 * present in the FW buffers it will wake the sleeping STA. We want to put
160 * enough packets for the driver to transmit all of its buffered data before
161 * the STA goes to sleep again. But we don't want to take too much mem-blocks
162 * as it might hurt the throughput of active STAs.
163 * The number of blocks (18) is enough for 2 large packets.
164 */
165#define WL1271_PS_STA_MAX_BLOCKS (2 * 9)
166
156#define WL1271_AP_BSS_INDEX 0 167#define WL1271_AP_BSS_INDEX 0
157#define WL1271_AP_DEF_INACTIV_SEC 300 168#define WL1271_AP_DEF_INACTIV_SEC 300
158#define WL1271_AP_DEF_BEACON_EXP 20 169#define WL1271_AP_DEF_BEACON_EXP 20
@@ -326,6 +337,8 @@ struct wl1271_link {
326 /* accounting for allocated / available TX blocks in FW */ 337 /* accounting for allocated / available TX blocks in FW */
327 u8 allocated_blks; 338 u8 allocated_blks;
328 u8 prev_freed_blks; 339 u8 prev_freed_blks;
340
341 u8 addr[ETH_ALEN];
329}; 342};
330 343
331struct wl1271 { 344struct wl1271 {
@@ -516,6 +529,12 @@ struct wl1271 {
516 529
517 /* the hlid of the link where the last transmitted skb came from */ 530 /* the hlid of the link where the last transmitted skb came from */
518 int last_tx_hlid; 531 int last_tx_hlid;
532
533 /* AP-mode - a bitmap of links currently in PS mode according to FW */
534 u32 ap_fw_ps_map;
535
536 /* AP-mode - a bitmap of links currently in PS mode in mac80211 */
537 unsigned long ap_ps_map;
519}; 538};
520 539
521struct wl1271_station { 540struct wl1271_station {