diff options
Diffstat (limited to 'drivers/net/wireless/rtlwifi/ps.c')
-rw-r--r-- | drivers/net/wireless/rtlwifi/ps.c | 258 |
1 files changed, 237 insertions, 21 deletions
diff --git a/drivers/net/wireless/rtlwifi/ps.c b/drivers/net/wireless/rtlwifi/ps.c index c8395fb0c050..2bb71195e976 100644 --- a/drivers/net/wireless/rtlwifi/ps.c +++ b/drivers/net/wireless/rtlwifi/ps.c | |||
@@ -36,7 +36,6 @@ bool rtl_ps_enable_nic(struct ieee80211_hw *hw) | |||
36 | struct rtl_priv *rtlpriv = rtl_priv(hw); | 36 | struct rtl_priv *rtlpriv = rtl_priv(hw); |
37 | struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); | 37 | struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); |
38 | struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); | 38 | struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); |
39 | bool init_status = true; | ||
40 | 39 | ||
41 | /*<1> reset trx ring */ | 40 | /*<1> reset trx ring */ |
42 | if (rtlhal->interface == INTF_PCI) | 41 | if (rtlhal->interface == INTF_PCI) |
@@ -49,7 +48,6 @@ bool rtl_ps_enable_nic(struct ieee80211_hw *hw) | |||
49 | /*<2> Enable Adapter */ | 48 | /*<2> Enable Adapter */ |
50 | rtlpriv->cfg->ops->hw_init(hw); | 49 | rtlpriv->cfg->ops->hw_init(hw); |
51 | RT_CLEAR_PS_LEVEL(ppsc, RT_RF_OFF_LEVL_HALT_NIC); | 50 | RT_CLEAR_PS_LEVEL(ppsc, RT_RF_OFF_LEVL_HALT_NIC); |
52 | /*init_status = false; */ | ||
53 | 51 | ||
54 | /*<3> Enable Interrupt */ | 52 | /*<3> Enable Interrupt */ |
55 | rtlpriv->cfg->ops->enable_interrupt(hw); | 53 | rtlpriv->cfg->ops->enable_interrupt(hw); |
@@ -57,7 +55,7 @@ bool rtl_ps_enable_nic(struct ieee80211_hw *hw) | |||
57 | /*<enable timer> */ | 55 | /*<enable timer> */ |
58 | rtl_watch_dog_timer_callback((unsigned long)hw); | 56 | rtl_watch_dog_timer_callback((unsigned long)hw); |
59 | 57 | ||
60 | return init_status; | 58 | return true; |
61 | } | 59 | } |
62 | EXPORT_SYMBOL(rtl_ps_enable_nic); | 60 | EXPORT_SYMBOL(rtl_ps_enable_nic); |
63 | 61 | ||
@@ -192,12 +190,13 @@ static void _rtl_ps_inactive_ps(struct ieee80211_hw *hw) | |||
192 | 190 | ||
193 | ppsc->swrf_processing = true; | 191 | ppsc->swrf_processing = true; |
194 | 192 | ||
195 | if (ppsc->inactive_pwrstate == ERFON && rtlhal->interface == INTF_PCI) { | 193 | if (ppsc->inactive_pwrstate == ERFOFF && |
194 | rtlhal->interface == INTF_PCI) { | ||
196 | if ((ppsc->reg_rfps_level & RT_RF_OFF_LEVL_ASPM) && | 195 | if ((ppsc->reg_rfps_level & RT_RF_OFF_LEVL_ASPM) && |
197 | RT_IN_PS_LEVEL(ppsc, RT_RF_OFF_LEVL_ASPM) && | 196 | RT_IN_PS_LEVEL(ppsc, RT_PS_LEVEL_ASPM) && |
198 | rtlhal->interface == INTF_PCI) { | 197 | rtlhal->interface == INTF_PCI) { |
199 | rtlpriv->intf_ops->disable_aspm(hw); | 198 | rtlpriv->intf_ops->disable_aspm(hw); |
200 | RT_CLEAR_PS_LEVEL(ppsc, RT_RF_OFF_LEVL_ASPM); | 199 | RT_CLEAR_PS_LEVEL(ppsc, RT_PS_LEVEL_ASPM); |
201 | } | 200 | } |
202 | } | 201 | } |
203 | 202 | ||
@@ -206,9 +205,10 @@ static void _rtl_ps_inactive_ps(struct ieee80211_hw *hw) | |||
206 | 205 | ||
207 | if (ppsc->inactive_pwrstate == ERFOFF && | 206 | if (ppsc->inactive_pwrstate == ERFOFF && |
208 | rtlhal->interface == INTF_PCI) { | 207 | rtlhal->interface == INTF_PCI) { |
209 | if (ppsc->reg_rfps_level & RT_RF_OFF_LEVL_ASPM) { | 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); | 210 | rtlpriv->intf_ops->enable_aspm(hw); |
211 | RT_SET_PS_LEVEL(ppsc, RT_RF_OFF_LEVL_ASPM); | 211 | RT_SET_PS_LEVEL(ppsc, RT_PS_LEVEL_ASPM); |
212 | } | 212 | } |
213 | } | 213 | } |
214 | 214 | ||
@@ -232,6 +232,9 @@ void rtl_ips_nic_off_wq_callback(void *data) | |||
232 | return; | 232 | return; |
233 | } | 233 | } |
234 | 234 | ||
235 | if (mac->link_state > MAC80211_NOLINK) | ||
236 | return; | ||
237 | |||
235 | if (is_hal_stop(rtlhal)) | 238 | if (is_hal_stop(rtlhal)) |
236 | return; | 239 | return; |
237 | 240 | ||
@@ -283,10 +286,14 @@ void rtl_ips_nic_off(struct ieee80211_hw *hw) | |||
283 | void rtl_ips_nic_on(struct ieee80211_hw *hw) | 286 | void rtl_ips_nic_on(struct ieee80211_hw *hw) |
284 | { | 287 | { |
285 | struct rtl_priv *rtlpriv = rtl_priv(hw); | 288 | struct rtl_priv *rtlpriv = rtl_priv(hw); |
289 | struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); | ||
286 | struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); | 290 | struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); |
287 | enum rf_pwrstate rtstate; | 291 | enum rf_pwrstate rtstate; |
288 | unsigned long flags; | 292 | unsigned long flags; |
289 | 293 | ||
294 | if (mac->opmode != NL80211_IFTYPE_STATION) | ||
295 | return; | ||
296 | |||
290 | spin_lock_irqsave(&rtlpriv->locks.ips_lock, flags); | 297 | spin_lock_irqsave(&rtlpriv->locks.ips_lock, flags); |
291 | 298 | ||
292 | if (ppsc->inactiveps) { | 299 | if (ppsc->inactiveps) { |
@@ -369,8 +376,7 @@ static void rtl_lps_set_psmode(struct ieee80211_hw *hw, u8 rt_psmode) | |||
369 | * mode and set RPWM to turn RF on. | 376 | * mode and set RPWM to turn RF on. |
370 | */ | 377 | */ |
371 | 378 | ||
372 | if ((ppsc->fwctrl_lps) && (ppsc->leisure_ps) && | 379 | if ((ppsc->fwctrl_lps) && ppsc->report_linked) { |
373 | ppsc->report_linked) { | ||
374 | bool fw_current_inps; | 380 | bool fw_current_inps; |
375 | if (ppsc->dot11_psmode == EACTIVE) { | 381 | if (ppsc->dot11_psmode == EACTIVE) { |
376 | RT_TRACE(rtlpriv, COMP_RF, DBG_DMESG, | 382 | RT_TRACE(rtlpriv, COMP_RF, DBG_DMESG, |
@@ -424,7 +430,7 @@ void rtl_lps_enter(struct ieee80211_hw *hw) | |||
424 | struct rtl_priv *rtlpriv = rtl_priv(hw); | 430 | struct rtl_priv *rtlpriv = rtl_priv(hw); |
425 | unsigned long flag; | 431 | unsigned long flag; |
426 | 432 | ||
427 | if (!(ppsc->fwctrl_lps && ppsc->leisure_ps)) | 433 | if (!ppsc->fwctrl_lps) |
428 | return; | 434 | return; |
429 | 435 | ||
430 | if (rtlpriv->sec.being_setkey) | 436 | if (rtlpriv->sec.being_setkey) |
@@ -445,17 +451,16 @@ void rtl_lps_enter(struct ieee80211_hw *hw) | |||
445 | 451 | ||
446 | spin_lock_irqsave(&rtlpriv->locks.lps_lock, flag); | 452 | spin_lock_irqsave(&rtlpriv->locks.lps_lock, flag); |
447 | 453 | ||
448 | if (ppsc->leisure_ps) { | 454 | /* Idle for a while if we connect to AP a while ago. */ |
449 | /* Idle for a while if we connect to AP a while ago. */ | 455 | if (mac->cnt_after_linked >= 2) { |
450 | if (mac->cnt_after_linked >= 2) { | 456 | if (ppsc->dot11_psmode == EACTIVE) { |
451 | if (ppsc->dot11_psmode == EACTIVE) { | 457 | RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD, |
452 | RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD, | ||
453 | ("Enter 802.11 power save mode...\n")); | 458 | ("Enter 802.11 power save mode...\n")); |
454 | 459 | ||
455 | rtl_lps_set_psmode(hw, EAUTOPS); | 460 | rtl_lps_set_psmode(hw, EAUTOPS); |
456 | } | ||
457 | } | 461 | } |
458 | } | 462 | } |
463 | |||
459 | spin_unlock_irqrestore(&rtlpriv->locks.lps_lock, flag); | 464 | spin_unlock_irqrestore(&rtlpriv->locks.lps_lock, flag); |
460 | } | 465 | } |
461 | 466 | ||
@@ -469,17 +474,17 @@ void rtl_lps_leave(struct ieee80211_hw *hw) | |||
469 | 474 | ||
470 | spin_lock_irqsave(&rtlpriv->locks.lps_lock, flag); | 475 | spin_lock_irqsave(&rtlpriv->locks.lps_lock, flag); |
471 | 476 | ||
472 | if (ppsc->fwctrl_lps && ppsc->leisure_ps) { | 477 | if (ppsc->fwctrl_lps) { |
473 | if (ppsc->dot11_psmode != EACTIVE) { | 478 | if (ppsc->dot11_psmode != EACTIVE) { |
474 | 479 | ||
475 | /*FIX ME */ | 480 | /*FIX ME */ |
476 | rtlpriv->cfg->ops->enable_interrupt(hw); | 481 | rtlpriv->cfg->ops->enable_interrupt(hw); |
477 | 482 | ||
478 | if (ppsc->reg_rfps_level & RT_RF_LPS_LEVEL_ASPM && | 483 | if (ppsc->reg_rfps_level & RT_RF_LPS_LEVEL_ASPM && |
479 | RT_IN_PS_LEVEL(ppsc, RT_RF_LPS_LEVEL_ASPM) && | 484 | RT_IN_PS_LEVEL(ppsc, RT_PS_LEVEL_ASPM) && |
480 | rtlhal->interface == INTF_PCI) { | 485 | rtlhal->interface == INTF_PCI) { |
481 | rtlpriv->intf_ops->disable_aspm(hw); | 486 | rtlpriv->intf_ops->disable_aspm(hw); |
482 | RT_CLEAR_PS_LEVEL(ppsc, RT_RF_LPS_LEVEL_ASPM); | 487 | RT_CLEAR_PS_LEVEL(ppsc, RT_PS_LEVEL_ASPM); |
483 | } | 488 | } |
484 | 489 | ||
485 | RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD, | 490 | RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD, |
@@ -490,3 +495,214 @@ void rtl_lps_leave(struct ieee80211_hw *hw) | |||
490 | } | 495 | } |
491 | spin_unlock_irqrestore(&rtlpriv->locks.lps_lock, flag); | 496 | spin_unlock_irqrestore(&rtlpriv->locks.lps_lock, flag); |
492 | } | 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 | } | ||