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