2012-04-03 19:15:49 +04:00
/*
* Copyright ( c ) 2012 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 <linux/slab.h>
# include <linux/export.h>
# include "dfs_pattern_detector.h"
# include "dfs_pri_detector.h"
2013-10-14 13:06:06 +04:00
# include "ath.h"
2012-04-03 19:15:49 +04:00
/**
* struct radar_types - contains array of patterns defined for one DFS domain
* @ domain : DFS regulatory domain
* @ num_radar_types : number of radar types to follow
* @ radar_types : radar types array
*/
struct radar_types {
enum nl80211_dfs_regions region ;
u32 num_radar_types ;
const struct radar_detector_specs * radar_types ;
} ;
/* percentage on ppb threshold to trigger detection */
# define MIN_PPB_THRESH 50
2015-04-01 02:44:42 +03:00
# define PPB_THRESH_RATE(PPB, RATE) ((PPB * RATE + 100 - RATE) / 100)
# define PPB_THRESH(PPB) PPB_THRESH_RATE(PPB, MIN_PPB_THRESH)
2012-04-03 19:15:49 +04:00
# define PRF2PRI(PRF) ((1000000 + PRF / 2) / PRF)
2012-10-31 15:23:01 +04:00
/* percentage of pulse width tolerance */
# define WIDTH_TOLERANCE 5
# define WIDTH_LOWER(X) ((X*(100-WIDTH_TOLERANCE)+50) / 100)
# define WIDTH_UPPER(X) ((X*(100+WIDTH_TOLERANCE)+50) / 100)
2012-04-03 19:15:49 +04:00
2015-03-04 16:43:45 +03:00
# define ETSI_PATTERN(ID, WMIN, WMAX, PMIN, PMAX, PRF, PPB, CHIRP) \
2012-04-03 19:15:49 +04:00
{ \
2012-10-31 15:23:01 +04:00
ID , WIDTH_LOWER ( WMIN ) , WIDTH_UPPER ( WMAX ) , \
( PRF2PRI ( PMAX ) - PRI_TOLERANCE ) , \
2012-04-03 19:15:49 +04:00
( PRF2PRI ( PMIN ) * PRF + PRI_TOLERANCE ) , PRF , PPB * PRF , \
2015-03-04 16:43:45 +03:00
PPB_THRESH ( PPB ) , PRI_TOLERANCE , CHIRP \
2012-04-03 19:15:49 +04:00
}
/* radar types as defined by ETSI EN-301-893 v1.5.1 */
static const struct radar_detector_specs etsi_radar_ref_types_v15 [ ] = {
2015-03-04 16:43:45 +03:00
ETSI_PATTERN ( 0 , 0 , 1 , 700 , 700 , 1 , 18 , false ) ,
ETSI_PATTERN ( 1 , 0 , 5 , 200 , 1000 , 1 , 10 , false ) ,
ETSI_PATTERN ( 2 , 0 , 15 , 200 , 1600 , 1 , 15 , false ) ,
ETSI_PATTERN ( 3 , 0 , 15 , 2300 , 4000 , 1 , 25 , false ) ,
ETSI_PATTERN ( 4 , 20 , 30 , 2000 , 4000 , 1 , 20 , false ) ,
ETSI_PATTERN ( 5 , 0 , 2 , 300 , 400 , 3 , 10 , false ) ,
ETSI_PATTERN ( 6 , 0 , 2 , 400 , 1200 , 3 , 15 , false ) ,
2012-04-03 19:15:49 +04:00
} ;
static const struct radar_types etsi_radar_types_v15 = {
. region = NL80211_DFS_ETSI ,
. num_radar_types = ARRAY_SIZE ( etsi_radar_ref_types_v15 ) ,
. radar_types = etsi_radar_ref_types_v15 ,
} ;
2015-03-04 16:43:45 +03:00
# define FCC_PATTERN(ID, WMIN, WMAX, PMIN, PMAX, PRF, PPB, CHIRP) \
2014-05-13 14:32:05 +04:00
{ \
ID , WIDTH_LOWER ( WMIN ) , WIDTH_UPPER ( WMAX ) , \
PMIN - PRI_TOLERANCE , \
PMAX * PRF + PRI_TOLERANCE , PRF , PPB * PRF , \
2015-03-04 16:43:45 +03:00
PPB_THRESH ( PPB ) , PRI_TOLERANCE , CHIRP \
2014-05-13 14:32:05 +04:00
}
2015-04-09 18:50:10 +03:00
/* radar types released on August 14, 2014
* type 1 PRI values randomly selected within the range of 518 and 3066.
* divide it to 3 groups is good enough for both of radar detection and
* avoiding false detection based on practical test results
* collected for more than a year .
*/
2014-05-13 14:32:05 +04:00
static const struct radar_detector_specs fcc_radar_ref_types [ ] = {
2015-03-04 16:43:45 +03:00
FCC_PATTERN ( 0 , 0 , 1 , 1428 , 1428 , 1 , 18 , false ) ,
2015-04-09 18:50:10 +03:00
FCC_PATTERN ( 101 , 0 , 1 , 518 , 938 , 1 , 57 , false ) ,
FCC_PATTERN ( 102 , 0 , 1 , 938 , 2000 , 1 , 27 , false ) ,
FCC_PATTERN ( 103 , 0 , 1 , 2000 , 3066 , 1 , 18 , false ) ,
FCC_PATTERN ( 2 , 0 , 5 , 150 , 230 , 1 , 23 , false ) ,
FCC_PATTERN ( 3 , 6 , 10 , 200 , 500 , 1 , 16 , false ) ,
FCC_PATTERN ( 4 , 11 , 20 , 200 , 500 , 1 , 12 , false ) ,
FCC_PATTERN ( 5 , 50 , 100 , 1000 , 2000 , 1 , 1 , true ) ,
FCC_PATTERN ( 6 , 0 , 1 , 333 , 333 , 1 , 9 , false ) ,
2014-05-13 14:32:05 +04:00
} ;
static const struct radar_types fcc_radar_types = {
. region = NL80211_DFS_FCC ,
. num_radar_types = ARRAY_SIZE ( fcc_radar_ref_types ) ,
. radar_types = fcc_radar_ref_types ,
} ;
2015-04-01 02:44:42 +03:00
# define JP_PATTERN(ID, WMIN, WMAX, PMIN, PMAX, PRF, PPB, RATE, CHIRP) \
{ \
ID , WIDTH_LOWER ( WMIN ) , WIDTH_UPPER ( WMAX ) , \
PMIN - PRI_TOLERANCE , \
PMAX * PRF + PRI_TOLERANCE , PRF , PPB * PRF , \
PPB_THRESH_RATE ( PPB , RATE ) , PRI_TOLERANCE , CHIRP \
}
2014-05-13 14:32:06 +04:00
static const struct radar_detector_specs jp_radar_ref_types [ ] = {
2015-04-01 02:44:43 +03:00
JP_PATTERN ( 0 , 0 , 1 , 1428 , 1428 , 1 , 18 , 29 , false ) ,
JP_PATTERN ( 1 , 2 , 3 , 3846 , 3846 , 1 , 18 , 29 , false ) ,
2015-04-01 02:44:42 +03:00
JP_PATTERN ( 2 , 0 , 1 , 1388 , 1388 , 1 , 18 , 50 , false ) ,
JP_PATTERN ( 3 , 1 , 2 , 4000 , 4000 , 1 , 18 , 50 , false ) ,
JP_PATTERN ( 4 , 0 , 5 , 150 , 230 , 1 , 23 , 50 , false ) ,
JP_PATTERN ( 5 , 6 , 10 , 200 , 500 , 1 , 16 , 50 , false ) ,
JP_PATTERN ( 6 , 11 , 20 , 200 , 500 , 1 , 12 , 50 , false ) ,
2015-09-17 14:29:07 +03:00
JP_PATTERN ( 7 , 50 , 100 , 1000 , 2000 , 1 , 3 , 50 , false ) ,
2015-04-01 02:44:42 +03:00
JP_PATTERN ( 5 , 0 , 1 , 333 , 333 , 1 , 9 , 50 , false ) ,
2014-05-13 14:32:06 +04:00
} ;
static const struct radar_types jp_radar_types = {
. region = NL80211_DFS_JP ,
. num_radar_types = ARRAY_SIZE ( jp_radar_ref_types ) ,
. radar_types = jp_radar_ref_types ,
} ;
2012-04-03 19:15:49 +04:00
static const struct radar_types * dfs_domains [ ] = {
& etsi_radar_types_v15 ,
2014-05-13 14:32:05 +04:00
& fcc_radar_types ,
2014-05-13 14:32:06 +04:00
& jp_radar_types ,
2012-04-03 19:15:49 +04:00
} ;
/**
* get_dfs_domain_radar_types ( ) - get radar types for a given DFS domain
* @ param domain DFS domain
* @ return radar_types ptr on success , NULL if DFS domain is not supported
*/
static const struct radar_types *
get_dfs_domain_radar_types ( enum nl80211_dfs_regions region )
{
u32 i ;
for ( i = 0 ; i < ARRAY_SIZE ( dfs_domains ) ; i + + ) {
if ( dfs_domains [ i ] - > region = = region )
return dfs_domains [ i ] ;
}
return NULL ;
}
/**
* struct channel_detector - detector elements for a DFS channel
* @ head : list_head
* @ freq : frequency for this channel detector in MHz
* @ detectors : array of dynamically created detector elements for this freq
*
* Channel detectors are required to provide multi - channel DFS detection , e . g .
* to support off - channel scanning . A pattern detector has a list of channels
* radar pulses have been reported for in the past .
*/
struct channel_detector {
struct list_head head ;
u16 freq ;
struct pri_detector * * detectors ;
} ;
/* channel_detector_reset() - reset detector lines for a given channel */
static void channel_detector_reset ( struct dfs_pattern_detector * dpd ,
struct channel_detector * cd )
{
u32 i ;
if ( cd = = NULL )
return ;
for ( i = 0 ; i < dpd - > num_radar_types ; i + + )
cd - > detectors [ i ] - > reset ( cd - > detectors [ i ] , dpd - > last_pulse_ts ) ;
}
/* channel_detector_exit() - destructor */
static void channel_detector_exit ( struct dfs_pattern_detector * dpd ,
struct channel_detector * cd )
{
u32 i ;
if ( cd = = NULL )
return ;
list_del ( & cd - > head ) ;
for ( i = 0 ; i < dpd - > num_radar_types ; i + + ) {
struct pri_detector * de = cd - > detectors [ i ] ;
if ( de ! = NULL )
de - > exit ( de ) ;
}
kfree ( cd - > detectors ) ;
kfree ( cd ) ;
}
static struct channel_detector *
channel_detector_create ( struct dfs_pattern_detector * dpd , u16 freq )
{
u32 sz , i ;
struct channel_detector * cd ;
2013-04-02 14:51:48 +04:00
cd = kmalloc ( sizeof ( * cd ) , GFP_ATOMIC ) ;
2012-04-03 19:15:49 +04:00
if ( cd = = NULL )
goto fail ;
INIT_LIST_HEAD ( & cd - > head ) ;
cd - > freq = freq ;
sz = sizeof ( cd - > detectors ) * dpd - > num_radar_types ;
2013-04-02 14:51:48 +04:00
cd - > detectors = kzalloc ( sz , GFP_ATOMIC ) ;
2012-04-03 19:15:49 +04:00
if ( cd - > detectors = = NULL )
goto fail ;
for ( i = 0 ; i < dpd - > num_radar_types ; i + + ) {
const struct radar_detector_specs * rs = & dpd - > radar_spec [ i ] ;
struct pri_detector * de = pri_detector_init ( rs ) ;
if ( de = = NULL )
goto fail ;
cd - > detectors [ i ] = de ;
}
list_add ( & cd - > head , & dpd - > channel_detectors ) ;
return cd ;
fail :
2013-10-14 13:06:03 +04:00
ath_dbg ( dpd - > common , DFS ,
2013-04-15 13:29:06 +04:00
" failed to allocate channel_detector for freq=%d \n " , freq ) ;
2012-04-03 19:15:49 +04:00
channel_detector_exit ( dpd , cd ) ;
return NULL ;
}
/**
* channel_detector_get ( ) - get channel detector for given frequency
* @ param dpd instance pointer
* @ param freq frequency in MHz
* @ return pointer to channel detector on success , NULL otherwise
*
* Return existing channel detector for the given frequency or return a
* newly create one .
*/
static struct channel_detector *
channel_detector_get ( struct dfs_pattern_detector * dpd , u16 freq )
{
struct channel_detector * cd ;
list_for_each_entry ( cd , & dpd - > channel_detectors , head ) {
if ( cd - > freq = = freq )
return cd ;
}
return channel_detector_create ( dpd , freq ) ;
}
/*
* DFS Pattern Detector
*/
/* dpd_reset(): reset all channel detectors */
static void dpd_reset ( struct dfs_pattern_detector * dpd )
{
struct channel_detector * cd ;
if ( ! list_empty ( & dpd - > channel_detectors ) )
list_for_each_entry ( cd , & dpd - > channel_detectors , head )
channel_detector_reset ( dpd , cd ) ;
}
static void dpd_exit ( struct dfs_pattern_detector * dpd )
{
struct channel_detector * cd , * cd0 ;
if ( ! list_empty ( & dpd - > channel_detectors ) )
list_for_each_entry_safe ( cd , cd0 , & dpd - > channel_detectors , head )
channel_detector_exit ( dpd , cd ) ;
kfree ( dpd ) ;
}
static bool
dpd_add_pulse ( struct dfs_pattern_detector * dpd , struct pulse_event * event )
{
u32 i ;
struct channel_detector * cd ;
2013-04-15 13:29:06 +04:00
/*
* pulses received for a non - supported or un - initialized
* domain are treated as detected radars for fail - safety
*/
if ( dpd - > region = = NL80211_DFS_UNSET )
2012-04-03 19:15:49 +04:00
return true ;
cd = channel_detector_get ( dpd , event - > freq ) ;
if ( cd = = NULL )
return false ;
dpd - > last_pulse_ts = event - > ts ;
2013-04-15 13:29:06 +04:00
/* reset detector on time stamp wraparound, caused by TSF reset */
if ( event - > ts < dpd - > last_pulse_ts )
2012-04-03 19:15:49 +04:00
dpd_reset ( dpd ) ;
2013-04-15 13:29:06 +04:00
2012-04-03 19:15:49 +04:00
/* do type individual pattern matching */
for ( i = 0 ; i < dpd - > num_radar_types ; i + + ) {
2013-04-15 13:29:06 +04:00
struct pri_detector * pd = cd - > detectors [ i ] ;
struct pri_sequence * ps = pd - > add_pulse ( pd , event ) ;
if ( ps ! = NULL ) {
2013-10-14 13:06:03 +04:00
ath_dbg ( dpd - > common , DFS ,
2013-04-15 13:29:06 +04:00
" DFS: radar found on freq=%d: id=%d, pri=%d, "
" count=%d, count_false=%d \n " ,
event - > freq , pd - > rs - > type_id ,
ps - > pri , ps - > count , ps - > count_falses ) ;
2015-03-10 19:49:29 +03:00
pd - > reset ( pd , dpd - > last_pulse_ts ) ;
2012-04-03 19:15:49 +04:00
return true ;
}
}
return false ;
}
2013-10-14 13:06:04 +04:00
static struct ath_dfs_pool_stats
dpd_get_stats ( struct dfs_pattern_detector * dpd )
{
return global_dfs_pool_stats ;
}
2012-04-03 19:15:49 +04:00
static bool dpd_set_domain ( struct dfs_pattern_detector * dpd ,
enum nl80211_dfs_regions region )
{
const struct radar_types * rt ;
struct channel_detector * cd , * cd0 ;
if ( dpd - > region = = region )
return true ;
dpd - > region = NL80211_DFS_UNSET ;
rt = get_dfs_domain_radar_types ( region ) ;
if ( rt = = NULL )
return false ;
/* delete all channel detectors for previous DFS domain */
if ( ! list_empty ( & dpd - > channel_detectors ) )
list_for_each_entry_safe ( cd , cd0 , & dpd - > channel_detectors , head )
channel_detector_exit ( dpd , cd ) ;
dpd - > radar_spec = rt - > radar_types ;
dpd - > num_radar_types = rt - > num_radar_types ;
dpd - > region = region ;
return true ;
}
static struct dfs_pattern_detector default_dpd = {
. exit = dpd_exit ,
2012-10-31 15:21:56 +04:00
. set_dfs_domain = dpd_set_domain ,
2012-04-03 19:15:49 +04:00
. add_pulse = dpd_add_pulse ,
2013-10-14 13:06:04 +04:00
. get_stats = dpd_get_stats ,
2012-04-03 19:15:49 +04:00
. region = NL80211_DFS_UNSET ,
} ;
struct dfs_pattern_detector *
2013-10-14 13:06:03 +04:00
dfs_pattern_detector_init ( struct ath_common * common ,
enum nl80211_dfs_regions region )
2012-04-03 19:15:49 +04:00
{
struct dfs_pattern_detector * dpd ;
2013-02-03 21:28:14 +04:00
2013-10-14 13:06:05 +04:00
if ( ! config_enabled ( CONFIG_CFG80211_CERTIFICATION_ONUS ) )
return NULL ;
2012-04-03 19:15:49 +04:00
dpd = kmalloc ( sizeof ( * dpd ) , GFP_KERNEL ) ;
2013-02-03 21:28:14 +04:00
if ( dpd = = NULL )
2012-04-03 19:15:49 +04:00
return NULL ;
2013-02-03 21:28:14 +04:00
2012-04-03 19:15:49 +04:00
* dpd = default_dpd ;
INIT_LIST_HEAD ( & dpd - > channel_detectors ) ;
2013-10-14 13:06:03 +04:00
dpd - > common = common ;
2012-10-31 15:21:56 +04:00
if ( dpd - > set_dfs_domain ( dpd , region ) )
2012-04-03 19:15:49 +04:00
return dpd ;
2013-04-15 13:29:06 +04:00
ath_dbg ( common , DFS , " Could not set DFS domain to %d " , region ) ;
2012-10-31 15:22:34 +04:00
kfree ( dpd ) ;
2012-04-03 19:15:49 +04:00
return NULL ;
}
EXPORT_SYMBOL ( dfs_pattern_detector_init ) ;