2008-01-24 21:38:38 +03:00
/*
* Copyright 2002 - 2005 , Instant802 Networks , Inc .
* Copyright 2005 - 2006 , Devicescape Software , Inc .
* Copyright 2007 Johannes Berg < johannes @ sipsolutions . net >
2008-09-10 10:19:48 +04:00
* Copyright 2008 Luis R . Rodriguez < lrodriguz @ atheros . com >
2008-01-24 21:38:38 +03:00
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*/
2008-09-10 10:19:48 +04:00
/**
* DOC : Wireless regulatory infrastructure
2008-01-24 21:38:38 +03:00
*
* The usual implementation is for a driver to read a device EEPROM to
* determine which regulatory domain it should be operating under , then
* looking up the allowable channels in a driver - local table and finally
* registering those channels in the wiphy structure .
*
2008-09-10 10:19:48 +04:00
* Another set of compliance enforcement is for drivers to use their
* own compliance limits which can be stored on the EEPROM . The host
* driver or firmware may ensure these are used .
*
* In addition to all this we provide an extra layer of regulatory
* conformance . For drivers which do not have any regulatory
* information CRDA provides the complete regulatory solution .
* For others it provides a community effort on further restrictions
* to enhance compliance .
*
* Note : When number of rules - - > infinity we will not be able to
* index on alpha2 any more , instead we ' ll probably have to
* rely on some SHA1 checksum of the regdomain for example .
*
2008-01-24 21:38:38 +03:00
*/
# include <linux/kernel.h>
2008-09-10 10:19:48 +04:00
# include <linux/list.h>
# include <linux/random.h>
# include <linux/nl80211.h>
# include <linux/platform_device.h>
# include <net/cfg80211.h>
2008-01-24 21:38:38 +03:00
# include "core.h"
2008-09-10 10:19:48 +04:00
# include "reg.h"
2009-03-10 05:07:42 +03:00
# include "nl80211.h"
2008-01-24 21:38:38 +03:00
2008-10-30 23:33:56 +03:00
/* Receipt of information from last regulatory request */
2008-10-21 13:01:33 +04:00
static struct regulatory_request * last_request ;
2008-09-15 12:56:48 +04:00
2008-09-10 10:19:48 +04:00
/* To trigger userspace events */
static struct platform_device * reg_pdev ;
2008-01-24 21:38:38 +03:00
2009-02-21 08:04:31 +03:00
/*
* Central wireless core regulatory domains , we only need two ,
2008-09-15 12:56:48 +04:00
* the current one and a world regulatory domain in case we have no
2009-02-21 08:04:31 +03:00
* information to give us an alpha2
*/
2009-01-30 20:26:42 +03:00
const struct ieee80211_regdomain * cfg80211_regdomain ;
2008-09-15 12:56:48 +04:00
2009-02-21 08:04:31 +03:00
/*
* We use this as a place for the rd structure built from the
2008-11-13 01:22:02 +03:00
* last parsed country IE to rest until CRDA gets back to us with
2009-02-21 08:04:31 +03:00
* what it thinks should apply for the same country
*/
2008-11-13 01:22:02 +03:00
static const struct ieee80211_regdomain * country_ie_regdomain ;
2009-07-31 04:38:08 +04:00
/*
* Protects static reg . c components :
* - cfg80211_world_regdom
* - cfg80211_regdom
* - country_ie_regdomain
* - last_request
*/
DEFINE_MUTEX ( reg_mutex ) ;
# define assert_reg_lock() WARN_ON(!mutex_is_locked(®_mutex))
2009-02-21 08:20:39 +03:00
/* Used to queue up regulatory hints */
2009-02-21 08:04:30 +03:00
static LIST_HEAD ( reg_requests_list ) ;
static spinlock_t reg_requests_lock ;
2009-02-21 08:20:39 +03:00
/* Used to queue up beacon hints for review */
static LIST_HEAD ( reg_pending_beacons ) ;
static spinlock_t reg_pending_beacons_lock ;
/* Used to keep track of processed beacon hints */
static LIST_HEAD ( reg_beacon_list ) ;
struct reg_beacon {
struct list_head list ;
struct ieee80211_channel chan ;
} ;
2008-09-15 12:56:48 +04:00
/* We keep a static world regulatory domain in case of the absence of CRDA */
static const struct ieee80211_regdomain world_regdom = {
2009-03-06 08:19:21 +03:00
. n_reg_rules = 5 ,
2008-09-15 12:56:48 +04:00
. alpha2 = " 00 " ,
. reg_rules = {
2009-02-21 08:20:37 +03:00
/* IEEE 802.11b/g, channels 1..11 */
REG_RULE ( 2412 - 10 , 2462 + 10 , 40 , 6 , 20 , 0 ) ,
2009-03-06 08:19:21 +03:00
/* IEEE 802.11b/g, channels 12..13. No HT40
* channel fits here . */
REG_RULE ( 2467 - 10 , 2472 + 10 , 20 , 6 , 20 ,
2009-02-21 08:20:38 +03:00
NL80211_RRF_PASSIVE_SCAN |
NL80211_RRF_NO_IBSS ) ,
2009-03-06 08:19:21 +03:00
/* IEEE 802.11 channel 14 - Only JP enables
* this and for 802.11 b only */
REG_RULE ( 2484 - 10 , 2484 + 10 , 20 , 6 , 20 ,
NL80211_RRF_PASSIVE_SCAN |
NL80211_RRF_NO_IBSS |
NL80211_RRF_NO_OFDM ) ,
/* IEEE 802.11a, channel 36..48 */
2009-03-06 08:19:22 +03:00
REG_RULE ( 5180 - 10 , 5240 + 10 , 40 , 6 , 20 ,
2009-03-06 08:19:21 +03:00
NL80211_RRF_PASSIVE_SCAN |
NL80211_RRF_NO_IBSS ) ,
2009-02-21 08:20:38 +03:00
/* NB: 5260 MHz - 5700 MHz requies DFS */
/* IEEE 802.11a, channel 149..165 */
2009-03-06 08:19:22 +03:00
REG_RULE ( 5745 - 10 , 5825 + 10 , 40 , 6 , 20 ,
2009-02-21 08:20:38 +03:00
NL80211_RRF_PASSIVE_SCAN |
NL80211_RRF_NO_IBSS ) ,
2008-09-15 12:56:48 +04:00
}
} ;
2008-09-15 13:10:52 +04:00
static const struct ieee80211_regdomain * cfg80211_world_regdom =
& world_regdom ;
2008-09-15 12:56:48 +04:00
2009-03-21 06:53:06 +03:00
static char * ieee80211_regdom = " 00 " ;
2008-09-15 12:56:48 +04:00
module_param ( ieee80211_regdom , charp , 0444 ) ;
MODULE_PARM_DESC ( ieee80211_regdom , " IEEE 802.11 regulatory domain code " ) ;
2009-03-21 06:53:06 +03:00
# ifdef CONFIG_WIRELESS_OLD_REGULATORY
2009-02-21 08:04:31 +03:00
/*
* We assume 40 MHz bandwidth for the old regulatory work .
2008-09-15 12:56:48 +04:00
* We make emphasis we are using the exact same frequencies
2009-02-21 08:04:31 +03:00
* as before
*/
2008-09-15 12:56:48 +04:00
static const struct ieee80211_regdomain us_regdom = {
. n_reg_rules = 6 ,
. alpha2 = " US " ,
. reg_rules = {
/* IEEE 802.11b/g, channels 1..11 */
REG_RULE ( 2412 - 10 , 2462 + 10 , 40 , 6 , 27 , 0 ) ,
/* IEEE 802.11a, channel 36 */
REG_RULE ( 5180 - 10 , 5180 + 10 , 40 , 6 , 23 , 0 ) ,
/* IEEE 802.11a, channel 40 */
REG_RULE ( 5200 - 10 , 5200 + 10 , 40 , 6 , 23 , 0 ) ,
/* IEEE 802.11a, channel 44 */
REG_RULE ( 5220 - 10 , 5220 + 10 , 40 , 6 , 23 , 0 ) ,
/* IEEE 802.11a, channels 48..64 */
REG_RULE ( 5240 - 10 , 5320 + 10 , 40 , 6 , 23 , 0 ) ,
/* IEEE 802.11a, channels 149..165, outdoor */
REG_RULE ( 5745 - 10 , 5825 + 10 , 40 , 6 , 30 , 0 ) ,
}
} ;
static const struct ieee80211_regdomain jp_regdom = {
. n_reg_rules = 3 ,
. alpha2 = " JP " ,
. reg_rules = {
/* IEEE 802.11b/g, channels 1..14 */
REG_RULE ( 2412 - 10 , 2484 + 10 , 40 , 6 , 20 , 0 ) ,
/* IEEE 802.11a, channels 34..48 */
REG_RULE ( 5170 - 10 , 5240 + 10 , 40 , 6 , 20 ,
NL80211_RRF_PASSIVE_SCAN ) ,
/* IEEE 802.11a, channels 52..64 */
REG_RULE ( 5260 - 10 , 5320 + 10 , 40 , 6 , 20 ,
NL80211_RRF_NO_IBSS |
NL80211_RRF_DFS ) ,
}
} ;
static const struct ieee80211_regdomain eu_regdom = {
. n_reg_rules = 6 ,
2009-02-21 08:04:31 +03:00
/*
* This alpha2 is bogus , we leave it here just for stupid
* backward compatibility
*/
2008-09-15 12:56:48 +04:00
. alpha2 = " EU " ,
. reg_rules = {
/* IEEE 802.11b/g, channels 1..13 */
REG_RULE ( 2412 - 10 , 2472 + 10 , 40 , 6 , 20 , 0 ) ,
/* IEEE 802.11a, channel 36 */
REG_RULE ( 5180 - 10 , 5180 + 10 , 40 , 6 , 23 ,
NL80211_RRF_PASSIVE_SCAN ) ,
/* IEEE 802.11a, channel 40 */
REG_RULE ( 5200 - 10 , 5200 + 10 , 40 , 6 , 23 ,
NL80211_RRF_PASSIVE_SCAN ) ,
/* IEEE 802.11a, channel 44 */
REG_RULE ( 5220 - 10 , 5220 + 10 , 40 , 6 , 23 ,
NL80211_RRF_PASSIVE_SCAN ) ,
/* IEEE 802.11a, channels 48..64 */
REG_RULE ( 5240 - 10 , 5320 + 10 , 40 , 6 , 20 ,
NL80211_RRF_NO_IBSS |
NL80211_RRF_DFS ) ,
/* IEEE 802.11a, channels 100..140 */
REG_RULE ( 5500 - 10 , 5700 + 10 , 40 , 6 , 30 ,
NL80211_RRF_NO_IBSS |
NL80211_RRF_DFS ) ,
}
} ;
static const struct ieee80211_regdomain * static_regdom ( char * alpha2 )
{
if ( alpha2 [ 0 ] = = ' U ' & & alpha2 [ 1 ] = = ' S ' )
return & us_regdom ;
if ( alpha2 [ 0 ] = = ' J ' & & alpha2 [ 1 ] = = ' P ' )
return & jp_regdom ;
if ( alpha2 [ 0 ] = = ' E ' & & alpha2 [ 1 ] = = ' U ' )
return & eu_regdom ;
/* Default, as per the old rules */
return & us_regdom ;
}
2008-09-15 13:10:52 +04:00
static bool is_old_static_regdom ( const struct ieee80211_regdomain * rd )
2008-09-15 12:56:48 +04:00
{
if ( rd = = & us_regdom | | rd = = & jp_regdom | | rd = = & eu_regdom )
return true ;
return false ;
}
2008-09-15 13:26:47 +04:00
# else
static inline bool is_old_static_regdom ( const struct ieee80211_regdomain * rd )
2008-09-15 12:56:48 +04:00
{
2008-09-15 13:26:47 +04:00
return false ;
2008-09-15 12:56:48 +04:00
}
2008-09-15 13:26:47 +04:00
# endif
2008-09-15 12:56:48 +04:00
static void reset_regdomains ( void )
{
2008-09-15 13:26:47 +04:00
/* avoid freeing static information or freeing something twice */
if ( cfg80211_regdomain = = cfg80211_world_regdom )
cfg80211_regdomain = NULL ;
if ( cfg80211_world_regdom = = & world_regdom )
cfg80211_world_regdom = NULL ;
if ( cfg80211_regdomain = = & world_regdom )
cfg80211_regdomain = NULL ;
if ( is_old_static_regdom ( cfg80211_regdomain ) )
cfg80211_regdomain = NULL ;
kfree ( cfg80211_regdomain ) ;
kfree ( cfg80211_world_regdom ) ;
2008-09-15 12:56:48 +04:00
2008-09-15 13:10:52 +04:00
cfg80211_world_regdom = & world_regdom ;
2008-09-15 12:56:48 +04:00
cfg80211_regdomain = NULL ;
}
2009-02-21 08:04:31 +03:00
/*
* Dynamic world regulatory domain requested by the wireless
* core upon initialization
*/
2008-09-15 13:10:52 +04:00
static void update_world_regdomain ( const struct ieee80211_regdomain * rd )
2008-09-15 12:56:48 +04:00
{
2008-10-21 13:01:33 +04:00
BUG_ON ( ! last_request ) ;
2008-09-15 12:56:48 +04:00
reset_regdomains ( ) ;
cfg80211_world_regdom = rd ;
cfg80211_regdomain = rd ;
}
2008-09-15 13:10:52 +04:00
bool is_world_regdom ( const char * alpha2 )
2008-09-10 10:19:48 +04:00
{
if ( ! alpha2 )
return false ;
if ( alpha2 [ 0 ] = = ' 0 ' & & alpha2 [ 1 ] = = ' 0 ' )
return true ;
return false ;
}
2008-01-24 21:38:38 +03:00
2008-09-15 13:10:52 +04:00
static bool is_alpha2_set ( const char * alpha2 )
2008-09-10 10:19:48 +04:00
{
if ( ! alpha2 )
return false ;
if ( alpha2 [ 0 ] ! = 0 & & alpha2 [ 1 ] ! = 0 )
return true ;
return false ;
}
2008-01-24 21:38:38 +03:00
2008-09-10 10:19:48 +04:00
static bool is_alpha_upper ( char letter )
{
/* ASCII A - Z */
if ( letter > = 65 & & letter < = 90 )
return true ;
return false ;
}
2008-01-24 21:38:38 +03:00
2008-09-15 13:10:52 +04:00
static bool is_unknown_alpha2 ( const char * alpha2 )
2008-09-10 10:19:48 +04:00
{
if ( ! alpha2 )
return false ;
2009-02-21 08:04:31 +03:00
/*
* Special case where regulatory domain was built by driver
* but a specific alpha2 cannot be determined
*/
2008-09-10 10:19:48 +04:00
if ( alpha2 [ 0 ] = = ' 9 ' & & alpha2 [ 1 ] = = ' 9 ' )
return true ;
return false ;
}
2008-01-24 21:38:38 +03:00
2008-11-13 01:22:02 +03:00
static bool is_intersected_alpha2 ( const char * alpha2 )
{
if ( ! alpha2 )
return false ;
2009-02-21 08:04:31 +03:00
/*
* Special case where regulatory domain is the
2008-11-13 01:22:02 +03:00
* result of an intersection between two regulatory domain
2009-02-21 08:04:31 +03:00
* structures
*/
2008-11-13 01:22:02 +03:00
if ( alpha2 [ 0 ] = = ' 9 ' & & alpha2 [ 1 ] = = ' 8 ' )
return true ;
return false ;
}
2008-09-15 13:10:52 +04:00
static bool is_an_alpha2 ( const char * alpha2 )
2008-09-10 10:19:48 +04:00
{
if ( ! alpha2 )
return false ;
if ( is_alpha_upper ( alpha2 [ 0 ] ) & & is_alpha_upper ( alpha2 [ 1 ] ) )
return true ;
return false ;
}
2008-01-24 21:38:38 +03:00
2008-09-15 13:10:52 +04:00
static bool alpha2_equal ( const char * alpha2_x , const char * alpha2_y )
2008-09-10 10:19:48 +04:00
{
if ( ! alpha2_x | | ! alpha2_y )
return false ;
if ( alpha2_x [ 0 ] = = alpha2_y [ 0 ] & &
alpha2_x [ 1 ] = = alpha2_y [ 1 ] )
return true ;
return false ;
}
2009-02-21 08:04:33 +03:00
static bool regdom_changes ( const char * alpha2 )
2008-09-10 10:19:48 +04:00
{
2009-02-21 08:04:25 +03:00
assert_cfg80211_lock ( ) ;
2008-09-10 10:19:48 +04:00
if ( ! cfg80211_regdomain )
return true ;
if ( alpha2_equal ( cfg80211_regdomain - > alpha2 , alpha2 ) )
return false ;
return true ;
}
2008-11-13 01:22:02 +03:00
/**
* country_ie_integrity_changes - tells us if the country IE has changed
* @ checksum : checksum of country IE of fields we are interested in
*
* If the country IE has not changed you can ignore it safely . This is
* useful to determine if two devices are seeing two different country IEs
* even on the same alpha2 . Note that this will return false if no IE has
* been set on the wireless core yet .
*/
static bool country_ie_integrity_changes ( u32 checksum )
{
/* If no IE has been set then the checksum doesn't change */
if ( unlikely ( ! last_request - > country_ie_checksum ) )
return false ;
if ( unlikely ( last_request - > country_ie_checksum ! = checksum ) )
return true ;
return false ;
}
2009-02-21 08:04:31 +03:00
/*
* This lets us keep regulatory code which is updated on a regulatory
* basis in userspace .
*/
2008-09-10 10:19:48 +04:00
static int call_crda ( const char * alpha2 )
{
char country_env [ 9 + 2 ] = " COUNTRY= " ;
char * envp [ ] = {
country_env ,
NULL
} ;
if ( ! is_world_regdom ( ( char * ) alpha2 ) )
printk ( KERN_INFO " cfg80211: Calling CRDA for country: %c%c \n " ,
alpha2 [ 0 ] , alpha2 [ 1 ] ) ;
else
printk ( KERN_INFO " cfg80211: Calling CRDA to update world "
" regulatory domain \n " ) ;
country_env [ 8 ] = alpha2 [ 0 ] ;
country_env [ 9 ] = alpha2 [ 1 ] ;
return kobject_uevent_env ( & reg_pdev - > dev . kobj , KOBJ_CHANGE , envp ) ;
}
/* Used by nl80211 before kmalloc'ing our regulatory domain */
2008-09-15 13:10:52 +04:00
bool reg_is_valid_request ( const char * alpha2 )
2008-09-10 10:19:48 +04:00
{
2009-05-14 01:04:41 +04:00
assert_cfg80211_lock ( ) ;
2008-10-21 13:01:33 +04:00
if ( ! last_request )
return false ;
return alpha2_equal ( last_request - > alpha2 , alpha2 ) ;
2008-09-10 10:19:48 +04:00
}
2008-01-24 21:38:38 +03:00
2008-09-10 10:19:48 +04:00
/* Sanity check on a regulatory rule */
2008-09-15 13:10:52 +04:00
static bool is_valid_reg_rule ( const struct ieee80211_reg_rule * rule )
2008-01-24 21:38:38 +03:00
{
2008-09-15 13:10:52 +04:00
const struct ieee80211_freq_range * freq_range = & rule - > freq_range ;
2008-09-10 10:19:48 +04:00
u32 freq_diff ;
2008-11-13 01:21:55 +03:00
if ( freq_range - > start_freq_khz < = 0 | | freq_range - > end_freq_khz < = 0 )
2008-09-10 10:19:48 +04:00
return false ;
if ( freq_range - > start_freq_khz > freq_range - > end_freq_khz )
return false ;
freq_diff = freq_range - > end_freq_khz - freq_range - > start_freq_khz ;
2009-03-04 00:55:21 +03:00
if ( freq_range - > end_freq_khz < = freq_range - > start_freq_khz | |
freq_range - > max_bandwidth_khz > freq_diff )
2008-09-10 10:19:48 +04:00
return false ;
return true ;
}
2008-09-15 13:10:52 +04:00
static bool is_valid_rd ( const struct ieee80211_regdomain * rd )
2008-09-10 10:19:48 +04:00
{
2008-09-15 13:10:52 +04:00
const struct ieee80211_reg_rule * reg_rule = NULL ;
2008-09-10 10:19:48 +04:00
unsigned int i ;
2008-01-24 21:38:38 +03:00
2008-09-10 10:19:48 +04:00
if ( ! rd - > n_reg_rules )
return false ;
2008-01-24 21:38:38 +03:00
2008-11-13 01:22:01 +03:00
if ( WARN_ON ( rd - > n_reg_rules > NL80211_MAX_SUPP_REG_RULES ) )
return false ;
2008-09-10 10:19:48 +04:00
for ( i = 0 ; i < rd - > n_reg_rules ; i + + ) {
reg_rule = & rd - > reg_rules [ i ] ;
if ( ! is_valid_reg_rule ( reg_rule ) )
return false ;
}
return true ;
2008-01-24 21:38:38 +03:00
}
2009-05-02 08:37:17 +04:00
static bool reg_does_bw_fit ( const struct ieee80211_freq_range * freq_range ,
u32 center_freq_khz ,
u32 bw_khz )
2008-09-10 10:19:48 +04:00
{
2009-05-02 08:37:17 +04:00
u32 start_freq_khz , end_freq_khz ;
start_freq_khz = center_freq_khz - ( bw_khz / 2 ) ;
end_freq_khz = center_freq_khz + ( bw_khz / 2 ) ;
if ( start_freq_khz > = freq_range - > start_freq_khz & &
end_freq_khz < = freq_range - > end_freq_khz )
return true ;
return false ;
2008-09-10 10:19:48 +04:00
}
2008-01-24 21:38:38 +03:00
2009-01-08 04:43:36 +03:00
/**
* freq_in_rule_band - tells us if a frequency is in a frequency band
* @ freq_range : frequency rule we want to query
* @ freq_khz : frequency we are inquiring about
*
* This lets us know if a specific frequency rule is or is not relevant to
* a specific frequency ' s band . Bands are device specific and artificial
* definitions ( the " 2.4 GHz band " and the " 5 GHz band " ) , however it is
* safe for now to assume that a frequency rule should not be part of a
* frequency ' s band if the start freq or end freq are off by more than 2 GHz .
* This resolution can be lowered and should be considered as we add
* regulatory rule support for other " bands " .
* */
static bool freq_in_rule_band ( const struct ieee80211_freq_range * freq_range ,
u32 freq_khz )
{
# define ONE_GHZ_IN_KHZ 1000000
if ( abs ( freq_khz - freq_range - > start_freq_khz ) < = ( 2 * ONE_GHZ_IN_KHZ ) )
return true ;
if ( abs ( freq_khz - freq_range - > end_freq_khz ) < = ( 2 * ONE_GHZ_IN_KHZ ) )
return true ;
return false ;
# undef ONE_GHZ_IN_KHZ
}
2009-02-21 08:04:31 +03:00
/*
* Converts a country IE to a regulatory domain . A regulatory domain
2008-11-13 01:22:02 +03:00
* structure has a lot of information which the IE doesn ' t yet have ,
* so for the other values we use upper max values as we will intersect
2009-02-21 08:04:31 +03:00
* with our userspace regulatory agent to get lower bounds .
*/
2008-11-13 01:22:02 +03:00
static struct ieee80211_regdomain * country_ie_2_rd (
u8 * country_ie ,
u8 country_ie_len ,
u32 * checksum )
{
struct ieee80211_regdomain * rd = NULL ;
unsigned int i = 0 ;
char alpha2 [ 2 ] ;
u32 flags = 0 ;
u32 num_rules = 0 , size_of_regd = 0 ;
u8 * triplets_start = NULL ;
u8 len_at_triplet = 0 ;
/* the last channel we have registered in a subband (triplet) */
int last_sub_max_channel = 0 ;
* checksum = 0xDEADBEEF ;
/* Country IE requirements */
BUG_ON ( country_ie_len < IEEE80211_COUNTRY_IE_MIN_LEN | |
country_ie_len & 0x01 ) ;
alpha2 [ 0 ] = country_ie [ 0 ] ;
alpha2 [ 1 ] = country_ie [ 1 ] ;
/*
* Third octet can be :
* ' I ' - Indoor
* ' O ' - Outdoor
*
* anything else we assume is no restrictions
*/
if ( country_ie [ 2 ] = = ' I ' )
flags = NL80211_RRF_NO_OUTDOOR ;
else if ( country_ie [ 2 ] = = ' O ' )
flags = NL80211_RRF_NO_INDOOR ;
country_ie + = 3 ;
country_ie_len - = 3 ;
triplets_start = country_ie ;
len_at_triplet = country_ie_len ;
* checksum ^ = ( ( flags ^ alpha2 [ 0 ] ^ alpha2 [ 1 ] ) < < 8 ) ;
2009-02-21 08:04:31 +03:00
/*
* We need to build a reg rule for each triplet , but first we must
2008-11-13 01:22:02 +03:00
* calculate the number of reg rules we will need . We will need one
2009-02-21 08:04:31 +03:00
* for each channel subband
*/
2008-11-13 01:22:02 +03:00
while ( country_ie_len > = 3 ) {
2009-01-23 02:05:46 +03:00
int end_channel = 0 ;
2008-11-13 01:22:02 +03:00
struct ieee80211_country_ie_triplet * triplet =
( struct ieee80211_country_ie_triplet * ) country_ie ;
int cur_sub_max_channel = 0 , cur_channel = 0 ;
if ( triplet - > ext . reg_extension_id > =
IEEE80211_COUNTRY_EXTENSION_ID ) {
country_ie + = 3 ;
country_ie_len - = 3 ;
continue ;
}
2009-01-23 02:05:46 +03:00
/* 2 GHz */
if ( triplet - > chans . first_channel < = 14 )
end_channel = triplet - > chans . first_channel +
triplet - > chans . num_channels ;
else
/*
* 5 GHz - - For example in country IEs if the first
* channel given is 36 and the number of channels is 4
* then the individual channel numbers defined for the
* 5 GHz PHY by these parameters are : 36 , 40 , 44 , and 48
* and not 36 , 37 , 38 , 39.
*
* See : http : //tinyurl.com/11d-clarification
*/
end_channel = triplet - > chans . first_channel +
( 4 * ( triplet - > chans . num_channels - 1 ) ) ;
2008-11-13 01:22:02 +03:00
cur_channel = triplet - > chans . first_channel ;
2009-01-23 02:05:46 +03:00
cur_sub_max_channel = end_channel ;
2008-11-13 01:22:02 +03:00
/* Basic sanity check */
if ( cur_sub_max_channel < cur_channel )
return NULL ;
2009-02-21 08:04:31 +03:00
/*
* Do not allow overlapping channels . Also channels
2008-11-13 01:22:02 +03:00
* passed in each subband must be monotonically
2009-02-21 08:04:31 +03:00
* increasing
*/
2008-11-13 01:22:02 +03:00
if ( last_sub_max_channel ) {
if ( cur_channel < = last_sub_max_channel )
return NULL ;
if ( cur_sub_max_channel < = last_sub_max_channel )
return NULL ;
}
2009-02-21 08:04:31 +03:00
/*
* When dot11RegulatoryClassesRequired is supported
2008-11-13 01:22:02 +03:00
* we can throw ext triplets as part of this soup ,
* for now we don ' t care when those change as we
2009-02-21 08:04:31 +03:00
* don ' t support them
*/
2008-11-13 01:22:02 +03:00
* checksum ^ = ( ( cur_channel ^ cur_sub_max_channel ) < < 8 ) |
( ( cur_sub_max_channel ^ cur_sub_max_channel ) < < 16 ) |
( ( triplet - > chans . max_power ^ cur_sub_max_channel ) < < 24 ) ;
last_sub_max_channel = cur_sub_max_channel ;
country_ie + = 3 ;
country_ie_len - = 3 ;
num_rules + + ;
2009-02-21 08:04:31 +03:00
/*
* Note : this is not a IEEE requirement but
* simply a memory requirement
*/
2008-11-13 01:22:02 +03:00
if ( num_rules > NL80211_MAX_SUPP_REG_RULES )
return NULL ;
}
country_ie = triplets_start ;
country_ie_len = len_at_triplet ;
size_of_regd = sizeof ( struct ieee80211_regdomain ) +
( num_rules * sizeof ( struct ieee80211_reg_rule ) ) ;
rd = kzalloc ( size_of_regd , GFP_KERNEL ) ;
if ( ! rd )
return NULL ;
rd - > n_reg_rules = num_rules ;
rd - > alpha2 [ 0 ] = alpha2 [ 0 ] ;
rd - > alpha2 [ 1 ] = alpha2 [ 1 ] ;
/* This time around we fill in the rd */
while ( country_ie_len > = 3 ) {
2009-01-08 04:43:37 +03:00
int end_channel = 0 ;
2008-11-13 01:22:02 +03:00
struct ieee80211_country_ie_triplet * triplet =
( struct ieee80211_country_ie_triplet * ) country_ie ;
struct ieee80211_reg_rule * reg_rule = NULL ;
struct ieee80211_freq_range * freq_range = NULL ;
struct ieee80211_power_rule * power_rule = NULL ;
2009-02-21 08:04:31 +03:00
/*
* Must parse if dot11RegulatoryClassesRequired is true ,
* we don ' t support this yet
*/
2008-11-13 01:22:02 +03:00
if ( triplet - > ext . reg_extension_id > =
IEEE80211_COUNTRY_EXTENSION_ID ) {
country_ie + = 3 ;
country_ie_len - = 3 ;
continue ;
}
reg_rule = & rd - > reg_rules [ i ] ;
freq_range = & reg_rule - > freq_range ;
power_rule = & reg_rule - > power_rule ;
reg_rule - > flags = flags ;
2009-01-08 04:43:37 +03:00
/* 2 GHz */
if ( triplet - > chans . first_channel < = 14 )
end_channel = triplet - > chans . first_channel +
triplet - > chans . num_channels ;
else
end_channel = triplet - > chans . first_channel +
( 4 * ( triplet - > chans . num_channels - 1 ) ) ;
2009-02-21 08:04:31 +03:00
/*
* The + 10 is since the regulatory domain expects
2008-11-13 01:22:02 +03:00
* the actual band edge , not the center of freq for
* its start and end freqs , assuming 20 MHz bandwidth on
2009-02-21 08:04:31 +03:00
* the channels passed
*/
2008-11-13 01:22:02 +03:00
freq_range - > start_freq_khz =
MHZ_TO_KHZ ( ieee80211_channel_to_frequency (
triplet - > chans . first_channel ) - 10 ) ;
freq_range - > end_freq_khz =
MHZ_TO_KHZ ( ieee80211_channel_to_frequency (
2009-01-08 04:43:37 +03:00
end_channel ) + 10 ) ;
2008-11-13 01:22:02 +03:00
2009-02-21 08:04:31 +03:00
/*
* These are large arbitrary values we use to intersect later .
* Increment this if we ever support > = 40 MHz channels
* in IEEE 802.11
*/
2008-11-13 01:22:02 +03:00
freq_range - > max_bandwidth_khz = MHZ_TO_KHZ ( 40 ) ;
power_rule - > max_antenna_gain = DBI_TO_MBI ( 100 ) ;
power_rule - > max_eirp = DBM_TO_MBM ( 100 ) ;
country_ie + = 3 ;
country_ie_len - = 3 ;
i + + ;
BUG_ON ( i > NL80211_MAX_SUPP_REG_RULES ) ;
}
return rd ;
}
2009-02-21 08:04:31 +03:00
/*
* Helper for regdom_intersect ( ) , this does the real
* mathematical intersection fun
*/
2008-10-30 23:33:53 +03:00
static int reg_rules_intersect (
const struct ieee80211_reg_rule * rule1 ,
const struct ieee80211_reg_rule * rule2 ,
struct ieee80211_reg_rule * intersected_rule )
{
const struct ieee80211_freq_range * freq_range1 , * freq_range2 ;
struct ieee80211_freq_range * freq_range ;
const struct ieee80211_power_rule * power_rule1 , * power_rule2 ;
struct ieee80211_power_rule * power_rule ;
u32 freq_diff ;
freq_range1 = & rule1 - > freq_range ;
freq_range2 = & rule2 - > freq_range ;
freq_range = & intersected_rule - > freq_range ;
power_rule1 = & rule1 - > power_rule ;
power_rule2 = & rule2 - > power_rule ;
power_rule = & intersected_rule - > power_rule ;
freq_range - > start_freq_khz = max ( freq_range1 - > start_freq_khz ,
freq_range2 - > start_freq_khz ) ;
freq_range - > end_freq_khz = min ( freq_range1 - > end_freq_khz ,
freq_range2 - > end_freq_khz ) ;
freq_range - > max_bandwidth_khz = min ( freq_range1 - > max_bandwidth_khz ,
freq_range2 - > max_bandwidth_khz ) ;
freq_diff = freq_range - > end_freq_khz - freq_range - > start_freq_khz ;
if ( freq_range - > max_bandwidth_khz > freq_diff )
freq_range - > max_bandwidth_khz = freq_diff ;
power_rule - > max_eirp = min ( power_rule1 - > max_eirp ,
power_rule2 - > max_eirp ) ;
power_rule - > max_antenna_gain = min ( power_rule1 - > max_antenna_gain ,
power_rule2 - > max_antenna_gain ) ;
intersected_rule - > flags = ( rule1 - > flags | rule2 - > flags ) ;
if ( ! is_valid_reg_rule ( intersected_rule ) )
return - EINVAL ;
return 0 ;
}
/**
* regdom_intersect - do the intersection between two regulatory domains
* @ rd1 : first regulatory domain
* @ rd2 : second regulatory domain
*
* Use this function to get the intersection between two regulatory domains .
* Once completed we will mark the alpha2 for the rd as intersected , " 98 " ,
* as no one single alpha2 can represent this regulatory domain .
*
* Returns a pointer to the regulatory domain structure which will hold the
* resulting intersection of rules between rd1 and rd2 . We will
* kzalloc ( ) this structure for you .
*/
static struct ieee80211_regdomain * regdom_intersect (
const struct ieee80211_regdomain * rd1 ,
const struct ieee80211_regdomain * rd2 )
{
int r , size_of_regd ;
unsigned int x , y ;
unsigned int num_rules = 0 , rule_idx = 0 ;
const struct ieee80211_reg_rule * rule1 , * rule2 ;
struct ieee80211_reg_rule * intersected_rule ;
struct ieee80211_regdomain * rd ;
/* This is just a dummy holder to help us count */
struct ieee80211_reg_rule irule ;
/* Uses the stack temporarily for counter arithmetic */
intersected_rule = & irule ;
memset ( intersected_rule , 0 , sizeof ( struct ieee80211_reg_rule ) ) ;
if ( ! rd1 | | ! rd2 )
return NULL ;
2009-02-21 08:04:31 +03:00
/*
* First we get a count of the rules we ' ll need , then we actually
2008-10-30 23:33:53 +03:00
* build them . This is to so we can malloc ( ) and free ( ) a
* regdomain once . The reason we use reg_rules_intersect ( ) here
* is it will return - EINVAL if the rule computed makes no sense .
2009-02-21 08:04:31 +03:00
* All rules that do check out OK are valid .
*/
2008-10-30 23:33:53 +03:00
for ( x = 0 ; x < rd1 - > n_reg_rules ; x + + ) {
rule1 = & rd1 - > reg_rules [ x ] ;
for ( y = 0 ; y < rd2 - > n_reg_rules ; y + + ) {
rule2 = & rd2 - > reg_rules [ y ] ;
if ( ! reg_rules_intersect ( rule1 , rule2 ,
intersected_rule ) )
num_rules + + ;
memset ( intersected_rule , 0 ,
sizeof ( struct ieee80211_reg_rule ) ) ;
}
}
if ( ! num_rules )
return NULL ;
size_of_regd = sizeof ( struct ieee80211_regdomain ) +
( ( num_rules + 1 ) * sizeof ( struct ieee80211_reg_rule ) ) ;
rd = kzalloc ( size_of_regd , GFP_KERNEL ) ;
if ( ! rd )
return NULL ;
for ( x = 0 ; x < rd1 - > n_reg_rules ; x + + ) {
rule1 = & rd1 - > reg_rules [ x ] ;
for ( y = 0 ; y < rd2 - > n_reg_rules ; y + + ) {
rule2 = & rd2 - > reg_rules [ y ] ;
2009-02-21 08:04:31 +03:00
/*
* This time around instead of using the stack lets
2008-10-30 23:33:53 +03:00
* write to the target rule directly saving ourselves
2009-02-21 08:04:31 +03:00
* a memcpy ( )
*/
2008-10-30 23:33:53 +03:00
intersected_rule = & rd - > reg_rules [ rule_idx ] ;
r = reg_rules_intersect ( rule1 , rule2 ,
intersected_rule ) ;
2009-02-21 08:04:31 +03:00
/*
* No need to memset here the intersected rule here as
* we ' re not using the stack anymore
*/
2008-10-30 23:33:53 +03:00
if ( r )
continue ;
rule_idx + + ;
}
}
if ( rule_idx ! = num_rules ) {
kfree ( rd ) ;
return NULL ;
}
rd - > n_reg_rules = num_rules ;
rd - > alpha2 [ 0 ] = ' 9 ' ;
rd - > alpha2 [ 1 ] = ' 8 ' ;
return rd ;
}
2009-02-21 08:04:31 +03:00
/*
* XXX : add support for the rest of enum nl80211_reg_rule_flags , we may
* want to just have the channel structure use these
*/
2008-09-10 10:19:48 +04:00
static u32 map_regdom_flags ( u32 rd_flags )
{
u32 channel_flags = 0 ;
if ( rd_flags & NL80211_RRF_PASSIVE_SCAN )
channel_flags | = IEEE80211_CHAN_PASSIVE_SCAN ;
if ( rd_flags & NL80211_RRF_NO_IBSS )
channel_flags | = IEEE80211_CHAN_NO_IBSS ;
if ( rd_flags & NL80211_RRF_DFS )
channel_flags | = IEEE80211_CHAN_RADAR ;
return channel_flags ;
}
2009-01-23 02:05:44 +03:00
static int freq_reg_info_regd ( struct wiphy * wiphy ,
u32 center_freq ,
2009-05-02 08:37:17 +04:00
u32 desired_bw_khz ,
2009-01-23 02:05:44 +03:00
const struct ieee80211_reg_rule * * reg_rule ,
const struct ieee80211_regdomain * custom_regd )
2008-01-24 21:38:38 +03:00
{
int i ;
2009-01-08 04:43:36 +03:00
bool band_rule_found = false ;
2009-01-08 04:43:34 +03:00
const struct ieee80211_regdomain * regd ;
2009-05-02 08:37:17 +04:00
bool bw_fits = false ;
if ( ! desired_bw_khz )
desired_bw_khz = MHZ_TO_KHZ ( 20 ) ;
2008-01-24 21:38:38 +03:00
2009-01-23 02:05:44 +03:00
regd = custom_regd ? custom_regd : cfg80211_regdomain ;
2009-01-08 04:43:34 +03:00
2009-02-21 08:04:31 +03:00
/*
* Follow the driver ' s regulatory domain , if present , unless a country
* IE has been processed or a user wants to help complaince further
*/
2009-03-10 05:07:41 +03:00
if ( last_request - > initiator ! = NL80211_REGDOM_SET_BY_COUNTRY_IE & &
last_request - > initiator ! = NL80211_REGDOM_SET_BY_USER & &
2009-01-08 04:43:34 +03:00
wiphy - > regd )
regd = wiphy - > regd ;
if ( ! regd )
2008-09-10 10:19:48 +04:00
return - EINVAL ;
2009-01-08 04:43:34 +03:00
for ( i = 0 ; i < regd - > n_reg_rules ; i + + ) {
2008-09-10 10:19:48 +04:00
const struct ieee80211_reg_rule * rr ;
const struct ieee80211_freq_range * fr = NULL ;
const struct ieee80211_power_rule * pr = NULL ;
2009-01-08 04:43:34 +03:00
rr = & regd - > reg_rules [ i ] ;
2008-09-10 10:19:48 +04:00
fr = & rr - > freq_range ;
pr = & rr - > power_rule ;
2009-01-08 04:43:36 +03:00
2009-02-21 08:04:31 +03:00
/*
* We only need to know if one frequency rule was
2009-01-08 04:43:36 +03:00
* was in center_freq ' s band , that ' s enough , so lets
2009-02-21 08:04:31 +03:00
* not overwrite it once found
*/
2009-01-08 04:43:36 +03:00
if ( ! band_rule_found )
band_rule_found = freq_in_rule_band ( fr , center_freq ) ;
2009-05-02 08:37:17 +04:00
bw_fits = reg_does_bw_fit ( fr ,
center_freq ,
desired_bw_khz ) ;
2009-01-08 04:43:36 +03:00
2009-05-02 08:37:17 +04:00
if ( band_rule_found & & bw_fits ) {
2008-09-10 10:19:48 +04:00
* reg_rule = rr ;
2009-05-02 08:37:17 +04:00
return 0 ;
2008-01-24 21:38:38 +03:00
}
}
2009-01-08 04:43:36 +03:00
if ( ! band_rule_found )
return - ERANGE ;
2009-05-02 08:37:17 +04:00
return - EINVAL ;
2008-09-10 10:19:48 +04:00
}
2009-01-23 02:05:45 +03:00
EXPORT_SYMBOL ( freq_reg_info ) ;
2008-09-10 10:19:48 +04:00
2009-05-02 08:37:17 +04:00
int freq_reg_info ( struct wiphy * wiphy ,
u32 center_freq ,
u32 desired_bw_khz ,
const struct ieee80211_reg_rule * * reg_rule )
2009-01-23 02:05:44 +03:00
{
2009-05-02 02:44:50 +04:00
assert_cfg80211_lock ( ) ;
2009-05-02 08:37:17 +04:00
return freq_reg_info_regd ( wiphy ,
center_freq ,
desired_bw_khz ,
reg_rule ,
NULL ) ;
2009-01-23 02:05:44 +03:00
}
2008-09-10 10:19:48 +04:00
2009-05-02 08:37:17 +04:00
/*
* Note that right now we assume the desired channel bandwidth
* is always 20 MHz for each individual channel ( HT40 uses 20 MHz
* per channel , the primary and the extension channel ) . To support
* smaller custom bandwidths such as 5 MHz or 10 MHz we ' ll need a
* new ieee80211_channel . target_bw and re run the regulatory check
* on the wiphy with the target_bw specified . Then we can simply use
* that below for the desired_bw_khz below .
*/
2009-01-08 04:43:33 +03:00
static void handle_channel ( struct wiphy * wiphy , enum ieee80211_band band ,
unsigned int chan_idx )
2008-09-10 10:19:48 +04:00
{
int r ;
2009-05-02 08:37:17 +04:00
u32 flags , bw_flags = 0 ;
u32 desired_bw_khz = MHZ_TO_KHZ ( 20 ) ;
2008-09-10 10:19:48 +04:00
const struct ieee80211_reg_rule * reg_rule = NULL ;
const struct ieee80211_power_rule * power_rule = NULL ;
2009-05-02 08:37:17 +04:00
const struct ieee80211_freq_range * freq_range = NULL ;
2009-01-08 04:43:33 +03:00
struct ieee80211_supported_band * sband ;
struct ieee80211_channel * chan ;
2009-02-21 08:04:30 +03:00
struct wiphy * request_wiphy = NULL ;
2009-01-08 04:43:33 +03:00
2009-02-21 08:04:25 +03:00
assert_cfg80211_lock ( ) ;
2009-02-21 08:04:26 +03:00
request_wiphy = wiphy_idx_to_wiphy ( last_request - > wiphy_idx ) ;
2009-01-08 04:43:33 +03:00
sband = wiphy - > bands [ band ] ;
BUG_ON ( chan_idx > = sband - > n_channels ) ;
chan = & sband - > channels [ chan_idx ] ;
flags = chan - > orig_flags ;
2008-09-10 10:19:48 +04:00
2009-05-02 08:37:17 +04:00
r = freq_reg_info ( wiphy ,
MHZ_TO_KHZ ( chan - > center_freq ) ,
desired_bw_khz ,
& reg_rule ) ;
2008-09-10 10:19:48 +04:00
if ( r ) {
2009-02-21 08:04:31 +03:00
/*
* This means no regulatory rule was found in the country IE
2009-01-08 04:43:36 +03:00
* with a frequency range on the center_freq ' s band , since
* IEEE - 802.11 allows for a country IE to have a subset of the
* regulatory information provided in a country we ignore
* disabling the channel unless at least one reg rule was
* found on the center_freq ' s band . For details see this
* clarification :
*
* http : //tinyurl.com/11d-clarification
*/
if ( r = = - ERANGE & &
2009-03-10 05:07:41 +03:00
last_request - > initiator = =
NL80211_REGDOM_SET_BY_COUNTRY_IE ) {
2009-01-08 04:43:36 +03:00
# ifdef CONFIG_CFG80211_REG_DEBUG
printk ( KERN_DEBUG " cfg80211: Leaving channel %d MHz "
" intact on %s - no rule found in band on "
" Country IE \n " ,
chan - > center_freq , wiphy_name ( wiphy ) ) ;
# endif
} else {
2009-02-21 08:04:31 +03:00
/*
* In this case we know the country IE has at least one reg rule
* for the band so we respect its band definitions
*/
2009-01-08 04:43:36 +03:00
# ifdef CONFIG_CFG80211_REG_DEBUG
2009-03-10 05:07:41 +03:00
if ( last_request - > initiator = =
NL80211_REGDOM_SET_BY_COUNTRY_IE )
2009-01-08 04:43:36 +03:00
printk ( KERN_DEBUG " cfg80211: Disabling "
" channel %d MHz on %s due to "
" Country IE \n " ,
chan - > center_freq , wiphy_name ( wiphy ) ) ;
# endif
flags | = IEEE80211_CHAN_DISABLED ;
chan - > flags = flags ;
}
2008-01-24 21:38:38 +03:00
return ;
}
2008-09-10 10:19:48 +04:00
power_rule = & reg_rule - > power_rule ;
2009-05-02 08:37:17 +04:00
freq_range = & reg_rule - > freq_range ;
if ( freq_range - > max_bandwidth_khz < MHZ_TO_KHZ ( 40 ) )
bw_flags = IEEE80211_CHAN_NO_HT40 ;
2008-09-10 10:19:48 +04:00
2009-03-10 05:07:41 +03:00
if ( last_request - > initiator = = NL80211_REGDOM_SET_BY_DRIVER & &
2009-02-21 08:04:26 +03:00
request_wiphy & & request_wiphy = = wiphy & &
request_wiphy - > strict_regulatory ) {
2009-02-21 08:04:31 +03:00
/*
* This gaurantees the driver ' s requested regulatory domain
2009-01-23 02:05:52 +03:00
* will always be used as a base for further regulatory
2009-02-21 08:04:31 +03:00
* settings
*/
2009-01-23 02:05:52 +03:00
chan - > flags = chan - > orig_flags =
2009-05-02 08:37:17 +04:00
map_regdom_flags ( reg_rule - > flags ) | bw_flags ;
2009-01-23 02:05:52 +03:00
chan - > max_antenna_gain = chan - > orig_mag =
( int ) MBI_TO_DBI ( power_rule - > max_antenna_gain ) ;
chan - > max_power = chan - > orig_mpwr =
( int ) MBM_TO_DBM ( power_rule - > max_eirp ) ;
return ;
}
2009-05-02 08:37:17 +04:00
chan - > flags = flags | bw_flags | map_regdom_flags ( reg_rule - > flags ) ;
2008-01-24 21:38:38 +03:00
chan - > max_antenna_gain = min ( chan - > orig_mag ,
2008-09-10 10:19:48 +04:00
( int ) MBI_TO_DBI ( power_rule - > max_antenna_gain ) ) ;
2008-04-03 23:32:54 +04:00
if ( chan - > orig_mpwr )
2008-09-10 10:19:48 +04:00
chan - > max_power = min ( chan - > orig_mpwr ,
( int ) MBM_TO_DBM ( power_rule - > max_eirp ) ) ;
2008-04-03 23:32:54 +04:00
else
2008-09-10 10:19:48 +04:00
chan - > max_power = ( int ) MBM_TO_DBM ( power_rule - > max_eirp ) ;
2008-01-24 21:38:38 +03:00
}
2009-01-08 04:43:33 +03:00
static void handle_band ( struct wiphy * wiphy , enum ieee80211_band band )
2008-01-24 21:38:38 +03:00
{
2009-01-08 04:43:33 +03:00
unsigned int i ;
struct ieee80211_supported_band * sband ;
BUG_ON ( ! wiphy - > bands [ band ] ) ;
sband = wiphy - > bands [ band ] ;
2008-01-24 21:38:38 +03:00
for ( i = 0 ; i < sband - > n_channels ; i + + )
2009-01-08 04:43:33 +03:00
handle_channel ( wiphy , band , i ) ;
2008-01-24 21:38:38 +03:00
}
2009-03-10 05:07:41 +03:00
static bool ignore_reg_update ( struct wiphy * wiphy ,
enum nl80211_reg_initiator initiator )
2008-11-13 01:22:03 +03:00
{
if ( ! last_request )
return true ;
2009-03-10 05:07:41 +03:00
if ( initiator = = NL80211_REGDOM_SET_BY_CORE & &
2009-01-23 02:05:49 +03:00
wiphy - > custom_regulatory )
2008-11-13 01:22:03 +03:00
return true ;
2009-02-21 08:04:31 +03:00
/*
* wiphy - > regd will be set once the device has its own
* desired regulatory domain set
*/
2009-01-23 02:05:52 +03:00
if ( wiphy - > strict_regulatory & & ! wiphy - > regd & &
! is_world_regdom ( last_request - > alpha2 ) )
2008-11-13 01:22:03 +03:00
return true ;
return false ;
}
2009-03-10 05:07:41 +03:00
static void update_all_wiphy_regulatory ( enum nl80211_reg_initiator initiator )
2008-01-24 21:38:38 +03:00
{
2009-07-07 05:56:12 +04:00
struct cfg80211_registered_device * rdev ;
2008-01-24 21:38:38 +03:00
2009-07-07 05:56:12 +04:00
list_for_each_entry ( rdev , & cfg80211_rdev_list , list )
wiphy_update_regulatory ( & rdev - > wiphy , initiator ) ;
2008-09-10 10:19:48 +04:00
}
2009-02-21 08:20:39 +03:00
static void handle_reg_beacon ( struct wiphy * wiphy ,
unsigned int chan_idx ,
struct reg_beacon * reg_beacon )
{
struct ieee80211_supported_band * sband ;
struct ieee80211_channel * chan ;
2009-04-02 22:08:09 +04:00
bool channel_changed = false ;
struct ieee80211_channel chan_before ;
2009-02-21 08:20:39 +03:00
assert_cfg80211_lock ( ) ;
sband = wiphy - > bands [ reg_beacon - > chan . band ] ;
chan = & sband - > channels [ chan_idx ] ;
if ( likely ( chan - > center_freq ! = reg_beacon - > chan . center_freq ) )
return ;
2009-04-02 22:08:09 +04:00
if ( chan - > beacon_found )
return ;
chan - > beacon_found = true ;
2009-07-31 04:43:48 +04:00
if ( wiphy - > disable_beacon_hints )
return ;
2009-04-02 22:08:09 +04:00
chan_before . center_freq = chan - > center_freq ;
chan_before . flags = chan - > flags ;
2009-07-31 04:43:48 +04:00
if ( chan - > flags & IEEE80211_CHAN_PASSIVE_SCAN ) {
2009-02-21 08:20:39 +03:00
chan - > flags & = ~ IEEE80211_CHAN_PASSIVE_SCAN ;
2009-04-02 22:08:09 +04:00
channel_changed = true ;
2009-02-21 08:20:39 +03:00
}
2009-07-31 04:43:48 +04:00
if ( chan - > flags & IEEE80211_CHAN_NO_IBSS ) {
2009-02-21 08:20:39 +03:00
chan - > flags & = ~ IEEE80211_CHAN_NO_IBSS ;
2009-04-02 22:08:09 +04:00
channel_changed = true ;
2009-02-21 08:20:39 +03:00
}
2009-04-02 22:08:09 +04:00
if ( channel_changed )
nl80211_send_beacon_hint_event ( wiphy , & chan_before , chan ) ;
2009-02-21 08:20:39 +03:00
}
/*
* Called when a scan on a wiphy finds a beacon on
* new channel
*/
static void wiphy_update_new_beacon ( struct wiphy * wiphy ,
struct reg_beacon * reg_beacon )
{
unsigned int i ;
struct ieee80211_supported_band * sband ;
assert_cfg80211_lock ( ) ;
if ( ! wiphy - > bands [ reg_beacon - > chan . band ] )
return ;
sband = wiphy - > bands [ reg_beacon - > chan . band ] ;
for ( i = 0 ; i < sband - > n_channels ; i + + )
handle_reg_beacon ( wiphy , i , reg_beacon ) ;
}
/*
* Called upon reg changes or a new wiphy is added
*/
static void wiphy_update_beacon_reg ( struct wiphy * wiphy )
{
unsigned int i ;
struct ieee80211_supported_band * sband ;
struct reg_beacon * reg_beacon ;
assert_cfg80211_lock ( ) ;
if ( list_empty ( & reg_beacon_list ) )
return ;
list_for_each_entry ( reg_beacon , & reg_beacon_list , list ) {
if ( ! wiphy - > bands [ reg_beacon - > chan . band ] )
continue ;
sband = wiphy - > bands [ reg_beacon - > chan . band ] ;
for ( i = 0 ; i < sband - > n_channels ; i + + )
handle_reg_beacon ( wiphy , i , reg_beacon ) ;
}
}
static bool reg_is_world_roaming ( struct wiphy * wiphy )
{
if ( is_world_regdom ( cfg80211_regdomain - > alpha2 ) | |
( wiphy - > regd & & is_world_regdom ( wiphy - > regd - > alpha2 ) ) )
return true ;
2009-05-02 08:34:15 +04:00
if ( last_request & &
last_request - > initiator ! = NL80211_REGDOM_SET_BY_COUNTRY_IE & &
2009-02-21 08:20:39 +03:00
wiphy - > custom_regulatory )
return true ;
return false ;
}
/* Reap the advantages of previously found beacons */
static void reg_process_beacons ( struct wiphy * wiphy )
{
2009-05-02 08:34:15 +04:00
/*
* Means we are just firing up cfg80211 , so no beacons would
* have been processed yet .
*/
if ( ! last_request )
return ;
2009-02-21 08:20:39 +03:00
if ( ! reg_is_world_roaming ( wiphy ) )
return ;
wiphy_update_beacon_reg ( wiphy ) ;
}
2009-05-02 08:37:17 +04:00
static bool is_ht40_not_allowed ( struct ieee80211_channel * chan )
{
if ( ! chan )
return true ;
if ( chan - > flags & IEEE80211_CHAN_DISABLED )
return true ;
/* This would happen when regulatory rules disallow HT40 completely */
if ( IEEE80211_CHAN_NO_HT40 = = ( chan - > flags & ( IEEE80211_CHAN_NO_HT40 ) ) )
return true ;
return false ;
}
static void reg_process_ht_flags_channel ( struct wiphy * wiphy ,
enum ieee80211_band band ,
unsigned int chan_idx )
{
struct ieee80211_supported_band * sband ;
struct ieee80211_channel * channel ;
struct ieee80211_channel * channel_before = NULL , * channel_after = NULL ;
unsigned int i ;
assert_cfg80211_lock ( ) ;
sband = wiphy - > bands [ band ] ;
BUG_ON ( chan_idx > = sband - > n_channels ) ;
channel = & sband - > channels [ chan_idx ] ;
if ( is_ht40_not_allowed ( channel ) ) {
channel - > flags | = IEEE80211_CHAN_NO_HT40 ;
return ;
}
/*
* We need to ensure the extension channels exist to
* be able to use HT40 - or HT40 + , this finds them ( or not )
*/
for ( i = 0 ; i < sband - > n_channels ; i + + ) {
struct ieee80211_channel * c = & sband - > channels [ i ] ;
if ( c - > center_freq = = ( channel - > center_freq - 20 ) )
channel_before = c ;
if ( c - > center_freq = = ( channel - > center_freq + 20 ) )
channel_after = c ;
}
/*
* Please note that this assumes target bandwidth is 20 MHz ,
* if that ever changes we also need to change the below logic
* to include that as well .
*/
if ( is_ht40_not_allowed ( channel_before ) )
2009-05-02 08:37:18 +04:00
channel - > flags | = IEEE80211_CHAN_NO_HT40MINUS ;
2009-05-02 08:37:17 +04:00
else
2009-05-02 08:37:18 +04:00
channel - > flags & = ~ IEEE80211_CHAN_NO_HT40MINUS ;
2009-05-02 08:37:17 +04:00
if ( is_ht40_not_allowed ( channel_after ) )
2009-05-02 08:37:18 +04:00
channel - > flags | = IEEE80211_CHAN_NO_HT40PLUS ;
2009-05-02 08:37:17 +04:00
else
2009-05-02 08:37:18 +04:00
channel - > flags & = ~ IEEE80211_CHAN_NO_HT40PLUS ;
2009-05-02 08:37:17 +04:00
}
static void reg_process_ht_flags_band ( struct wiphy * wiphy ,
enum ieee80211_band band )
{
unsigned int i ;
struct ieee80211_supported_band * sband ;
BUG_ON ( ! wiphy - > bands [ band ] ) ;
sband = wiphy - > bands [ band ] ;
for ( i = 0 ; i < sband - > n_channels ; i + + )
reg_process_ht_flags_channel ( wiphy , band , i ) ;
}
static void reg_process_ht_flags ( struct wiphy * wiphy )
{
enum ieee80211_band band ;
if ( ! wiphy )
return ;
for ( band = 0 ; band < IEEE80211_NUM_BANDS ; band + + ) {
if ( wiphy - > bands [ band ] )
reg_process_ht_flags_band ( wiphy , band ) ;
}
}
2009-03-10 05:07:41 +03:00
void wiphy_update_regulatory ( struct wiphy * wiphy ,
enum nl80211_reg_initiator initiator )
2008-09-10 10:19:48 +04:00
{
enum ieee80211_band band ;
2009-01-23 02:05:50 +03:00
2009-03-10 05:07:41 +03:00
if ( ignore_reg_update ( wiphy , initiator ) )
2009-02-21 08:20:39 +03:00
goto out ;
2008-09-10 10:19:48 +04:00
for ( band = 0 ; band < IEEE80211_NUM_BANDS ; band + + ) {
2008-01-24 21:38:38 +03:00
if ( wiphy - > bands [ band ] )
2009-01-08 04:43:33 +03:00
handle_band ( wiphy , band ) ;
2008-09-10 10:19:48 +04:00
}
2009-02-21 08:20:39 +03:00
out :
reg_process_beacons ( wiphy ) ;
2009-05-02 08:37:17 +04:00
reg_process_ht_flags ( wiphy ) ;
2009-01-08 04:43:32 +03:00
if ( wiphy - > reg_notifier )
2009-01-23 02:05:51 +03:00
wiphy - > reg_notifier ( wiphy , last_request ) ;
2008-09-10 10:19:48 +04:00
}
2009-01-23 02:05:44 +03:00
static void handle_channel_custom ( struct wiphy * wiphy ,
enum ieee80211_band band ,
unsigned int chan_idx ,
const struct ieee80211_regdomain * regd )
{
int r ;
2009-05-02 08:37:17 +04:00
u32 desired_bw_khz = MHZ_TO_KHZ ( 20 ) ;
u32 bw_flags = 0 ;
2009-01-23 02:05:44 +03:00
const struct ieee80211_reg_rule * reg_rule = NULL ;
const struct ieee80211_power_rule * power_rule = NULL ;
2009-05-02 08:37:17 +04:00
const struct ieee80211_freq_range * freq_range = NULL ;
2009-01-23 02:05:44 +03:00
struct ieee80211_supported_band * sband ;
struct ieee80211_channel * chan ;
2009-07-31 04:38:08 +04:00
assert_reg_lock ( ) ;
2009-05-02 02:44:50 +04:00
2009-01-23 02:05:44 +03:00
sband = wiphy - > bands [ band ] ;
BUG_ON ( chan_idx > = sband - > n_channels ) ;
chan = & sband - > channels [ chan_idx ] ;
2009-05-02 08:37:17 +04:00
r = freq_reg_info_regd ( wiphy ,
MHZ_TO_KHZ ( chan - > center_freq ) ,
desired_bw_khz ,
& reg_rule ,
regd ) ;
2009-01-23 02:05:44 +03:00
if ( r ) {
chan - > flags = IEEE80211_CHAN_DISABLED ;
return ;
}
power_rule = & reg_rule - > power_rule ;
2009-05-02 08:37:17 +04:00
freq_range = & reg_rule - > freq_range ;
if ( freq_range - > max_bandwidth_khz < MHZ_TO_KHZ ( 40 ) )
bw_flags = IEEE80211_CHAN_NO_HT40 ;
2009-01-23 02:05:44 +03:00
2009-05-02 08:37:17 +04:00
chan - > flags | = map_regdom_flags ( reg_rule - > flags ) | bw_flags ;
2009-01-23 02:05:44 +03:00
chan - > max_antenna_gain = ( int ) MBI_TO_DBI ( power_rule - > max_antenna_gain ) ;
chan - > max_power = ( int ) MBM_TO_DBM ( power_rule - > max_eirp ) ;
}
static void handle_band_custom ( struct wiphy * wiphy , enum ieee80211_band band ,
const struct ieee80211_regdomain * regd )
{
unsigned int i ;
struct ieee80211_supported_band * sband ;
BUG_ON ( ! wiphy - > bands [ band ] ) ;
sband = wiphy - > bands [ band ] ;
for ( i = 0 ; i < sband - > n_channels ; i + + )
handle_channel_custom ( wiphy , band , i , regd ) ;
}
/* Used by drivers prior to wiphy registration */
void wiphy_apply_custom_regulatory ( struct wiphy * wiphy ,
const struct ieee80211_regdomain * regd )
{
enum ieee80211_band band ;
2009-05-20 01:49:47 +04:00
unsigned int bands_set = 0 ;
2009-05-02 02:44:50 +04:00
2009-07-31 04:38:08 +04:00
mutex_lock ( & reg_mutex ) ;
2009-01-23 02:05:44 +03:00
for ( band = 0 ; band < IEEE80211_NUM_BANDS ; band + + ) {
2009-05-20 01:49:47 +04:00
if ( ! wiphy - > bands [ band ] )
continue ;
handle_band_custom ( wiphy , band , regd ) ;
bands_set + + ;
2008-09-10 10:19:48 +04:00
}
2009-07-31 04:38:08 +04:00
mutex_unlock ( & reg_mutex ) ;
2009-05-20 01:49:47 +04:00
/*
* no point in calling this if it won ' t have any effect
* on your device ' s supportd bands .
*/
WARN_ON ( ! bands_set ) ;
2008-09-10 10:19:48 +04:00
}
2009-01-23 02:05:44 +03:00
EXPORT_SYMBOL ( wiphy_apply_custom_regulatory ) ;
2009-01-08 04:43:34 +03:00
static int reg_copy_regd ( const struct ieee80211_regdomain * * dst_regd ,
const struct ieee80211_regdomain * src_regd )
{
struct ieee80211_regdomain * regd ;
int size_of_regd = 0 ;
unsigned int i ;
size_of_regd = sizeof ( struct ieee80211_regdomain ) +
( ( src_regd - > n_reg_rules + 1 ) * sizeof ( struct ieee80211_reg_rule ) ) ;
regd = kzalloc ( size_of_regd , GFP_KERNEL ) ;
if ( ! regd )
return - ENOMEM ;
memcpy ( regd , src_regd , sizeof ( struct ieee80211_regdomain ) ) ;
for ( i = 0 ; i < src_regd - > n_reg_rules ; i + + )
memcpy ( & regd - > reg_rules [ i ] , & src_regd - > reg_rules [ i ] ,
sizeof ( struct ieee80211_reg_rule ) ) ;
* dst_regd = regd ;
return 0 ;
}
2008-09-10 10:19:48 +04:00
2009-02-21 08:04:31 +03:00
/*
* Return value which can be used by ignore_request ( ) to indicate
* it has been determined we should intersect two regulatory domains
*/
2008-10-30 23:33:53 +03:00
# define REG_INTERSECT 1
2008-10-24 22:32:23 +04:00
/* This has the logic which determines when a new request
* should be ignored . */
2009-02-21 08:24:16 +03:00
static int ignore_request ( struct wiphy * wiphy ,
struct regulatory_request * pending_request )
2008-10-24 22:32:23 +04:00
{
2009-02-21 08:04:26 +03:00
struct wiphy * last_wiphy = NULL ;
2009-02-21 08:04:25 +03:00
assert_cfg80211_lock ( ) ;
2008-10-24 22:32:23 +04:00
/* All initial requests are respected */
if ( ! last_request )
return 0 ;
2009-02-21 08:24:16 +03:00
switch ( pending_request - > initiator ) {
2009-03-10 05:07:41 +03:00
case NL80211_REGDOM_SET_BY_CORE :
2009-02-21 08:04:23 +03:00
return - EINVAL ;
2009-03-10 05:07:41 +03:00
case NL80211_REGDOM_SET_BY_COUNTRY_IE :
2009-02-21 08:04:26 +03:00
last_wiphy = wiphy_idx_to_wiphy ( last_request - > wiphy_idx ) ;
2009-02-21 08:24:16 +03:00
if ( unlikely ( ! is_an_alpha2 ( pending_request - > alpha2 ) ) )
2008-10-24 22:32:23 +04:00
return - EINVAL ;
2009-03-10 05:07:41 +03:00
if ( last_request - > initiator = =
NL80211_REGDOM_SET_BY_COUNTRY_IE ) {
2009-02-21 08:04:26 +03:00
if ( last_wiphy ! = wiphy ) {
2008-10-24 22:32:23 +04:00
/*
* Two cards with two APs claiming different
2009-08-11 18:18:42 +04:00
* Country IE alpha2s . We could
2008-10-24 22:32:23 +04:00
* intersect them , but that seems unlikely
* to be correct . Reject second one for now .
*/
2009-02-21 08:24:16 +03:00
if ( regdom_changes ( pending_request - > alpha2 ) )
2008-10-24 22:32:23 +04:00
return - EOPNOTSUPP ;
return - EALREADY ;
}
2009-02-21 08:04:31 +03:00
/*
* Two consecutive Country IE hints on the same wiphy .
* This should be picked up early by the driver / stack
*/
2009-02-21 08:24:16 +03:00
if ( WARN_ON ( regdom_changes ( pending_request - > alpha2 ) ) )
2008-10-24 22:32:23 +04:00
return 0 ;
return - EALREADY ;
}
2008-11-13 01:22:02 +03:00
return REG_INTERSECT ;
2009-03-10 05:07:41 +03:00
case NL80211_REGDOM_SET_BY_DRIVER :
if ( last_request - > initiator = = NL80211_REGDOM_SET_BY_CORE ) {
2009-01-23 02:05:48 +03:00
if ( is_old_static_regdom ( cfg80211_regdomain ) )
return 0 ;
2009-02-21 08:24:16 +03:00
if ( regdom_changes ( pending_request - > alpha2 ) )
2009-01-23 02:05:48 +03:00
return 0 ;
2008-10-24 22:32:23 +04:00
return - EALREADY ;
2009-01-23 02:05:48 +03:00
}
2009-02-21 08:04:32 +03:00
/*
* This would happen if you unplug and plug your card
* back in or if you add a new device for which the previously
* loaded card also agrees on the regulatory domain .
*/
2009-03-10 05:07:41 +03:00
if ( last_request - > initiator = = NL80211_REGDOM_SET_BY_DRIVER & &
2009-02-21 08:24:16 +03:00
! regdom_changes ( pending_request - > alpha2 ) )
2009-02-21 08:04:32 +03:00
return - EALREADY ;
2009-01-08 04:43:34 +03:00
return REG_INTERSECT ;
2009-03-10 05:07:41 +03:00
case NL80211_REGDOM_SET_BY_USER :
if ( last_request - > initiator = = NL80211_REGDOM_SET_BY_COUNTRY_IE )
2008-10-30 23:33:53 +03:00
return REG_INTERSECT ;
2009-02-21 08:04:31 +03:00
/*
* If the user knows better the user should set the regdom
* to their country before the IE is picked up
*/
2009-03-10 05:07:41 +03:00
if ( last_request - > initiator = = NL80211_REGDOM_SET_BY_USER & &
2008-11-13 01:22:02 +03:00
last_request - > intersect )
return - EOPNOTSUPP ;
2009-02-21 08:04:31 +03:00
/*
* Process user requests only after previous user / driver / core
* requests have been processed
*/
2009-03-10 05:07:41 +03:00
if ( last_request - > initiator = = NL80211_REGDOM_SET_BY_CORE | |
last_request - > initiator = = NL80211_REGDOM_SET_BY_DRIVER | |
last_request - > initiator = = NL80211_REGDOM_SET_BY_USER ) {
2009-02-21 08:04:33 +03:00
if ( regdom_changes ( last_request - > alpha2 ) )
2009-01-23 02:05:47 +03:00
return - EAGAIN ;
}
2009-01-23 02:05:48 +03:00
if ( ! is_old_static_regdom ( cfg80211_regdomain ) & &
2009-02-21 08:24:16 +03:00
! regdom_changes ( pending_request - > alpha2 ) )
2009-01-23 02:05:48 +03:00
return - EALREADY ;
2008-10-24 22:32:23 +04:00
return 0 ;
}
return - EINVAL ;
}
2009-02-21 08:24:13 +03:00
/**
* __regulatory_hint - hint to the wireless core a regulatory domain
* @ wiphy : if the hint comes from country information from an AP , this
* is required to be set to the wiphy that received the information
2009-02-21 08:24:14 +03:00
* @ pending_request : the regulatory request currently being processed
2009-02-21 08:24:13 +03:00
*
* The Wireless subsystem can use this function to hint to the wireless core
2009-02-21 08:24:14 +03:00
* what it believes should be the current regulatory domain .
2009-02-21 08:24:13 +03:00
*
* Returns zero if all went fine , % - EALREADY if a regulatory domain had
* already been set or other standard error codes .
*
2009-07-31 04:38:08 +04:00
* Caller must hold & cfg80211_mutex and & reg_mutex
2009-02-21 08:24:13 +03:00
*/
2009-02-21 08:24:14 +03:00
static int __regulatory_hint ( struct wiphy * wiphy ,
struct regulatory_request * pending_request )
2008-09-10 10:19:48 +04:00
{
2008-10-30 23:33:53 +03:00
bool intersect = false ;
2008-09-10 10:19:48 +04:00
int r = 0 ;
2009-02-21 08:04:25 +03:00
assert_cfg80211_lock ( ) ;
2009-02-21 08:24:16 +03:00
r = ignore_request ( wiphy , pending_request ) ;
2008-10-30 23:33:53 +03:00
2009-01-08 04:43:34 +03:00
if ( r = = REG_INTERSECT ) {
2009-03-10 05:07:41 +03:00
if ( pending_request - > initiator = =
NL80211_REGDOM_SET_BY_DRIVER ) {
2009-01-08 04:43:34 +03:00
r = reg_copy_regd ( & wiphy - > regd , cfg80211_regdomain ) ;
2009-02-21 08:24:15 +03:00
if ( r ) {
kfree ( pending_request ) ;
2009-01-08 04:43:34 +03:00
return r ;
2009-02-21 08:24:15 +03:00
}
2009-01-08 04:43:34 +03:00
}
2008-10-30 23:33:53 +03:00
intersect = true ;
2009-01-08 04:43:34 +03:00
} else if ( r ) {
2009-02-21 08:04:31 +03:00
/*
* If the regulatory domain being requested by the
2009-01-08 04:43:34 +03:00
* driver has already been set just copy it to the
2009-02-21 08:04:31 +03:00
* wiphy
*/
2009-02-21 08:24:14 +03:00
if ( r = = - EALREADY & &
2009-03-10 05:07:41 +03:00
pending_request - > initiator = =
NL80211_REGDOM_SET_BY_DRIVER ) {
2009-01-08 04:43:34 +03:00
r = reg_copy_regd ( & wiphy - > regd , cfg80211_regdomain ) ;
2009-02-21 08:24:15 +03:00
if ( r ) {
kfree ( pending_request ) ;
2009-01-08 04:43:34 +03:00
return r ;
2009-02-21 08:24:15 +03:00
}
2009-01-08 04:43:34 +03:00
r = - EALREADY ;
goto new_request ;
}
2009-02-21 08:24:15 +03:00
kfree ( pending_request ) ;
2008-09-10 10:19:48 +04:00
return r ;
2009-01-08 04:43:34 +03:00
}
2008-09-10 10:19:48 +04:00
2009-01-08 04:43:34 +03:00
new_request :
2009-02-21 08:24:15 +03:00
kfree ( last_request ) ;
2008-11-13 01:21:56 +03:00
2009-02-21 08:24:15 +03:00
last_request = pending_request ;
last_request - > intersect = intersect ;
2008-11-13 01:21:56 +03:00
2009-02-21 08:24:15 +03:00
pending_request = NULL ;
2009-01-08 04:43:34 +03:00
/* When r == REG_INTERSECT we do need to call CRDA */
2009-03-10 05:07:42 +03:00
if ( r < 0 ) {
/*
* Since CRDA will not be called in this case as we already
* have applied the requested regulatory domain before we just
* inform userspace we have processed the request
*/
if ( r = = - EALREADY )
nl80211_send_reg_change_event ( last_request ) ;
2009-01-08 04:43:34 +03:00
return r ;
2009-03-10 05:07:42 +03:00
}
2009-01-08 04:43:34 +03:00
2009-02-21 08:24:15 +03:00
return call_crda ( last_request - > alpha2 ) ;
2008-09-10 10:19:48 +04:00
}
2009-05-02 09:17:27 +04:00
/* This processes *all* regulatory hints */
2009-02-21 08:24:15 +03:00
static void reg_process_hint ( struct regulatory_request * reg_request )
2009-02-21 08:04:30 +03:00
{
int r = 0 ;
struct wiphy * wiphy = NULL ;
BUG_ON ( ! reg_request - > alpha2 ) ;
mutex_lock ( & cfg80211_mutex ) ;
2009-07-31 04:38:08 +04:00
mutex_lock ( & reg_mutex ) ;
2009-02-21 08:04:30 +03:00
if ( wiphy_idx_valid ( reg_request - > wiphy_idx ) )
wiphy = wiphy_idx_to_wiphy ( reg_request - > wiphy_idx ) ;
2009-03-10 05:07:41 +03:00
if ( reg_request - > initiator = = NL80211_REGDOM_SET_BY_DRIVER & &
2009-02-21 08:04:30 +03:00
! wiphy ) {
2009-02-21 08:24:15 +03:00
kfree ( reg_request ) ;
2009-02-21 08:04:30 +03:00
goto out ;
}
2009-02-21 08:24:14 +03:00
r = __regulatory_hint ( wiphy , reg_request ) ;
2009-02-21 08:04:30 +03:00
/* This is required so that the orig_* parameters are saved */
if ( r = = - EALREADY & & wiphy & & wiphy - > strict_regulatory )
wiphy_update_regulatory ( wiphy , reg_request - > initiator ) ;
out :
2009-07-31 04:38:08 +04:00
mutex_unlock ( & reg_mutex ) ;
2009-02-21 08:04:30 +03:00
mutex_unlock ( & cfg80211_mutex ) ;
}
2009-03-10 05:07:41 +03:00
/* Processes regulatory hints, this is all the NL80211_REGDOM_SET_BY_* */
2009-02-21 08:04:30 +03:00
static void reg_process_pending_hints ( void )
{
struct regulatory_request * reg_request ;
spin_lock ( & reg_requests_lock ) ;
while ( ! list_empty ( & reg_requests_list ) ) {
reg_request = list_first_entry ( & reg_requests_list ,
struct regulatory_request ,
list ) ;
list_del_init ( & reg_request - > list ) ;
2009-02-21 08:24:15 +03:00
spin_unlock ( & reg_requests_lock ) ;
reg_process_hint ( reg_request ) ;
2009-02-21 08:04:30 +03:00
spin_lock ( & reg_requests_lock ) ;
}
spin_unlock ( & reg_requests_lock ) ;
}
2009-02-21 08:20:39 +03:00
/* Processes beacon hints -- this has nothing to do with country IEs */
static void reg_process_pending_beacon_hints ( void )
{
2009-07-07 05:56:12 +04:00
struct cfg80211_registered_device * rdev ;
2009-02-21 08:20:39 +03:00
struct reg_beacon * pending_beacon , * tmp ;
2009-07-31 04:38:08 +04:00
/*
* No need to hold the reg_mutex here as we just touch wiphys
* and do not read or access regulatory variables .
*/
2009-02-21 08:20:39 +03:00
mutex_lock ( & cfg80211_mutex ) ;
/* This goes through the _pending_ beacon list */
spin_lock_bh ( & reg_pending_beacons_lock ) ;
if ( list_empty ( & reg_pending_beacons ) ) {
spin_unlock_bh ( & reg_pending_beacons_lock ) ;
goto out ;
}
list_for_each_entry_safe ( pending_beacon , tmp ,
& reg_pending_beacons , list ) {
list_del_init ( & pending_beacon - > list ) ;
/* Applies the beacon hint to current wiphys */
2009-07-07 05:56:12 +04:00
list_for_each_entry ( rdev , & cfg80211_rdev_list , list )
wiphy_update_new_beacon ( & rdev - > wiphy , pending_beacon ) ;
2009-02-21 08:20:39 +03:00
/* Remembers the beacon hint for new wiphys or reg changes */
list_add_tail ( & pending_beacon - > list , & reg_beacon_list ) ;
}
spin_unlock_bh ( & reg_pending_beacons_lock ) ;
out :
mutex_unlock ( & cfg80211_mutex ) ;
}
2009-02-21 08:04:30 +03:00
static void reg_todo ( struct work_struct * work )
{
reg_process_pending_hints ( ) ;
2009-02-21 08:20:39 +03:00
reg_process_pending_beacon_hints ( ) ;
2009-02-21 08:04:30 +03:00
}
static DECLARE_WORK ( reg_work , reg_todo ) ;
static void queue_regulatory_request ( struct regulatory_request * request )
{
spin_lock ( & reg_requests_lock ) ;
list_add_tail ( & request - > list , & reg_requests_list ) ;
spin_unlock ( & reg_requests_lock ) ;
schedule_work ( & reg_work ) ;
}
/* Core regulatory hint -- happens once during cfg80211_init() */
2009-02-21 08:04:23 +03:00
static int regulatory_hint_core ( const char * alpha2 )
{
struct regulatory_request * request ;
BUG_ON ( last_request ) ;
request = kzalloc ( sizeof ( struct regulatory_request ) ,
GFP_KERNEL ) ;
if ( ! request )
return - ENOMEM ;
request - > alpha2 [ 0 ] = alpha2 [ 0 ] ;
request - > alpha2 [ 1 ] = alpha2 [ 1 ] ;
2009-03-10 05:07:41 +03:00
request - > initiator = NL80211_REGDOM_SET_BY_CORE ;
2009-02-21 08:04:23 +03:00
2009-02-21 08:04:30 +03:00
queue_regulatory_request ( request ) ;
2009-02-21 08:04:23 +03:00
2009-05-14 01:04:42 +04:00
/*
* This ensures last_request is populated once modules
* come swinging in and calling regulatory hints and
* wiphy_apply_custom_regulatory ( ) .
*/
flush_scheduled_work ( ) ;
2009-02-21 08:04:30 +03:00
return 0 ;
2009-02-21 08:04:23 +03:00
}
2009-02-21 08:04:30 +03:00
/* User hints */
int regulatory_hint_user ( const char * alpha2 )
2008-09-10 10:19:48 +04:00
{
2009-02-21 08:04:30 +03:00
struct regulatory_request * request ;
2008-10-24 22:32:21 +04:00
BUG_ON ( ! alpha2 ) ;
2008-09-10 10:19:48 +04:00
2009-02-21 08:04:30 +03:00
request = kzalloc ( sizeof ( struct regulatory_request ) , GFP_KERNEL ) ;
if ( ! request )
return - ENOMEM ;
request - > wiphy_idx = WIPHY_IDX_STALE ;
request - > alpha2 [ 0 ] = alpha2 [ 0 ] ;
request - > alpha2 [ 1 ] = alpha2 [ 1 ] ;
2009-03-10 05:07:41 +03:00
request - > initiator = NL80211_REGDOM_SET_BY_USER ,
2009-02-21 08:04:30 +03:00
queue_regulatory_request ( request ) ;
return 0 ;
}
/* Driver hints */
int regulatory_hint ( struct wiphy * wiphy , const char * alpha2 )
{
struct regulatory_request * request ;
BUG_ON ( ! alpha2 ) ;
BUG_ON ( ! wiphy ) ;
request = kzalloc ( sizeof ( struct regulatory_request ) , GFP_KERNEL ) ;
if ( ! request )
return - ENOMEM ;
request - > wiphy_idx = get_wiphy_idx ( wiphy ) ;
/* Must have registered wiphy first */
BUG_ON ( ! wiphy_idx_valid ( request - > wiphy_idx ) ) ;
request - > alpha2 [ 0 ] = alpha2 [ 0 ] ;
request - > alpha2 [ 1 ] = alpha2 [ 1 ] ;
2009-03-10 05:07:41 +03:00
request - > initiator = NL80211_REGDOM_SET_BY_DRIVER ;
2009-02-21 08:04:30 +03:00
queue_regulatory_request ( request ) ;
return 0 ;
2008-09-10 10:19:48 +04:00
}
EXPORT_SYMBOL ( regulatory_hint ) ;
2009-07-31 04:38:08 +04:00
/* Caller must hold reg_mutex */
2008-11-13 01:22:02 +03:00
static bool reg_same_country_ie_hint ( struct wiphy * wiphy ,
u32 country_ie_checksum )
{
2009-02-21 08:04:26 +03:00
struct wiphy * request_wiphy ;
2009-07-31 04:38:08 +04:00
assert_reg_lock ( ) ;
2009-02-21 08:04:25 +03:00
2009-03-21 06:53:05 +03:00
if ( unlikely ( last_request - > initiator ! =
NL80211_REGDOM_SET_BY_COUNTRY_IE ) )
return false ;
2009-02-21 08:04:26 +03:00
request_wiphy = wiphy_idx_to_wiphy ( last_request - > wiphy_idx ) ;
if ( ! request_wiphy )
2008-11-13 01:22:02 +03:00
return false ;
2009-02-21 08:04:26 +03:00
if ( likely ( request_wiphy ! = wiphy ) )
2008-11-13 01:22:02 +03:00
return ! country_ie_integrity_changes ( country_ie_checksum ) ;
2009-02-21 08:04:31 +03:00
/*
* We should not have let these through at this point , they
2008-11-13 01:22:02 +03:00
* should have been picked up earlier by the first alpha2 check
2009-02-21 08:04:31 +03:00
* on the device
*/
2008-11-13 01:22:02 +03:00
if ( WARN_ON ( ! country_ie_integrity_changes ( country_ie_checksum ) ) )
return true ;
return false ;
}
2009-07-31 04:38:07 +04:00
/*
* We hold wdev_lock ( ) here so we cannot hold cfg80211_mutex ( ) and
* therefore cannot iterate over the rdev list here .
*/
2008-11-13 01:22:02 +03:00
void regulatory_hint_11d ( struct wiphy * wiphy ,
u8 * country_ie ,
u8 country_ie_len )
{
struct ieee80211_regdomain * rd = NULL ;
char alpha2 [ 2 ] ;
u32 checksum = 0 ;
enum environment_cap env = ENVIRON_ANY ;
2009-02-21 08:04:30 +03:00
struct regulatory_request * request ;
2008-11-13 01:22:02 +03:00
2009-07-31 04:38:08 +04:00
mutex_lock ( & reg_mutex ) ;
2008-11-13 01:22:02 +03:00
2009-07-31 04:38:06 +04:00
if ( unlikely ( ! last_request ) )
goto out ;
2009-02-21 08:04:27 +03:00
2008-11-13 01:22:02 +03:00
/* IE len must be evenly divisible by 2 */
if ( country_ie_len & 0x01 )
goto out ;
if ( country_ie_len < IEEE80211_COUNTRY_IE_MIN_LEN )
goto out ;
2009-02-21 08:04:31 +03:00
/*
* Pending country IE processing , this can happen after we
2008-11-13 01:22:02 +03:00
* call CRDA and wait for a response if a beacon was received before
2009-02-21 08:04:31 +03:00
* we were able to process the last regulatory_hint_11d ( ) call
*/
2008-11-13 01:22:02 +03:00
if ( country_ie_regdomain )
goto out ;
alpha2 [ 0 ] = country_ie [ 0 ] ;
alpha2 [ 1 ] = country_ie [ 1 ] ;
if ( country_ie [ 2 ] = = ' I ' )
env = ENVIRON_INDOOR ;
else if ( country_ie [ 2 ] = = ' O ' )
env = ENVIRON_OUTDOOR ;
2009-02-21 08:04:31 +03:00
/*
2009-07-31 04:38:09 +04:00
* We will run this only upon a successful connection on cfg80211 .
2009-07-31 04:38:07 +04:00
* We leave conflict resolution to the workqueue , where can hold
* cfg80211_mutex .
2009-02-21 08:04:31 +03:00
*/
2009-03-21 06:53:05 +03:00
if ( likely ( last_request - > initiator = =
NL80211_REGDOM_SET_BY_COUNTRY_IE & &
2009-07-31 04:38:07 +04:00
wiphy_idx_valid ( last_request - > wiphy_idx ) ) )
goto out ;
2008-11-13 01:22:02 +03:00
rd = country_ie_2_rd ( country_ie , country_ie_len , & checksum ) ;
if ( ! rd )
goto out ;
2009-02-21 08:04:28 +03:00
/*
* This will not happen right now but we leave it here for the
2008-11-13 01:22:02 +03:00
* the future when we want to add suspend / resume support and having
* the user move to another country after doing so , or having the user
2009-02-21 08:04:28 +03:00
* move to another AP . Right now we just trust the first AP .
*
* If we hit this before we add this support we want to be informed of
* it as it would indicate a mistake in the current design
*/
if ( WARN_ON ( reg_same_country_ie_hint ( wiphy , checksum ) ) )
2009-02-21 08:04:29 +03:00
goto free_rd_out ;
2008-11-13 01:22:02 +03:00
2009-02-21 08:04:30 +03:00
request = kzalloc ( sizeof ( struct regulatory_request ) , GFP_KERNEL ) ;
if ( ! request )
goto free_rd_out ;
2009-02-21 08:04:31 +03:00
/*
* We keep this around for when CRDA comes back with a response so
* we can intersect with that
*/
2008-11-13 01:22:02 +03:00
country_ie_regdomain = rd ;
2009-02-21 08:04:30 +03:00
request - > wiphy_idx = get_wiphy_idx ( wiphy ) ;
request - > alpha2 [ 0 ] = rd - > alpha2 [ 0 ] ;
request - > alpha2 [ 1 ] = rd - > alpha2 [ 1 ] ;
2009-03-10 05:07:41 +03:00
request - > initiator = NL80211_REGDOM_SET_BY_COUNTRY_IE ;
2009-02-21 08:04:30 +03:00
request - > country_ie_checksum = checksum ;
request - > country_ie_env = env ;
2009-07-31 04:38:08 +04:00
mutex_unlock ( & reg_mutex ) ;
2008-11-13 01:22:02 +03:00
2009-02-21 08:04:30 +03:00
queue_regulatory_request ( request ) ;
return ;
2009-02-21 08:04:29 +03:00
free_rd_out :
kfree ( rd ) ;
2008-11-13 01:22:02 +03:00
out :
2009-07-31 04:38:08 +04:00
mutex_unlock ( & reg_mutex ) ;
2008-11-13 01:22:02 +03:00
}
2008-09-10 10:19:48 +04:00
2009-02-21 08:20:39 +03:00
static bool freq_is_chan_12_13_14 ( u16 freq )
{
if ( freq = = ieee80211_channel_to_frequency ( 12 ) | |
freq = = ieee80211_channel_to_frequency ( 13 ) | |
freq = = ieee80211_channel_to_frequency ( 14 ) )
return true ;
return false ;
}
int regulatory_hint_found_beacon ( struct wiphy * wiphy ,
struct ieee80211_channel * beacon_chan ,
gfp_t gfp )
{
struct reg_beacon * reg_beacon ;
if ( likely ( ( beacon_chan - > beacon_found | |
( beacon_chan - > flags & IEEE80211_CHAN_RADAR ) | |
( beacon_chan - > band = = IEEE80211_BAND_2GHZ & &
! freq_is_chan_12_13_14 ( beacon_chan - > center_freq ) ) ) ) )
return 0 ;
reg_beacon = kzalloc ( sizeof ( struct reg_beacon ) , gfp ) ;
if ( ! reg_beacon )
return - ENOMEM ;
# ifdef CONFIG_CFG80211_REG_DEBUG
printk ( KERN_DEBUG " cfg80211: Found new beacon on "
" frequency: %d MHz (Ch %d) on %s \n " ,
beacon_chan - > center_freq ,
ieee80211_frequency_to_channel ( beacon_chan - > center_freq ) ,
wiphy_name ( wiphy ) ) ;
# endif
memcpy ( & reg_beacon - > chan , beacon_chan ,
sizeof ( struct ieee80211_channel ) ) ;
/*
* Since we can be called from BH or and non - BH context
* we must use spin_lock_bh ( )
*/
spin_lock_bh ( & reg_pending_beacons_lock ) ;
list_add_tail ( & reg_beacon - > list , & reg_pending_beacons ) ;
spin_unlock_bh ( & reg_pending_beacons_lock ) ;
schedule_work ( & reg_work ) ;
return 0 ;
}
2008-09-15 13:10:52 +04:00
static void print_rd_rules ( const struct ieee80211_regdomain * rd )
2008-09-10 10:19:48 +04:00
{
unsigned int i ;
2008-09-15 13:10:52 +04:00
const struct ieee80211_reg_rule * reg_rule = NULL ;
const struct ieee80211_freq_range * freq_range = NULL ;
const struct ieee80211_power_rule * power_rule = NULL ;
2008-09-10 10:19:48 +04:00
printk ( KERN_INFO " \t (start_freq - end_freq @ bandwidth), "
" (max_antenna_gain, max_eirp) \n " ) ;
for ( i = 0 ; i < rd - > n_reg_rules ; i + + ) {
reg_rule = & rd - > reg_rules [ i ] ;
freq_range = & reg_rule - > freq_range ;
power_rule = & reg_rule - > power_rule ;
2009-02-21 08:04:31 +03:00
/*
* There may not be documentation for max antenna gain
* in certain regions
*/
2008-09-10 10:19:48 +04:00
if ( power_rule - > max_antenna_gain )
printk ( KERN_INFO " \t (%d KHz - %d KHz @ %d KHz), "
" (%d mBi, %d mBm) \n " ,
freq_range - > start_freq_khz ,
freq_range - > end_freq_khz ,
freq_range - > max_bandwidth_khz ,
power_rule - > max_antenna_gain ,
power_rule - > max_eirp ) ;
else
printk ( KERN_INFO " \t (%d KHz - %d KHz @ %d KHz), "
" (N/A, %d mBm) \n " ,
freq_range - > start_freq_khz ,
freq_range - > end_freq_khz ,
freq_range - > max_bandwidth_khz ,
power_rule - > max_eirp ) ;
}
}
2008-09-15 13:10:52 +04:00
static void print_regdomain ( const struct ieee80211_regdomain * rd )
2008-09-10 10:19:48 +04:00
{
2008-11-13 01:22:02 +03:00
if ( is_intersected_alpha2 ( rd - > alpha2 ) ) {
2009-03-10 05:07:41 +03:00
if ( last_request - > initiator = =
NL80211_REGDOM_SET_BY_COUNTRY_IE ) {
2009-07-07 05:56:12 +04:00
struct cfg80211_registered_device * rdev ;
rdev = cfg80211_rdev_by_wiphy_idx (
2009-02-21 08:04:26 +03:00
last_request - > wiphy_idx ) ;
2009-07-07 05:56:12 +04:00
if ( rdev ) {
2008-11-13 01:22:02 +03:00
printk ( KERN_INFO " cfg80211: Current regulatory "
" domain updated by AP to: %c%c \n " ,
2009-07-07 05:56:12 +04:00
rdev - > country_ie_alpha2 [ 0 ] ,
rdev - > country_ie_alpha2 [ 1 ] ) ;
2008-11-13 01:22:02 +03:00
} else
printk ( KERN_INFO " cfg80211: Current regulatory "
" domain intersected: \n " ) ;
} else
printk ( KERN_INFO " cfg80211: Current regulatory "
2009-01-08 04:43:35 +03:00
" domain intersected: \n " ) ;
2008-11-13 01:22:02 +03:00
} else if ( is_world_regdom ( rd - > alpha2 ) )
2008-09-10 10:19:48 +04:00
printk ( KERN_INFO " cfg80211: World regulatory "
" domain updated: \n " ) ;
else {
if ( is_unknown_alpha2 ( rd - > alpha2 ) )
printk ( KERN_INFO " cfg80211: Regulatory domain "
" changed to driver built-in settings "
" (unknown country) \n " ) ;
else
printk ( KERN_INFO " cfg80211: Regulatory domain "
" changed to country: %c%c \n " ,
rd - > alpha2 [ 0 ] , rd - > alpha2 [ 1 ] ) ;
}
print_rd_rules ( rd ) ;
}
2008-10-28 18:49:41 +03:00
static void print_regdomain_info ( const struct ieee80211_regdomain * rd )
2008-09-10 10:19:48 +04:00
{
printk ( KERN_INFO " cfg80211: Regulatory domain: %c%c \n " ,
rd - > alpha2 [ 0 ] , rd - > alpha2 [ 1 ] ) ;
print_rd_rules ( rd ) ;
}
2008-11-13 01:22:02 +03:00
# ifdef CONFIG_CFG80211_REG_DEBUG
static void reg_country_ie_process_debug (
const struct ieee80211_regdomain * rd ,
const struct ieee80211_regdomain * country_ie_regdomain ,
const struct ieee80211_regdomain * intersected_rd )
{
printk ( KERN_DEBUG " cfg80211: Received country IE: \n " ) ;
print_regdomain_info ( country_ie_regdomain ) ;
printk ( KERN_DEBUG " cfg80211: CRDA thinks this should applied: \n " ) ;
print_regdomain_info ( rd ) ;
if ( intersected_rd ) {
printk ( KERN_DEBUG " cfg80211: We intersect both of these "
" and get: \n " ) ;
2009-01-23 02:05:43 +03:00
print_regdomain_info ( intersected_rd ) ;
2008-11-13 01:22:02 +03:00
return ;
}
printk ( KERN_DEBUG " cfg80211: Intersection between both failed \n " ) ;
}
# else
static inline void reg_country_ie_process_debug (
const struct ieee80211_regdomain * rd ,
const struct ieee80211_regdomain * country_ie_regdomain ,
const struct ieee80211_regdomain * intersected_rd )
{
}
# endif
2008-10-24 22:32:20 +04:00
/* Takes ownership of rd only if it doesn't fail */
2008-09-15 13:10:52 +04:00
static int __set_regdom ( const struct ieee80211_regdomain * rd )
2008-09-10 10:19:48 +04:00
{
2008-10-30 23:33:53 +03:00
const struct ieee80211_regdomain * intersected_rd = NULL ;
2009-07-07 05:56:12 +04:00
struct cfg80211_registered_device * rdev = NULL ;
2009-02-21 08:04:26 +03:00
struct wiphy * request_wiphy ;
2008-09-10 10:19:48 +04:00
/* Some basic sanity checks first */
if ( is_world_regdom ( rd - > alpha2 ) ) {
2008-10-21 13:01:33 +04:00
if ( WARN_ON ( ! reg_is_valid_request ( rd - > alpha2 ) ) )
2008-09-10 10:19:48 +04:00
return - EINVAL ;
update_world_regdomain ( rd ) ;
return 0 ;
}
if ( ! is_alpha2_set ( rd - > alpha2 ) & & ! is_an_alpha2 ( rd - > alpha2 ) & &
! is_unknown_alpha2 ( rd - > alpha2 ) )
return - EINVAL ;
2008-10-21 13:01:33 +04:00
if ( ! last_request )
2008-09-10 10:19:48 +04:00
return - EINVAL ;
2009-02-21 08:04:31 +03:00
/*
* Lets only bother proceeding on the same alpha2 if the current
2008-11-13 01:22:02 +03:00
* rd is non static ( it means CRDA was present and was used last )
2009-02-21 08:04:31 +03:00
* and the pending request came in from a country IE
*/
2009-03-10 05:07:41 +03:00
if ( last_request - > initiator ! = NL80211_REGDOM_SET_BY_COUNTRY_IE ) {
2009-02-21 08:04:31 +03:00
/*
* If someone else asked us to change the rd lets only bother
* checking if the alpha2 changes if CRDA was already called
*/
2008-11-13 01:22:02 +03:00
if ( ! is_old_static_regdom ( cfg80211_regdomain ) & &
2009-02-21 08:04:33 +03:00
! regdom_changes ( rd - > alpha2 ) )
2008-11-13 01:22:02 +03:00
return - EINVAL ;
}
2009-02-21 08:04:31 +03:00
/*
* Now lets set the regulatory domain , update all driver channels
2008-09-10 10:19:48 +04:00
* and finally inform them of what we have done , in case they want
* to review or adjust their own settings based on their own
2009-02-21 08:04:31 +03:00
* internal EEPROM data
*/
2008-09-10 10:19:48 +04:00
2008-10-21 13:01:33 +04:00
if ( WARN_ON ( ! reg_is_valid_request ( rd - > alpha2 ) ) )
2008-09-10 10:19:48 +04:00
return - EINVAL ;
2008-11-13 01:21:57 +03:00
if ( ! is_valid_rd ( rd ) ) {
printk ( KERN_ERR " cfg80211: Invalid "
" regulatory domain detected: \n " ) ;
print_regdomain_info ( rd ) ;
return - EINVAL ;
2008-09-10 10:19:48 +04:00
}
2009-02-21 08:04:26 +03:00
request_wiphy = wiphy_idx_to_wiphy ( last_request - > wiphy_idx ) ;
2008-11-13 01:21:58 +03:00
if ( ! last_request - > intersect ) {
2009-01-08 04:43:34 +03:00
int r ;
2009-03-10 05:07:41 +03:00
if ( last_request - > initiator ! = NL80211_REGDOM_SET_BY_DRIVER ) {
2009-01-08 04:43:34 +03:00
reset_regdomains ( ) ;
cfg80211_regdomain = rd ;
return 0 ;
}
2009-02-21 08:04:31 +03:00
/*
* For a driver hint , lets copy the regulatory domain the
* driver wanted to the wiphy to deal with conflicts
*/
2009-01-08 04:43:34 +03:00
2009-06-09 05:54:37 +04:00
/*
* Userspace could have sent two replies with only
* one kernel request .
*/
if ( request_wiphy - > regd )
return - EALREADY ;
2009-01-08 04:43:34 +03:00
2009-02-21 08:04:26 +03:00
r = reg_copy_regd ( & request_wiphy - > regd , rd ) ;
2009-01-08 04:43:34 +03:00
if ( r )
return r ;
2008-11-13 01:21:58 +03:00
reset_regdomains ( ) ;
cfg80211_regdomain = rd ;
return 0 ;
}
/* Intersection requires a bit more work */
2009-03-10 05:07:41 +03:00
if ( last_request - > initiator ! = NL80211_REGDOM_SET_BY_COUNTRY_IE ) {
2008-11-13 01:21:58 +03:00
2008-10-30 23:33:53 +03:00
intersected_rd = regdom_intersect ( rd , cfg80211_regdomain ) ;
if ( ! intersected_rd )
return - EINVAL ;
2008-11-13 01:21:58 +03:00
2009-02-21 08:04:31 +03:00
/*
* We can trash what CRDA provided now .
2009-01-08 04:43:34 +03:00
* However if a driver requested this specific regulatory
2009-02-21 08:04:31 +03:00
* domain we keep it for its private use
*/
2009-03-10 05:07:41 +03:00
if ( last_request - > initiator = = NL80211_REGDOM_SET_BY_DRIVER )
2009-02-21 08:04:26 +03:00
request_wiphy - > regd = rd ;
2009-01-08 04:43:34 +03:00
else
kfree ( rd ) ;
2008-11-13 01:21:58 +03:00
rd = NULL ;
reset_regdomains ( ) ;
cfg80211_regdomain = intersected_rd ;
return 0 ;
2008-10-30 23:33:53 +03:00
}
2008-11-13 01:22:02 +03:00
/*
* Country IE requests are handled a bit differently , we intersect
* the country IE rd with what CRDA believes that country should have
*/
2009-06-01 02:24:34 +04:00
/*
* Userspace could have sent two replies with only
* one kernel request . By the second reply we would have
* already processed and consumed the country_ie_regdomain .
*/
if ( ! country_ie_regdomain )
return - EALREADY ;
2009-03-21 06:53:07 +03:00
BUG_ON ( rd = = country_ie_regdomain ) ;
2008-11-13 01:22:02 +03:00
2009-03-21 06:53:07 +03:00
/*
* Intersect what CRDA returned and our what we
* had built from the Country IE received
*/
2008-11-13 01:22:02 +03:00
2009-03-21 06:53:07 +03:00
intersected_rd = regdom_intersect ( rd , country_ie_regdomain ) ;
2008-11-13 01:22:02 +03:00
2009-03-21 06:53:07 +03:00
reg_country_ie_process_debug ( rd ,
country_ie_regdomain ,
intersected_rd ) ;
2008-11-13 01:22:02 +03:00
2009-03-21 06:53:07 +03:00
kfree ( country_ie_regdomain ) ;
country_ie_regdomain = NULL ;
2008-11-13 01:22:02 +03:00
if ( ! intersected_rd )
return - EINVAL ;
2009-07-07 05:56:12 +04:00
rdev = wiphy_to_dev ( request_wiphy ) ;
2008-11-13 01:22:02 +03:00
2009-07-07 05:56:12 +04:00
rdev - > country_ie_alpha2 [ 0 ] = rd - > alpha2 [ 0 ] ;
rdev - > country_ie_alpha2 [ 1 ] = rd - > alpha2 [ 1 ] ;
rdev - > env = last_request - > country_ie_env ;
2008-11-13 01:22:02 +03:00
BUG_ON ( intersected_rd = = rd ) ;
kfree ( rd ) ;
rd = NULL ;
2008-11-13 01:21:58 +03:00
reset_regdomains ( ) ;
2008-11-13 01:22:02 +03:00
cfg80211_regdomain = intersected_rd ;
2008-09-10 10:19:48 +04:00
return 0 ;
}
2009-02-21 08:04:31 +03:00
/*
* Use this call to set the current regulatory domain . Conflicts with
2008-09-10 10:19:48 +04:00
* multiple drivers can be ironed out later . Caller must ' ve already
2009-02-21 08:04:31 +03:00
* kmalloc ' d the rd structure . Caller must hold cfg80211_mutex
*/
2008-09-15 13:10:52 +04:00
int set_regdom ( const struct ieee80211_regdomain * rd )
2008-09-10 10:19:48 +04:00
{
int r ;
2009-02-21 08:04:25 +03:00
assert_cfg80211_lock ( ) ;
2009-07-31 04:38:08 +04:00
mutex_lock ( & reg_mutex ) ;
2008-09-10 10:19:48 +04:00
/* Note that this doesn't update the wiphys, this is done below */
r = __set_regdom ( rd ) ;
2008-10-24 22:32:20 +04:00
if ( r ) {
kfree ( rd ) ;
2009-07-31 04:38:08 +04:00
mutex_unlock ( & reg_mutex ) ;
2008-09-10 10:19:48 +04:00
return r ;
2008-10-24 22:32:20 +04:00
}
2008-09-10 10:19:48 +04:00
/* This would make this whole thing pointless */
2008-11-13 01:21:59 +03:00
if ( ! last_request - > intersect )
BUG_ON ( rd ! = cfg80211_regdomain ) ;
2008-09-10 10:19:48 +04:00
/* update all wiphys now with the new established regulatory domain */
2008-10-21 13:01:33 +04:00
update_all_wiphy_regulatory ( last_request - > initiator ) ;
2008-09-10 10:19:48 +04:00
2008-11-13 01:21:59 +03:00
print_regdomain ( cfg80211_regdomain ) ;
2008-09-10 10:19:48 +04:00
2009-03-10 05:07:42 +03:00
nl80211_send_reg_change_event ( last_request ) ;
2009-07-31 04:38:08 +04:00
mutex_unlock ( & reg_mutex ) ;
2008-09-10 10:19:48 +04:00
return r ;
}
2009-02-21 08:04:21 +03:00
/* Caller must hold cfg80211_mutex */
2008-11-13 01:22:02 +03:00
void reg_device_remove ( struct wiphy * wiphy )
{
2009-03-25 04:21:08 +03:00
struct wiphy * request_wiphy = NULL ;
2009-02-21 08:04:26 +03:00
2009-02-21 08:04:25 +03:00
assert_cfg80211_lock ( ) ;
2009-07-31 04:38:08 +04:00
mutex_lock ( & reg_mutex ) ;
2009-04-25 01:09:31 +04:00
kfree ( wiphy - > regd ) ;
2009-03-25 04:21:08 +03:00
if ( last_request )
request_wiphy = wiphy_idx_to_wiphy ( last_request - > wiphy_idx ) ;
2009-02-21 08:04:26 +03:00
2009-04-25 01:09:31 +04:00
if ( ! request_wiphy | | request_wiphy ! = wiphy )
2009-07-31 04:38:08 +04:00
goto out ;
2009-04-25 01:09:31 +04:00
2009-02-21 08:04:26 +03:00
last_request - > wiphy_idx = WIPHY_IDX_STALE ;
2008-11-13 01:22:02 +03:00
last_request - > country_ie_env = ENVIRON_ANY ;
2009-07-31 04:38:08 +04:00
out :
mutex_unlock ( & reg_mutex ) ;
2008-11-13 01:22:02 +03:00
}
2008-09-10 10:19:48 +04:00
int regulatory_init ( void )
{
2009-02-21 08:04:24 +03:00
int err = 0 ;
2008-09-15 12:56:48 +04:00
2008-09-10 10:19:48 +04:00
reg_pdev = platform_device_register_simple ( " regulatory " , 0 , NULL , 0 ) ;
if ( IS_ERR ( reg_pdev ) )
return PTR_ERR ( reg_pdev ) ;
2008-09-15 12:56:48 +04:00
2009-02-21 08:04:30 +03:00
spin_lock_init ( & reg_requests_lock ) ;
2009-02-21 08:20:39 +03:00
spin_lock_init ( & reg_pending_beacons_lock ) ;
2009-02-21 08:04:30 +03:00
2008-09-15 12:56:48 +04:00
# ifdef CONFIG_WIRELESS_OLD_REGULATORY
2008-09-15 13:10:52 +04:00
cfg80211_regdomain = static_regdom ( ieee80211_regdom ) ;
2008-09-15 12:56:48 +04:00
2008-09-15 13:26:47 +04:00
printk ( KERN_INFO " cfg80211: Using static regulatory domain info \n " ) ;
2008-09-15 12:56:48 +04:00
print_regdomain_info ( cfg80211_regdomain ) ;
# else
2008-09-15 13:10:52 +04:00
cfg80211_regdomain = cfg80211_world_regdom ;
2008-09-15 12:56:48 +04:00
2009-02-21 08:04:24 +03:00
# endif
2009-07-15 04:23:15 +04:00
/* We always try to get an update for the static regdomain */
err = regulatory_hint_core ( cfg80211_regdomain - > alpha2 ) ;
2009-02-21 08:04:23 +03:00
if ( err ) {
2009-02-21 08:04:24 +03:00
if ( err = = - ENOMEM )
return err ;
/*
* N . B . kobject_uevent_env ( ) can fail mainly for when we ' re out
* memory which is handled and propagated appropriately above
* but it can also fail during a netlink_broadcast ( ) or during
* early boot for call_usermodehelper ( ) . For now treat these
* errors as non - fatal .
*/
printk ( KERN_ERR " cfg80211: kobject_uevent_env() was unable "
" to call CRDA during init " ) ;
# ifdef CONFIG_CFG80211_REG_DEBUG
/* We want to find out exactly why when debugging */
WARN_ON ( err ) ;
2008-09-15 12:56:48 +04:00
# endif
2009-02-21 08:04:24 +03:00
}
2008-09-15 12:56:48 +04:00
2009-07-15 04:23:15 +04:00
/*
* Finally , if the user set the module parameter treat it
* as a user hint .
*/
if ( ! is_world_regdom ( ieee80211_regdom ) )
regulatory_hint_user ( ieee80211_regdom ) ;
2008-09-10 10:19:48 +04:00
return 0 ;
}
void regulatory_exit ( void )
{
2009-02-21 08:04:30 +03:00
struct regulatory_request * reg_request , * tmp ;
2009-02-21 08:20:39 +03:00
struct reg_beacon * reg_beacon , * btmp ;
2009-02-21 08:04:30 +03:00
cancel_work_sync ( & reg_work ) ;
2009-02-21 08:04:21 +03:00
mutex_lock ( & cfg80211_mutex ) ;
2009-07-31 04:38:08 +04:00
mutex_lock ( & reg_mutex ) ;
2008-09-15 12:56:48 +04:00
2008-09-10 10:19:48 +04:00
reset_regdomains ( ) ;
2008-09-15 12:56:48 +04:00
2008-11-13 01:22:02 +03:00
kfree ( country_ie_regdomain ) ;
country_ie_regdomain = NULL ;
2008-10-21 13:01:33 +04:00
kfree ( last_request ) ;
2008-09-10 10:19:48 +04:00
platform_device_unregister ( reg_pdev ) ;
2008-09-15 12:56:48 +04:00
2009-02-21 08:20:39 +03:00
spin_lock_bh ( & reg_pending_beacons_lock ) ;
if ( ! list_empty ( & reg_pending_beacons ) ) {
list_for_each_entry_safe ( reg_beacon , btmp ,
& reg_pending_beacons , list ) {
list_del ( & reg_beacon - > list ) ;
kfree ( reg_beacon ) ;
}
}
spin_unlock_bh ( & reg_pending_beacons_lock ) ;
if ( ! list_empty ( & reg_beacon_list ) ) {
list_for_each_entry_safe ( reg_beacon , btmp ,
& reg_beacon_list , list ) {
list_del ( & reg_beacon - > list ) ;
kfree ( reg_beacon ) ;
}
}
2009-02-21 08:04:30 +03:00
spin_lock ( & reg_requests_lock ) ;
if ( ! list_empty ( & reg_requests_list ) ) {
list_for_each_entry_safe ( reg_request , tmp ,
& reg_requests_list , list ) {
list_del ( & reg_request - > list ) ;
kfree ( reg_request ) ;
}
}
spin_unlock ( & reg_requests_lock ) ;
2009-07-31 04:38:08 +04:00
mutex_unlock ( & reg_mutex ) ;
2009-02-21 08:04:21 +03:00
mutex_unlock ( & cfg80211_mutex ) ;
2008-01-24 21:38:38 +03:00
}