aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless/wl12xx/ps.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/wireless/wl12xx/ps.c')
-rw-r--r--drivers/net/wireless/wl12xx/ps.c90
1 files changed, 83 insertions, 7 deletions
diff --git a/drivers/net/wireless/wl12xx/ps.c b/drivers/net/wireless/wl12xx/ps.c
index 60a3738eadb0..971f13e792da 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
@@ -68,7 +69,7 @@ void wl1271_ps_elp_sleep(struct wl1271 *wl)
68 } 69 }
69} 70}
70 71
71int wl1271_ps_elp_wakeup(struct wl1271 *wl, bool chip_awake) 72int wl1271_ps_elp_wakeup(struct wl1271 *wl)
72{ 73{
73 DECLARE_COMPLETION_ONSTACK(compl); 74 DECLARE_COMPLETION_ONSTACK(compl);
74 unsigned long flags; 75 unsigned long flags;
@@ -86,7 +87,7 @@ int wl1271_ps_elp_wakeup(struct wl1271 *wl, bool chip_awake)
86 * the completion variable in one entity. 87 * the completion variable in one entity.
87 */ 88 */
88 spin_lock_irqsave(&wl->wl_lock, flags); 89 spin_lock_irqsave(&wl->wl_lock, flags);
89 if (work_pending(&wl->irq_work) || chip_awake) 90 if (test_bit(WL1271_FLAG_IRQ_RUNNING, &wl->flags))
90 pending = true; 91 pending = true;
91 else 92 else
92 wl->elp_compl = &compl; 93 wl->elp_compl = &compl;
@@ -139,8 +140,7 @@ int wl1271_ps_set_mode(struct wl1271 *wl, enum wl1271_cmd_ps_mode mode,
139 return ret; 140 return ret;
140 } 141 }
141 142
142 ret = wl1271_cmd_ps_mode(wl, STATION_POWER_SAVE_MODE, 143 ret = wl1271_cmd_ps_mode(wl, STATION_POWER_SAVE_MODE);
143 rates, send);
144 if (ret < 0) 144 if (ret < 0)
145 return ret; 145 return ret;
146 146
@@ -149,7 +149,7 @@ int wl1271_ps_set_mode(struct wl1271 *wl, enum wl1271_cmd_ps_mode mode,
149 case STATION_ACTIVE_MODE: 149 case STATION_ACTIVE_MODE:
150 default: 150 default:
151 wl1271_debug(DEBUG_PSM, "leaving psm"); 151 wl1271_debug(DEBUG_PSM, "leaving psm");
152 ret = wl1271_ps_elp_wakeup(wl, false); 152 ret = wl1271_ps_elp_wakeup(wl);
153 if (ret < 0) 153 if (ret < 0)
154 return ret; 154 return ret;
155 155
@@ -163,8 +163,7 @@ int wl1271_ps_set_mode(struct wl1271 *wl, enum wl1271_cmd_ps_mode mode,
163 if (ret < 0) 163 if (ret < 0)
164 return ret; 164 return ret;
165 165
166 ret = wl1271_cmd_ps_mode(wl, STATION_ACTIVE_MODE, 166 ret = wl1271_cmd_ps_mode(wl, STATION_ACTIVE_MODE);
167 rates, send);
168 if (ret < 0) 167 if (ret < 0)
169 return ret; 168 return ret;
170 169
@@ -175,4 +174,81 @@ int wl1271_ps_set_mode(struct wl1271 *wl, enum wl1271_cmd_ps_mode mode,
175 return ret; 174 return ret;
176} 175}
177 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 }
178 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}