Commit | Line | Data |
---|---|---|
78de2c06 LF |
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 | * The full GNU General Public License is included in this distribution in the | |
15 | * file called LICENSE. | |
16 | * | |
17 | * Contact Information: | |
18 | * wlanfae <wlanfae@realtek.com> | |
19 | * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, | |
20 | * Hsinchu 300, Taiwan. | |
21 | * | |
22 | * Larry Finger <Larry.Finger@lwfinger.net> | |
23 | * | |
24 | *****************************************************************************/ | |
25 | ||
26 | #include "wifi.h" | |
27 | #include "base.h" | |
28 | #include "ps.h" | |
29 | #include "btcoexist/rtl_btc.h" | |
30 | ||
31 | bool stg_rtl_ps_enable_nic(struct ieee80211_hw *hw) | |
32 | { | |
33 | struct rtl_priv *rtlpriv = rtl_priv(hw); | |
34 | struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); | |
35 | struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); | |
36 | bool init_status = true; | |
37 | ||
38 | /*<1> reset trx ring */ | |
39 | if (rtlhal->interface == INTF_PCI) | |
40 | rtlpriv->intf_ops->reset_trx_ring(hw); | |
41 | ||
42 | if (is_hal_stop(rtlhal)) | |
43 | RT_TRACE(COMP_ERR, DBG_WARNING, ("Driver is already down!\n")); | |
44 | ||
45 | /*<2> Enable Adapter */ | |
46 | rtlpriv->cfg->ops->hw_init(hw); | |
47 | RT_CLEAR_PS_LEVEL(ppsc, RT_RF_OFF_LEVL_HALT_NIC); | |
48 | /*init_status = false; */ | |
49 | ||
50 | /*<3> Enable Interrupt */ | |
51 | rtlpriv->cfg->ops->enable_interrupt(hw); | |
52 | ||
53 | /*<enable timer> */ | |
54 | rtl92e_watch_dog_timer_callback((unsigned long)hw); | |
55 | ||
56 | return init_status; | |
57 | } | |
58 | EXPORT_SYMBOL(stg_rtl_ps_enable_nic); | |
59 | ||
60 | bool stg_rtl_ps_disable_nic(struct ieee80211_hw *hw) | |
61 | { | |
62 | bool status = true; | |
63 | struct rtl_priv *rtlpriv = rtl_priv(hw); | |
64 | ||
65 | /*<1> Stop all timer */ | |
66 | rtl92e_deinit_deferred_work(hw); | |
67 | ||
68 | /*<2> Disable Interrupt */ | |
69 | rtlpriv->cfg->ops->disable_interrupt(hw); | |
70 | ||
71 | /*<3> Disable Adapter */ | |
72 | rtlpriv->cfg->ops->hw_disable(hw); | |
73 | ||
74 | return status; | |
75 | } | |
76 | EXPORT_SYMBOL(stg_rtl_ps_disable_nic); | |
77 | ||
78 | bool stg_rtl_ps_set_rf_state(struct ieee80211_hw *hw, | |
79 | enum rf_pwrstate state_toset, | |
80 | u32 changesource, bool protect_or_not) | |
81 | { | |
82 | struct rtl_priv *rtlpriv = rtl_priv(hw); | |
83 | struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); | |
84 | enum rf_pwrstate rtstate; | |
85 | bool b_actionallowed = false; | |
86 | u16 rfwait_cnt = 0; | |
87 | ||
88 | /*protect_or_not = true; */ | |
89 | ||
90 | if (protect_or_not) | |
91 | goto no_protect; | |
92 | ||
93 | /* | |
94 | *Only one thread can change | |
95 | *the RF state at one time, and others | |
96 | *should wait to be executed. | |
97 | */ | |
98 | while (true) { | |
99 | spin_lock(&rtlpriv->locks.rf_ps_lock); | |
100 | if (ppsc->rfchange_inprogress) { | |
101 | spin_unlock(&rtlpriv->locks.rf_ps_lock); | |
102 | ||
103 | RT_TRACE(COMP_ERR, DBG_WARNING, | |
104 | ("RF Change in progress! Wait to set..state_toset(%d)\n", | |
105 | state_toset)); | |
106 | ||
107 | /* Set RF after the previous action is done. */ | |
108 | while (ppsc->rfchange_inprogress) { | |
109 | rfwait_cnt++; | |
110 | mdelay(1); | |
111 | /* | |
112 | *Wait too long, return false to avoid | |
113 | *to be stuck here. | |
114 | */ | |
115 | if (rfwait_cnt > 100) | |
116 | return false; | |
117 | } | |
118 | } else { | |
119 | ppsc->rfchange_inprogress = true; | |
120 | spin_unlock(&rtlpriv->locks.rf_ps_lock); | |
121 | break; | |
122 | } | |
123 | } | |
124 | ||
125 | no_protect: | |
126 | rtstate = ppsc->rfpwr_state; | |
127 | ||
128 | switch (state_toset) { | |
129 | case ERFON: | |
130 | ppsc->rfoff_reason &= (~changesource); | |
131 | ||
132 | if ((changesource == RF_CHANGE_BY_HW) && | |
133 | (ppsc->b_hwradiooff)) { | |
134 | ppsc->b_hwradiooff = false; | |
135 | } | |
136 | if (!ppsc->rfoff_reason) { | |
137 | ppsc->rfoff_reason = 0; | |
138 | b_actionallowed = true; | |
139 | } | |
140 | break; | |
141 | case ERFOFF: | |
142 | if ((changesource == RF_CHANGE_BY_HW) && | |
143 | (!ppsc->b_hwradiooff)) { | |
144 | ppsc->b_hwradiooff = true; | |
145 | } | |
146 | ppsc->rfoff_reason |= changesource; | |
147 | b_actionallowed = true; | |
148 | break; | |
149 | case ERFSLEEP: | |
150 | ppsc->rfoff_reason |= changesource; | |
151 | b_actionallowed = true; | |
152 | break; | |
153 | default: | |
154 | RT_TRACE(COMP_ERR, DBG_EMERG, ("switch case not process\n")); | |
155 | break; | |
156 | } | |
157 | ||
158 | if (b_actionallowed) | |
159 | rtlpriv->cfg->ops->set_rf_power_state(hw, state_toset); | |
160 | ||
161 | if (!protect_or_not) { | |
162 | spin_lock(&rtlpriv->locks.rf_ps_lock); | |
163 | ppsc->rfchange_inprogress = false; | |
164 | spin_unlock(&rtlpriv->locks.rf_ps_lock); | |
165 | } | |
166 | ||
167 | return b_actionallowed; | |
168 | } | |
169 | EXPORT_SYMBOL(stg_rtl_ps_set_rf_state); | |
170 | ||
171 | static void _rtl_ps_inactive_ps(struct ieee80211_hw *hw) | |
172 | { | |
173 | struct rtl_priv *rtlpriv = rtl_priv(hw); | |
174 | struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); | |
175 | struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); | |
176 | ||
177 | ppsc->b_swrf_processing = true; | |
178 | ||
179 | if (ppsc->inactive_pwrstate == ERFON && rtlhal->interface == INTF_PCI) { | |
180 | if ((ppsc->reg_rfps_level & RT_RF_OFF_LEVL_ASPM) && | |
181 | RT_IN_PS_LEVEL(ppsc, RT_PS_LEVEL_ASPM) && | |
182 | rtlhal->interface == INTF_PCI) { | |
183 | rtlpriv->intf_ops->disable_aspm(hw); | |
184 | RT_CLEAR_PS_LEVEL(ppsc, RT_PS_LEVEL_ASPM); | |
185 | } | |
186 | } | |
187 | ||
188 | stg_rtl_ps_set_rf_state(hw, ppsc->inactive_pwrstate, | |
189 | RF_CHANGE_BY_IPS, false); | |
190 | ||
191 | if (ppsc->inactive_pwrstate == ERFOFF && | |
192 | rtlhal->interface == INTF_PCI) { | |
193 | if (ppsc->reg_rfps_level & RT_RF_OFF_LEVL_ASPM && | |
194 | !RT_IN_PS_LEVEL(ppsc, RT_PS_LEVEL_ASPM)) { | |
195 | rtlpriv->intf_ops->enable_aspm(hw); | |
196 | RT_SET_PS_LEVEL(ppsc, RT_PS_LEVEL_ASPM); | |
197 | } | |
198 | } | |
199 | ||
200 | ppsc->b_swrf_processing = false; | |
201 | } | |
202 | ||
203 | void rtl92e_ips_nic_off_wq_callback(void *data) | |
204 | { | |
205 | struct rtl_works *rtlworks = | |
206 | container_of_dwork_rtl(data, struct rtl_works, ips_nic_off_wq); | |
207 | struct ieee80211_hw *hw = rtlworks->hw; | |
208 | struct rtl_priv *rtlpriv = rtl_priv(hw); | |
209 | struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); | |
210 | struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); | |
211 | struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); | |
212 | enum rf_pwrstate rtstate; | |
213 | ||
214 | if (mac->opmode != NL80211_IFTYPE_STATION) { | |
215 | RT_TRACE(COMP_ERR, DBG_WARNING, ("not station return\n")); | |
216 | return; | |
217 | } | |
218 | ||
219 | if (mac->p2p_in_use) | |
220 | return; | |
221 | ||
222 | if (mac->link_state > MAC80211_NOLINK) | |
223 | return; | |
224 | ||
225 | if (is_hal_stop(rtlhal)) | |
226 | return; | |
227 | ||
228 | if (rtlpriv->sec.being_setkey) | |
229 | return; | |
230 | ||
231 | if (rtlpriv->cfg->ops->bt_turn_off_bt_coexist_before_enter_lps) | |
232 | rtlpriv->cfg->ops->bt_turn_off_bt_coexist_before_enter_lps(hw); | |
233 | ||
234 | if (ppsc->b_inactiveps) { | |
235 | rtstate = ppsc->rfpwr_state; | |
236 | ||
237 | /* | |
238 | *Do not enter IPS in the following conditions: | |
239 | *(1) RF is already OFF or Sleep | |
240 | *(2) b_swrf_processing (indicates the IPS is still under going) | |
241 | *(3) Connectted (only disconnected can trigger IPS) | |
242 | *(4) IBSS (send Beacon) | |
243 | *(5) AP mode (send Beacon) | |
244 | *(6) monitor mode (rcv packet) | |
245 | */ | |
246 | ||
247 | if (rtstate == ERFON && | |
248 | !ppsc->b_swrf_processing && | |
249 | (mac->link_state == MAC80211_NOLINK) && | |
250 | !mac->act_scanning) { | |
251 | RT_TRACE(COMP_RF, DBG_LOUD, | |
252 | ("IPSEnter(): Turn off RF.\n")); | |
253 | ||
254 | ppsc->inactive_pwrstate = ERFOFF; | |
255 | ppsc->b_in_powersavemode = true; | |
256 | ||
257 | /* call before RF off */ | |
258 | if (rtlpriv->cfg->ops->get_btc_status()) | |
259 | rtlpriv->btcoexist.btc_ops->btc_ips_notify(rtlpriv, | |
260 | ppsc->inactive_pwrstate); | |
261 | ||
262 | /*rtl92e_pci_reset_trx_ring(hw); */ | |
263 | _rtl_ps_inactive_ps(hw); | |
264 | } | |
265 | } | |
266 | } | |
267 | ||
268 | void rtl92e_ips_nic_off(struct ieee80211_hw *hw) | |
269 | { | |
270 | struct rtl_priv *rtlpriv = rtl_priv(hw); | |
271 | ||
272 | /* | |
273 | *because when link with ap, mac80211 will ask us | |
274 | *to disable nic quickly after scan before linking, | |
275 | *this will cause link failed, so we delay 100ms here | |
276 | */ | |
277 | queue_delayed_work(rtlpriv->works.rtl_wq, | |
278 | &rtlpriv->works.ips_nic_off_wq, MSECS(100)); | |
279 | } | |
280 | ||
281 | /* NOTICE: any opmode should exc nic_on, or disable without | |
282 | * nic_on may something wrong, like adhoc TP*/ | |
283 | void rtl92e_ips_nic_on(struct ieee80211_hw *hw) | |
284 | { | |
285 | struct rtl_priv *rtlpriv = rtl_priv(hw); | |
286 | struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); | |
287 | enum rf_pwrstate rtstate; | |
288 | ||
289 | cancel_delayed_work(&rtlpriv->works.ips_nic_off_wq); | |
290 | ||
291 | spin_lock(&rtlpriv->locks.ips_lock); | |
292 | if (ppsc->b_inactiveps) { | |
293 | rtstate = ppsc->rfpwr_state; | |
294 | ||
295 | if (rtstate != ERFON && | |
296 | !ppsc->b_swrf_processing && | |
297 | ppsc->rfoff_reason <= RF_CHANGE_BY_IPS) { | |
298 | ppsc->inactive_pwrstate = ERFON; | |
299 | ppsc->b_in_powersavemode = false; | |
300 | _rtl_ps_inactive_ps(hw); | |
301 | /* call after RF on */ | |
302 | if (rtlpriv->cfg->ops->get_btc_status()) | |
303 | rtlpriv->btcoexist.btc_ops->btc_ips_notify(rtlpriv, | |
304 | ppsc->inactive_pwrstate); | |
305 | } | |
306 | } | |
307 | spin_unlock(&rtlpriv->locks.ips_lock); | |
308 | } | |
309 | ||
310 | /*for FW LPS*/ | |
311 | ||
312 | /* | |
313 | *Determine if we can set Fw into PS mode | |
314 | *in current condition.Return true if it | |
315 | *can enter PS mode. | |
316 | */ | |
317 | static bool rtl_get_fwlps_doze(struct ieee80211_hw *hw) | |
318 | { | |
319 | struct rtl_priv *rtlpriv = rtl_priv(hw); | |
320 | struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); | |
321 | struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); | |
322 | u32 ps_timediff; | |
323 | ||
324 | ps_timediff = jiffies_to_msecs(jiffies - | |
325 | ppsc->last_delaylps_stamp_jiffies); | |
326 | ||
327 | if (ps_timediff < 2000) { | |
328 | RT_TRACE(COMP_POWER, DBG_LOUD, | |
329 | ("Delay enter Fw LPS for DHCP, ARP, or EAPOL exchanging state\n")); | |
330 | return false; | |
331 | } | |
332 | ||
333 | if (mac->link_state != MAC80211_LINKED) | |
334 | return false; | |
335 | ||
336 | if (mac->opmode == NL80211_IFTYPE_ADHOC) | |
337 | return false; | |
338 | ||
339 | return true; | |
340 | } | |
341 | ||
342 | /* Change current and default preamble mode.*/ | |
343 | void rtl_lps_set_psmode(struct ieee80211_hw *hw, u8 rt_psmode) | |
344 | { | |
345 | struct rtl_priv *rtlpriv = rtl_priv(hw); | |
346 | struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); | |
347 | struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); | |
348 | bool enter_fwlps; | |
349 | ||
350 | if (mac->opmode == NL80211_IFTYPE_ADHOC) | |
351 | return; | |
352 | ||
353 | if (mac->link_state != MAC80211_LINKED) | |
354 | return; | |
355 | ||
356 | if (ppsc->dot11_psmode == rt_psmode) | |
357 | return; | |
358 | ||
359 | /* Update power save mode configured. */ | |
360 | ppsc->dot11_psmode = rt_psmode; | |
361 | ||
362 | /* | |
363 | *<FW control LPS> | |
364 | *1. Enter PS mode | |
365 | * Set RPWM to Fw to turn RF off and send H2C fw_pwrmode | |
366 | * cmd to set Fw into PS mode. | |
367 | *2. Leave PS mode | |
368 | * Send H2C fw_pwrmode cmd to Fw to set Fw into Active | |
369 | * mode and set RPWM to turn RF on. | |
370 | */ | |
371 | ||
372 | if ((ppsc->b_fwctrl_lps) && ppsc->report_linked) { | |
373 | if (ppsc->dot11_psmode == EACTIVE) { | |
374 | RT_TRACE(COMP_RF, DBG_DMESG, | |
375 | ("FW LPS leave ps_mode:%x\n", | |
376 | FW_PS_ACTIVE_MODE)); | |
377 | enter_fwlps = false; | |
378 | ppsc->pwr_mode = FW_PS_ACTIVE_MODE; | |
379 | ppsc->smart_ps = 0; | |
380 | rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_FW_LPS_ACTION, | |
381 | (u8 *)(&enter_fwlps)); | |
382 | if (ppsc->p2p_ps_info.opp_ps) | |
383 | rtl92e_p2p_ps_cmd(hw , P2P_PS_ENABLE); | |
384 | ||
385 | if (rtlpriv->cfg->ops->get_btc_status()) | |
386 | rtlpriv->btcoexist.btc_ops->btc_lps_notify(rtlpriv, rt_psmode); | |
387 | } else { | |
388 | if (rtl_get_fwlps_doze(hw)) { | |
389 | RT_TRACE(COMP_RF, DBG_DMESG, | |
390 | ("FW LPS enter ps_mode:%x\n", | |
391 | ppsc->fwctrl_psmode)); | |
392 | if (rtlpriv->cfg->ops->get_btc_status()) | |
393 | rtlpriv->btcoexist.btc_ops->btc_lps_notify(rtlpriv, rt_psmode); | |
394 | enter_fwlps = true; | |
395 | ppsc->pwr_mode = ppsc->fwctrl_psmode; | |
396 | ppsc->smart_ps = 2; | |
397 | rtlpriv->cfg->ops->set_hw_reg(hw, | |
398 | HW_VAR_FW_LPS_ACTION, | |
399 | (u8 *)(&enter_fwlps)); | |
400 | ||
401 | } else { | |
402 | /* Reset the power save related parameters. */ | |
403 | ppsc->dot11_psmode = EACTIVE; | |
404 | } | |
405 | } | |
406 | } | |
407 | } | |
408 | ||
409 | /*Enter the leisure power save mode.*/ | |
410 | void rtl92e_lps_enter(struct ieee80211_hw *hw) | |
411 | { | |
412 | struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); | |
413 | struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); | |
414 | struct rtl_priv *rtlpriv = rtl_priv(hw); | |
415 | unsigned long flag; | |
416 | ||
417 | if (!ppsc->b_fwctrl_lps) | |
418 | return; | |
419 | ||
420 | if (rtlpriv->sec.being_setkey) | |
421 | return; | |
422 | ||
423 | if (rtlpriv->link_info.b_busytraffic) | |
424 | return; | |
425 | ||
426 | /*sleep after linked 10s, to let DHCP and 4-way handshake ok enough!! */ | |
427 | if (mac->cnt_after_linked < 5) | |
428 | return; | |
429 | ||
430 | if (mac->opmode == NL80211_IFTYPE_ADHOC) | |
431 | return; | |
432 | ||
433 | if (mac->link_state != MAC80211_LINKED) | |
434 | return; | |
435 | ||
436 | spin_lock_irqsave(&rtlpriv->locks.lps_lock, flag); | |
437 | ||
438 | /* Idle for a while if we connect to AP a while ago. */ | |
439 | if (mac->cnt_after_linked >= 2) { | |
440 | if (ppsc->dot11_psmode == EACTIVE) { | |
441 | RT_TRACE(COMP_POWER, DBG_LOUD, | |
442 | ("Enter 802.11 power save mode...\n")); | |
443 | ||
444 | rtl_lps_set_psmode(hw, EAUTOPS); | |
445 | } | |
446 | } | |
447 | ||
448 | spin_unlock_irqrestore(&rtlpriv->locks.lps_lock, flag); | |
449 | } | |
450 | EXPORT_SYMBOL(rtl92e_lps_enter); | |
451 | ||
452 | /*Leave the leisure power save mode.*/ | |
453 | void rtl92e_lps_leave(struct ieee80211_hw *hw) | |
454 | { | |
455 | struct rtl_priv *rtlpriv = rtl_priv(hw); | |
456 | struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); | |
457 | struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); | |
458 | unsigned long flag; | |
459 | ||
460 | spin_lock_irqsave(&rtlpriv->locks.lps_lock, flag); | |
461 | ||
462 | if (ppsc->b_fwctrl_lps) { | |
463 | if (ppsc->dot11_psmode != EACTIVE) { | |
464 | if (ppsc->reg_rfps_level & RT_RF_LPS_LEVEL_ASPM && | |
465 | RT_IN_PS_LEVEL(ppsc, RT_PS_LEVEL_ASPM) && | |
466 | rtlhal->interface == INTF_PCI) { | |
467 | rtlpriv->intf_ops->disable_aspm(hw); | |
468 | RT_CLEAR_PS_LEVEL(ppsc, RT_PS_LEVEL_ASPM); | |
469 | } | |
470 | ||
471 | RT_TRACE(COMP_POWER, DBG_LOUD, | |
472 | ("Busy Traffic,Leave 802.11 power save..\n")); | |
473 | ||
474 | rtl_lps_set_psmode(hw, EACTIVE); | |
475 | } | |
476 | } | |
477 | spin_unlock_irqrestore(&rtlpriv->locks.lps_lock, flag); | |
478 | } | |
479 | EXPORT_SYMBOL(rtl92e_lps_leave); | |
480 | ||
481 | /* For sw LPS*/ | |
482 | void rtl92e_swlps_beacon(struct ieee80211_hw *hw, void *data, unsigned int len) | |
483 | { | |
484 | struct rtl_priv *rtlpriv = rtl_priv(hw); | |
485 | struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); | |
486 | struct ieee80211_hdr *hdr = (void *)data; | |
487 | struct ieee80211_tim_ie *tim_ie; | |
488 | u8 *tim; | |
489 | u8 tim_len; | |
490 | bool u_buffed; | |
491 | bool m_buffed; | |
492 | ||
493 | if (mac->opmode != NL80211_IFTYPE_STATION) | |
494 | return; | |
495 | ||
496 | if (!rtlpriv->psc.b_swctrl_lps) | |
497 | return; | |
498 | ||
499 | if (rtlpriv->mac80211.link_state != MAC80211_LINKED) | |
500 | return; | |
501 | ||
502 | if (!rtlpriv->psc.sw_ps_enabled) | |
503 | return; | |
504 | ||
505 | if (rtlpriv->psc.b_fwctrl_lps) | |
506 | return; | |
507 | ||
508 | if (likely(!(hw->conf.flags & IEEE80211_CONF_PS))) | |
509 | return; | |
510 | ||
511 | /* check if this really is a beacon */ | |
512 | if (!ieee80211_is_beacon(hdr->frame_control)) | |
513 | return; | |
514 | ||
515 | /* min. beacon length + FCS_LEN */ | |
516 | if (len <= 40 + FCS_LEN) | |
517 | return; | |
518 | ||
519 | /* and only beacons from the associated BSSID, please */ | |
520 | if (!ether_addr_equal(hdr->addr3, rtlpriv->mac80211.bssid)) | |
521 | return; | |
522 | ||
523 | rtlpriv->psc.last_beacon = jiffies; | |
524 | ||
525 | tim = rtl92e_find_ie(data, len - FCS_LEN, WLAN_EID_TIM); | |
526 | if (!tim) | |
527 | return; | |
528 | ||
529 | if (tim[1] < sizeof(*tim_ie)) | |
530 | return; | |
531 | ||
532 | tim_len = tim[1]; | |
533 | tim_ie = (struct ieee80211_tim_ie *)&tim[2]; | |
534 | ||
535 | if (!WARN_ON_ONCE(!hw->conf.ps_dtim_period)) | |
536 | rtlpriv->psc.dtim_counter = tim_ie->dtim_count; | |
537 | ||
538 | /* Check whenever the PHY can be turned off again. */ | |
539 | ||
540 | /* 1. What about buffered unicast traffic for our AID? */ | |
541 | u_buffed = ieee80211_check_tim(tim_ie, tim_len, | |
542 | rtlpriv->mac80211.assoc_id); | |
543 | ||
544 | /* 2. Maybe the AP wants to send multicast/broadcast data? */ | |
545 | m_buffed = tim_ie->bitmap_ctrl & 0x01; | |
546 | rtlpriv->psc.multi_buffered = m_buffed; | |
547 | ||
548 | /* unicast will process by mac80211 through | |
549 | * set ~IEEE80211_CONF_PS, So we just check | |
550 | * multicast frames here */ | |
551 | if (!m_buffed) {/*&&) { !rtlpriv->psc.tx_doing) { */ | |
552 | /* back to low-power land. and delay is | |
553 | * prevent null power save frame tx fail */ | |
554 | queue_delayed_work(rtlpriv->works.rtl_wq, | |
555 | &rtlpriv->works.ps_work, MSECS(5)); | |
556 | } else { | |
557 | RT_TRACE(COMP_POWER, DBG_DMESG, | |
558 | ("u_bufferd: %x, m_buffered: %x\n", | |
559 | u_buffed, m_buffed)); | |
560 | } | |
561 | } | |
562 | ||
563 | void rtl92e_swlps_rf_awake(struct ieee80211_hw *hw) | |
564 | { | |
565 | struct rtl_priv *rtlpriv = rtl_priv(hw); | |
566 | struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); | |
567 | struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); | |
568 | unsigned long flag; | |
569 | ||
570 | if (!rtlpriv->psc.b_swctrl_lps) | |
571 | return; | |
572 | if (mac->link_state != MAC80211_LINKED) | |
573 | return; | |
574 | ||
575 | if (ppsc->reg_rfps_level & RT_RF_LPS_LEVEL_ASPM && | |
576 | RT_IN_PS_LEVEL(ppsc, RT_PS_LEVEL_ASPM)) { | |
577 | rtlpriv->intf_ops->disable_aspm(hw); | |
578 | RT_CLEAR_PS_LEVEL(ppsc, RT_PS_LEVEL_ASPM); | |
579 | } | |
580 | ||
581 | spin_lock_irqsave(&rtlpriv->locks.lps_lock, flag); | |
582 | stg_rtl_ps_set_rf_state(hw, ERFON, RF_CHANGE_BY_PS, false); | |
583 | spin_unlock_irqrestore(&rtlpriv->locks.lps_lock, flag); | |
584 | } | |
585 | ||
586 | void rtl92e_swlps_rfon_wq_callback(void *data) | |
587 | { | |
588 | struct rtl_works *rtlworks = | |
589 | container_of_dwork_rtl(data, struct rtl_works, ps_rfon_wq); | |
590 | struct ieee80211_hw *hw = rtlworks->hw; | |
591 | ||
592 | rtl92e_swlps_rf_awake(hw); | |
593 | } | |
594 | ||
595 | void rtl92e_swlps_rf_sleep(struct ieee80211_hw *hw) | |
596 | { | |
597 | struct rtl_priv *rtlpriv = rtl_priv(hw); | |
598 | struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); | |
599 | struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); | |
600 | unsigned long flag; | |
601 | u8 sleep_intv; | |
602 | ||
603 | if (!rtlpriv->psc.sw_ps_enabled) | |
604 | return; | |
605 | ||
606 | if ((rtlpriv->sec.being_setkey) || | |
607 | (mac->opmode == NL80211_IFTYPE_ADHOC)) | |
608 | return; | |
609 | ||
610 | /*sleep after linked 10s, to let DHCP and 4-way handshake ok enough!! */ | |
611 | if ((mac->link_state != MAC80211_LINKED) || (mac->cnt_after_linked < 5)) | |
612 | return; | |
613 | ||
614 | if (rtlpriv->link_info.b_busytraffic) | |
615 | return; | |
616 | ||
617 | spin_lock(&rtlpriv->locks.rf_ps_lock); | |
618 | if (rtlpriv->psc.rfchange_inprogress) { | |
619 | spin_unlock(&rtlpriv->locks.rf_ps_lock); | |
620 | return; | |
621 | } | |
622 | spin_unlock(&rtlpriv->locks.rf_ps_lock); | |
623 | ||
624 | spin_lock_irqsave(&rtlpriv->locks.lps_lock, flag); | |
625 | stg_rtl_ps_set_rf_state(hw, ERFSLEEP, RF_CHANGE_BY_PS , false); | |
626 | spin_unlock_irqrestore(&rtlpriv->locks.lps_lock, flag); | |
627 | ||
628 | if (ppsc->reg_rfps_level & RT_RF_OFF_LEVL_ASPM && | |
629 | !RT_IN_PS_LEVEL(ppsc, RT_PS_LEVEL_ASPM)) { | |
630 | rtlpriv->intf_ops->enable_aspm(hw); | |
631 | RT_SET_PS_LEVEL(ppsc, RT_PS_LEVEL_ASPM); | |
632 | } | |
633 | ||
634 | /* here is power save alg, when this beacon is DTIM | |
635 | * we will set sleep time to dtim_period * n; | |
636 | * when this beacon is not DTIM, we will set sleep | |
637 | * time to sleep_intv = rtlpriv->psc.dtim_counter or | |
638 | * MAX_SW_LPS_SLEEP_INTV(default set to 5) */ | |
639 | ||
640 | if (rtlpriv->psc.dtim_counter == 0) { | |
641 | if (hw->conf.ps_dtim_period == 1) | |
642 | sleep_intv = hw->conf.ps_dtim_period * 2; | |
643 | else | |
644 | sleep_intv = hw->conf.ps_dtim_period; | |
645 | } else { | |
646 | sleep_intv = rtlpriv->psc.dtim_counter; | |
647 | } | |
648 | ||
649 | if (sleep_intv > MAX_SW_LPS_SLEEP_INTV) | |
650 | sleep_intv = MAX_SW_LPS_SLEEP_INTV; | |
651 | ||
652 | /* this print should always be dtim_conter = 0 & | |
653 | * sleep = dtim_period, that meaons, we should | |
654 | * awake before every dtim */ | |
655 | RT_TRACE(COMP_POWER, DBG_DMESG, | |
656 | ("dtim_counter:%x will sleep :%d beacon_intv\n", | |
657 | rtlpriv->psc.dtim_counter, sleep_intv)); | |
658 | ||
659 | /* we tested that 40ms is enough for sw & hw sw delay */ | |
660 | queue_delayed_work(rtlpriv->works.rtl_wq, &rtlpriv->works.ps_rfon_wq, | |
661 | MSECS(sleep_intv*mac->vif->bss_conf.beacon_int-40)); | |
662 | } | |
663 | ||
664 | ||
665 | void rtl92e_swlps_wq_callback(void *data) | |
666 | { | |
667 | struct rtl_works *rtlworks = | |
668 | container_of_dwork_rtl(data, struct rtl_works, ps_work); | |
669 | struct ieee80211_hw *hw = rtlworks->hw; | |
670 | struct rtl_priv *rtlpriv = rtl_priv(hw); | |
671 | bool ps = false; | |
672 | ||
673 | ps = (hw->conf.flags & IEEE80211_CONF_PS); | |
674 | ||
675 | /* we can sleep after ps null send ok */ | |
676 | if (rtlpriv->psc.state_inap) { | |
677 | rtl92e_swlps_rf_sleep(hw); | |
678 | ||
679 | if (rtlpriv->psc.state && !ps) { | |
680 | rtlpriv->psc.sleep_ms = | |
681 | jiffies_to_msecs(jiffies - | |
682 | rtlpriv->psc.last_action); | |
683 | } | |
684 | ||
685 | if (ps) | |
686 | rtlpriv->psc.last_slept = jiffies; | |
687 | ||
688 | rtlpriv->psc.last_action = jiffies; | |
689 | rtlpriv->psc.state = ps; | |
690 | } | |
691 | } | |
692 | ||
693 | static void rtl_p2p_noa_ie(struct ieee80211_hw *hw, void *data, | |
694 | unsigned int len) | |
695 | { | |
696 | struct rtl_priv *rtlpriv = rtl_priv(hw); | |
697 | struct ieee80211_mgmt *mgmt = (void *)data; | |
698 | struct rtl_p2p_ps_info *p2pinfo = &(rtlpriv->psc.p2p_ps_info); | |
699 | u8 *pos, *end, *ie; | |
700 | u16 noa_len; | |
701 | static u8 p2p_oui_ie_type[4] = {0x50, 0x6f, 0x9a, 0x09}; | |
702 | u8 noa_num, index , i, noa_index = 0; | |
703 | bool find_p2p_ie = false , find_p2p_ps_ie = false; | |
704 | pos = (u8 *)mgmt->u.beacon.variable; | |
705 | end = data + len; | |
706 | ie = NULL; | |
707 | ||
708 | while (pos + 1 < end) { | |
709 | if (pos + 2 + pos[1] > end) | |
710 | return; | |
711 | ||
712 | if (pos[0] == 221 && pos[1] > 4) { | |
713 | if (memcmp(&pos[2], p2p_oui_ie_type, 4) == 0) { | |
714 | ie = pos + 2+4; | |
715 | break; | |
716 | } | |
717 | } | |
718 | pos += 2 + pos[1]; | |
719 | } | |
720 | ||
721 | if (ie == NULL) | |
722 | return; | |
723 | find_p2p_ie = true; | |
724 | /*to find noa ie*/ | |
725 | while (ie + 1 < end) { | |
726 | noa_len = READEF2BYTE((__le16 *)&ie[1]); | |
727 | if (ie + 3 + ie[1] > end) | |
728 | return; | |
729 | ||
730 | if (ie[0] == 12) { | |
731 | find_p2p_ps_ie = true; | |
732 | if ((noa_len - 2) % 13 != 0) { | |
733 | RT_TRACE(COMP_INIT, DBG_LOUD, | |
734 | ("P2P notice of absence: invalid length%d\n", | |
735 | noa_len)); | |
736 | return; | |
737 | } else { | |
738 | noa_num = (noa_len - 2) / 13; | |
739 | } | |
740 | noa_index = ie[3]; | |
741 | if (rtlpriv->psc.p2p_ps_info.p2p_ps_mode == | |
742 | P2P_PS_NONE || noa_index != p2pinfo->noa_index) { | |
743 | RT_TRACE(COMP_FW, DBG_LOUD, | |
744 | ("update NOA ie.\n")); | |
745 | p2pinfo->noa_index = noa_index; | |
746 | p2pinfo->opp_ps = (ie[4] >> 7); | |
747 | p2pinfo->ctwindow = ie[4] & 0x7F; | |
748 | p2pinfo->noa_num = noa_num; | |
749 | index = 5; | |
750 | for (i = 0; i < noa_num; i++) { | |
751 | p2pinfo->noa_count_type[i] = | |
752 | READEF1BYTE(ie+index); | |
753 | index += 1; | |
754 | p2pinfo->noa_duration[i] = | |
755 | READEF4BYTE((__le32 *)ie+index); | |
756 | index += 4; | |
757 | p2pinfo->noa_interval[i] = | |
758 | READEF4BYTE((__le32 *)ie+index); | |
759 | index += 4; | |
760 | p2pinfo->noa_start_time[i] = | |
761 | READEF4BYTE((__le32 *)ie+index); | |
762 | index += 4; | |
763 | } | |
764 | ||
765 | if (p2pinfo->opp_ps == 1) { | |
766 | p2pinfo->p2p_ps_mode = P2P_PS_CTWINDOW; | |
767 | /* Driver should wait LPS | |
768 | * entering CTWindow*/ | |
769 | if (rtlpriv->psc.b_fw_current_inpsmode) { | |
770 | rtl92e_p2p_ps_cmd(hw, | |
771 | P2P_PS_ENABLE); | |
772 | } | |
773 | } else if (p2pinfo->noa_num > 0) { | |
774 | p2pinfo->p2p_ps_mode = P2P_PS_NOA; | |
775 | rtl92e_p2p_ps_cmd(hw, P2P_PS_ENABLE); | |
776 | } else if (p2pinfo->p2p_ps_mode > P2P_PS_NONE) { | |
777 | rtl92e_p2p_ps_cmd(hw, P2P_PS_DISABLE); | |
778 | } | |
779 | } | |
780 | ||
781 | break; | |
782 | } | |
783 | ie += 3 + noa_len; | |
784 | } | |
785 | ||
786 | if (find_p2p_ie) { | |
787 | if ((p2pinfo->p2p_ps_mode > P2P_PS_NONE) && | |
788 | (!find_p2p_ps_ie)) | |
789 | rtl92e_p2p_ps_cmd(hw, P2P_PS_DISABLE); | |
790 | } | |
791 | } | |
792 | ||
793 | static void rtl_p2p_action_ie(struct ieee80211_hw *hw, void *data, | |
794 | unsigned int len) | |
795 | { | |
796 | struct rtl_priv *rtlpriv = rtl_priv(hw); | |
797 | struct ieee80211_mgmt *mgmt = (void *)data; | |
798 | struct rtl_p2p_ps_info *p2pinfo = &(rtlpriv->psc.p2p_ps_info); | |
799 | bool find_p2p_ie = false, find_p2p_ps_ie = false; | |
800 | u8 noa_num, index, i, noa_index = 0; | |
801 | u8 *pos, *end, *ie; | |
802 | u16 noa_len; | |
803 | static u8 p2p_oui_ie_type[4] = {0x50, 0x6f, 0x9a, 0x09}; | |
804 | ||
805 | pos = (u8 *)&mgmt->u.action.category; | |
806 | end = data + len; | |
807 | ie = NULL; | |
808 | ||
809 | if (pos[0] == 0x7f) { | |
810 | if (memcmp(&pos[1], p2p_oui_ie_type, 4) == 0) | |
811 | ie = pos + 3+4; | |
812 | } | |
813 | ||
814 | if (ie == NULL) | |
815 | return; | |
816 | find_p2p_ie = true; | |
817 | ||
818 | RT_TRACE(COMP_FW, DBG_LOUD, ("action frame find P2P IE.\n")); | |
819 | /*to find noa ie*/ | |
820 | while (ie + 1 < end) { | |
821 | noa_len = READEF2BYTE((__le16 *)&ie[1]); | |
822 | if (ie + 3 + ie[1] > end) | |
823 | return; | |
824 | ||
825 | if (ie[0] == 12) { | |
826 | RT_TRACE(COMP_FW, DBG_LOUD, ("find NOA IE\n")); | |
827 | RT_PRINT_DATA(rtlpriv, COMP_FW, DBG_LOUD, "noa ie ", | |
828 | ie, noa_len); | |
829 | find_p2p_ps_ie = true; | |
830 | if ((noa_len - 2) % 13 != 0) { | |
831 | RT_TRACE(COMP_FW, DBG_LOUD, | |
832 | ("P2P notice of absence: invalid length%d\n", | |
833 | noa_len)); | |
834 | return; | |
835 | } else { | |
836 | noa_num = (noa_len - 2) / 13; | |
837 | } | |
838 | noa_index = ie[3]; | |
839 | if (rtlpriv->psc.p2p_ps_info.p2p_ps_mode == | |
840 | P2P_PS_NONE || | |
841 | noa_index != p2pinfo->noa_index) { | |
842 | p2pinfo->noa_index = noa_index; | |
843 | p2pinfo->opp_ps = (ie[4] >> 7); | |
844 | p2pinfo->ctwindow = ie[4] & 0x7F; | |
845 | p2pinfo->noa_num = noa_num; | |
846 | index = 5; | |
847 | for (i = 0; i < noa_num; i++) { | |
848 | p2pinfo->noa_count_type[i] = | |
849 | READEF1BYTE(ie+index); | |
850 | index += 1; | |
851 | p2pinfo->noa_duration[i] = | |
852 | READEF4BYTE((__le32 *)ie+index); | |
853 | index += 4; | |
854 | p2pinfo->noa_interval[i] = | |
855 | READEF4BYTE((__le32 *)ie+index); | |
856 | index += 4; | |
857 | p2pinfo->noa_start_time[i] = | |
858 | READEF4BYTE((__le32 *)ie+index); | |
859 | index += 4; | |
860 | } | |
861 | ||
862 | if (p2pinfo->opp_ps == 1) { | |
863 | p2pinfo->p2p_ps_mode = P2P_PS_CTWINDOW; | |
864 | /* Driver should wait LPS | |
865 | * entering CTWindow */ | |
866 | if (rtlpriv->psc.b_fw_current_inpsmode) { | |
867 | rtl92e_p2p_ps_cmd(hw, | |
868 | P2P_PS_ENABLE); | |
869 | } | |
870 | } else if (p2pinfo->noa_num > 0) { | |
871 | p2pinfo->p2p_ps_mode = P2P_PS_NOA; | |
872 | rtl92e_p2p_ps_cmd(hw, P2P_PS_ENABLE); | |
873 | } else if (p2pinfo->p2p_ps_mode > P2P_PS_NONE) { | |
874 | rtl92e_p2p_ps_cmd(hw, P2P_PS_DISABLE); | |
875 | } | |
876 | } | |
877 | ||
878 | break; | |
879 | } | |
880 | ie += 3 + noa_len; | |
881 | } | |
882 | } | |
883 | ||
884 | void rtl92e_p2p_ps_cmd(struct ieee80211_hw *hw, u8 p2p_ps_state) | |
885 | { | |
886 | struct rtl_priv *rtlpriv = rtl_priv(hw); | |
887 | struct rtl_ps_ctl *rtlps = rtl_psc(rtl_priv(hw)); | |
888 | struct rtl_p2p_ps_info *p2pinfo = &(rtlpriv->psc.p2p_ps_info); | |
889 | ||
890 | RT_TRACE(COMP_FW, DBG_LOUD, ("p2p state %x\n", p2p_ps_state)); | |
891 | switch (p2p_ps_state) { | |
892 | case P2P_PS_DISABLE: | |
893 | p2pinfo->p2p_ps_state = p2p_ps_state; | |
894 | rtlpriv->cfg->ops->set_hw_reg(hw, | |
895 | HW_VAR_H2C_FW_P2P_PS_OFFLOAD, | |
896 | (u8 *)(&p2p_ps_state)); | |
897 | ||
898 | p2pinfo->noa_index = 0; | |
899 | p2pinfo->ctwindow = 0; | |
900 | p2pinfo->opp_ps = 0; | |
901 | p2pinfo->noa_num = 0; | |
902 | p2pinfo->p2p_ps_mode = P2P_PS_NONE; | |
903 | if (rtlps->b_fw_current_inpsmode) { | |
904 | if (rtlps->smart_ps == 0) { | |
905 | rtlps->smart_ps = 2; | |
906 | rtlpriv->cfg->ops->set_hw_reg(hw, | |
907 | HW_VAR_H2C_FW_PWRMODE, | |
908 | (u8 *)(&rtlps->pwr_mode)); | |
909 | } | |
910 | } | |
911 | break; | |
912 | case P2P_PS_ENABLE: | |
913 | if (p2pinfo->p2p_ps_mode > P2P_PS_NONE) { | |
914 | p2pinfo->p2p_ps_state = p2p_ps_state; | |
915 | ||
916 | if (p2pinfo->ctwindow > 0) { | |
917 | if (rtlps->smart_ps != 0) { | |
918 | rtlps->smart_ps = 0; | |
919 | rtlpriv->cfg->ops->set_hw_reg( | |
920 | hw, HW_VAR_H2C_FW_PWRMODE, | |
921 | (u8 *)(&rtlps->pwr_mode)); | |
922 | } | |
923 | } | |
924 | rtlpriv->cfg->ops->set_hw_reg(hw, | |
925 | HW_VAR_H2C_FW_P2P_PS_OFFLOAD, | |
926 | (u8 *)(&p2p_ps_state)); | |
927 | } | |
928 | break; | |
929 | case P2P_PS_SCAN: | |
930 | case P2P_PS_SCAN_DONE: | |
931 | case P2P_PS_ALLSTASLEEP: | |
932 | if (p2pinfo->p2p_ps_mode > P2P_PS_NONE) { | |
933 | p2pinfo->p2p_ps_state = p2p_ps_state; | |
934 | rtlpriv->cfg->ops->set_hw_reg(hw, | |
935 | HW_VAR_H2C_FW_P2P_PS_OFFLOAD, | |
936 | (u8 *)(&p2p_ps_state)); | |
937 | } | |
938 | break; | |
939 | default: | |
940 | break; | |
941 | } | |
942 | RT_TRACE(COMP_FW, DBG_LOUD, (" ctwindow %x oppps %x\n", | |
943 | p2pinfo->ctwindow , p2pinfo->opp_ps)); | |
944 | RT_TRACE(COMP_FW, DBG_LOUD, | |
945 | ("count %x duration %x index %x interval %x start time %x noa num %x\n", | |
946 | p2pinfo->noa_count_type[0], | |
947 | p2pinfo->noa_duration[0], | |
948 | p2pinfo->noa_index, | |
949 | p2pinfo->noa_interval[0], | |
950 | p2pinfo->noa_start_time[0], | |
951 | p2pinfo->noa_num)); | |
952 | RT_TRACE(COMP_FW, DBG_LOUD, ("end\n")); | |
953 | } | |
954 | ||
955 | void rtl92e_p2p_info(struct ieee80211_hw *hw, void *data, unsigned int len) | |
956 | { | |
957 | struct rtl_priv *rtlpriv = rtl_priv(hw); | |
958 | struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); | |
959 | struct ieee80211_hdr *hdr = (void *)data; | |
960 | ||
961 | if (!mac->p2p) | |
962 | return; | |
963 | if (mac->link_state != MAC80211_LINKED) | |
964 | return; | |
965 | /* min. beacon length + FCS_LEN */ | |
966 | if (len <= 40 + FCS_LEN) | |
967 | return; | |
968 | ||
969 | /* and only beacons from the associated BSSID, please */ | |
970 | if (!ether_addr_equal(hdr->addr3, rtlpriv->mac80211.bssid)) | |
971 | return; | |
972 | ||
973 | /* check if this really is a beacon */ | |
974 | if (!(ieee80211_is_beacon(hdr->frame_control) || | |
975 | ieee80211_is_probe_resp(hdr->frame_control) || | |
976 | ieee80211_is_action(hdr->frame_control))) | |
977 | return; | |
978 | ||
979 | if (ieee80211_is_action(hdr->frame_control)) | |
980 | rtl_p2p_action_ie(hw , data , len - FCS_LEN); | |
981 | else | |
982 | rtl_p2p_noa_ie(hw , data , len - FCS_LEN); | |
983 | } |