2010-12-08 11:12:31 -06:00
/******************************************************************************
*
2012-01-07 20:46:42 -06:00
* Copyright ( c ) 2009 - 2012 Realtek Corporation .
2010-12-08 11:12:31 -06:00
*
* This program is free software ; you can redistribute it and / or modify it
* under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation .
*
* This program is distributed in the hope that it will be useful , but WITHOUT
* ANY WARRANTY ; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE . See the GNU General Public License for
* more details .
*
* You should have received a copy of the GNU General Public License along with
* this program ; if not , write to the Free Software Foundation , Inc . ,
* 51 Franklin Street , Fifth Floor , Boston , MA 02110 , USA
*
* The full GNU General Public License is included in this distribution in the
* file called LICENSE .
*
* Contact Information :
* wlanfae < wlanfae @ realtek . com >
* Realtek Corporation , No . 2 , Innovation Road II , Hsinchu Science Park ,
* Hsinchu 300 , Taiwan .
*
* Larry Finger < Larry . Finger @ lwfinger . net >
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2011-05-27 16:14:23 -04:00
# include <linux/export.h>
2010-12-08 11:12:31 -06:00
# include "wifi.h"
# include "base.h"
# include "ps.h"
bool rtl_ps_enable_nic ( struct ieee80211_hw * hw )
{
struct rtl_priv * rtlpriv = rtl_priv ( hw ) ;
struct rtl_ps_ctl * ppsc = rtl_psc ( rtl_priv ( hw ) ) ;
struct rtl_hal * rtlhal = rtl_hal ( rtl_priv ( hw ) ) ;
/*<1> reset trx ring */
if ( rtlhal - > interface = = INTF_PCI )
rtlpriv - > intf_ops - > reset_trx_ring ( hw ) ;
if ( is_hal_stop ( rtlhal ) )
RT_TRACE ( rtlpriv , COMP_ERR , DBG_WARNING ,
2012-01-04 19:40:41 -08:00
" Driver is already down! \n " ) ;
2010-12-08 11:12:31 -06:00
/*<2> Enable Adapter */
2012-01-30 09:54:49 -06:00
if ( rtlpriv - > cfg - > ops - > hw_init ( hw ) )
return 1 ;
2010-12-08 11:12:31 -06:00
RT_CLEAR_PS_LEVEL ( ppsc , RT_RF_OFF_LEVL_HALT_NIC ) ;
/*<3> Enable Interrupt */
rtlpriv - > cfg - > ops - > enable_interrupt ( hw ) ;
/*<enable timer> */
rtl_watch_dog_timer_callback ( ( unsigned long ) hw ) ;
2011-04-25 12:53:14 -05:00
return true ;
2010-12-08 11:12:31 -06:00
}
EXPORT_SYMBOL ( rtl_ps_enable_nic ) ;
bool rtl_ps_disable_nic ( struct ieee80211_hw * hw )
{
struct rtl_priv * rtlpriv = rtl_priv ( hw ) ;
/*<1> Stop all timer */
rtl_deinit_deferred_work ( hw ) ;
/*<2> Disable Interrupt */
rtlpriv - > cfg - > ops - > disable_interrupt ( hw ) ;
2011-05-31 08:49:23 +09:00
tasklet_kill ( & rtlpriv - > works . irq_tasklet ) ;
2010-12-08 11:12:31 -06:00
/*<3> Disable Adapter */
rtlpriv - > cfg - > ops - > hw_disable ( hw ) ;
2011-03-27 16:19:57 -05:00
return true ;
2010-12-08 11:12:31 -06:00
}
EXPORT_SYMBOL ( rtl_ps_disable_nic ) ;
bool rtl_ps_set_rf_state ( struct ieee80211_hw * hw ,
enum rf_pwrstate state_toset ,
2011-06-20 10:46:05 +09:00
u32 changesource )
2010-12-08 11:12:31 -06:00
{
struct rtl_priv * rtlpriv = rtl_priv ( hw ) ;
struct rtl_ps_ctl * ppsc = rtl_psc ( rtl_priv ( hw ) ) ;
2011-02-19 16:28:57 -06:00
bool actionallowed = false ;
2010-12-08 11:12:31 -06:00
switch ( state_toset ) {
case ERFON :
ppsc - > rfoff_reason & = ( ~ changesource ) ;
if ( ( changesource = = RF_CHANGE_BY_HW ) & &
2011-06-20 10:47:51 +09:00
( ppsc - > hwradiooff ) ) {
2011-02-19 16:28:57 -06:00
ppsc - > hwradiooff = false ;
2010-12-08 11:12:31 -06:00
}
if ( ! ppsc - > rfoff_reason ) {
ppsc - > rfoff_reason = 0 ;
2011-02-19 16:28:57 -06:00
actionallowed = true ;
2010-12-08 11:12:31 -06:00
}
break ;
case ERFOFF :
2012-02-09 11:17:23 +00:00
if ( ( changesource = = RF_CHANGE_BY_HW ) & & ! ppsc - > hwradiooff ) {
2011-02-19 16:28:57 -06:00
ppsc - > hwradiooff = true ;
2010-12-08 11:12:31 -06:00
}
ppsc - > rfoff_reason | = changesource ;
2011-02-19 16:28:57 -06:00
actionallowed = true ;
2010-12-08 11:12:31 -06:00
break ;
case ERFSLEEP :
ppsc - > rfoff_reason | = changesource ;
2011-02-19 16:28:57 -06:00
actionallowed = true ;
2010-12-08 11:12:31 -06:00
break ;
default :
RT_TRACE ( rtlpriv , COMP_ERR , DBG_EMERG ,
2012-01-04 19:40:41 -08:00
" switch case not processed \n " ) ;
2010-12-08 11:12:31 -06:00
break ;
}
2011-02-19 16:28:57 -06:00
if ( actionallowed )
2010-12-08 11:12:31 -06:00
rtlpriv - > cfg - > ops - > set_rf_power_state ( hw , state_toset ) ;
2011-02-19 16:28:57 -06:00
return actionallowed ;
2010-12-08 11:12:31 -06:00
}
EXPORT_SYMBOL ( rtl_ps_set_rf_state ) ;
static void _rtl_ps_inactive_ps ( struct ieee80211_hw * hw )
{
struct rtl_priv * rtlpriv = rtl_priv ( hw ) ;
struct rtl_hal * rtlhal = rtl_hal ( rtl_priv ( hw ) ) ;
struct rtl_ps_ctl * ppsc = rtl_psc ( rtl_priv ( hw ) ) ;
2011-02-19 16:28:57 -06:00
ppsc - > swrf_processing = true ;
2010-12-08 11:12:31 -06:00
2011-05-14 10:15:17 -05:00
if ( ppsc - > inactive_pwrstate = = ERFON & &
2011-04-25 12:53:14 -05:00
rtlhal - > interface = = INTF_PCI ) {
2010-12-08 11:12:31 -06:00
if ( ( ppsc - > reg_rfps_level & RT_RF_OFF_LEVL_ASPM ) & &
2011-04-25 12:53:14 -05:00
RT_IN_PS_LEVEL ( ppsc , RT_PS_LEVEL_ASPM ) & &
2010-12-08 11:12:31 -06:00
rtlhal - > interface = = INTF_PCI ) {
rtlpriv - > intf_ops - > disable_aspm ( hw ) ;
2011-04-25 12:53:14 -05:00
RT_CLEAR_PS_LEVEL ( ppsc , RT_PS_LEVEL_ASPM ) ;
2010-12-08 11:12:31 -06:00
}
}
2011-06-20 10:46:05 +09:00
rtl_ps_set_rf_state ( hw , ppsc - > inactive_pwrstate , RF_CHANGE_BY_IPS ) ;
2010-12-08 11:12:31 -06:00
if ( ppsc - > inactive_pwrstate = = ERFOFF & &
rtlhal - > interface = = INTF_PCI ) {
2011-04-25 12:53:14 -05:00
if ( ppsc - > reg_rfps_level & RT_RF_OFF_LEVL_ASPM & &
! RT_IN_PS_LEVEL ( ppsc , RT_PS_LEVEL_ASPM ) ) {
2010-12-08 11:12:31 -06:00
rtlpriv - > intf_ops - > enable_aspm ( hw ) ;
2011-04-25 12:53:14 -05:00
RT_SET_PS_LEVEL ( ppsc , RT_PS_LEVEL_ASPM ) ;
2010-12-08 11:12:31 -06:00
}
}
2011-02-19 16:28:57 -06:00
ppsc - > swrf_processing = false ;
2010-12-08 11:12:31 -06:00
}
void rtl_ips_nic_off_wq_callback ( void * data )
{
struct rtl_works * rtlworks =
container_of_dwork_rtl ( data , struct rtl_works , ips_nic_off_wq ) ;
struct ieee80211_hw * hw = rtlworks - > hw ;
struct rtl_priv * rtlpriv = rtl_priv ( hw ) ;
struct rtl_hal * rtlhal = rtl_hal ( rtl_priv ( hw ) ) ;
struct rtl_mac * mac = rtl_mac ( rtl_priv ( hw ) ) ;
struct rtl_ps_ctl * ppsc = rtl_psc ( rtl_priv ( hw ) ) ;
enum rf_pwrstate rtstate ;
if ( mac - > opmode ! = NL80211_IFTYPE_STATION ) {
RT_TRACE ( rtlpriv , COMP_ERR , DBG_WARNING ,
2012-01-04 19:40:41 -08:00
" not station return \n " ) ;
2010-12-08 11:12:31 -06:00
return ;
}
2011-04-25 12:53:14 -05:00
if ( mac - > link_state > MAC80211_NOLINK )
return ;
2010-12-08 11:12:31 -06:00
if ( is_hal_stop ( rtlhal ) )
return ;
if ( rtlpriv - > sec . being_setkey )
return ;
2011-02-19 16:28:57 -06:00
if ( ppsc - > inactiveps ) {
2010-12-08 11:12:31 -06:00
rtstate = ppsc - > rfpwr_state ;
/*
* Do not enter IPS in the following conditions :
* ( 1 ) RF is already OFF or Sleep
2011-02-19 16:28:57 -06:00
* ( 2 ) swrf_processing ( indicates the IPS is still under going )
2010-12-08 11:12:31 -06:00
* ( 3 ) Connectted ( only disconnected can trigger IPS )
* ( 4 ) IBSS ( send Beacon )
* ( 5 ) AP mode ( send Beacon )
* ( 6 ) monitor mode ( rcv packet )
*/
if ( rtstate = = ERFON & &
2011-02-19 16:28:57 -06:00
! ppsc - > swrf_processing & &
2010-12-08 11:12:31 -06:00
( mac - > link_state = = MAC80211_NOLINK ) & &
! mac - > act_scanning ) {
RT_TRACE ( rtlpriv , COMP_RF , DBG_TRACE ,
2012-01-04 19:40:41 -08:00
" IPSEnter(): Turn off RF \n " ) ;
2010-12-08 11:12:31 -06:00
ppsc - > inactive_pwrstate = ERFOFF ;
2011-02-19 16:28:57 -06:00
ppsc - > in_powersavemode = true ;
2010-12-08 11:12:31 -06:00
/*rtl_pci_reset_trx_ring(hw); */
_rtl_ps_inactive_ps ( hw ) ;
}
}
}
void rtl_ips_nic_off ( struct ieee80211_hw * hw )
{
struct rtl_priv * rtlpriv = rtl_priv ( hw ) ;
/*
* because when link with ap , mac80211 will ask us
* to disable nic quickly after scan before linking ,
* this will cause link failed , so we delay 100 ms here
*/
queue_delayed_work ( rtlpriv - > works . rtl_wq ,
& rtlpriv - > works . ips_nic_off_wq , MSECS ( 100 ) ) ;
}
void rtl_ips_nic_on ( struct ieee80211_hw * hw )
{
struct rtl_priv * rtlpriv = rtl_priv ( hw ) ;
2011-04-25 12:53:14 -05:00
struct rtl_mac * mac = rtl_mac ( rtl_priv ( hw ) ) ;
2010-12-08 11:12:31 -06:00
struct rtl_ps_ctl * ppsc = rtl_psc ( rtl_priv ( hw ) ) ;
enum rf_pwrstate rtstate ;
2011-12-16 21:17:16 -06:00
unsigned long flags ;
2010-12-08 11:12:31 -06:00
2011-04-25 12:53:14 -05:00
if ( mac - > opmode ! = NL80211_IFTYPE_STATION )
return ;
2011-12-16 21:17:16 -06:00
spin_lock_irqsave ( & rtlpriv - > locks . ips_lock , flags ) ;
2010-12-08 11:12:31 -06:00
2011-02-19 16:28:57 -06:00
if ( ppsc - > inactiveps ) {
2010-12-08 11:12:31 -06:00
rtstate = ppsc - > rfpwr_state ;
if ( rtstate ! = ERFON & &
2011-02-19 16:28:57 -06:00
! ppsc - > swrf_processing & &
2010-12-08 11:12:31 -06:00
ppsc - > rfoff_reason < = RF_CHANGE_BY_IPS ) {
ppsc - > inactive_pwrstate = ERFON ;
2011-02-19 16:28:57 -06:00
ppsc - > in_powersavemode = false ;
2010-12-08 11:12:31 -06:00
_rtl_ps_inactive_ps ( hw ) ;
}
}
2011-12-16 21:17:16 -06:00
spin_unlock_irqrestore ( & rtlpriv - > locks . ips_lock , flags ) ;
2010-12-08 11:12:31 -06:00
}
/*for FW LPS*/
/*
* Determine if we can set Fw into PS mode
* in current condition . Return TRUE if it
* can enter PS mode .
*/
static bool rtl_get_fwlps_doze ( struct ieee80211_hw * hw )
{
struct rtl_priv * rtlpriv = rtl_priv ( hw ) ;
struct rtl_mac * mac = rtl_mac ( rtl_priv ( hw ) ) ;
struct rtl_ps_ctl * ppsc = rtl_psc ( rtl_priv ( hw ) ) ;
u32 ps_timediff ;
ps_timediff = jiffies_to_msecs ( jiffies -
ppsc - > last_delaylps_stamp_jiffies ) ;
if ( ps_timediff < 2000 ) {
RT_TRACE ( rtlpriv , COMP_POWER , DBG_LOUD ,
2012-01-04 19:40:41 -08:00
" Delay enter Fw LPS for DHCP, ARP, or EAPOL exchanging state \n " ) ;
2010-12-08 11:12:31 -06:00
return false ;
}
if ( mac - > link_state ! = MAC80211_LINKED )
return false ;
if ( mac - > opmode = = NL80211_IFTYPE_ADHOC )
return false ;
return true ;
}
/* Change current and default preamble mode.*/
static void rtl_lps_set_psmode ( struct ieee80211_hw * hw , u8 rt_psmode )
{
struct rtl_priv * rtlpriv = rtl_priv ( hw ) ;
struct rtl_mac * mac = rtl_mac ( rtl_priv ( hw ) ) ;
struct rtl_ps_ctl * ppsc = rtl_psc ( rtl_priv ( hw ) ) ;
u8 rpwm_val , fw_pwrmode ;
if ( mac - > opmode = = NL80211_IFTYPE_ADHOC )
return ;
if ( mac - > link_state ! = MAC80211_LINKED )
return ;
if ( ppsc - > dot11_psmode = = rt_psmode )
return ;
/* Update power save mode configured. */
ppsc - > dot11_psmode = rt_psmode ;
/*
* < FW control LPS >
* 1. Enter PS mode
* Set RPWM to Fw to turn RF off and send H2C fw_pwrmode
* cmd to set Fw into PS mode .
* 2. Leave PS mode
* Send H2C fw_pwrmode cmd to Fw to set Fw into Active
* mode and set RPWM to turn RF on .
*/
2011-04-25 12:53:14 -05:00
if ( ( ppsc - > fwctrl_lps ) & & ppsc - > report_linked ) {
2011-02-19 16:28:57 -06:00
bool fw_current_inps ;
2010-12-08 11:12:31 -06:00
if ( ppsc - > dot11_psmode = = EACTIVE ) {
RT_TRACE ( rtlpriv , COMP_RF , DBG_DMESG ,
2012-01-04 19:40:41 -08:00
" FW LPS leave ps_mode:%x \n " ,
FW_PS_ACTIVE_MODE ) ;
2010-12-08 11:12:31 -06:00
rpwm_val = 0x0C ; /* RF on */
fw_pwrmode = FW_PS_ACTIVE_MODE ;
rtlpriv - > cfg - > ops - > set_hw_reg ( hw , HW_VAR_SET_RPWM ,
( u8 * ) ( & rpwm_val ) ) ;
rtlpriv - > cfg - > ops - > set_hw_reg ( hw ,
HW_VAR_H2C_FW_PWRMODE ,
( u8 * ) ( & fw_pwrmode ) ) ;
2011-02-19 16:28:57 -06:00
fw_current_inps = false ;
2010-12-08 11:12:31 -06:00
rtlpriv - > cfg - > ops - > set_hw_reg ( hw ,
HW_VAR_FW_PSMODE_STATUS ,
2011-02-19 16:28:57 -06:00
( u8 * ) ( & fw_current_inps ) ) ;
2010-12-08 11:12:31 -06:00
} else {
if ( rtl_get_fwlps_doze ( hw ) ) {
RT_TRACE ( rtlpriv , COMP_RF , DBG_DMESG ,
2012-01-04 19:40:41 -08:00
" FW LPS enter ps_mode:%x \n " ,
ppsc - > fwctrl_psmode ) ;
2010-12-08 11:12:31 -06:00
rpwm_val = 0x02 ; /* RF off */
2011-02-19 16:28:57 -06:00
fw_current_inps = true ;
2010-12-08 11:12:31 -06:00
rtlpriv - > cfg - > ops - > set_hw_reg ( hw ,
HW_VAR_FW_PSMODE_STATUS ,
2011-02-19 16:28:57 -06:00
( u8 * ) ( & fw_current_inps ) ) ;
2010-12-08 11:12:31 -06:00
rtlpriv - > cfg - > ops - > set_hw_reg ( hw ,
HW_VAR_H2C_FW_PWRMODE ,
( u8 * ) ( & ppsc - > fwctrl_psmode ) ) ;
rtlpriv - > cfg - > ops - > set_hw_reg ( hw ,
HW_VAR_SET_RPWM ,
( u8 * ) ( & rpwm_val ) ) ;
} else {
/* Reset the power save related parameters. */
ppsc - > dot11_psmode = EACTIVE ;
}
}
}
}
/*Enter the leisure power save mode.*/
void rtl_lps_enter ( struct ieee80211_hw * hw )
{
struct rtl_mac * mac = rtl_mac ( rtl_priv ( hw ) ) ;
struct rtl_ps_ctl * ppsc = rtl_psc ( rtl_priv ( hw ) ) ;
struct rtl_priv * rtlpriv = rtl_priv ( hw ) ;
2011-04-25 12:53:14 -05:00
if ( ! ppsc - > fwctrl_lps )
2010-12-08 11:12:31 -06:00
return ;
if ( rtlpriv - > sec . being_setkey )
return ;
2011-02-19 16:28:57 -06:00
if ( rtlpriv - > link_info . busytraffic )
2010-12-08 11:12:31 -06:00
return ;
/*sleep after linked 10s, to let DHCP and 4-way handshake ok enough!! */
if ( mac - > cnt_after_linked < 5 )
return ;
if ( mac - > opmode = = NL80211_IFTYPE_ADHOC )
return ;
if ( mac - > link_state ! = MAC80211_LINKED )
return ;
2011-12-12 12:43:24 +01:00
mutex_lock ( & rtlpriv - > locks . ps_mutex ) ;
2010-12-08 11:12:31 -06:00
2011-04-25 12:53:14 -05:00
/* Idle for a while if we connect to AP a while ago. */
if ( mac - > cnt_after_linked > = 2 ) {
if ( ppsc - > dot11_psmode = = EACTIVE ) {
RT_TRACE ( rtlpriv , COMP_POWER , DBG_LOUD ,
2012-01-04 19:40:41 -08:00
" Enter 802.11 power save mode... \n " ) ;
2010-12-08 11:12:31 -06:00
2011-04-25 12:53:14 -05:00
rtl_lps_set_psmode ( hw , EAUTOPS ) ;
2010-12-08 11:12:31 -06:00
}
}
2011-04-25 12:53:14 -05:00
2011-12-12 12:43:24 +01:00
mutex_unlock ( & rtlpriv - > locks . ps_mutex ) ;
2010-12-08 11:12:31 -06:00
}
/*Leave the leisure power save mode.*/
void rtl_lps_leave ( struct ieee80211_hw * hw )
{
struct rtl_priv * rtlpriv = rtl_priv ( hw ) ;
struct rtl_ps_ctl * ppsc = rtl_psc ( rtl_priv ( hw ) ) ;
struct rtl_hal * rtlhal = rtl_hal ( rtl_priv ( hw ) ) ;
2011-12-12 12:43:24 +01:00
mutex_lock ( & rtlpriv - > locks . ps_mutex ) ;
2010-12-08 11:12:31 -06:00
2011-04-25 12:53:14 -05:00
if ( ppsc - > fwctrl_lps ) {
2010-12-08 11:12:31 -06:00
if ( ppsc - > dot11_psmode ! = EACTIVE ) {
/*FIX ME */
rtlpriv - > cfg - > ops - > enable_interrupt ( hw ) ;
if ( ppsc - > reg_rfps_level & RT_RF_LPS_LEVEL_ASPM & &
2011-04-25 12:53:14 -05:00
RT_IN_PS_LEVEL ( ppsc , RT_PS_LEVEL_ASPM ) & &
2010-12-08 11:12:31 -06:00
rtlhal - > interface = = INTF_PCI ) {
rtlpriv - > intf_ops - > disable_aspm ( hw ) ;
2011-04-25 12:53:14 -05:00
RT_CLEAR_PS_LEVEL ( ppsc , RT_PS_LEVEL_ASPM ) ;
2010-12-08 11:12:31 -06:00
}
RT_TRACE ( rtlpriv , COMP_POWER , DBG_LOUD ,
2012-01-04 19:40:41 -08:00
" Busy Traffic,Leave 802.11 power save.. \n " ) ;
2010-12-08 11:12:31 -06:00
rtl_lps_set_psmode ( hw , EACTIVE ) ;
}
}
2011-12-12 12:43:24 +01:00
mutex_unlock ( & rtlpriv - > locks . ps_mutex ) ;
2010-12-08 11:12:31 -06:00
}
2011-04-25 12:53:14 -05:00
/* For sw LPS*/
void rtl_swlps_beacon ( struct ieee80211_hw * hw , void * data , unsigned int len )
{
struct rtl_priv * rtlpriv = rtl_priv ( hw ) ;
struct rtl_mac * mac = rtl_mac ( rtl_priv ( hw ) ) ;
struct ieee80211_hdr * hdr = ( void * ) data ;
struct ieee80211_tim_ie * tim_ie ;
u8 * tim ;
u8 tim_len ;
bool u_buffed ;
bool m_buffed ;
if ( mac - > opmode ! = NL80211_IFTYPE_STATION )
return ;
if ( ! rtlpriv - > psc . swctrl_lps )
return ;
if ( rtlpriv - > mac80211 . link_state ! = MAC80211_LINKED )
return ;
if ( ! rtlpriv - > psc . sw_ps_enabled )
return ;
if ( rtlpriv - > psc . fwctrl_lps )
return ;
if ( likely ( ! ( hw - > conf . flags & IEEE80211_CONF_PS ) ) )
return ;
/* check if this really is a beacon */
if ( ! ieee80211_is_beacon ( hdr - > frame_control ) )
return ;
/* min. beacon length + FCS_LEN */
if ( len < = 40 + FCS_LEN )
return ;
/* and only beacons from the associated BSSID, please */
if ( compare_ether_addr ( hdr - > addr3 , rtlpriv - > mac80211 . bssid ) )
return ;
rtlpriv - > psc . last_beacon = jiffies ;
tim = rtl_find_ie ( data , len - FCS_LEN , WLAN_EID_TIM ) ;
if ( ! tim )
return ;
if ( tim [ 1 ] < sizeof ( * tim_ie ) )
return ;
tim_len = tim [ 1 ] ;
tim_ie = ( struct ieee80211_tim_ie * ) & tim [ 2 ] ;
if ( ! WARN_ON_ONCE ( ! hw - > conf . ps_dtim_period ) )
rtlpriv - > psc . dtim_counter = tim_ie - > dtim_count ;
/* Check whenever the PHY can be turned off again. */
/* 1. What about buffered unicast traffic for our AID? */
u_buffed = ieee80211_check_tim ( tim_ie , tim_len ,
rtlpriv - > mac80211 . assoc_id ) ;
/* 2. Maybe the AP wants to send multicast/broadcast data? */
m_buffed = tim_ie - > bitmap_ctrl & 0x01 ;
rtlpriv - > psc . multi_buffered = m_buffed ;
/* unicast will process by mac80211 through
* set ~ IEEE80211_CONF_PS , So we just check
* multicast frames here */
if ( ! m_buffed ) {
/* back to low-power land. and delay is
* prevent null power save frame tx fail */
queue_delayed_work ( rtlpriv - > works . rtl_wq ,
& rtlpriv - > works . ps_work , MSECS ( 5 ) ) ;
} else {
2012-01-04 19:40:41 -08:00
RT_TRACE ( rtlpriv , COMP_POWER , DBG_DMESG ,
" u_bufferd: %x, m_buffered: %x \n " , u_buffed , m_buffed ) ;
2011-04-25 12:53:14 -05:00
}
}
void rtl_swlps_rf_awake ( struct ieee80211_hw * hw )
{
struct rtl_priv * rtlpriv = rtl_priv ( hw ) ;
struct rtl_ps_ctl * ppsc = rtl_psc ( rtl_priv ( hw ) ) ;
struct rtl_mac * mac = rtl_mac ( rtl_priv ( hw ) ) ;
if ( ! rtlpriv - > psc . swctrl_lps )
return ;
if ( mac - > link_state ! = MAC80211_LINKED )
return ;
if ( ppsc - > reg_rfps_level & RT_RF_LPS_LEVEL_ASPM & &
RT_IN_PS_LEVEL ( ppsc , RT_PS_LEVEL_ASPM ) ) {
rtlpriv - > intf_ops - > disable_aspm ( hw ) ;
RT_CLEAR_PS_LEVEL ( ppsc , RT_PS_LEVEL_ASPM ) ;
}
2011-12-12 12:43:24 +01:00
mutex_lock ( & rtlpriv - > locks . ps_mutex ) ;
2011-06-20 10:46:05 +09:00
rtl_ps_set_rf_state ( hw , ERFON , RF_CHANGE_BY_PS ) ;
2011-12-12 12:43:24 +01:00
mutex_unlock ( & rtlpriv - > locks . ps_mutex ) ;
2011-04-25 12:53:14 -05:00
}
void rtl_swlps_rfon_wq_callback ( void * data )
{
struct rtl_works * rtlworks =
container_of_dwork_rtl ( data , struct rtl_works , ps_rfon_wq ) ;
struct ieee80211_hw * hw = rtlworks - > hw ;
rtl_swlps_rf_awake ( hw ) ;
}
void rtl_swlps_rf_sleep ( struct ieee80211_hw * hw )
{
struct rtl_priv * rtlpriv = rtl_priv ( hw ) ;
struct rtl_mac * mac = rtl_mac ( rtl_priv ( hw ) ) ;
struct rtl_ps_ctl * ppsc = rtl_psc ( rtl_priv ( hw ) ) ;
u8 sleep_intv ;
if ( ! rtlpriv - > psc . sw_ps_enabled )
return ;
if ( ( rtlpriv - > sec . being_setkey ) | |
( mac - > opmode = = NL80211_IFTYPE_ADHOC ) )
return ;
/*sleep after linked 10s, to let DHCP and 4-way handshake ok enough!! */
if ( ( mac - > link_state ! = MAC80211_LINKED ) | | ( mac - > cnt_after_linked < 5 ) )
return ;
if ( rtlpriv - > link_info . busytraffic )
return ;
2011-12-12 12:43:24 +01:00
mutex_lock ( & rtlpriv - > locks . ps_mutex ) ;
2011-06-20 10:46:05 +09:00
rtl_ps_set_rf_state ( hw , ERFSLEEP , RF_CHANGE_BY_PS ) ;
2011-12-12 12:43:24 +01:00
mutex_unlock ( & rtlpriv - > locks . ps_mutex ) ;
2011-04-25 12:53:14 -05:00
if ( ppsc - > reg_rfps_level & RT_RF_OFF_LEVL_ASPM & &
! RT_IN_PS_LEVEL ( ppsc , RT_PS_LEVEL_ASPM ) ) {
rtlpriv - > intf_ops - > enable_aspm ( hw ) ;
RT_SET_PS_LEVEL ( ppsc , RT_PS_LEVEL_ASPM ) ;
}
/* here is power save alg, when this beacon is DTIM
* we will set sleep time to dtim_period * n ;
* when this beacon is not DTIM , we will set sleep
* time to sleep_intv = rtlpriv - > psc . dtim_counter or
* MAX_SW_LPS_SLEEP_INTV ( default set to 5 ) */
if ( rtlpriv - > psc . dtim_counter = = 0 ) {
if ( hw - > conf . ps_dtim_period = = 1 )
sleep_intv = hw - > conf . ps_dtim_period * 2 ;
else
sleep_intv = hw - > conf . ps_dtim_period ;
} else {
sleep_intv = rtlpriv - > psc . dtim_counter ;
}
if ( sleep_intv > MAX_SW_LPS_SLEEP_INTV )
sleep_intv = MAX_SW_LPS_SLEEP_INTV ;
/* this print should always be dtim_conter = 0 &
* sleep = dtim_period , that meaons , we should
* awake before every dtim */
RT_TRACE ( rtlpriv , COMP_POWER , DBG_DMESG ,
2012-01-04 19:40:41 -08:00
" dtim_counter:%x will sleep :%d beacon_intv \n " ,
rtlpriv - > psc . dtim_counter , sleep_intv ) ;
2011-04-25 12:53:14 -05:00
/* we tested that 40ms is enough for sw & hw sw delay */
queue_delayed_work ( rtlpriv - > works . rtl_wq , & rtlpriv - > works . ps_rfon_wq ,
MSECS ( sleep_intv * mac - > vif - > bss_conf . beacon_int - 40 ) ) ;
}
void rtl_swlps_wq_callback ( void * data )
{
struct rtl_works * rtlworks = container_of_dwork_rtl ( data ,
struct rtl_works ,
ps_work ) ;
struct ieee80211_hw * hw = rtlworks - > hw ;
struct rtl_priv * rtlpriv = rtl_priv ( hw ) ;
bool ps = false ;
ps = ( hw - > conf . flags & IEEE80211_CONF_PS ) ;
/* we can sleep after ps null send ok */
if ( rtlpriv - > psc . state_inap ) {
rtl_swlps_rf_sleep ( hw ) ;
if ( rtlpriv - > psc . state & & ! ps ) {
rtlpriv - > psc . sleep_ms = jiffies_to_msecs ( jiffies -
rtlpriv - > psc . last_action ) ;
}
if ( ps )
rtlpriv - > psc . last_slept = jiffies ;
rtlpriv - > psc . last_action = jiffies ;
rtlpriv - > psc . state = ps ;
}
}