diff options
author | Glenn Elliott <gelliott@cs.unc.edu> | 2012-03-04 19:47:13 -0500 |
---|---|---|
committer | Glenn Elliott <gelliott@cs.unc.edu> | 2012-03-04 19:47:13 -0500 |
commit | c71c03bda1e86c9d5198c5d83f712e695c4f2a1e (patch) | |
tree | ecb166cb3e2b7e2adb3b5e292245fefd23381ac8 /drivers/net/wireless/rtlwifi/ps.c | |
parent | ea53c912f8a86a8567697115b6a0d8152beee5c8 (diff) | |
parent | 6a00f206debf8a5c8899055726ad127dbeeed098 (diff) |
Merge branch 'mpi-master' into wip-k-fmlpwip-k-fmlp
Conflicts:
litmus/sched_cedf.c
Diffstat (limited to 'drivers/net/wireless/rtlwifi/ps.c')
-rw-r--r-- | drivers/net/wireless/rtlwifi/ps.c | 708 |
1 files changed, 708 insertions, 0 deletions
diff --git a/drivers/net/wireless/rtlwifi/ps.c b/drivers/net/wireless/rtlwifi/ps.c new file mode 100644 index 000000000000..39b0297ce925 --- /dev/null +++ b/drivers/net/wireless/rtlwifi/ps.c | |||
@@ -0,0 +1,708 @@ | |||
1 | /****************************************************************************** | ||
2 | * | ||
3 | * Copyright(c) 2009-2010 Realtek Corporation. | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify it | ||
6 | * under the terms of version 2 of the GNU General Public License as | ||
7 | * published by the Free Software Foundation. | ||
8 | * | ||
9 | * This program is distributed in the hope that it will be useful, but WITHOUT | ||
10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
12 | * more details. | ||
13 | * | ||
14 | * You should have received a copy of the GNU General Public License along with | ||
15 | * this program; if not, write to the Free Software Foundation, Inc., | ||
16 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA | ||
17 | * | ||
18 | * The full GNU General Public License is included in this distribution in the | ||
19 | * file called LICENSE. | ||
20 | * | ||
21 | * Contact Information: | ||
22 | * wlanfae <wlanfae@realtek.com> | ||
23 | * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, | ||
24 | * Hsinchu 300, Taiwan. | ||
25 | * | ||
26 | * Larry Finger <Larry.Finger@lwfinger.net> | ||
27 | * | ||
28 | *****************************************************************************/ | ||
29 | |||
30 | #include "wifi.h" | ||
31 | #include "base.h" | ||
32 | #include "ps.h" | ||
33 | |||
34 | bool rtl_ps_enable_nic(struct ieee80211_hw *hw) | ||
35 | { | ||
36 | struct rtl_priv *rtlpriv = rtl_priv(hw); | ||
37 | struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); | ||
38 | struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); | ||
39 | |||
40 | /*<1> reset trx ring */ | ||
41 | if (rtlhal->interface == INTF_PCI) | ||
42 | rtlpriv->intf_ops->reset_trx_ring(hw); | ||
43 | |||
44 | if (is_hal_stop(rtlhal)) | ||
45 | RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING, | ||
46 | ("Driver is already down!\n")); | ||
47 | |||
48 | /*<2> Enable Adapter */ | ||
49 | rtlpriv->cfg->ops->hw_init(hw); | ||
50 | RT_CLEAR_PS_LEVEL(ppsc, RT_RF_OFF_LEVL_HALT_NIC); | ||
51 | |||
52 | /*<3> Enable Interrupt */ | ||
53 | rtlpriv->cfg->ops->enable_interrupt(hw); | ||
54 | |||
55 | /*<enable timer> */ | ||
56 | rtl_watch_dog_timer_callback((unsigned long)hw); | ||
57 | |||
58 | return true; | ||
59 | } | ||
60 | EXPORT_SYMBOL(rtl_ps_enable_nic); | ||
61 | |||
62 | bool rtl_ps_disable_nic(struct ieee80211_hw *hw) | ||
63 | { | ||
64 | struct rtl_priv *rtlpriv = rtl_priv(hw); | ||
65 | |||
66 | /*<1> Stop all timer */ | ||
67 | rtl_deinit_deferred_work(hw); | ||
68 | |||
69 | /*<2> Disable Interrupt */ | ||
70 | rtlpriv->cfg->ops->disable_interrupt(hw); | ||
71 | |||
72 | /*<3> Disable Adapter */ | ||
73 | rtlpriv->cfg->ops->hw_disable(hw); | ||
74 | |||
75 | return true; | ||
76 | } | ||
77 | EXPORT_SYMBOL(rtl_ps_disable_nic); | ||
78 | |||
79 | bool rtl_ps_set_rf_state(struct ieee80211_hw *hw, | ||
80 | enum rf_pwrstate state_toset, | ||
81 | u32 changesource, bool protect_or_not) | ||
82 | { | ||
83 | struct rtl_priv *rtlpriv = rtl_priv(hw); | ||
84 | struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); | ||
85 | enum rf_pwrstate rtstate; | ||
86 | bool actionallowed = false; | ||
87 | u16 rfwait_cnt = 0; | ||
88 | unsigned long flag; | ||
89 | |||
90 | /*protect_or_not = true; */ | ||
91 | |||
92 | if (protect_or_not) | ||
93 | goto no_protect; | ||
94 | |||
95 | /* | ||
96 | *Only one thread can change | ||
97 | *the RF state at one time, and others | ||
98 | *should wait to be executed. | ||
99 | */ | ||
100 | while (true) { | ||
101 | spin_lock_irqsave(&rtlpriv->locks.rf_ps_lock, flag); | ||
102 | if (ppsc->rfchange_inprogress) { | ||
103 | spin_unlock_irqrestore(&rtlpriv->locks.rf_ps_lock, | ||
104 | flag); | ||
105 | |||
106 | RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING, | ||
107 | ("RF Change in progress!" | ||
108 | "Wait to set..state_toset(%d).\n", | ||
109 | state_toset)); | ||
110 | |||
111 | /* Set RF after the previous action is done. */ | ||
112 | while (ppsc->rfchange_inprogress) { | ||
113 | rfwait_cnt++; | ||
114 | mdelay(1); | ||
115 | |||
116 | /* | ||
117 | *Wait too long, return false to avoid | ||
118 | *to be stuck here. | ||
119 | */ | ||
120 | if (rfwait_cnt > 100) | ||
121 | return false; | ||
122 | } | ||
123 | } else { | ||
124 | ppsc->rfchange_inprogress = true; | ||
125 | spin_unlock_irqrestore(&rtlpriv->locks.rf_ps_lock, | ||
126 | flag); | ||
127 | break; | ||
128 | } | ||
129 | } | ||
130 | |||
131 | no_protect: | ||
132 | rtstate = ppsc->rfpwr_state; | ||
133 | |||
134 | switch (state_toset) { | ||
135 | case ERFON: | ||
136 | ppsc->rfoff_reason &= (~changesource); | ||
137 | |||
138 | if ((changesource == RF_CHANGE_BY_HW) && | ||
139 | (ppsc->hwradiooff == true)) { | ||
140 | ppsc->hwradiooff = false; | ||
141 | } | ||
142 | |||
143 | if (!ppsc->rfoff_reason) { | ||
144 | ppsc->rfoff_reason = 0; | ||
145 | actionallowed = true; | ||
146 | } | ||
147 | |||
148 | break; | ||
149 | |||
150 | case ERFOFF: | ||
151 | |||
152 | if ((changesource == RF_CHANGE_BY_HW) | ||
153 | && (ppsc->hwradiooff == false)) { | ||
154 | ppsc->hwradiooff = true; | ||
155 | } | ||
156 | |||
157 | ppsc->rfoff_reason |= changesource; | ||
158 | actionallowed = true; | ||
159 | break; | ||
160 | |||
161 | case ERFSLEEP: | ||
162 | ppsc->rfoff_reason |= changesource; | ||
163 | actionallowed = true; | ||
164 | break; | ||
165 | |||
166 | default: | ||
167 | RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, | ||
168 | ("switch case not process\n")); | ||
169 | break; | ||
170 | } | ||
171 | |||
172 | if (actionallowed) | ||
173 | rtlpriv->cfg->ops->set_rf_power_state(hw, state_toset); | ||
174 | |||
175 | if (!protect_or_not) { | ||
176 | spin_lock_irqsave(&rtlpriv->locks.rf_ps_lock, flag); | ||
177 | ppsc->rfchange_inprogress = false; | ||
178 | spin_unlock_irqrestore(&rtlpriv->locks.rf_ps_lock, flag); | ||
179 | } | ||
180 | |||
181 | return actionallowed; | ||
182 | } | ||
183 | EXPORT_SYMBOL(rtl_ps_set_rf_state); | ||
184 | |||
185 | static void _rtl_ps_inactive_ps(struct ieee80211_hw *hw) | ||
186 | { | ||
187 | struct rtl_priv *rtlpriv = rtl_priv(hw); | ||
188 | struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); | ||
189 | struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); | ||
190 | |||
191 | ppsc->swrf_processing = true; | ||
192 | |||
193 | if (ppsc->inactive_pwrstate == ERFON && | ||
194 | rtlhal->interface == INTF_PCI) { | ||
195 | if ((ppsc->reg_rfps_level & RT_RF_OFF_LEVL_ASPM) && | ||
196 | RT_IN_PS_LEVEL(ppsc, RT_PS_LEVEL_ASPM) && | ||
197 | rtlhal->interface == INTF_PCI) { | ||
198 | rtlpriv->intf_ops->disable_aspm(hw); | ||
199 | RT_CLEAR_PS_LEVEL(ppsc, RT_PS_LEVEL_ASPM); | ||
200 | } | ||
201 | } | ||
202 | |||
203 | rtl_ps_set_rf_state(hw, ppsc->inactive_pwrstate, | ||
204 | RF_CHANGE_BY_IPS, false); | ||
205 | |||
206 | if (ppsc->inactive_pwrstate == ERFOFF && | ||
207 | rtlhal->interface == INTF_PCI) { | ||
208 | if (ppsc->reg_rfps_level & RT_RF_OFF_LEVL_ASPM && | ||
209 | !RT_IN_PS_LEVEL(ppsc, RT_PS_LEVEL_ASPM)) { | ||
210 | rtlpriv->intf_ops->enable_aspm(hw); | ||
211 | RT_SET_PS_LEVEL(ppsc, RT_PS_LEVEL_ASPM); | ||
212 | } | ||
213 | } | ||
214 | |||
215 | ppsc->swrf_processing = false; | ||
216 | } | ||
217 | |||
218 | void rtl_ips_nic_off_wq_callback(void *data) | ||
219 | { | ||
220 | struct rtl_works *rtlworks = | ||
221 | container_of_dwork_rtl(data, struct rtl_works, ips_nic_off_wq); | ||
222 | struct ieee80211_hw *hw = rtlworks->hw; | ||
223 | struct rtl_priv *rtlpriv = rtl_priv(hw); | ||
224 | struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); | ||
225 | struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); | ||
226 | struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); | ||
227 | enum rf_pwrstate rtstate; | ||
228 | |||
229 | if (mac->opmode != NL80211_IFTYPE_STATION) { | ||
230 | RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING, | ||
231 | ("not station return\n")); | ||
232 | return; | ||
233 | } | ||
234 | |||
235 | if (mac->link_state > MAC80211_NOLINK) | ||
236 | return; | ||
237 | |||
238 | if (is_hal_stop(rtlhal)) | ||
239 | return; | ||
240 | |||
241 | if (rtlpriv->sec.being_setkey) | ||
242 | return; | ||
243 | |||
244 | if (ppsc->inactiveps) { | ||
245 | rtstate = ppsc->rfpwr_state; | ||
246 | |||
247 | /* | ||
248 | *Do not enter IPS in the following conditions: | ||
249 | *(1) RF is already OFF or Sleep | ||
250 | *(2) swrf_processing (indicates the IPS is still under going) | ||
251 | *(3) Connectted (only disconnected can trigger IPS) | ||
252 | *(4) IBSS (send Beacon) | ||
253 | *(5) AP mode (send Beacon) | ||
254 | *(6) monitor mode (rcv packet) | ||
255 | */ | ||
256 | |||
257 | if (rtstate == ERFON && | ||
258 | !ppsc->swrf_processing && | ||
259 | (mac->link_state == MAC80211_NOLINK) && | ||
260 | !mac->act_scanning) { | ||
261 | RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE, | ||
262 | ("IPSEnter(): Turn off RF.\n")); | ||
263 | |||
264 | ppsc->inactive_pwrstate = ERFOFF; | ||
265 | ppsc->in_powersavemode = true; | ||
266 | |||
267 | /*rtl_pci_reset_trx_ring(hw); */ | ||
268 | _rtl_ps_inactive_ps(hw); | ||
269 | } | ||
270 | } | ||
271 | } | ||
272 | |||
273 | void rtl_ips_nic_off(struct ieee80211_hw *hw) | ||
274 | { | ||
275 | struct rtl_priv *rtlpriv = rtl_priv(hw); | ||
276 | |||
277 | /* | ||
278 | *because when link with ap, mac80211 will ask us | ||
279 | *to disable nic quickly after scan before linking, | ||
280 | *this will cause link failed, so we delay 100ms here | ||
281 | */ | ||
282 | queue_delayed_work(rtlpriv->works.rtl_wq, | ||
283 | &rtlpriv->works.ips_nic_off_wq, MSECS(100)); | ||
284 | } | ||
285 | |||
286 | void rtl_ips_nic_on(struct ieee80211_hw *hw) | ||
287 | { | ||
288 | struct rtl_priv *rtlpriv = rtl_priv(hw); | ||
289 | struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); | ||
290 | struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); | ||
291 | enum rf_pwrstate rtstate; | ||
292 | unsigned long flags; | ||
293 | |||
294 | if (mac->opmode != NL80211_IFTYPE_STATION) | ||
295 | return; | ||
296 | |||
297 | spin_lock_irqsave(&rtlpriv->locks.ips_lock, flags); | ||
298 | |||
299 | if (ppsc->inactiveps) { | ||
300 | rtstate = ppsc->rfpwr_state; | ||
301 | |||
302 | if (rtstate != ERFON && | ||
303 | !ppsc->swrf_processing && | ||
304 | ppsc->rfoff_reason <= RF_CHANGE_BY_IPS) { | ||
305 | |||
306 | ppsc->inactive_pwrstate = ERFON; | ||
307 | ppsc->in_powersavemode = false; | ||
308 | |||
309 | _rtl_ps_inactive_ps(hw); | ||
310 | } | ||
311 | } | ||
312 | |||
313 | spin_unlock_irqrestore(&rtlpriv->locks.ips_lock, flags); | ||
314 | } | ||
315 | |||
316 | /*for FW LPS*/ | ||
317 | |||
318 | /* | ||
319 | *Determine if we can set Fw into PS mode | ||
320 | *in current condition.Return TRUE if it | ||
321 | *can enter PS mode. | ||
322 | */ | ||
323 | static bool rtl_get_fwlps_doze(struct ieee80211_hw *hw) | ||
324 | { | ||
325 | struct rtl_priv *rtlpriv = rtl_priv(hw); | ||
326 | struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); | ||
327 | struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); | ||
328 | u32 ps_timediff; | ||
329 | |||
330 | ps_timediff = jiffies_to_msecs(jiffies - | ||
331 | ppsc->last_delaylps_stamp_jiffies); | ||
332 | |||
333 | if (ps_timediff < 2000) { | ||
334 | RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD, | ||
335 | ("Delay enter Fw LPS for DHCP, ARP," | ||
336 | " or EAPOL exchanging state.\n")); | ||
337 | return false; | ||
338 | } | ||
339 | |||
340 | if (mac->link_state != MAC80211_LINKED) | ||
341 | return false; | ||
342 | |||
343 | if (mac->opmode == NL80211_IFTYPE_ADHOC) | ||
344 | return false; | ||
345 | |||
346 | return true; | ||
347 | } | ||
348 | |||
349 | /* Change current and default preamble mode.*/ | ||
350 | static void rtl_lps_set_psmode(struct ieee80211_hw *hw, u8 rt_psmode) | ||
351 | { | ||
352 | struct rtl_priv *rtlpriv = rtl_priv(hw); | ||
353 | struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); | ||
354 | struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); | ||
355 | u8 rpwm_val, fw_pwrmode; | ||
356 | |||
357 | if (mac->opmode == NL80211_IFTYPE_ADHOC) | ||
358 | return; | ||
359 | |||
360 | if (mac->link_state != MAC80211_LINKED) | ||
361 | return; | ||
362 | |||
363 | if (ppsc->dot11_psmode == rt_psmode) | ||
364 | return; | ||
365 | |||
366 | /* Update power save mode configured. */ | ||
367 | ppsc->dot11_psmode = rt_psmode; | ||
368 | |||
369 | /* | ||
370 | *<FW control LPS> | ||
371 | *1. Enter PS mode | ||
372 | * Set RPWM to Fw to turn RF off and send H2C fw_pwrmode | ||
373 | * cmd to set Fw into PS mode. | ||
374 | *2. Leave PS mode | ||
375 | * Send H2C fw_pwrmode cmd to Fw to set Fw into Active | ||
376 | * mode and set RPWM to turn RF on. | ||
377 | */ | ||
378 | |||
379 | if ((ppsc->fwctrl_lps) && ppsc->report_linked) { | ||
380 | bool fw_current_inps; | ||
381 | if (ppsc->dot11_psmode == EACTIVE) { | ||
382 | RT_TRACE(rtlpriv, COMP_RF, DBG_DMESG, | ||
383 | ("FW LPS leave ps_mode:%x\n", | ||
384 | FW_PS_ACTIVE_MODE)); | ||
385 | |||
386 | rpwm_val = 0x0C; /* RF on */ | ||
387 | fw_pwrmode = FW_PS_ACTIVE_MODE; | ||
388 | rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_SET_RPWM, | ||
389 | (u8 *) (&rpwm_val)); | ||
390 | rtlpriv->cfg->ops->set_hw_reg(hw, | ||
391 | HW_VAR_H2C_FW_PWRMODE, | ||
392 | (u8 *) (&fw_pwrmode)); | ||
393 | fw_current_inps = false; | ||
394 | |||
395 | rtlpriv->cfg->ops->set_hw_reg(hw, | ||
396 | HW_VAR_FW_PSMODE_STATUS, | ||
397 | (u8 *) (&fw_current_inps)); | ||
398 | |||
399 | } else { | ||
400 | if (rtl_get_fwlps_doze(hw)) { | ||
401 | RT_TRACE(rtlpriv, COMP_RF, DBG_DMESG, | ||
402 | ("FW LPS enter ps_mode:%x\n", | ||
403 | ppsc->fwctrl_psmode)); | ||
404 | |||
405 | rpwm_val = 0x02; /* RF off */ | ||
406 | fw_current_inps = true; | ||
407 | rtlpriv->cfg->ops->set_hw_reg(hw, | ||
408 | HW_VAR_FW_PSMODE_STATUS, | ||
409 | (u8 *) (&fw_current_inps)); | ||
410 | rtlpriv->cfg->ops->set_hw_reg(hw, | ||
411 | HW_VAR_H2C_FW_PWRMODE, | ||
412 | (u8 *) (&ppsc->fwctrl_psmode)); | ||
413 | |||
414 | rtlpriv->cfg->ops->set_hw_reg(hw, | ||
415 | HW_VAR_SET_RPWM, | ||
416 | (u8 *) (&rpwm_val)); | ||
417 | } else { | ||
418 | /* Reset the power save related parameters. */ | ||
419 | ppsc->dot11_psmode = EACTIVE; | ||
420 | } | ||
421 | } | ||
422 | } | ||
423 | } | ||
424 | |||
425 | /*Enter the leisure power save mode.*/ | ||
426 | void rtl_lps_enter(struct ieee80211_hw *hw) | ||
427 | { | ||
428 | struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); | ||
429 | struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); | ||
430 | struct rtl_priv *rtlpriv = rtl_priv(hw); | ||
431 | unsigned long flag; | ||
432 | |||
433 | if (!ppsc->fwctrl_lps) | ||
434 | return; | ||
435 | |||
436 | if (rtlpriv->sec.being_setkey) | ||
437 | return; | ||
438 | |||
439 | if (rtlpriv->link_info.busytraffic) | ||
440 | return; | ||
441 | |||
442 | /*sleep after linked 10s, to let DHCP and 4-way handshake ok enough!! */ | ||
443 | if (mac->cnt_after_linked < 5) | ||
444 | return; | ||
445 | |||
446 | if (mac->opmode == NL80211_IFTYPE_ADHOC) | ||
447 | return; | ||
448 | |||
449 | if (mac->link_state != MAC80211_LINKED) | ||
450 | return; | ||
451 | |||
452 | spin_lock_irqsave(&rtlpriv->locks.lps_lock, flag); | ||
453 | |||
454 | /* Idle for a while if we connect to AP a while ago. */ | ||
455 | if (mac->cnt_after_linked >= 2) { | ||
456 | if (ppsc->dot11_psmode == EACTIVE) { | ||
457 | RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD, | ||
458 | ("Enter 802.11 power save mode...\n")); | ||
459 | |||
460 | rtl_lps_set_psmode(hw, EAUTOPS); | ||
461 | } | ||
462 | } | ||
463 | |||
464 | spin_unlock_irqrestore(&rtlpriv->locks.lps_lock, flag); | ||
465 | } | ||
466 | |||
467 | /*Leave the leisure power save mode.*/ | ||
468 | void rtl_lps_leave(struct ieee80211_hw *hw) | ||
469 | { | ||
470 | struct rtl_priv *rtlpriv = rtl_priv(hw); | ||
471 | struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); | ||
472 | struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); | ||
473 | unsigned long flag; | ||
474 | |||
475 | spin_lock_irqsave(&rtlpriv->locks.lps_lock, flag); | ||
476 | |||
477 | if (ppsc->fwctrl_lps) { | ||
478 | if (ppsc->dot11_psmode != EACTIVE) { | ||
479 | |||
480 | /*FIX ME */ | ||
481 | rtlpriv->cfg->ops->enable_interrupt(hw); | ||
482 | |||
483 | if (ppsc->reg_rfps_level & RT_RF_LPS_LEVEL_ASPM && | ||
484 | RT_IN_PS_LEVEL(ppsc, RT_PS_LEVEL_ASPM) && | ||
485 | rtlhal->interface == INTF_PCI) { | ||
486 | rtlpriv->intf_ops->disable_aspm(hw); | ||
487 | RT_CLEAR_PS_LEVEL(ppsc, RT_PS_LEVEL_ASPM); | ||
488 | } | ||
489 | |||
490 | RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD, | ||
491 | ("Busy Traffic,Leave 802.11 power save..\n")); | ||
492 | |||
493 | rtl_lps_set_psmode(hw, EACTIVE); | ||
494 | } | ||
495 | } | ||
496 | spin_unlock_irqrestore(&rtlpriv->locks.lps_lock, flag); | ||
497 | } | ||
498 | |||
499 | /* For sw LPS*/ | ||
500 | void rtl_swlps_beacon(struct ieee80211_hw *hw, void *data, unsigned int len) | ||
501 | { | ||
502 | struct rtl_priv *rtlpriv = rtl_priv(hw); | ||
503 | struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); | ||
504 | struct ieee80211_hdr *hdr = (void *) data; | ||
505 | struct ieee80211_tim_ie *tim_ie; | ||
506 | u8 *tim; | ||
507 | u8 tim_len; | ||
508 | bool u_buffed; | ||
509 | bool m_buffed; | ||
510 | |||
511 | if (mac->opmode != NL80211_IFTYPE_STATION) | ||
512 | return; | ||
513 | |||
514 | if (!rtlpriv->psc.swctrl_lps) | ||
515 | return; | ||
516 | |||
517 | if (rtlpriv->mac80211.link_state != MAC80211_LINKED) | ||
518 | return; | ||
519 | |||
520 | if (!rtlpriv->psc.sw_ps_enabled) | ||
521 | return; | ||
522 | |||
523 | if (rtlpriv->psc.fwctrl_lps) | ||
524 | return; | ||
525 | |||
526 | if (likely(!(hw->conf.flags & IEEE80211_CONF_PS))) | ||
527 | return; | ||
528 | |||
529 | /* check if this really is a beacon */ | ||
530 | if (!ieee80211_is_beacon(hdr->frame_control)) | ||
531 | return; | ||
532 | |||
533 | /* min. beacon length + FCS_LEN */ | ||
534 | if (len <= 40 + FCS_LEN) | ||
535 | return; | ||
536 | |||
537 | /* and only beacons from the associated BSSID, please */ | ||
538 | if (compare_ether_addr(hdr->addr3, rtlpriv->mac80211.bssid)) | ||
539 | return; | ||
540 | |||
541 | rtlpriv->psc.last_beacon = jiffies; | ||
542 | |||
543 | tim = rtl_find_ie(data, len - FCS_LEN, WLAN_EID_TIM); | ||
544 | if (!tim) | ||
545 | return; | ||
546 | |||
547 | if (tim[1] < sizeof(*tim_ie)) | ||
548 | return; | ||
549 | |||
550 | tim_len = tim[1]; | ||
551 | tim_ie = (struct ieee80211_tim_ie *) &tim[2]; | ||
552 | |||
553 | if (!WARN_ON_ONCE(!hw->conf.ps_dtim_period)) | ||
554 | rtlpriv->psc.dtim_counter = tim_ie->dtim_count; | ||
555 | |||
556 | /* Check whenever the PHY can be turned off again. */ | ||
557 | |||
558 | /* 1. What about buffered unicast traffic for our AID? */ | ||
559 | u_buffed = ieee80211_check_tim(tim_ie, tim_len, | ||
560 | rtlpriv->mac80211.assoc_id); | ||
561 | |||
562 | /* 2. Maybe the AP wants to send multicast/broadcast data? */ | ||
563 | m_buffed = tim_ie->bitmap_ctrl & 0x01; | ||
564 | rtlpriv->psc.multi_buffered = m_buffed; | ||
565 | |||
566 | /* unicast will process by mac80211 through | ||
567 | * set ~IEEE80211_CONF_PS, So we just check | ||
568 | * multicast frames here */ | ||
569 | if (!m_buffed) { | ||
570 | /* back to low-power land. and delay is | ||
571 | * prevent null power save frame tx fail */ | ||
572 | queue_delayed_work(rtlpriv->works.rtl_wq, | ||
573 | &rtlpriv->works.ps_work, MSECS(5)); | ||
574 | } else { | ||
575 | RT_TRACE(rtlpriv, COMP_POWER, DBG_DMESG, ("u_bufferd: %x, " | ||
576 | "m_buffered: %x\n", u_buffed, m_buffed)); | ||
577 | } | ||
578 | } | ||
579 | |||
580 | void rtl_swlps_rf_awake(struct ieee80211_hw *hw) | ||
581 | { | ||
582 | struct rtl_priv *rtlpriv = rtl_priv(hw); | ||
583 | struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); | ||
584 | struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); | ||
585 | unsigned long flag; | ||
586 | |||
587 | if (!rtlpriv->psc.swctrl_lps) | ||
588 | return; | ||
589 | if (mac->link_state != MAC80211_LINKED) | ||
590 | return; | ||
591 | |||
592 | if (ppsc->reg_rfps_level & RT_RF_LPS_LEVEL_ASPM && | ||
593 | RT_IN_PS_LEVEL(ppsc, RT_PS_LEVEL_ASPM)) { | ||
594 | rtlpriv->intf_ops->disable_aspm(hw); | ||
595 | RT_CLEAR_PS_LEVEL(ppsc, RT_PS_LEVEL_ASPM); | ||
596 | } | ||
597 | |||
598 | spin_lock_irqsave(&rtlpriv->locks.lps_lock, flag); | ||
599 | rtl_ps_set_rf_state(hw, ERFON, RF_CHANGE_BY_PS, false); | ||
600 | spin_unlock_irqrestore(&rtlpriv->locks.lps_lock, flag); | ||
601 | } | ||
602 | |||
603 | void rtl_swlps_rfon_wq_callback(void *data) | ||
604 | { | ||
605 | struct rtl_works *rtlworks = | ||
606 | container_of_dwork_rtl(data, struct rtl_works, ps_rfon_wq); | ||
607 | struct ieee80211_hw *hw = rtlworks->hw; | ||
608 | |||
609 | rtl_swlps_rf_awake(hw); | ||
610 | } | ||
611 | |||
612 | void rtl_swlps_rf_sleep(struct ieee80211_hw *hw) | ||
613 | { | ||
614 | struct rtl_priv *rtlpriv = rtl_priv(hw); | ||
615 | struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); | ||
616 | struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); | ||
617 | unsigned long flag; | ||
618 | u8 sleep_intv; | ||
619 | |||
620 | if (!rtlpriv->psc.sw_ps_enabled) | ||
621 | return; | ||
622 | |||
623 | if ((rtlpriv->sec.being_setkey) || | ||
624 | (mac->opmode == NL80211_IFTYPE_ADHOC)) | ||
625 | return; | ||
626 | |||
627 | /*sleep after linked 10s, to let DHCP and 4-way handshake ok enough!! */ | ||
628 | if ((mac->link_state != MAC80211_LINKED) || (mac->cnt_after_linked < 5)) | ||
629 | return; | ||
630 | |||
631 | if (rtlpriv->link_info.busytraffic) | ||
632 | return; | ||
633 | |||
634 | spin_lock_irqsave(&rtlpriv->locks.rf_ps_lock, flag); | ||
635 | if (rtlpriv->psc.rfchange_inprogress) { | ||
636 | spin_unlock_irqrestore(&rtlpriv->locks.rf_ps_lock, flag); | ||
637 | return; | ||
638 | } | ||
639 | spin_unlock_irqrestore(&rtlpriv->locks.rf_ps_lock, flag); | ||
640 | |||
641 | spin_lock_irqsave(&rtlpriv->locks.lps_lock, flag); | ||
642 | rtl_ps_set_rf_state(hw, ERFSLEEP, RF_CHANGE_BY_PS, false); | ||
643 | spin_unlock_irqrestore(&rtlpriv->locks.lps_lock, flag); | ||
644 | |||
645 | if (ppsc->reg_rfps_level & RT_RF_OFF_LEVL_ASPM && | ||
646 | !RT_IN_PS_LEVEL(ppsc, RT_PS_LEVEL_ASPM)) { | ||
647 | rtlpriv->intf_ops->enable_aspm(hw); | ||
648 | RT_SET_PS_LEVEL(ppsc, RT_PS_LEVEL_ASPM); | ||
649 | } | ||
650 | |||
651 | /* here is power save alg, when this beacon is DTIM | ||
652 | * we will set sleep time to dtim_period * n; | ||
653 | * when this beacon is not DTIM, we will set sleep | ||
654 | * time to sleep_intv = rtlpriv->psc.dtim_counter or | ||
655 | * MAX_SW_LPS_SLEEP_INTV(default set to 5) */ | ||
656 | |||
657 | if (rtlpriv->psc.dtim_counter == 0) { | ||
658 | if (hw->conf.ps_dtim_period == 1) | ||
659 | sleep_intv = hw->conf.ps_dtim_period * 2; | ||
660 | else | ||
661 | sleep_intv = hw->conf.ps_dtim_period; | ||
662 | } else { | ||
663 | sleep_intv = rtlpriv->psc.dtim_counter; | ||
664 | } | ||
665 | |||
666 | if (sleep_intv > MAX_SW_LPS_SLEEP_INTV) | ||
667 | sleep_intv = MAX_SW_LPS_SLEEP_INTV; | ||
668 | |||
669 | /* this print should always be dtim_conter = 0 & | ||
670 | * sleep = dtim_period, that meaons, we should | ||
671 | * awake before every dtim */ | ||
672 | RT_TRACE(rtlpriv, COMP_POWER, DBG_DMESG, | ||
673 | ("dtim_counter:%x will sleep :%d" | ||
674 | " beacon_intv\n", rtlpriv->psc.dtim_counter, sleep_intv)); | ||
675 | |||
676 | /* we tested that 40ms is enough for sw & hw sw delay */ | ||
677 | queue_delayed_work(rtlpriv->works.rtl_wq, &rtlpriv->works.ps_rfon_wq, | ||
678 | MSECS(sleep_intv * mac->vif->bss_conf.beacon_int - 40)); | ||
679 | } | ||
680 | |||
681 | |||
682 | void rtl_swlps_wq_callback(void *data) | ||
683 | { | ||
684 | struct rtl_works *rtlworks = container_of_dwork_rtl(data, | ||
685 | struct rtl_works, | ||
686 | ps_work); | ||
687 | struct ieee80211_hw *hw = rtlworks->hw; | ||
688 | struct rtl_priv *rtlpriv = rtl_priv(hw); | ||
689 | bool ps = false; | ||
690 | |||
691 | ps = (hw->conf.flags & IEEE80211_CONF_PS); | ||
692 | |||
693 | /* we can sleep after ps null send ok */ | ||
694 | if (rtlpriv->psc.state_inap) { | ||
695 | rtl_swlps_rf_sleep(hw); | ||
696 | |||
697 | if (rtlpriv->psc.state && !ps) { | ||
698 | rtlpriv->psc.sleep_ms = jiffies_to_msecs(jiffies - | ||
699 | rtlpriv->psc.last_action); | ||
700 | } | ||
701 | |||
702 | if (ps) | ||
703 | rtlpriv->psc.last_slept = jiffies; | ||
704 | |||
705 | rtlpriv->psc.last_action = jiffies; | ||
706 | rtlpriv->psc.state = ps; | ||
707 | } | ||
708 | } | ||