2008-12-20 12:53:29 +03:00
/*
2009-11-08 18:39:55 +03:00
Copyright ( C ) 2004 - 2009 Ivo van Doorn < IvDoorn @ gmail . com >
2008-12-20 12:53:29 +03:00
< http : //rt2x00.serialmonkey.com>
This program is free software ; you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation ; either version 2 of the License , or
( at your option ) any later version .
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
2013-12-06 15:32:11 +04:00
along with this program ; if not , see < http : //www.gnu.org/licenses/>.
2008-12-20 12:53:29 +03:00
*/
/*
Module : rt2x00lib
Abstract : rt2x00 generic link tuning routines .
*/
# include <linux/kernel.h>
# include <linux/module.h>
# include "rt2x00.h"
# include "rt2x00lib.h"
2008-12-20 12:54:54 +03:00
/*
* When we lack RSSI information return something less then - 80 to
* tell the driver to tune the device to maximum sensitivity .
*/
# define DEFAULT_RSSI -128
2015-08-19 10:46:21 +03:00
static inline int rt2x00link_get_avg_rssi ( struct ewma_rssi * ewma )
2013-10-09 13:00:38 +04:00
{
unsigned long avg ;
2015-08-19 10:46:21 +03:00
avg = ewma_rssi_read ( ewma ) ;
2013-10-09 13:00:38 +04:00
if ( avg )
return - avg ;
return DEFAULT_RSSI ;
}
2008-12-20 12:54:54 +03:00
2008-12-20 12:53:29 +03:00
static int rt2x00link_antenna_get_link_rssi ( struct rt2x00_dev * rt2x00dev )
{
struct link_ant * ant = & rt2x00dev - > link . ant ;
2013-10-09 13:00:38 +04:00
if ( rt2x00dev - > link . qual . rx_success )
return rt2x00link_get_avg_rssi ( & ant - > rssi_ant ) ;
2008-12-20 12:53:29 +03:00
return DEFAULT_RSSI ;
}
2009-08-09 01:54:24 +04:00
static int rt2x00link_antenna_get_rssi_history ( struct rt2x00_dev * rt2x00dev )
2008-12-20 12:53:29 +03:00
{
struct link_ant * ant = & rt2x00dev - > link . ant ;
2009-08-09 01:54:24 +04:00
if ( ant - > rssi_history )
return ant - > rssi_history ;
2008-12-20 12:53:29 +03:00
return DEFAULT_RSSI ;
}
static void rt2x00link_antenna_update_rssi_history ( struct rt2x00_dev * rt2x00dev ,
int rssi )
{
struct link_ant * ant = & rt2x00dev - > link . ant ;
2009-08-09 01:54:24 +04:00
ant - > rssi_history = rssi ;
2008-12-20 12:53:29 +03:00
}
static void rt2x00link_antenna_reset ( struct rt2x00_dev * rt2x00dev )
{
2015-08-19 10:46:21 +03:00
ewma_rssi_init ( & rt2x00dev - > link . ant . rssi_ant ) ;
2008-12-20 12:53:29 +03:00
}
static void rt2x00lib_antenna_diversity_sample ( struct rt2x00_dev * rt2x00dev )
{
struct link_ant * ant = & rt2x00dev - > link . ant ;
struct antenna_setup new_ant ;
2009-08-09 01:54:24 +04:00
int other_antenna ;
int sample_current = rt2x00link_antenna_get_link_rssi ( rt2x00dev ) ;
int sample_other = rt2x00link_antenna_get_rssi_history ( rt2x00dev ) ;
2008-12-20 12:53:29 +03:00
memcpy ( & new_ant , & ant - > active , sizeof ( new_ant ) ) ;
/*
* We are done sampling . Now we should evaluate the results .
*/
ant - > flags & = ~ ANTENNA_MODE_SAMPLE ;
/*
* During the last period we have sampled the RSSI
2009-07-17 23:39:19 +04:00
* from both antennas . It now is time to determine
2008-12-20 12:53:29 +03:00
* which antenna demonstrated the best performance .
* When we are already on the antenna with the best
2009-08-09 01:54:24 +04:00
* performance , just create a good starting point
* for the history and we are done .
2008-12-20 12:53:29 +03:00
*/
2009-08-09 01:54:24 +04:00
if ( sample_current > = sample_other ) {
rt2x00link_antenna_update_rssi_history ( rt2x00dev ,
sample_current ) ;
2008-12-20 12:53:29 +03:00
return ;
2009-08-09 01:54:24 +04:00
}
other_antenna = ( ant - > active . rx = = ANTENNA_A ) ? ANTENNA_B : ANTENNA_A ;
2008-12-20 12:53:29 +03:00
if ( ant - > flags & ANTENNA_RX_DIVERSITY )
2009-08-09 01:54:24 +04:00
new_ant . rx = other_antenna ;
2008-12-20 12:53:29 +03:00
if ( ant - > flags & ANTENNA_TX_DIVERSITY )
2009-08-09 01:54:24 +04:00
new_ant . tx = other_antenna ;
2008-12-20 12:53:29 +03:00
2009-07-18 22:21:52 +04:00
rt2x00lib_config_antenna ( rt2x00dev , new_ant ) ;
2008-12-20 12:53:29 +03:00
}
static void rt2x00lib_antenna_diversity_eval ( struct rt2x00_dev * rt2x00dev )
{
struct link_ant * ant = & rt2x00dev - > link . ant ;
struct antenna_setup new_ant ;
int rssi_curr ;
int rssi_old ;
memcpy ( & new_ant , & ant - > active , sizeof ( new_ant ) ) ;
/*
* Get current RSSI value along with the historical value ,
* after that update the history with the current value .
*/
rssi_curr = rt2x00link_antenna_get_link_rssi ( rt2x00dev ) ;
2009-08-09 01:54:24 +04:00
rssi_old = rt2x00link_antenna_get_rssi_history ( rt2x00dev ) ;
rt2x00link_antenna_update_rssi_history ( rt2x00dev , rssi_curr ) ;
2008-12-20 12:53:29 +03:00
/*
* Legacy driver indicates that we should swap antenna ' s
* when the difference in RSSI is greater that 5. This
* also should be done when the RSSI was actually better
* then the previous sample .
* When the difference exceeds the threshold we should
* sample the rssi from the other antenna to make a valid
* comparison between the 2 antennas .
*/
if ( abs ( rssi_curr - rssi_old ) < 5 )
return ;
ant - > flags | = ANTENNA_MODE_SAMPLE ;
if ( ant - > flags & ANTENNA_RX_DIVERSITY )
new_ant . rx = ( new_ant . rx = = ANTENNA_A ) ? ANTENNA_B : ANTENNA_A ;
if ( ant - > flags & ANTENNA_TX_DIVERSITY )
new_ant . tx = ( new_ant . tx = = ANTENNA_A ) ? ANTENNA_B : ANTENNA_A ;
2009-07-18 22:21:52 +04:00
rt2x00lib_config_antenna ( rt2x00dev , new_ant ) ;
2008-12-20 12:53:29 +03:00
}
2009-08-09 01:54:24 +04:00
static bool rt2x00lib_antenna_diversity ( struct rt2x00_dev * rt2x00dev )
2008-12-20 12:53:29 +03:00
{
struct link_ant * ant = & rt2x00dev - > link . ant ;
/*
* Determine if software diversity is enabled for
* either the TX or RX antenna ( or both ) .
*/
if ( ! ( ant - > flags & ANTENNA_RX_DIVERSITY ) & &
! ( ant - > flags & ANTENNA_TX_DIVERSITY ) ) {
ant - > flags = 0 ;
2009-08-09 01:54:24 +04:00
return true ;
2008-12-20 12:53:29 +03:00
}
/*
* If we have only sampled the data over the last period
* we should now harvest the data . Otherwise just evaluate
* the data . The latter should only be performed once
* every 2 seconds .
*/
2009-08-09 01:54:24 +04:00
if ( ant - > flags & ANTENNA_MODE_SAMPLE ) {
2008-12-20 12:53:29 +03:00
rt2x00lib_antenna_diversity_sample ( rt2x00dev ) ;
2009-08-09 01:54:24 +04:00
return true ;
} else if ( rt2x00dev - > link . count & 1 ) {
2008-12-20 12:53:29 +03:00
rt2x00lib_antenna_diversity_eval ( rt2x00dev ) ;
2009-08-09 01:54:24 +04:00
return true ;
}
return false ;
2008-12-20 12:53:29 +03:00
}
void rt2x00link_update_stats ( struct rt2x00_dev * rt2x00dev ,
struct sk_buff * skb ,
struct rxdone_entry_desc * rxdesc )
{
2008-12-20 12:54:54 +03:00
struct link * link = & rt2x00dev - > link ;
2008-12-20 12:53:29 +03:00
struct link_qual * qual = & rt2x00dev - > link . qual ;
struct link_ant * ant = & rt2x00dev - > link . ant ;
struct ieee80211_hdr * hdr = ( struct ieee80211_hdr * ) skb - > data ;
2010-10-09 15:33:16 +04:00
/*
* No need to update the stats for ! = STA interfaces
*/
if ( ! rt2x00dev - > intf_sta_count )
return ;
2008-12-20 12:53:29 +03:00
/*
* Frame was received successfully since non - succesfull
* frames would have been dropped by the hardware .
*/
qual - > rx_success + + ;
/*
* We are only interested in quality statistics from
* beacons which came from the BSS which we are
* associated with .
*/
if ( ! ieee80211_is_beacon ( hdr - > frame_control ) | |
! ( rxdesc - > dev_flags & RXDONE_MY_BSS ) )
return ;
/*
* Update global RSSI
*/
2015-08-19 10:46:21 +03:00
ewma_rssi_add ( & link - > avg_rssi , - rxdesc - > rssi ) ;
2008-12-20 12:53:29 +03:00
/*
* Update antenna RSSI
*/
2015-08-19 10:46:21 +03:00
ewma_rssi_add ( & ant - > rssi_ant , - rxdesc - > rssi ) ;
2008-12-20 12:53:29 +03:00
}
void rt2x00link_start_tuner ( struct rt2x00_dev * rt2x00dev )
{
2008-12-20 12:54:54 +03:00
struct link * link = & rt2x00dev - > link ;
2008-12-20 12:53:29 +03:00
/*
2016-12-19 13:52:54 +03:00
* Single monitor mode interfaces should never have
* work with link tuners .
2008-12-20 12:53:29 +03:00
*/
2016-12-19 13:52:54 +03:00
if ( ! rt2x00dev - > intf_ap_count & & ! rt2x00dev - > intf_sta_count )
2008-12-20 12:53:29 +03:00
return ;
2016-12-19 13:52:54 +03:00
/*
2010-07-11 14:24:47 +04:00
* While scanning , link tuning is disabled . By default
* the most sensitive settings will be used to make sure
2011-03-31 05:57:33 +04:00
* that all beacons and probe responses will be received
2010-07-11 14:24:47 +04:00
* during the scan .
*/
if ( test_bit ( DEVICE_STATE_SCANNING , & rt2x00dev - > flags ) )
return ;
2008-12-20 12:53:29 +03:00
rt2x00link_reset_tuner ( rt2x00dev , false ) ;
2009-11-05 22:22:03 +03:00
if ( test_bit ( DEVICE_STATE_PRESENT , & rt2x00dev - > flags ) )
ieee80211_queue_delayed_work ( rt2x00dev - > hw ,
& link - > work , LINK_TUNE_INTERVAL ) ;
2008-12-20 12:53:29 +03:00
}
void rt2x00link_stop_tuner ( struct rt2x00_dev * rt2x00dev )
{
cancel_delayed_work_sync ( & rt2x00dev - > link . work ) ;
}
void rt2x00link_reset_tuner ( struct rt2x00_dev * rt2x00dev , bool antenna )
{
2008-12-20 12:54:54 +03:00
struct link_qual * qual = & rt2x00dev - > link . qual ;
2010-07-11 14:25:17 +04:00
u8 vgc_level = qual - > vgc_level_reg ;
2008-12-20 12:54:54 +03:00
2008-12-20 12:53:29 +03:00
if ( ! test_bit ( DEVICE_STATE_ENABLED_RADIO , & rt2x00dev - > flags ) )
return ;
/*
* Reset link information .
* Both the currently active vgc level as well as
* the link tuner counter should be reset . Resetting
* the counter is important for devices where the
* device should only perform link tuning during the
* first minute after being enabled .
*/
rt2x00dev - > link . count = 0 ;
2008-12-20 12:54:54 +03:00
memset ( qual , 0 , sizeof ( * qual ) ) ;
2015-08-19 10:46:21 +03:00
ewma_rssi_init ( & rt2x00dev - > link . avg_rssi ) ;
2008-12-20 12:53:29 +03:00
2010-07-11 14:25:17 +04:00
/*
* Restore the VGC level as stored in the registers ,
* the driver can use this to determine if the register
* must be updated during reset or not .
*/
qual - > vgc_level_reg = vgc_level ;
2008-12-20 12:53:29 +03:00
/*
* Reset the link tuner .
*/
2008-12-20 12:54:54 +03:00
rt2x00dev - > ops - > lib - > reset_tuner ( rt2x00dev , qual ) ;
2008-12-20 12:53:29 +03:00
if ( antenna )
rt2x00link_antenna_reset ( rt2x00dev ) ;
}
2009-03-28 22:51:41 +03:00
static void rt2x00link_reset_qual ( struct rt2x00_dev * rt2x00dev )
2009-03-01 19:42:00 +03:00
{
struct link_qual * qual = & rt2x00dev - > link . qual ;
qual - > rx_success = 0 ;
qual - > rx_failed = 0 ;
qual - > tx_success = 0 ;
qual - > tx_failed = 0 ;
}
2016-12-19 13:52:54 +03:00
static void rt2x00link_tuner_sta ( struct rt2x00_dev * rt2x00dev , struct link * link )
2008-12-20 12:53:29 +03:00
{
struct link_qual * qual = & rt2x00dev - > link . qual ;
/*
* Update statistics .
*/
rt2x00dev - > ops - > lib - > link_stats ( rt2x00dev , qual ) ;
rt2x00dev - > low_level_stats . dot11FCSErrorCount + = qual - > rx_failed ;
2008-12-20 12:54:54 +03:00
/*
* Update quality RSSI for link tuning ,
* when we have received some frames and we managed to
* collect the RSSI data we could use this . Otherwise we
* must fallback to the default RSSI value .
*/
2013-10-09 13:00:38 +04:00
if ( ! qual - > rx_success )
2008-12-20 12:54:54 +03:00
qual - > rssi = DEFAULT_RSSI ;
else
2013-10-09 13:00:38 +04:00
qual - > rssi = rt2x00link_get_avg_rssi ( & link - > avg_rssi ) ;
2008-12-20 12:54:54 +03:00
2008-12-20 12:53:29 +03:00
/*
2010-07-11 14:24:22 +04:00
* Check if link tuning is supported by the hardware , some hardware
* do not support link tuning at all , while other devices can disable
* the feature from the EEPROM .
2008-12-20 12:53:29 +03:00
*/
2013-10-11 15:18:41 +04:00
if ( rt2x00_has_cap_link_tuning ( rt2x00dev ) )
2008-12-20 12:54:54 +03:00
rt2x00dev - > ops - > lib - > link_tuner ( rt2x00dev , qual , link - > count ) ;
2008-12-20 12:53:29 +03:00
/*
* Send a signal to the led to update the led signal strength .
*/
2009-08-09 01:54:51 +04:00
rt2x00leds_led_quality ( rt2x00dev , qual - > rssi ) ;
2008-12-20 12:53:29 +03:00
/*
2009-08-09 01:54:24 +04:00
* Evaluate antenna setup , make this the last step when
* rt2x00lib_antenna_diversity made changes the quality
* statistics will be reset .
2009-03-01 19:42:00 +03:00
*/
2009-08-09 01:54:24 +04:00
if ( rt2x00lib_antenna_diversity ( rt2x00dev ) )
rt2x00link_reset_qual ( rt2x00dev ) ;
2016-12-19 13:52:54 +03:00
}
static void rt2x00link_tuner ( struct work_struct * work )
{
struct rt2x00_dev * rt2x00dev =
container_of ( work , struct rt2x00_dev , link . work . work ) ;
struct link * link = & rt2x00dev - > link ;
/*
* When the radio is shutting down we should
* immediately cease all link tuning .
*/
if ( ! test_bit ( DEVICE_STATE_ENABLED_RADIO , & rt2x00dev - > flags ) | |
test_bit ( DEVICE_STATE_SCANNING , & rt2x00dev - > flags ) )
return ;
2016-12-19 13:52:57 +03:00
/* Do not race with rt2x00mac_config(). */
mutex_lock ( & rt2x00dev - > conf_mutex ) ;
2016-12-19 13:52:54 +03:00
if ( rt2x00dev - > intf_sta_count )
rt2x00link_tuner_sta ( rt2x00dev , link ) ;
if ( rt2x00dev - > ops - > lib - > gain_calibration & &
( link - > count % ( AGC_SECONDS / LINK_TUNE_SECONDS ) ) = = 0 )
rt2x00dev - > ops - > lib - > gain_calibration ( rt2x00dev ) ;
if ( rt2x00dev - > ops - > lib - > vco_calibration & &
rt2x00_has_cap_vco_recalibration ( rt2x00dev ) & &
( link - > count % ( VCO_SECONDS / LINK_TUNE_SECONDS ) ) = = 0 )
rt2x00dev - > ops - > lib - > vco_calibration ( rt2x00dev ) ;
2009-03-01 19:42:00 +03:00
2016-12-19 13:52:57 +03:00
mutex_unlock ( & rt2x00dev - > conf_mutex ) ;
2008-12-20 12:53:29 +03:00
/*
* Increase tuner counter , and reschedule the next link tuner run .
*/
2008-12-20 12:54:54 +03:00
link - > count + + ;
2009-11-05 22:22:03 +03:00
if ( test_bit ( DEVICE_STATE_PRESENT , & rt2x00dev - > flags ) )
ieee80211_queue_delayed_work ( rt2x00dev - > hw ,
& link - > work , LINK_TUNE_INTERVAL ) ;
2008-12-20 12:53:29 +03:00
}
2010-07-11 14:25:46 +04:00
void rt2x00link_start_watchdog ( struct rt2x00_dev * rt2x00dev )
{
struct link * link = & rt2x00dev - > link ;
2011-03-28 15:30:59 +04:00
if ( test_bit ( DEVICE_STATE_PRESENT , & rt2x00dev - > flags ) & &
rt2x00dev - > ops - > lib - > watchdog )
ieee80211_queue_delayed_work ( rt2x00dev - > hw ,
& link - > watchdog_work ,
WATCHDOG_INTERVAL ) ;
2010-07-11 14:25:46 +04:00
}
void rt2x00link_stop_watchdog ( struct rt2x00_dev * rt2x00dev )
{
cancel_delayed_work_sync ( & rt2x00dev - > link . watchdog_work ) ;
}
static void rt2x00link_watchdog ( struct work_struct * work )
{
struct rt2x00_dev * rt2x00dev =
container_of ( work , struct rt2x00_dev , link . watchdog_work . work ) ;
struct link * link = & rt2x00dev - > link ;
/*
* When the radio is shutting down we should
* immediately cease the watchdog monitoring .
*/
if ( ! test_bit ( DEVICE_STATE_ENABLED_RADIO , & rt2x00dev - > flags ) )
return ;
rt2x00dev - > ops - > lib - > watchdog ( rt2x00dev ) ;
if ( test_bit ( DEVICE_STATE_PRESENT , & rt2x00dev - > flags ) )
2011-01-30 15:24:05 +03:00
ieee80211_queue_delayed_work ( rt2x00dev - > hw ,
& link - > watchdog_work ,
WATCHDOG_INTERVAL ) ;
2010-07-11 14:25:46 +04:00
}
2008-12-20 12:53:29 +03:00
void rt2x00link_register ( struct rt2x00_dev * rt2x00dev )
{
2010-07-11 14:25:46 +04:00
INIT_DELAYED_WORK ( & rt2x00dev - > link . watchdog_work , rt2x00link_watchdog ) ;
2008-12-20 12:53:29 +03:00
INIT_DELAYED_WORK ( & rt2x00dev - > link . work , rt2x00link_tuner ) ;
}