]>
Commit | Line | Data |
---|---|---|
f5fc0f86 LC |
1 | /* |
2 | * This file is part of wl1271 | |
3 | * | |
4 | * Copyright (C) 2008-2009 Nokia Corporation | |
5 | * | |
6 | * Contact: Luciano Coelho <luciano.coelho@nokia.com> | |
7 | * | |
8 | * This program is free software; you can redistribute it and/or | |
9 | * modify it under the terms of the GNU General Public License | |
10 | * version 2 as published by the Free Software Foundation. | |
11 | * | |
12 | * This program is distributed in the hope that it will be useful, but | |
13 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
15 | * General Public License for more details. | |
16 | * | |
17 | * You should have received a copy of the GNU General Public License | |
18 | * along with this program; if not, write to the Free Software | |
19 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | |
20 | * 02110-1301 USA | |
21 | * | |
22 | */ | |
23 | ||
00d20100 SL |
24 | #include "ps.h" |
25 | #include "io.h" | |
b622d992 | 26 | #include "tx.h" |
0f4e3122 | 27 | #include "debug.h" |
f5fc0f86 LC |
28 | |
29 | #define WL1271_WAKEUP_TIMEOUT 500 | |
30 | ||
1ab0f212 EP |
31 | #define ELP_ENTRY_DELAY 5 |
32 | ||
37b70a81 | 33 | void wl1271_elp_work(struct work_struct *work) |
f5fc0f86 | 34 | { |
37b70a81 JO |
35 | struct delayed_work *dwork; |
36 | struct wl1271 *wl; | |
c29bb001 | 37 | struct wl12xx_vif *wlvif; |
b0f0ad39 | 38 | int ret; |
37b70a81 JO |
39 | |
40 | dwork = container_of(work, struct delayed_work, work); | |
41 | wl = container_of(dwork, struct wl1271, elp_work); | |
42 | ||
43 | wl1271_debug(DEBUG_PSM, "elp work"); | |
44 | ||
45 | mutex_lock(&wl->mutex); | |
46 | ||
8c7f4f31 JO |
47 | if (unlikely(wl->state == WL1271_STATE_OFF)) |
48 | goto out; | |
49 | ||
a665d6e2 EP |
50 | /* our work might have been already cancelled */ |
51 | if (unlikely(!test_bit(WL1271_FLAG_ELP_REQUESTED, &wl->flags))) | |
52 | goto out; | |
53 | ||
c29bb001 | 54 | if (test_bit(WL1271_FLAG_IN_ELP, &wl->flags)) |
37b70a81 | 55 | goto out; |
f5fc0f86 | 56 | |
c29bb001 | 57 | wl12xx_for_each_wlvif(wl, wlvif) { |
a0c7b782 EP |
58 | if (wlvif->bss_type == BSS_TYPE_AP_BSS) |
59 | goto out; | |
60 | ||
5c0dc2fc | 61 | if (!test_bit(WLVIF_FLAG_IN_PS, &wlvif->flags) && |
a0c7b782 | 62 | test_bit(WLVIF_FLAG_IN_USE, &wlvif->flags)) |
c29bb001 EP |
63 | goto out; |
64 | } | |
65 | ||
37b70a81 | 66 | wl1271_debug(DEBUG_PSM, "chip to elp"); |
b0f0ad39 IY |
67 | ret = wlcore_raw_write32(wl, HW_ACCESS_ELP_CTRL_REG, ELPCTRL_SLEEP); |
68 | if (ret < 0) { | |
69 | wl12xx_queue_recovery_work(wl); | |
70 | goto out; | |
71 | } | |
72 | ||
71449f8d | 73 | set_bit(WL1271_FLAG_IN_ELP, &wl->flags); |
37b70a81 JO |
74 | |
75 | out: | |
76 | mutex_unlock(&wl->mutex); | |
77 | } | |
78 | ||
37b70a81 JO |
79 | /* Routines to toggle sleep mode while in ELP */ |
80 | void wl1271_ps_elp_sleep(struct wl1271 *wl) | |
81 | { | |
c29bb001 | 82 | struct wl12xx_vif *wlvif; |
1ab0f212 | 83 | u32 timeout; |
c29bb001 | 84 | |
26b5858a | 85 | if (wl->sleep_auth != WL1271_PSM_ELP) |
441101f6 LC |
86 | return; |
87 | ||
a665d6e2 EP |
88 | /* we shouldn't get consecutive sleep requests */ |
89 | if (WARN_ON(test_and_set_bit(WL1271_FLAG_ELP_REQUESTED, &wl->flags))) | |
90 | return; | |
91 | ||
c29bb001 | 92 | wl12xx_for_each_wlvif(wl, wlvif) { |
a0c7b782 EP |
93 | if (wlvif->bss_type == BSS_TYPE_AP_BSS) |
94 | return; | |
95 | ||
5c0dc2fc | 96 | if (!test_bit(WLVIF_FLAG_IN_PS, &wlvif->flags) && |
a0c7b782 | 97 | test_bit(WLVIF_FLAG_IN_USE, &wlvif->flags)) |
c29bb001 EP |
98 | return; |
99 | } | |
a665d6e2 | 100 | |
1ab0f212 EP |
101 | if (wl->conf.conn.forced_ps) |
102 | timeout = ELP_ENTRY_DELAY; | |
103 | else | |
104 | timeout = wl->conf.conn.dynamic_ps_timeout; | |
105 | ||
a665d6e2 | 106 | ieee80211_queue_delayed_work(wl->hw, &wl->elp_work, |
1ab0f212 | 107 | msecs_to_jiffies(timeout)); |
f5fc0f86 LC |
108 | } |
109 | ||
a620865e | 110 | int wl1271_ps_elp_wakeup(struct wl1271 *wl) |
f5fc0f86 LC |
111 | { |
112 | DECLARE_COMPLETION_ONSTACK(compl); | |
113 | unsigned long flags; | |
114 | int ret; | |
115 | u32 start_time = jiffies; | |
116 | bool pending = false; | |
117 | ||
a665d6e2 EP |
118 | /* |
119 | * we might try to wake up even if we didn't go to sleep | |
120 | * before (e.g. on boot) | |
121 | */ | |
122 | if (!test_and_clear_bit(WL1271_FLAG_ELP_REQUESTED, &wl->flags)) | |
123 | return 0; | |
124 | ||
125 | /* don't cancel_sync as it might contend for a mutex and deadlock */ | |
126 | cancel_delayed_work(&wl->elp_work); | |
127 | ||
71449f8d | 128 | if (!test_bit(WL1271_FLAG_IN_ELP, &wl->flags)) |
f5fc0f86 LC |
129 | return 0; |
130 | ||
131 | wl1271_debug(DEBUG_PSM, "waking up chip from elp"); | |
132 | ||
133 | /* | |
134 | * The spinlock is required here to synchronize both the work and | |
135 | * the completion variable in one entity. | |
136 | */ | |
137 | spin_lock_irqsave(&wl->wl_lock, flags); | |
a620865e | 138 | if (test_bit(WL1271_FLAG_IRQ_RUNNING, &wl->flags)) |
f5fc0f86 LC |
139 | pending = true; |
140 | else | |
141 | wl->elp_compl = &compl; | |
142 | spin_unlock_irqrestore(&wl->wl_lock, flags); | |
143 | ||
b0f0ad39 IY |
144 | ret = wlcore_raw_write32(wl, HW_ACCESS_ELP_CTRL_REG, ELPCTRL_WAKE_UP); |
145 | if (ret < 0) { | |
146 | wl12xx_queue_recovery_work(wl); | |
147 | goto err; | |
148 | } | |
f5fc0f86 LC |
149 | |
150 | if (!pending) { | |
151 | ret = wait_for_completion_timeout( | |
152 | &compl, msecs_to_jiffies(WL1271_WAKEUP_TIMEOUT)); | |
153 | if (ret == 0) { | |
154 | wl1271_error("ELP wakeup timeout!"); | |
baacb9ae | 155 | wl12xx_queue_recovery_work(wl); |
f5fc0f86 LC |
156 | ret = -ETIMEDOUT; |
157 | goto err; | |
158 | } else if (ret < 0) { | |
159 | wl1271_error("ELP wakeup completion error."); | |
160 | goto err; | |
161 | } | |
162 | } | |
163 | ||
71449f8d | 164 | clear_bit(WL1271_FLAG_IN_ELP, &wl->flags); |
f5fc0f86 LC |
165 | |
166 | wl1271_debug(DEBUG_PSM, "wakeup time: %u ms", | |
167 | jiffies_to_msecs(jiffies - start_time)); | |
168 | goto out; | |
169 | ||
170 | err: | |
171 | spin_lock_irqsave(&wl->wl_lock, flags); | |
172 | wl->elp_compl = NULL; | |
173 | spin_unlock_irqrestore(&wl->wl_lock, flags); | |
174 | return ret; | |
175 | ||
176 | out: | |
177 | return 0; | |
178 | } | |
179 | ||
0603d891 | 180 | int wl1271_ps_set_mode(struct wl1271 *wl, struct wl12xx_vif *wlvif, |
248a0018 | 181 | enum wl1271_cmd_ps_mode mode) |
f5fc0f86 LC |
182 | { |
183 | int ret; | |
f1d63a59 | 184 | u16 timeout = wl->conf.conn.dynamic_ps_timeout; |
f5fc0f86 LC |
185 | |
186 | switch (mode) { | |
f1d63a59 | 187 | case STATION_AUTO_PS_MODE: |
5c0dc2fc | 188 | case STATION_POWER_SAVE_MODE: |
f1d63a59 ES |
189 | wl1271_debug(DEBUG_PSM, "entering psm (mode=%d,timeout=%u)", |
190 | mode, timeout); | |
1922167b | 191 | |
dae728fe ES |
192 | ret = wl1271_acx_wake_up_conditions(wl, wlvif, |
193 | wl->conf.conn.wake_up_event, | |
194 | wl->conf.conn.listen_interval); | |
03f06b7e JO |
195 | if (ret < 0) { |
196 | wl1271_error("couldn't set wake up conditions"); | |
197 | return ret; | |
198 | } | |
199 | ||
f1d63a59 | 200 | ret = wl1271_cmd_ps_mode(wl, wlvif, mode, timeout); |
f5fc0f86 LC |
201 | if (ret < 0) |
202 | return ret; | |
203 | ||
5c0dc2fc | 204 | set_bit(WLVIF_FLAG_IN_PS, &wlvif->flags); |
ed471d34 | 205 | |
e832837b VG |
206 | /* |
207 | * enable beacon early termination. | |
208 | * Not relevant for 5GHz and for high rates. | |
209 | */ | |
210 | if ((wlvif->band == IEEE80211_BAND_2GHZ) && | |
211 | (wlvif->basic_rate < CONF_HW_BIT_RATE_9MBPS)) { | |
ed471d34 ES |
212 | ret = wl1271_acx_bet_enable(wl, wlvif, true); |
213 | if (ret < 0) | |
214 | return ret; | |
215 | } | |
f5fc0f86 LC |
216 | break; |
217 | case STATION_ACTIVE_MODE: | |
f5fc0f86 | 218 | wl1271_debug(DEBUG_PSM, "leaving psm"); |
f5fc0f86 | 219 | |
11f70f97 | 220 | /* disable beacon early termination */ |
e832837b VG |
221 | if ((wlvif->band == IEEE80211_BAND_2GHZ) && |
222 | (wlvif->basic_rate < CONF_HW_BIT_RATE_9MBPS)) { | |
0603d891 | 223 | ret = wl1271_acx_bet_enable(wl, wlvif, false); |
0e44eb20 SL |
224 | if (ret < 0) |
225 | return ret; | |
226 | } | |
11f70f97 | 227 | |
f1d63a59 | 228 | ret = wl1271_cmd_ps_mode(wl, wlvif, mode, 0); |
f5fc0f86 LC |
229 | if (ret < 0) |
230 | return ret; | |
231 | ||
5c0dc2fc | 232 | clear_bit(WLVIF_FLAG_IN_PS, &wlvif->flags); |
f5fc0f86 | 233 | break; |
f1d63a59 ES |
234 | default: |
235 | wl1271_warning("trying to set ps to unsupported mode %d", mode); | |
236 | ret = -EINVAL; | |
f5fc0f86 LC |
237 | } |
238 | ||
239 | return ret; | |
240 | } | |
b622d992 AN |
241 | |
242 | static void wl1271_ps_filter_frames(struct wl1271 *wl, u8 hlid) | |
243 | { | |
f1a46384 | 244 | int i; |
b622d992 AN |
245 | struct sk_buff *skb; |
246 | struct ieee80211_tx_info *info; | |
247 | unsigned long flags; | |
f1a46384 | 248 | int filtered[NUM_TX_QUEUES]; |
b622d992 | 249 | |
f8e0af6b | 250 | /* filter all frames currently in the low level queues for this hlid */ |
b622d992 | 251 | for (i = 0; i < NUM_TX_QUEUES; i++) { |
f1a46384 | 252 | filtered[i] = 0; |
b622d992 | 253 | while ((skb = skb_dequeue(&wl->links[hlid].tx_queue[i]))) { |
f8e0af6b AN |
254 | filtered[i]++; |
255 | ||
256 | if (WARN_ON(wl12xx_is_dummy_packet(wl, skb))) | |
257 | continue; | |
258 | ||
b622d992 AN |
259 | info = IEEE80211_SKB_CB(skb); |
260 | info->flags |= IEEE80211_TX_STAT_TX_FILTERED; | |
261 | info->status.rates[0].idx = -1; | |
c27d3acc | 262 | ieee80211_tx_status_ni(wl->hw, skb); |
b622d992 AN |
263 | } |
264 | } | |
265 | ||
266 | spin_lock_irqsave(&wl->wl_lock, flags); | |
f1a46384 AN |
267 | for (i = 0; i < NUM_TX_QUEUES; i++) |
268 | wl->tx_queue_count[i] -= filtered[i]; | |
b622d992 AN |
269 | spin_unlock_irqrestore(&wl->wl_lock, flags); |
270 | ||
271 | wl1271_handle_tx_low_watermark(wl); | |
272 | } | |
273 | ||
6e8cd331 EP |
274 | void wl12xx_ps_link_start(struct wl1271 *wl, struct wl12xx_vif *wlvif, |
275 | u8 hlid, bool clean_queues) | |
b622d992 AN |
276 | { |
277 | struct ieee80211_sta *sta; | |
6e8cd331 | 278 | struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif); |
b622d992 AN |
279 | |
280 | if (test_bit(hlid, &wl->ap_ps_map)) | |
281 | return; | |
282 | ||
9b17f1b3 AN |
283 | wl1271_debug(DEBUG_PSM, "start mac80211 PSM on hlid %d pkts %d " |
284 | "clean_queues %d", hlid, wl->links[hlid].allocated_pkts, | |
b622d992 AN |
285 | clean_queues); |
286 | ||
287 | rcu_read_lock(); | |
6e8cd331 | 288 | sta = ieee80211_find_sta(vif, wl->links[hlid].addr); |
b622d992 AN |
289 | if (!sta) { |
290 | wl1271_error("could not find sta %pM for starting ps", | |
291 | wl->links[hlid].addr); | |
292 | rcu_read_unlock(); | |
293 | return; | |
294 | } | |
295 | ||
296 | ieee80211_sta_ps_transition_ni(sta, true); | |
297 | rcu_read_unlock(); | |
298 | ||
299 | /* do we want to filter all frames from this link's queues? */ | |
300 | if (clean_queues) | |
301 | wl1271_ps_filter_frames(wl, hlid); | |
302 | ||
303 | __set_bit(hlid, &wl->ap_ps_map); | |
304 | } | |
305 | ||
6e8cd331 | 306 | void wl12xx_ps_link_end(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 hlid) |
b622d992 AN |
307 | { |
308 | struct ieee80211_sta *sta; | |
6e8cd331 | 309 | struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif); |
b622d992 AN |
310 | |
311 | if (!test_bit(hlid, &wl->ap_ps_map)) | |
312 | return; | |
313 | ||
314 | wl1271_debug(DEBUG_PSM, "end mac80211 PSM on hlid %d", hlid); | |
315 | ||
316 | __clear_bit(hlid, &wl->ap_ps_map); | |
317 | ||
318 | rcu_read_lock(); | |
6e8cd331 | 319 | sta = ieee80211_find_sta(vif, wl->links[hlid].addr); |
b622d992 AN |
320 | if (!sta) { |
321 | wl1271_error("could not find sta %pM for ending ps", | |
322 | wl->links[hlid].addr); | |
323 | goto end; | |
324 | } | |
325 | ||
326 | ieee80211_sta_ps_transition_ni(sta, false); | |
327 | end: | |
328 | rcu_read_unlock(); | |
329 | } |