2008-08-04 11:16:41 +04:00
/*
2009-03-13 06:37:23 +03:00
* Copyright ( c ) 2008 - 2009 Atheros Communications Inc .
2008-08-04 11:16:41 +04:00
*
* 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 .
*/
2012-03-19 04:30:52 +04:00
# define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
2008-08-04 11:16:41 +04:00
# include <linux/kernel.h>
2011-05-28 00:14:23 +04:00
# include <linux/export.h>
2009-03-31 06:30:29 +04:00
# include <net/cfg80211.h>
# include <net/mac80211.h>
# include "regd.h"
2008-08-04 11:16:41 +04:00
# include "regd_common.h"
2011-12-08 22:29:24 +04:00
static int __ath_regd_init ( struct ath_regulatory * reg ) ;
2009-01-23 02:16:48 +03:00
/*
* This is a set of common rules used by our world regulatory domains .
* We have 12 world regulatory domains . To save space we consolidate
* the regulatory domains in 5 structures by frequency and change
* the flags on our reg_notifier ( ) on a case by case basis .
*/
2008-08-04 11:16:41 +04:00
2009-01-23 02:16:48 +03:00
/* Only these channels all allow active scan on all world regulatory domains */
# define ATH9K_2GHZ_CH01_11 REG_RULE(2412-10, 2462+10, 40, 0, 20, 0)
/* We enable active scan on these a case by case basis by regulatory domain */
# define ATH9K_2GHZ_CH12_13 REG_RULE(2467-10, 2472+10, 40, 0, 20,\
2013-10-21 21:22:25 +04:00
NL80211_RRF_NO_IR )
2009-01-23 02:16:48 +03:00
# define ATH9K_2GHZ_CH14 REG_RULE(2484-10, 2484+10, 40, 0, 20,\
2013-10-21 21:22:25 +04:00
NL80211_RRF_NO_IR | \
NL80211_RRF_NO_OFDM )
2009-01-23 02:16:48 +03:00
/* We allow IBSS on these on a case by case basis by regulatory domain */
2013-06-06 07:45:36 +04:00
# define ATH9K_5GHZ_5150_5350 REG_RULE(5150-10, 5350+10, 80, 0, 30,\
2013-10-21 21:22:25 +04:00
NL80211_RRF_NO_IR )
2013-06-06 07:45:36 +04:00
# define ATH9K_5GHZ_5470_5850 REG_RULE(5470-10, 5850+10, 80, 0, 30,\
2013-10-21 21:22:25 +04:00
NL80211_RRF_NO_IR )
2013-06-06 07:45:36 +04:00
# define ATH9K_5GHZ_5725_5850 REG_RULE(5725-10, 5850+10, 80, 0, 30,\
2013-10-21 21:22:25 +04:00
NL80211_RRF_NO_IR )
2009-01-23 02:16:48 +03:00
# define ATH9K_2GHZ_ALL ATH9K_2GHZ_CH01_11, \
ATH9K_2GHZ_CH12_13 , \
ATH9K_2GHZ_CH14
# define ATH9K_5GHZ_ALL ATH9K_5GHZ_5150_5350, \
ATH9K_5GHZ_5470_5850
2010-03-30 19:44:33 +04:00
2009-01-23 02:16:48 +03:00
/* This one skips what we call "mid band" */
# define ATH9K_5GHZ_NO_MIDBAND ATH9K_5GHZ_5150_5350, \
ATH9K_5GHZ_5725_5850
/* Can be used for:
* 0x60 , 0x61 , 0x62 */
2009-03-31 06:30:29 +04:00
static const struct ieee80211_regdomain ath_world_regdom_60_61_62 = {
2009-01-23 02:16:48 +03:00
. n_reg_rules = 5 ,
. alpha2 = " 99 " ,
. reg_rules = {
ATH9K_2GHZ_ALL ,
ATH9K_5GHZ_ALL ,
}
} ;
/* Can be used by 0x63 and 0x65 */
2009-03-31 06:30:29 +04:00
static const struct ieee80211_regdomain ath_world_regdom_63_65 = {
2009-01-23 02:16:48 +03:00
. n_reg_rules = 4 ,
. alpha2 = " 99 " ,
. reg_rules = {
ATH9K_2GHZ_CH01_11 ,
ATH9K_2GHZ_CH12_13 ,
ATH9K_5GHZ_NO_MIDBAND ,
}
} ;
/* Can be used by 0x64 only */
2009-03-31 06:30:29 +04:00
static const struct ieee80211_regdomain ath_world_regdom_64 = {
2009-01-23 02:16:48 +03:00
. n_reg_rules = 3 ,
. alpha2 = " 99 " ,
. reg_rules = {
ATH9K_2GHZ_CH01_11 ,
ATH9K_5GHZ_NO_MIDBAND ,
}
} ;
/* Can be used by 0x66 and 0x69 */
2009-03-31 06:30:29 +04:00
static const struct ieee80211_regdomain ath_world_regdom_66_69 = {
2009-01-23 02:16:48 +03:00
. n_reg_rules = 3 ,
. alpha2 = " 99 " ,
. reg_rules = {
ATH9K_2GHZ_CH01_11 ,
ATH9K_5GHZ_ALL ,
}
} ;
2011-04-14 15:11:30 +04:00
/* Can be used by 0x67, 0x68, 0x6A and 0x6C */
static const struct ieee80211_regdomain ath_world_regdom_67_68_6A_6C = {
2009-01-23 02:16:48 +03:00
. n_reg_rules = 4 ,
. alpha2 = " 99 " ,
. reg_rules = {
ATH9K_2GHZ_CH01_11 ,
ATH9K_2GHZ_CH12_13 ,
ATH9K_5GHZ_ALL ,
}
} ;
2008-08-04 11:16:41 +04:00
2013-10-30 02:39:04 +04:00
static bool dynamic_country_user_possible ( struct ath_regulatory * reg )
{
if ( config_enabled ( CONFIG_ATH_REG_DYNAMIC_USER_CERT_TESTING ) )
return true ;
switch ( reg - > country_code ) {
case CTRY_UNITED_STATES :
case CTRY_JAPAN1 :
case CTRY_JAPAN2 :
case CTRY_JAPAN3 :
case CTRY_JAPAN4 :
case CTRY_JAPAN5 :
case CTRY_JAPAN6 :
case CTRY_JAPAN7 :
case CTRY_JAPAN8 :
case CTRY_JAPAN9 :
case CTRY_JAPAN10 :
case CTRY_JAPAN11 :
case CTRY_JAPAN12 :
case CTRY_JAPAN13 :
case CTRY_JAPAN14 :
case CTRY_JAPAN15 :
case CTRY_JAPAN16 :
case CTRY_JAPAN17 :
case CTRY_JAPAN18 :
case CTRY_JAPAN19 :
case CTRY_JAPAN20 :
case CTRY_JAPAN21 :
case CTRY_JAPAN22 :
case CTRY_JAPAN23 :
case CTRY_JAPAN24 :
case CTRY_JAPAN25 :
case CTRY_JAPAN26 :
case CTRY_JAPAN27 :
case CTRY_JAPAN28 :
case CTRY_JAPAN29 :
case CTRY_JAPAN30 :
case CTRY_JAPAN31 :
case CTRY_JAPAN32 :
case CTRY_JAPAN33 :
case CTRY_JAPAN34 :
case CTRY_JAPAN35 :
case CTRY_JAPAN36 :
case CTRY_JAPAN37 :
case CTRY_JAPAN38 :
case CTRY_JAPAN39 :
case CTRY_JAPAN40 :
case CTRY_JAPAN41 :
case CTRY_JAPAN42 :
case CTRY_JAPAN43 :
case CTRY_JAPAN44 :
case CTRY_JAPAN45 :
case CTRY_JAPAN46 :
case CTRY_JAPAN47 :
case CTRY_JAPAN48 :
case CTRY_JAPAN49 :
case CTRY_JAPAN50 :
case CTRY_JAPAN51 :
case CTRY_JAPAN52 :
case CTRY_JAPAN53 :
case CTRY_JAPAN54 :
case CTRY_JAPAN55 :
case CTRY_JAPAN56 :
case CTRY_JAPAN57 :
case CTRY_JAPAN58 :
case CTRY_JAPAN59 :
return false ;
}
return true ;
}
2013-10-30 02:39:05 +04:00
static bool ath_reg_dyn_country_user_allow ( struct ath_regulatory * reg )
{
if ( ! config_enabled ( CONFIG_ATH_REG_DYNAMIC_USER_REG_HINTS ) )
return false ;
if ( ! dynamic_country_user_possible ( reg ) )
return false ;
return true ;
}
2009-02-12 21:38:55 +03:00
static inline bool is_wwr_sku ( u16 regd )
{
2010-02-07 18:01:55 +03:00
return ( ( regd & COUNTRY_ERD_FLAG ) ! = COUNTRY_ERD_FLAG ) & &
( ( ( regd & WORLD_SKU_MASK ) = = WORLD_SKU_PREFIX ) | |
( regd = = WORLD ) ) ;
2009-02-12 21:38:55 +03:00
}
2009-03-31 06:30:29 +04:00
static u16 ath_regd_get_eepromRD ( struct ath_regulatory * reg )
2008-08-04 11:16:41 +04:00
{
2009-03-31 06:30:28 +04:00
return reg - > current_rd & ~ WORLDWIDE_ROAMING_FLAG ;
2008-08-04 11:16:41 +04:00
}
2009-03-31 06:30:29 +04:00
bool ath_is_world_regd ( struct ath_regulatory * reg )
2009-01-23 02:16:48 +03:00
{
2009-03-31 06:30:29 +04:00
return is_wwr_sku ( ath_regd_get_eepromRD ( reg ) ) ;
2008-08-04 11:16:41 +04:00
}
2009-03-31 06:30:29 +04:00
EXPORT_SYMBOL ( ath_is_world_regd ) ;
2008-08-04 11:16:41 +04:00
2009-03-31 06:30:30 +04:00
static const struct ieee80211_regdomain * ath_default_world_regdomain ( void )
2008-08-04 11:16:41 +04:00
{
2009-01-23 02:16:48 +03:00
/* this is the most restrictive */
2009-03-31 06:30:29 +04:00
return & ath_world_regdom_64 ;
2009-01-23 02:16:48 +03:00
}
2008-08-04 11:16:41 +04:00
2009-03-31 06:30:30 +04:00
static const struct
2009-03-31 06:30:29 +04:00
ieee80211_regdomain * ath_world_regdomain ( struct ath_regulatory * reg )
2009-01-23 02:16:48 +03:00
{
2014-02-13 20:13:12 +04:00
switch ( reg - > regpair - > reg_domain ) {
2009-01-23 02:16:48 +03:00
case 0x60 :
case 0x61 :
case 0x62 :
2009-03-31 06:30:29 +04:00
return & ath_world_regdom_60_61_62 ;
2009-01-23 02:16:48 +03:00
case 0x63 :
case 0x65 :
2009-03-31 06:30:29 +04:00
return & ath_world_regdom_63_65 ;
2009-01-23 02:16:48 +03:00
case 0x64 :
2009-03-31 06:30:29 +04:00
return & ath_world_regdom_64 ;
2009-01-23 02:16:48 +03:00
case 0x66 :
case 0x69 :
2009-03-31 06:30:29 +04:00
return & ath_world_regdom_66_69 ;
2009-01-23 02:16:48 +03:00
case 0x67 :
case 0x68 :
case 0x6A :
2011-04-14 15:11:30 +04:00
case 0x6C :
return & ath_world_regdom_67_68_6A_6C ;
2009-01-23 02:16:48 +03:00
default :
WARN_ON ( 1 ) ;
2009-03-31 06:30:29 +04:00
return ath_default_world_regdomain ( ) ;
2008-08-04 11:16:41 +04:00
}
}
2011-01-25 07:15:33 +03:00
bool ath_is_49ghz_allowed ( u16 regdomain )
{
/* possibly more */
return regdomain = = MKK9_MKKC ;
}
EXPORT_SYMBOL ( ath_is_49ghz_allowed ) ;
2009-01-28 23:17:48 +03:00
/* Frequency is one where radar detection is required */
2009-03-31 06:30:29 +04:00
static bool ath_is_radar_freq ( u16 center_freq )
2009-01-28 23:17:48 +03:00
{
return ( center_freq > = 5260 & & center_freq < = 5700 ) ;
}
2013-10-21 21:14:48 +04:00
static void ath_force_clear_no_ir_chan ( struct wiphy * wiphy ,
struct ieee80211_channel * ch )
{
const struct ieee80211_reg_rule * reg_rule ;
2013-10-29 22:34:24 +04:00
reg_rule = freq_reg_info ( wiphy , MHZ_TO_KHZ ( ch - > center_freq ) ) ;
2013-10-21 21:14:48 +04:00
if ( IS_ERR ( reg_rule ) )
return ;
if ( ! ( reg_rule - > flags & NL80211_RRF_NO_IR ) )
if ( ch - > flags & IEEE80211_CHAN_NO_IR )
ch - > flags & = ~ IEEE80211_CHAN_NO_IR ;
}
static void ath_force_clear_no_ir_freq ( struct wiphy * wiphy , u16 center_freq )
{
struct ieee80211_channel * ch ;
ch = ieee80211_get_channel ( wiphy , center_freq ) ;
if ( ! ch )
return ;
ath_force_clear_no_ir_chan ( wiphy , ch ) ;
}
static void ath_force_no_ir_chan ( struct ieee80211_channel * ch )
{
ch - > flags | = IEEE80211_CHAN_NO_IR ;
}
static void ath_force_no_ir_freq ( struct wiphy * wiphy , u16 center_freq )
{
struct ieee80211_channel * ch ;
ch = ieee80211_get_channel ( wiphy , center_freq ) ;
if ( ! ch )
return ;
ath_force_no_ir_chan ( ch ) ;
}
2013-10-21 21:14:51 +04:00
static void
__ath_reg_apply_beaconing_flags ( struct wiphy * wiphy ,
2013-10-30 03:10:07 +04:00
struct ath_regulatory * reg ,
2013-10-21 21:14:51 +04:00
enum nl80211_reg_initiator initiator ,
struct ieee80211_channel * ch )
{
if ( ath_is_radar_freq ( ch - > center_freq ) | |
( ch - > flags & IEEE80211_CHAN_RADAR ) )
return ;
switch ( initiator ) {
case NL80211_REGDOM_SET_BY_COUNTRY_IE :
ath_force_clear_no_ir_chan ( wiphy , ch ) ;
break ;
2013-10-30 03:10:07 +04:00
case NL80211_REGDOM_SET_BY_USER :
if ( ath_reg_dyn_country_user_allow ( reg ) )
ath_force_clear_no_ir_chan ( wiphy , ch ) ;
break ;
2013-10-21 21:14:51 +04:00
default :
if ( ch - > beacon_found )
ch - > flags & = ~ IEEE80211_CHAN_NO_IR ;
}
}
2009-01-28 23:17:49 +03:00
/*
2013-10-21 21:14:50 +04:00
* These exception rules do not apply radar frequencies .
2009-02-21 08:20:40 +03:00
*
2013-10-21 21:14:50 +04:00
* - We enable initiating radiation if the country IE says its fine :
2009-02-21 08:20:40 +03:00
* - If no country IE has been processed and a we determine we have
2013-10-21 21:14:50 +04:00
* received a beacon on a channel we can enable initiating radiation .
2009-01-28 23:17:49 +03:00
*/
2009-03-31 06:30:34 +04:00
static void
ath_reg_apply_beaconing_flags ( struct wiphy * wiphy ,
2013-10-30 03:10:07 +04:00
struct ath_regulatory * reg ,
2009-03-31 06:30:34 +04:00
enum nl80211_reg_initiator initiator )
2008-08-04 11:16:41 +04:00
{
2009-02-21 08:20:40 +03:00
enum ieee80211_band band ;
2009-01-23 02:16:48 +03:00
struct ieee80211_supported_band * sband ;
struct ieee80211_channel * ch ;
unsigned int i ;
2009-02-21 08:20:40 +03:00
for ( band = 0 ; band < IEEE80211_NUM_BANDS ; band + + ) {
if ( ! wiphy - > bands [ band ] )
2009-01-23 02:16:48 +03:00
continue ;
2009-02-21 08:20:40 +03:00
sband = wiphy - > bands [ band ] ;
for ( i = 0 ; i < sband - > n_channels ; i + + ) {
ch = & sband - > channels [ i ] ;
2013-10-30 03:10:07 +04:00
__ath_reg_apply_beaconing_flags ( wiphy , reg ,
initiator , ch ) ;
2009-02-21 08:20:40 +03:00
}
2009-01-23 02:16:48 +03:00
}
2008-08-04 11:16:41 +04:00
}
2013-10-21 21:14:48 +04:00
/**
2013-10-21 21:14:49 +04:00
* ath_reg_apply_ir_flags ( )
2013-10-21 21:14:48 +04:00
* @ wiphy : the wiphy to use
* @ initiator : the regulatory hint initiator
*
* If no country IE has been received always enable passive scan
* and no - ibss on these channels . This is only done for specific
* regulatory SKUs .
*
* If a country IE has been received check its rule for this
* channel first before enabling active scan . The passive scan
* would have been enforced by the initial processing of our
* custom regulatory domain .
*/
2009-03-31 06:30:34 +04:00
static void
2013-10-21 21:14:49 +04:00
ath_reg_apply_ir_flags ( struct wiphy * wiphy ,
2013-10-30 03:10:07 +04:00
struct ath_regulatory * reg ,
enum nl80211_reg_initiator initiator )
2008-08-04 11:16:41 +04:00
{
2009-01-23 02:16:48 +03:00
struct ieee80211_supported_band * sband ;
sband = wiphy - > bands [ IEEE80211_BAND_2GHZ ] ;
2011-11-08 17:01:13 +04:00
if ( ! sband )
return ;
2009-01-23 02:16:48 +03:00
2013-10-21 21:14:48 +04:00
switch ( initiator ) {
case NL80211_REGDOM_SET_BY_COUNTRY_IE :
ath_force_clear_no_ir_freq ( wiphy , 2467 ) ;
ath_force_clear_no_ir_freq ( wiphy , 2472 ) ;
break ;
2013-10-30 03:10:07 +04:00
case NL80211_REGDOM_SET_BY_USER :
if ( ! ath_reg_dyn_country_user_allow ( reg ) )
break ;
ath_force_clear_no_ir_freq ( wiphy , 2467 ) ;
ath_force_clear_no_ir_freq ( wiphy , 2472 ) ;
break ;
2013-10-21 21:14:48 +04:00
default :
ath_force_no_ir_freq ( wiphy , 2467 ) ;
ath_force_no_ir_freq ( wiphy , 2472 ) ;
2008-08-04 11:16:41 +04:00
}
}
2009-01-23 02:16:48 +03:00
/* Always apply Radar/DFS rules on freq range 5260 MHz - 5700 MHz */
2009-03-31 06:30:30 +04:00
static void ath_reg_apply_radar_flags ( struct wiphy * wiphy )
2008-08-04 11:16:41 +04:00
{
2009-01-23 02:16:48 +03:00
struct ieee80211_supported_band * sband ;
struct ieee80211_channel * ch ;
unsigned int i ;
2008-08-04 11:16:41 +04:00
2009-01-23 02:16:48 +03:00
if ( ! wiphy - > bands [ IEEE80211_BAND_5GHZ ] )
return ;
2008-08-04 11:16:41 +04:00
2009-01-23 02:16:48 +03:00
sband = wiphy - > bands [ IEEE80211_BAND_5GHZ ] ;
2008-08-04 11:16:41 +04:00
2009-01-23 02:16:48 +03:00
for ( i = 0 ; i < sband - > n_channels ; i + + ) {
ch = & sband - > channels [ i ] ;
2009-03-31 06:30:29 +04:00
if ( ! ath_is_radar_freq ( ch - > center_freq ) )
2009-01-23 02:16:48 +03:00
continue ;
/* We always enable radar detection/DFS on this
* frequency range . Additionally we also apply on
* this frequency range :
* - If STA mode does not yet have DFS supports disable
* active scanning
* - If adhoc mode does not support DFS yet then
* disable adhoc in the frequency .
* - If AP mode does not yet support radar detection / DFS
* do not allow AP mode
*/
if ( ! ( ch - > flags & IEEE80211_CHAN_DISABLED ) )
ch - > flags | = IEEE80211_CHAN_RADAR |
2013-10-21 21:22:25 +04:00
IEEE80211_CHAN_NO_IR ;
2009-01-23 02:16:48 +03:00
}
}
2008-08-04 11:16:41 +04:00
2009-03-31 06:30:30 +04:00
static void ath_reg_apply_world_flags ( struct wiphy * wiphy ,
2009-03-31 06:30:34 +04:00
enum nl80211_reg_initiator initiator ,
struct ath_regulatory * reg )
2009-01-23 02:16:48 +03:00
{
2014-02-13 20:13:12 +04:00
switch ( reg - > regpair - > reg_domain ) {
2009-01-23 02:16:48 +03:00
case 0x60 :
case 0x63 :
case 0x66 :
case 0x67 :
2011-04-15 01:55:36 +04:00
case 0x6C :
2013-10-30 03:10:07 +04:00
ath_reg_apply_beaconing_flags ( wiphy , reg , initiator ) ;
2009-01-23 02:16:48 +03:00
break ;
case 0x68 :
2013-10-30 03:10:07 +04:00
ath_reg_apply_beaconing_flags ( wiphy , reg , initiator ) ;
ath_reg_apply_ir_flags ( wiphy , reg , initiator ) ;
2009-01-23 02:16:48 +03:00
break ;
2013-10-30 03:10:07 +04:00
default :
if ( ath_reg_dyn_country_user_allow ( reg ) )
ath_reg_apply_beaconing_flags ( wiphy , reg , initiator ) ;
2009-01-23 02:16:48 +03:00
}
}
2008-08-04 11:16:41 +04:00
2011-12-08 22:29:24 +04:00
static u16 ath_regd_find_country_by_name ( char * alpha2 )
{
unsigned int i ;
for ( i = 0 ; i < ARRAY_SIZE ( allCountries ) ; i + + ) {
if ( ! memcmp ( allCountries [ i ] . isoName , alpha2 , 2 ) )
return allCountries [ i ] . countryCode ;
}
return - 1 ;
}
2013-10-15 04:42:07 +04:00
static int __ath_reg_dyn_country ( struct wiphy * wiphy ,
struct ath_regulatory * reg ,
struct regulatory_request * request )
{
u16 country_code ;
2013-11-12 22:33:20 +04:00
if ( request - > initiator = = NL80211_REGDOM_SET_BY_COUNTRY_IE & &
! ath_is_world_regd ( reg ) )
2013-10-15 04:42:07 +04:00
return - EINVAL ;
country_code = ath_regd_find_country_by_name ( request - > alpha2 ) ;
if ( country_code = = ( u16 ) - 1 )
return - EINVAL ;
reg - > current_rd = COUNTRY_ERD_FLAG ;
reg - > current_rd | = country_code ;
__ath_regd_init ( reg ) ;
ath_reg_apply_world_flags ( wiphy , request - > initiator , reg ) ;
return 0 ;
}
static void ath_reg_dyn_country ( struct wiphy * wiphy ,
struct ath_regulatory * reg ,
struct regulatory_request * request )
{
if ( __ath_reg_dyn_country ( wiphy , reg , request ) )
return ;
printk ( KERN_DEBUG " ath: regdomain 0x%0x "
" dynamically updated by %s \n " ,
reg - > current_rd ,
reg_initiator_name ( request - > initiator ) ) ;
}
2013-01-11 22:39:36 +04:00
void ath_reg_notifier_apply ( struct wiphy * wiphy ,
struct regulatory_request * request ,
struct ath_regulatory * reg )
2009-01-23 02:16:48 +03:00
{
2011-12-08 22:29:24 +04:00
struct ath_common * common = container_of ( reg , struct ath_common ,
regulatory ) ;
2009-01-23 02:16:48 +03:00
/* We always apply this */
2009-03-31 06:30:29 +04:00
ath_reg_apply_radar_flags ( wiphy ) ;
2008-08-04 11:16:41 +04:00
2010-12-16 06:24:12 +03:00
/*
* This would happen when we have sent a custom regulatory request
* a world regulatory domain and the scheduler hasn ' t yet processed
* any pending requests in the queue .
*/
if ( ! request )
2013-01-11 22:39:36 +04:00
return ;
2010-12-16 06:24:12 +03:00
2014-10-22 17:27:53 +04:00
reg - > region = request - > dfs_region ;
2009-01-23 02:16:48 +03:00
switch ( request - > initiator ) {
2009-03-10 05:07:41 +03:00
case NL80211_REGDOM_SET_BY_CORE :
2011-12-08 22:29:24 +04:00
/*
* If common - > reg_world_copy is world roaming it means we * were *
* world roaming . . . so we now have to restore that data .
*/
if ( ! ath_is_world_regd ( & common - > reg_world_copy ) )
break ;
memcpy ( reg , & common - > reg_world_copy ,
sizeof ( struct ath_regulatory ) ) ;
break ;
case NL80211_REGDOM_SET_BY_DRIVER :
2013-10-15 04:42:08 +04:00
break ;
2009-03-10 05:07:41 +03:00
case NL80211_REGDOM_SET_BY_USER :
2013-10-30 02:39:05 +04:00
if ( ath_reg_dyn_country_user_allow ( reg ) )
ath_reg_dyn_country ( wiphy , reg , request ) ;
2009-01-23 02:16:48 +03:00
break ;
2009-03-10 05:07:41 +03:00
case NL80211_REGDOM_SET_BY_COUNTRY_IE :
2013-10-15 04:42:07 +04:00
ath_reg_dyn_country ( wiphy , reg , request ) ;
2009-01-23 02:16:48 +03:00
break ;
}
2008-08-04 11:16:41 +04:00
}
2009-03-31 06:30:29 +04:00
EXPORT_SYMBOL ( ath_reg_notifier_apply ) ;
2008-08-04 11:16:41 +04:00
2009-03-31 06:30:30 +04:00
static bool ath_regd_is_eeprom_valid ( struct ath_regulatory * reg )
2009-03-31 06:30:28 +04:00
{
2010-03-30 19:44:33 +04:00
u16 rd = ath_regd_get_eepromRD ( reg ) ;
2009-01-23 02:16:48 +03:00
int i ;
2008-08-04 11:16:41 +04:00
2009-01-23 02:16:48 +03:00
if ( rd & COUNTRY_ERD_FLAG ) {
/* EEPROM value is a country code */
u16 cc = rd & ~ COUNTRY_ERD_FLAG ;
2009-06-03 00:30:56 +04:00
printk ( KERN_DEBUG
" ath: EEPROM indicates we should expect "
" a country code \n " ) ;
2009-01-23 02:16:48 +03:00
for ( i = 0 ; i < ARRAY_SIZE ( allCountries ) ; i + + )
if ( allCountries [ i ] . countryCode = = cc )
return true ;
} else {
/* EEPROM value is a regpair value */
2009-06-03 00:30:56 +04:00
if ( rd ! = CTRY_DEFAULT )
printk ( KERN_DEBUG " ath: EEPROM indicates we "
" should expect a direct regpair map \n " ) ;
2009-01-23 02:16:48 +03:00
for ( i = 0 ; i < ARRAY_SIZE ( regDomainPairs ) ; i + + )
2014-02-13 20:13:12 +04:00
if ( regDomainPairs [ i ] . reg_domain = = rd )
2009-01-23 02:16:48 +03:00
return true ;
2008-08-04 11:16:41 +04:00
}
2009-03-31 06:30:28 +04:00
printk ( KERN_DEBUG
2009-03-31 06:30:29 +04:00
" ath: invalid regulatory domain/country code 0x%x \n " , rd ) ;
2008-08-04 11:16:41 +04:00
return false ;
}
2009-01-23 02:16:48 +03:00
/* EEPROM country code to regpair mapping */
2008-08-04 11:16:41 +04:00
static struct country_code_to_enum_rd *
2009-03-31 06:30:29 +04:00
ath_regd_find_country ( u16 countryCode )
2008-08-04 11:16:41 +04:00
{
int i ;
for ( i = 0 ; i < ARRAY_SIZE ( allCountries ) ; i + + ) {
if ( allCountries [ i ] . countryCode = = countryCode )
return & allCountries [ i ] ;
}
return NULL ;
}
2009-01-23 02:16:48 +03:00
/* EEPROM rd code to regpair mapping */
static struct country_code_to_enum_rd *
2009-03-31 06:30:29 +04:00
ath_regd_find_country_by_rd ( int regdmn )
2009-01-23 02:16:48 +03:00
{
int i ;
for ( i = 0 ; i < ARRAY_SIZE ( allCountries ) ; i + + ) {
if ( allCountries [ i ] . regDmnEnum = = regdmn )
return & allCountries [ i ] ;
}
return NULL ;
}
/* Returns the map of the EEPROM set RD to a country code */
2009-03-31 06:30:29 +04:00
static u16 ath_regd_get_default_country ( u16 rd )
2008-08-04 11:16:41 +04:00
{
if ( rd & COUNTRY_ERD_FLAG ) {
struct country_code_to_enum_rd * country = NULL ;
u16 cc = rd & ~ COUNTRY_ERD_FLAG ;
2009-03-31 06:30:29 +04:00
country = ath_regd_find_country ( cc ) ;
2008-08-04 11:16:41 +04:00
if ( country ! = NULL )
return cc ;
}
return CTRY_DEFAULT ;
}
2009-01-23 02:16:48 +03:00
static struct reg_dmn_pair_mapping *
2009-03-31 06:30:29 +04:00
ath_get_regpair ( int regdmn )
2008-08-04 11:16:41 +04:00
{
int i ;
2009-01-23 02:16:48 +03:00
if ( regdmn = = NO_ENUMRD )
return NULL ;
2008-08-04 11:16:41 +04:00
for ( i = 0 ; i < ARRAY_SIZE ( regDomainPairs ) ; i + + ) {
2014-02-13 20:13:12 +04:00
if ( regDomainPairs [ i ] . reg_domain = = regdmn )
2009-01-23 02:16:48 +03:00
return & regDomainPairs [ i ] ;
2008-08-04 11:16:41 +04:00
}
2009-01-23 02:16:48 +03:00
return NULL ;
2008-08-04 11:16:41 +04:00
}
2009-03-31 06:30:34 +04:00
static int
ath_regd_init_wiphy ( struct ath_regulatory * reg ,
struct wiphy * wiphy ,
2013-01-11 22:39:36 +04:00
void ( * reg_notifier ) ( struct wiphy * wiphy ,
struct regulatory_request * request ) )
2009-03-31 06:30:30 +04:00
{
const struct ieee80211_regdomain * regd ;
wiphy - > reg_notifier = reg_notifier ;
2013-12-14 23:09:05 +04:00
wiphy - > regulatory_flags | = REGULATORY_STRICT_REG |
REGULATORY_CUSTOM_REG ;
2009-03-31 06:30:30 +04:00
if ( ath_is_world_regd ( reg ) ) {
/*
* Anything applied here ( prior to wiphy registration ) gets
* saved on the wiphy orig_ * parameters
*/
regd = ath_world_regdomain ( reg ) ;
2013-12-14 23:09:05 +04:00
wiphy - > regulatory_flags | = REGULATORY_COUNTRY_IE_FOLLOW_POWER ;
2009-03-31 06:30:30 +04:00
} else {
/*
2011-03-31 05:57:33 +04:00
* This gets applied in the case of the absence of CRDA ,
2009-03-31 06:30:30 +04:00
* it ' s our own custom world regulatory domain , similar to
* cfg80211 ' s but we enable passive scanning .
*/
regd = ath_default_world_regdomain ( ) ;
}
2013-12-14 23:09:05 +04:00
2009-03-31 06:30:30 +04:00
wiphy_apply_custom_regulatory ( wiphy , regd ) ;
ath_reg_apply_radar_flags ( wiphy ) ;
ath_reg_apply_world_flags ( wiphy , NL80211_REGDOM_SET_BY_DRIVER , reg ) ;
return 0 ;
}
2009-07-20 19:32:47 +04:00
/*
* Some users have reported their EEPROM programmed with
* 0x8000 set , this is not a supported regulatory domain
* but since we have more than one user with it we need
* a solution for them . We default to 0x64 , which is the
* default Atheros world regulatory domain .
*/
static void ath_regd_sanitize ( struct ath_regulatory * reg )
{
if ( reg - > current_rd ! = COUNTRY_ERD_FLAG )
return ;
printk ( KERN_DEBUG " ath: EEPROM regdomain sanitized \n " ) ;
reg - > current_rd = 0x64 ;
}
2011-12-08 22:29:23 +04:00
static int __ath_regd_init ( struct ath_regulatory * reg )
2008-08-04 11:16:41 +04:00
{
struct country_code_to_enum_rd * country = NULL ;
2009-02-12 21:38:54 +03:00
u16 regdmn ;
2008-08-04 11:16:41 +04:00
2009-06-03 00:30:56 +04:00
if ( ! reg )
return - EINVAL ;
2009-07-20 19:32:47 +04:00
ath_regd_sanitize ( reg ) ;
2009-06-03 00:30:56 +04:00
printk ( KERN_DEBUG " ath: EEPROM regdomain: 0x%0x \n " , reg - > current_rd ) ;
2009-03-31 06:30:29 +04:00
if ( ! ath_regd_is_eeprom_valid ( reg ) ) {
2012-03-19 04:30:52 +04:00
pr_err ( " Invalid EEPROM contents \n " ) ;
2009-01-23 02:16:48 +03:00
return - EINVAL ;
2008-08-04 11:16:41 +04:00
}
2009-03-31 06:30:29 +04:00
regdmn = ath_regd_get_eepromRD ( reg ) ;
reg - > country_code = ath_regd_get_default_country ( regdmn ) ;
2008-08-04 11:16:41 +04:00
2009-03-31 06:30:28 +04:00
if ( reg - > country_code = = CTRY_DEFAULT & &
2009-06-03 00:30:56 +04:00
regdmn = = CTRY_DEFAULT ) {
printk ( KERN_DEBUG " ath: EEPROM indicates default "
" country code should be used \n " ) ;
2009-03-31 06:30:28 +04:00
reg - > country_code = CTRY_UNITED_STATES ;
2009-06-03 00:30:56 +04:00
}
2008-08-04 11:16:41 +04:00
2009-03-31 06:30:28 +04:00
if ( reg - > country_code = = CTRY_DEFAULT ) {
2008-08-04 11:16:41 +04:00
country = NULL ;
} else {
2009-06-03 00:30:56 +04:00
printk ( KERN_DEBUG " ath: doing EEPROM country->regdmn "
" map search \n " ) ;
2009-03-31 06:30:29 +04:00
country = ath_regd_find_country ( reg - > country_code ) ;
2008-08-04 11:16:41 +04:00
if ( country = = NULL ) {
2009-03-31 06:30:28 +04:00
printk ( KERN_DEBUG
2009-06-03 00:30:56 +04:00
" ath: no valid country maps found for "
" country code: 0x%0x \n " ,
2009-03-31 06:30:28 +04:00
reg - > country_code ) ;
2009-01-23 02:16:48 +03:00
return - EINVAL ;
2009-06-03 00:30:56 +04:00
} else {
2008-08-04 11:16:41 +04:00
regdmn = country - > regDmnEnum ;
2009-06-03 00:30:56 +04:00
printk ( KERN_DEBUG " ath: country maps to "
" regdmn code: 0x%0x \n " ,
regdmn ) ;
}
2008-08-04 11:16:41 +04:00
}
2009-03-31 06:30:29 +04:00
reg - > regpair = ath_get_regpair ( regdmn ) ;
2008-08-04 11:16:41 +04:00
2009-03-31 06:30:28 +04:00
if ( ! reg - > regpair ) {
2009-03-31 06:30:29 +04:00
printk ( KERN_DEBUG " ath: "
2009-01-23 02:16:48 +03:00
" No regulatory domain pair found, cannot continue \n " ) ;
return - EINVAL ;
2008-08-04 11:16:41 +04:00
}
2009-01-23 02:16:48 +03:00
if ( ! country )
2009-03-31 06:30:29 +04:00
country = ath_regd_find_country_by_rd ( regdmn ) ;
2008-08-04 11:16:41 +04:00
2009-01-23 02:16:48 +03:00
if ( country ) {
2009-03-31 06:30:28 +04:00
reg - > alpha2 [ 0 ] = country - > isoName [ 0 ] ;
reg - > alpha2 [ 1 ] = country - > isoName [ 1 ] ;
2008-08-04 11:16:41 +04:00
} else {
2009-03-31 06:30:28 +04:00
reg - > alpha2 [ 0 ] = ' 0 ' ;
reg - > alpha2 [ 1 ] = ' 0 ' ;
2008-08-04 11:16:41 +04:00
}
2009-03-31 06:30:29 +04:00
printk ( KERN_DEBUG " ath: Country alpha2 being used: %c%c \n " ,
2009-03-31 06:30:28 +04:00
reg - > alpha2 [ 0 ] , reg - > alpha2 [ 1 ] ) ;
2009-06-03 00:30:56 +04:00
printk ( KERN_DEBUG " ath: Regpair used: 0x%0x \n " ,
2014-02-13 20:13:12 +04:00
reg - > regpair - > reg_domain ) ;
2008-08-04 11:16:41 +04:00
2011-12-08 22:29:23 +04:00
return 0 ;
}
int
ath_regd_init ( struct ath_regulatory * reg ,
struct wiphy * wiphy ,
2013-01-11 22:39:36 +04:00
void ( * reg_notifier ) ( struct wiphy * wiphy ,
struct regulatory_request * request ) )
2011-12-08 22:29:23 +04:00
{
2011-12-08 22:29:24 +04:00
struct ath_common * common = container_of ( reg , struct ath_common ,
regulatory ) ;
2011-12-08 22:29:23 +04:00
int r ;
r = __ath_regd_init ( reg ) ;
if ( r )
return r ;
2011-12-08 22:29:24 +04:00
if ( ath_is_world_regd ( reg ) )
memcpy ( & common - > reg_world_copy , reg ,
sizeof ( struct ath_regulatory ) ) ;
2009-03-31 06:30:30 +04:00
ath_regd_init_wiphy ( reg , wiphy , reg_notifier ) ;
2011-12-08 22:29:23 +04:00
2009-01-23 02:16:48 +03:00
return 0 ;
2008-08-04 11:16:41 +04:00
}
2009-03-31 06:30:29 +04:00
EXPORT_SYMBOL ( ath_regd_init ) ;
2008-08-04 11:16:41 +04:00
2009-03-31 06:30:29 +04:00
u32 ath_regd_get_band_ctl ( struct ath_regulatory * reg ,
enum ieee80211_band band )
2008-08-04 11:16:41 +04:00
{
2009-03-31 06:30:28 +04:00
if ( ! reg - > regpair | |
( reg - > country_code = = CTRY_DEFAULT & &
2009-03-31 06:30:29 +04:00
is_wwr_sku ( ath_regd_get_eepromRD ( reg ) ) ) ) {
2009-03-31 06:30:27 +04:00
return SD_NO_CTL ;
}
2014-10-22 17:27:53 +04:00
if ( ath_regd_get_eepromRD ( reg ) = = CTRY_DEFAULT ) {
switch ( reg - > region ) {
case NL80211_DFS_FCC :
return CTL_FCC ;
case NL80211_DFS_ETSI :
return CTL_ETSI ;
case NL80211_DFS_JP :
return CTL_MKK ;
default :
break ;
}
}
2009-03-31 06:30:27 +04:00
switch ( band ) {
case IEEE80211_BAND_2GHZ :
2009-03-31 06:30:28 +04:00
return reg - > regpair - > reg_2ghz_ctl ;
2009-03-31 06:30:27 +04:00
case IEEE80211_BAND_5GHZ :
2009-03-31 06:30:28 +04:00
return reg - > regpair - > reg_5ghz_ctl ;
2009-03-31 06:30:27 +04:00
default :
return NO_CTL ;
2008-08-04 11:16:41 +04:00
}
2009-03-31 06:30:27 +04:00
}
2009-03-31 06:30:29 +04:00
EXPORT_SYMBOL ( ath_regd_get_band_ctl ) ;