2011-12-14 20:16:34 -08:00
/*
* Copyright ( c ) 2008 - 2011 Atheros Communications Inc .
* Copyright ( c ) 2011 Neratec Solutions AG
*
* Permission to use , copy , modify , and / or distribute this software for any
* purpose with or without fee is hereby granted , provided that the above
* copyright notice and this permission notice appear in all copies .
*
* THE SOFTWARE IS PROVIDED " AS IS " AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS . IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL , DIRECT , INDIRECT , OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE , DATA OR PROFITS , WHETHER IN AN
* ACTION OF CONTRACT , NEGLIGENCE OR OTHER TORTIOUS ACTION , ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE .
*/
# include "hw.h"
# include "hw-ops.h"
# include "ath9k.h"
# include "dfs.h"
# include "dfs_debug.h"
/* internal struct to pass radar data */
struct ath_radar_data {
u8 pulse_bw_info ;
u8 rssi ;
u8 ext_rssi ;
u8 pulse_length_ext ;
u8 pulse_length_pri ;
} ;
2015-06-16 12:52:16 +02:00
/**** begin: CHIRP ************************************************************/
/* min and max gradients for defined FCC chirping pulses, given by
* - 20 MHz chirp width over a pulse width of 50u s
* - 5 MHz chirp width over a pulse width of 100u s
*/
static const int BIN_DELTA_MIN = 1 ;
static const int BIN_DELTA_MAX = 10 ;
/* we need at least 3 deltas / 4 samples for a reliable chirp detection */
# define NUM_DIFFS 3
static const int FFT_NUM_SAMPLES = ( NUM_DIFFS + 1 ) ;
/* Threshold for difference of delta peaks */
static const int MAX_DIFF = 2 ;
/* width range to be checked for chirping */
static const int MIN_CHIRP_PULSE_WIDTH = 20 ;
static const int MAX_CHIRP_PULSE_WIDTH = 110 ;
struct ath9k_dfs_fft_20 {
u8 bin [ 28 ] ;
u8 lower_bins [ 3 ] ;
} __packed ;
struct ath9k_dfs_fft_40 {
u8 bin [ 64 ] ;
u8 lower_bins [ 3 ] ;
u8 upper_bins [ 3 ] ;
} __packed ;
static inline int fft_max_index ( u8 * bins )
{
return ( bins [ 2 ] & 0xfc ) > > 2 ;
}
static inline int fft_max_magnitude ( u8 * bins )
{
return ( bins [ 0 ] & 0xc0 ) > > 6 | bins [ 1 ] < < 2 | ( bins [ 2 ] & 0x03 ) < < 10 ;
}
static inline u8 fft_bitmap_weight ( u8 * bins )
{
return bins [ 0 ] & 0x3f ;
}
static int ath9k_get_max_index_ht40 ( struct ath9k_dfs_fft_40 * fft ,
bool is_ctl , bool is_ext )
{
const int DFS_UPPER_BIN_OFFSET = 64 ;
/* if detected radar on both channels, select the significant one */
if ( is_ctl & & is_ext ) {
/* first check wether channels have 'strong' bins */
is_ctl = fft_bitmap_weight ( fft - > lower_bins ) ! = 0 ;
is_ext = fft_bitmap_weight ( fft - > upper_bins ) ! = 0 ;
/* if still unclear, take higher magnitude */
if ( is_ctl & & is_ext ) {
int mag_lower = fft_max_magnitude ( fft - > lower_bins ) ;
int mag_upper = fft_max_magnitude ( fft - > upper_bins ) ;
if ( mag_upper > mag_lower )
is_ctl = false ;
else
is_ext = false ;
}
}
if ( is_ctl )
return fft_max_index ( fft - > lower_bins ) ;
return fft_max_index ( fft - > upper_bins ) + DFS_UPPER_BIN_OFFSET ;
}
static bool ath9k_check_chirping ( struct ath_softc * sc , u8 * data ,
int datalen , bool is_ctl , bool is_ext )
{
int i ;
int max_bin [ FFT_NUM_SAMPLES ] ;
struct ath_hw * ah = sc - > sc_ah ;
struct ath_common * common = ath9k_hw_common ( ah ) ;
int prev_delta ;
if ( IS_CHAN_HT40 ( ah - > curchan ) ) {
struct ath9k_dfs_fft_40 * fft = ( struct ath9k_dfs_fft_40 * ) data ;
int num_fft_packets = datalen / sizeof ( * fft ) ;
if ( num_fft_packets = = 0 )
return false ;
ath_dbg ( common , DFS , " HT40: datalen=%d, num_fft_packets=%d \n " ,
datalen , num_fft_packets ) ;
if ( num_fft_packets < ( FFT_NUM_SAMPLES ) ) {
ath_dbg ( common , DFS , " not enough packets for chirp \n " ) ;
return false ;
}
/* HW sometimes adds 2 garbage bytes in front of FFT samples */
if ( ( datalen % sizeof ( * fft ) ) = = 2 ) {
fft = ( struct ath9k_dfs_fft_40 * ) ( data + 2 ) ;
ath_dbg ( common , DFS , " fixing datalen by 2 \n " ) ;
}
if ( IS_CHAN_HT40MINUS ( ah - > curchan ) ) {
int temp = is_ctl ;
is_ctl = is_ext ;
is_ext = temp ;
}
for ( i = 0 ; i < FFT_NUM_SAMPLES ; i + + )
max_bin [ i ] = ath9k_get_max_index_ht40 ( fft + i , is_ctl ,
is_ext ) ;
} else {
struct ath9k_dfs_fft_20 * fft = ( struct ath9k_dfs_fft_20 * ) data ;
int num_fft_packets = datalen / sizeof ( * fft ) ;
if ( num_fft_packets = = 0 )
return false ;
ath_dbg ( common , DFS , " HT20: datalen=%d, num_fft_packets=%d \n " ,
datalen , num_fft_packets ) ;
if ( num_fft_packets < ( FFT_NUM_SAMPLES ) ) {
ath_dbg ( common , DFS , " not enough packets for chirp \n " ) ;
return false ;
}
/* in ht20, this is a 6-bit signed number => shift it to 0 */
for ( i = 0 ; i < FFT_NUM_SAMPLES ; i + + )
max_bin [ i ] = fft_max_index ( fft [ i ] . lower_bins ) ^ 0x20 ;
}
ath_dbg ( common , DFS , " bin_max = [%d, %d, %d, %d] \n " ,
max_bin [ 0 ] , max_bin [ 1 ] , max_bin [ 2 ] , max_bin [ 3 ] ) ;
/* Check for chirp attributes within specs
* a ) delta of adjacent max_bins is within range
* b ) delta of adjacent deltas are within tolerance
*/
prev_delta = 0 ;
for ( i = 0 ; i < NUM_DIFFS ; i + + ) {
int ddelta = - 1 ;
int delta = max_bin [ i + 1 ] - max_bin [ i ] ;
/* ensure gradient is within valid range */
if ( abs ( delta ) < BIN_DELTA_MIN | | abs ( delta ) > BIN_DELTA_MAX ) {
ath_dbg ( common , DFS , " CHIRP: invalid delta %d "
" in sample %d \n " , delta , i ) ;
return false ;
}
if ( i = = 0 )
goto done ;
ddelta = delta - prev_delta ;
if ( abs ( ddelta ) > MAX_DIFF ) {
ath_dbg ( common , DFS , " CHIRP: ddelta %d too high \n " ,
ddelta ) ;
return false ;
}
done :
ath_dbg ( common , DFS , " CHIRP - %d: delta=%d, ddelta=%d \n " ,
i , delta , ddelta ) ;
prev_delta = delta ;
}
return true ;
}
/**** end: CHIRP **************************************************************/
2011-12-14 20:16:34 -08:00
/* convert pulse duration to usecs, considering clock mode */
static u32 dur_to_usecs ( struct ath_hw * ah , u32 dur )
{
const u32 AR93X_NSECS_PER_DUR = 800 ;
const u32 AR93X_NSECS_PER_DUR_FAST = ( 8000 / 11 ) ;
u32 nsecs ;
if ( IS_CHAN_A_FAST_CLOCK ( ah , ah - > curchan ) )
nsecs = dur * AR93X_NSECS_PER_DUR_FAST ;
else
nsecs = dur * AR93X_NSECS_PER_DUR ;
return ( nsecs + 500 ) / 1000 ;
}
# define PRI_CH_RADAR_FOUND 0x01
# define EXT_CH_RADAR_FOUND 0x02
static bool
ath9k_postprocess_radar_event ( struct ath_softc * sc ,
2012-04-03 17:15:51 +02:00
struct ath_radar_data * ard ,
struct pulse_event * pe )
2011-12-14 20:16:34 -08:00
{
u8 rssi ;
u16 dur ;
/*
* Only the last 2 bits of the BW info are relevant , they indicate
* which channel the radar was detected in .
*/
2012-04-03 17:15:51 +02:00
ard - > pulse_bw_info & = 0x03 ;
2011-12-14 20:16:34 -08:00
2012-04-03 17:15:51 +02:00
switch ( ard - > pulse_bw_info ) {
2011-12-14 20:16:34 -08:00
case PRI_CH_RADAR_FOUND :
/* radar in ctrl channel */
2012-04-03 17:15:51 +02:00
dur = ard - > pulse_length_pri ;
2011-12-14 20:16:34 -08:00
DFS_STAT_INC ( sc , pri_phy_errors ) ;
/*
* cannot use ctrl channel RSSI
* if extension channel is stronger
*/
2012-04-03 17:15:51 +02:00
rssi = ( ard - > ext_rssi > = ( ard - > rssi + 3 ) ) ? 0 : ard - > rssi ;
2011-12-14 20:16:34 -08:00
break ;
case EXT_CH_RADAR_FOUND :
/* radar in extension channel */
2012-04-03 17:15:51 +02:00
dur = ard - > pulse_length_ext ;
2011-12-14 20:16:34 -08:00
DFS_STAT_INC ( sc , ext_phy_errors ) ;
/*
* cannot use extension channel RSSI
* if control channel is stronger
*/
2012-04-03 17:15:51 +02:00
rssi = ( ard - > rssi > = ( ard - > ext_rssi + 12 ) ) ? 0 : ard - > ext_rssi ;
2011-12-14 20:16:34 -08:00
break ;
case ( PRI_CH_RADAR_FOUND | EXT_CH_RADAR_FOUND ) :
/*
* Conducted testing , when pulse is on DC , both pri and ext
* durations are reported to be same
*
* Radiated testing , when pulse is on DC , different pri and
* ext durations are reported , so take the larger of the two
*/
2012-04-03 17:15:51 +02:00
if ( ard - > pulse_length_ext > = ard - > pulse_length_pri )
dur = ard - > pulse_length_ext ;
2011-12-14 20:16:34 -08:00
else
2012-04-03 17:15:51 +02:00
dur = ard - > pulse_length_pri ;
2011-12-14 20:16:34 -08:00
DFS_STAT_INC ( sc , dc_phy_errors ) ;
/* when both are present use stronger one */
2012-04-03 17:15:51 +02:00
rssi = ( ard - > rssi < ard - > ext_rssi ) ? ard - > ext_rssi : ard - > rssi ;
2011-12-14 20:16:34 -08:00
break ;
default :
/*
* Bogus bandwidth info was received in descriptor ,
* so ignore this PHY error
*/
DFS_STAT_INC ( sc , bwinfo_discards ) ;
return false ;
}
if ( rssi = = 0 ) {
DFS_STAT_INC ( sc , rssi_discards ) ;
return false ;
}
/* convert duration to usecs */
2012-04-03 17:15:51 +02:00
pe - > width = dur_to_usecs ( sc - > sc_ah , dur ) ;
pe - > rssi = rssi ;
2011-12-14 20:16:34 -08:00
DFS_STAT_INC ( sc , pulses_detected ) ;
return true ;
}
2015-03-10 17:49:30 +01:00
static void
ath9k_dfs_process_radar_pulse ( struct ath_softc * sc , struct pulse_event * pe )
{
struct dfs_pattern_detector * pd = sc - > dfs_detector ;
DFS_STAT_INC ( sc , pulses_processed ) ;
if ( pd = = NULL )
return ;
if ( ! pd - > add_pulse ( pd , pe ) )
return ;
DFS_STAT_INC ( sc , radar_detected ) ;
ieee80211_radar_detected ( sc - > hw ) ;
}
2011-12-14 20:16:34 -08:00
/*
* DFS : check PHY - error for radar pulse and feed the detector
*/
void ath9k_dfs_process_phyerr ( struct ath_softc * sc , void * data ,
struct ath_rx_status * rs , u64 mactime )
{
struct ath_radar_data ard ;
u16 datalen ;
char * vdata_end ;
2012-04-03 17:15:51 +02:00
struct pulse_event pe ;
2011-12-14 20:16:34 -08:00
struct ath_hw * ah = sc - > sc_ah ;
struct ath_common * common = ath9k_hw_common ( ah ) ;
2012-04-20 17:20:34 +02:00
DFS_STAT_INC ( sc , pulses_total ) ;
2012-04-03 17:15:51 +02:00
if ( ( rs - > rs_phyerr ! = ATH9K_PHYERR_RADAR ) & &
( rs - > rs_phyerr ! = ATH9K_PHYERR_FALSE_RADAR_EXT ) ) {
2011-12-19 19:30:43 -08:00
ath_dbg ( common , DFS ,
2011-12-14 20:16:34 -08:00
" Error: rs_phyer=0x%x not a radar error \n " ,
rs - > rs_phyerr ) ;
2012-04-20 17:20:34 +02:00
DFS_STAT_INC ( sc , pulses_no_dfs ) ;
2011-12-14 20:16:34 -08:00
return ;
}
datalen = rs - > rs_datalen ;
if ( datalen = = 0 ) {
DFS_STAT_INC ( sc , datalen_discards ) ;
return ;
}
2013-12-14 18:03:36 +01:00
ard . rssi = rs - > rs_rssi_ctl [ 0 ] ;
ard . ext_rssi = rs - > rs_rssi_ext [ 0 ] ;
2011-12-14 20:16:34 -08:00
/*
* hardware stores this as 8 bit signed value .
* we will cap it at 0 if it is a negative number
*/
if ( ard . rssi & 0x80 )
ard . rssi = 0 ;
if ( ard . ext_rssi & 0x80 )
ard . ext_rssi = 0 ;
vdata_end = ( char * ) data + datalen ;
ard . pulse_bw_info = vdata_end [ - 1 ] ;
ard . pulse_length_ext = vdata_end [ - 2 ] ;
ard . pulse_length_pri = vdata_end [ - 3 ] ;
2012-04-03 17:15:51 +02:00
pe . freq = ah - > curchan - > channel ;
pe . ts = mactime ;
2015-03-10 17:49:30 +01:00
if ( ! ath9k_postprocess_radar_event ( sc , & ard , & pe ) )
return ;
2015-06-16 12:52:16 +02:00
if ( pe . width > MIN_CHIRP_PULSE_WIDTH & &
pe . width < MAX_CHIRP_PULSE_WIDTH ) {
bool is_ctl = ! ! ( ard . pulse_bw_info & PRI_CH_RADAR_FOUND ) ;
bool is_ext = ! ! ( ard . pulse_bw_info & EXT_CH_RADAR_FOUND ) ;
int clen = datalen - 3 ;
pe . chirp = ath9k_check_chirping ( sc , data , clen , is_ctl , is_ext ) ;
} else {
pe . chirp = false ;
}
2015-03-10 17:49:30 +01:00
ath_dbg ( common , DFS ,
" ath9k_dfs_process_phyerr: type=%d, freq=%d, ts=%llu, "
" width=%d, rssi=%d, delta_ts=%llu \n " ,
ard . pulse_bw_info , pe . freq , pe . ts , pe . width , pe . rssi ,
pe . ts - sc - > dfs_prev_pulse_ts ) ;
sc - > dfs_prev_pulse_ts = pe . ts ;
if ( ard . pulse_bw_info & PRI_CH_RADAR_FOUND )
ath9k_dfs_process_radar_pulse ( sc , & pe ) ;
2015-06-16 11:46:42 +02:00
if ( IS_CHAN_HT40 ( ah - > curchan ) & &
ard . pulse_bw_info & EXT_CH_RADAR_FOUND ) {
2015-03-10 17:49:30 +01:00
pe . freq + = IS_CHAN_HT40PLUS ( ah - > curchan ) ? 20 : - 20 ;
ath9k_dfs_process_radar_pulse ( sc , & pe ) ;
2011-12-14 20:16:34 -08:00
}
}
2015-03-10 17:49:30 +01:00
# undef PRI_CH_RADAR_FOUND
# undef EXT_CH_RADAR_FOUND