Commit b2e1b302 authored by Luis R. Rodriguez's avatar Luis R. Rodriguez Committed by John W. Linville

cfg80211: Add new wireless regulatory infrastructure

This adds the new wireless regulatory infrastructure. The
main motiviation behind this was to centralize regulatory
code as each driver was implementing their own regulatory solution,
and to replace the initial centralized code we have where:

* only 3 regulatory domains are supported: US, JP and EU
* regulatory domains can only be changed through module parameter
* all rules were built statically in the kernel

We now have support for regulatory domains for many countries
and regulatory domains are now queried through a userspace agent
through udev allowing distributions to update regulatory rules
without updating the kernel.

Each driver can regulatory_hint() a regulatory domain
based on either their EEPROM mapped regulatory domain value to a
respective ISO/IEC 3166-1 country code or pass an internally built
regulatory domain. We also add support to let the user set the
regulatory domain through userspace in case of faulty EEPROMs to
further help compliance.

Support for world roaming will be added soon for cards capable of
this.

For more information see:

http://wireless.kernel.org/en/developers/Regulatory/CRDA

For now we leave an option to enable the old module parameter,
ieee80211_regdom, and to build the 3 old regdomains statically
(US, JP and EU). This option is CONFIG_WIRELESS_OLD_REGULATORY.
These old static definitions and the module parameter is being
scheduled for removal for 2.6.29. Note that if you use this
you won't make use of a world regulatory domain as its pointless.
If you leave this option enabled and if CRDA is present and you
use US or JP we will try to ask CRDA to update us a regulatory
domain for us.
Signed-off-by: default avatarLuis R. Rodriguez <lrodriguez@atheros.com>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent 63f2c046
......@@ -6,6 +6,24 @@ be removed from this file.
---------------------------
What: old static regulatory information and ieee80211_regdom module parameter
When: 2.6.29
Why: The old regulatory infrastructure has been replaced with a new one
which does not require statically defined regulatory domains. We do
not want to keep static regulatory domains in the kernel due to the
the dynamic nature of regulatory law and localization. We kept around
the old static definitions for the regulatory domains of:
* US
* JP
* EU
and used by default the US when CONFIG_WIRELESS_OLD_REGULATORY was
set. We also kept around the ieee80211_regdom module parameter in case
some applications were relying on it. Changing regulatory domains
can now be done instead by using nl80211, as is done with iw.
Who: Luis R. Rodriguez <lrodriguez@atheros.com>
---------------------------
What: dev->power.power_state
When: July 2007
Why: Broken design for runtime control over driver power states, confusing
......
Linux wireless regulatory documentation
---------------------------------------
This document gives a brief review over how the Linux wireless
regulatory infrastructure works.
More up to date information can be obtained at the project's web page:
http://wireless.kernel.org/en/developers/Regulatory
Keeping regulatory domains in userspace
---------------------------------------
Due to the dynamic nature of regulatory domains we keep them
in userspace and provide a framework for userspace to upload
to the kernel one regulatory domain to be used as the central
core regulatory domain all wireless devices should adhere to.
How to get regulatory domains to the kernel
-------------------------------------------
Userspace gets a regulatory domain in the kernel by having
a userspace agent build it and send it via nl80211. Only
expected regulatory domains will be respected by the kernel.
A currently available userspace agent which can accomplish this
is CRDA - central regulatory domain agent. Its documented here:
http://wireless.kernel.org/en/developers/Regulatory/CRDA
Essentially the kernel will send a udev event when it knows
it needs a new regulatory domain. A udev rule can be put in place
to trigger crda to send the respective regulatory domain for a
specific ISO/IEC 3166 alpha2.
Below is an example udev rule which can be used:
# Example file, should be put in /etc/udev/rules.d/regulatory.rules
KERNEL=="regulatory*", ACTION=="change", SUBSYSTEM=="platform", RUN+="/sbin/crda"
The alpha2 is passed as an environment variable under the variable COUNTRY.
Who asks for regulatory domains?
--------------------------------
* Users
Users can use iw:
http://wireless.kernel.org/en/users/Documentation/iw
An example:
# set regulatory domain to "Costa Rica"
iw reg set CR
This will request the kernel to set the regulatory domain to
the specificied alpha2. The kernel in turn will then ask userspace
to provide a regulatory domain for the alpha2 specified by the user
by sending a uevent.
* Wireless subsystems for Country Information elements
The kernel will send a uevent to inform userspace a new
regulatory domain is required. More on this to be added
as its integration is added.
* Drivers
If drivers determine they need a specific regulatory domain
set they can inform the wireless core using regulatory_hint().
They have two options -- they either provide an alpha2 so that
crda can provide back a regulatory domain for that country or
they can build their own regulatory domain based on internal
custom knowledge so the wireless core can respect it.
*Most* drivers will rely on the first mechanism of providing a
regulatory hint with an alpha2. For these drivers there is an additional
check that can be used to ensure compliance based on custom EEPROM
regulatory data. This additional check can be used by drivers by
registering on its struct wiphy a reg_notifier() callback. This notifier
is called when the core's regulatory domain has been changed. The driver
can use this to review the changes made and also review who made them
(driver, user, country IE) and determine what to allow based on its
internal EEPROM data. Devices drivers wishing to be capable of world
roaming should use this callback. More on world roaming will be
added to this document when its support is enabled.
Device drivers who provide their own built regulatory domain
do not need a callback as the channels registered by them are
the only ones that will be allowed and therefore *additional*
cannels cannot be enabled.
Example code - drivers hinting an alpha2:
------------------------------------------
This example comes from the zd1211rw device driver. You can start
by having a mapping of your device's EEPROM country/regulatory
domain value to to a specific alpha2 as follows:
static struct zd_reg_alpha2_map reg_alpha2_map[] = {
{ ZD_REGDOMAIN_FCC, "US" },
{ ZD_REGDOMAIN_IC, "CA" },
{ ZD_REGDOMAIN_ETSI, "DE" }, /* Generic ETSI, use most restrictive */
{ ZD_REGDOMAIN_JAPAN, "JP" },
{ ZD_REGDOMAIN_JAPAN_ADD, "JP" },
{ ZD_REGDOMAIN_SPAIN, "ES" },
{ ZD_REGDOMAIN_FRANCE, "FR" },
Then you can define a routine to map your read EEPROM value to an alpha2,
as follows:
static int zd_reg2alpha2(u8 regdomain, char *alpha2)
{
unsigned int i;
struct zd_reg_alpha2_map *reg_map;
for (i = 0; i < ARRAY_SIZE(reg_alpha2_map); i++) {
reg_map = &reg_alpha2_map[i];
if (regdomain == reg_map->reg) {
alpha2[0] = reg_map->alpha2[0];
alpha2[1] = reg_map->alpha2[1];
return 0;
}
}
return 1;
}
Lastly, you can then hint to the core of your discovered alpha2, if a match
was found. You need to do this after you have registered your wiphy. You
are expected to do this during initialization.
r = zd_reg2alpha2(mac->regdomain, alpha2);
if (!r)
regulatory_hint(hw->wiphy, alpha2, NULL);
Example code - drivers providing a built in regulatory domain:
--------------------------------------------------------------
If you have regulatory information you can obtain from your
driver and you *need* to use this we let you build a regulatory domain
structure and pass it to the wireless core. To do this you should
kmalloc() a structure big enough to hold your regulatory domain
structure and you should then fill it with your data. Finally you simply
call regulatory_hint() with the regulatory domain structure in it.
Bellow is a simple example, with a regulatory domain cached using the stack.
Your implementation may vary (read EEPROM cache instead, for example).
Example cache of some regulatory domain
struct ieee80211_regdomain mydriver_jp_regdom = {
.n_reg_rules = 3,
.alpha2 = "JP",
//.alpha2 = "99", /* If I have no alpha2 to map it to */
.reg_rules = {
/* IEEE 802.11b/g, channels 1..14 */
REG_RULE(2412-20, 2484+20, 40, 6, 20, 0),
/* IEEE 802.11a, channels 34..48 */
REG_RULE(5170-20, 5240+20, 40, 6, 20,
NL80211_RRF_PASSIVE_SCAN),
/* IEEE 802.11a, channels 52..64 */
REG_RULE(5260-20, 5320+20, 40, 6, 20,
NL80211_RRF_NO_IBSS |
NL80211_RRF_DFS),
}
};
Then in some part of your code after your wiphy has been registered:
int r;
struct ieee80211_regdomain *rd;
int size_of_regd;
int num_rules = mydriver_jp_regdom.n_reg_rules;
unsigned int i;
size_of_regd = sizeof(struct ieee80211_regdomain) +
(num_rules * sizeof(struct ieee80211_reg_rule));
rd = kzalloc(size_of_regd, GFP_KERNEL);
if (!rd)
return -ENOMEM;
memcpy(rd, &mydriver_jp_regdom, sizeof(struct ieee80211_regdomain));
for (i=0; i < num_rules; i++) {
memcpy(&rd->reg_rules[i], &mydriver_jp_regdom.reg_rules[i],
sizeof(struct ieee80211_reg_rule));
}
r = regulatory_hint(hw->wiphy, NULL, rd);
if (r) {
kfree(rd);
return r;
}
......@@ -92,6 +92,20 @@
* @NL80211_CMD_SET_BSS: Set BSS attributes for BSS identified by
* %NL80211_ATTR_IFINDEX.
*
* @NL80211_CMD_SET_REG: Set current regulatory domain. CRDA sends this command
* after being queried by the kernel. CRDA replies by sending a regulatory
* domain structure which consists of %NL80211_ATTR_REG_ALPHA set to our
* current alpha2 if it found a match. It also provides
* NL80211_ATTR_REG_RULE_FLAGS, and a set of regulatory rules. Each
* regulatory rule is a nested set of attributes given by
* %NL80211_ATTR_REG_RULE_FREQ_[START|END] and
* %NL80211_ATTR_FREQ_RANGE_MAX_BW with an attached power rule given by
* %NL80211_ATTR_REG_RULE_POWER_MAX_ANT_GAIN and
* %NL80211_ATTR_REG_RULE_POWER_MAX_EIRP.
* @NL80211_CMD_REQ_SET_REG: ask the wireless core to set the regulatory domain
* to the the specified ISO/IEC 3166-1 alpha2 country code. The core will
* store this as a valid request and then query userspace for it.
*
* @NL80211_CMD_MAX: highest used command number
* @__NL80211_CMD_AFTER_LAST: internal use
*/
......@@ -131,7 +145,10 @@ enum nl80211_commands {
NL80211_CMD_SET_BSS,
/* add commands here */
NL80211_CMD_SET_REG,
NL80211_CMD_REQ_SET_REG,
/* add new commands above here */
/* used to define NL80211_CMD_MAX below */
__NL80211_CMD_AFTER_LAST,
......@@ -197,10 +214,21 @@ enum nl80211_commands {
* info given for %NL80211_CMD_GET_MPATH, nested attribute described at
* &enum nl80211_mpath_info.
*
*
* @NL80211_ATTR_MNTR_FLAGS: flags, nested element with NLA_FLAG attributes of
* &enum nl80211_mntr_flags.
*
* @NL80211_ATTR_REG_ALPHA2: an ISO-3166-alpha2 country code for which the
* current regulatory domain should be set to or is already set to.
* For example, 'CR', for Costa Rica. This attribute is used by the kernel
* to query the CRDA to retrieve one regulatory domain. This attribute can
* also be used by userspace to query the kernel for the currently set
* regulatory domain. We chose an alpha2 as that is also used by the
* IEEE-802.11d country information element to identify a country.
* Users can also simply ask the wireless core to set regulatory domain
* to a specific alpha2.
* @NL80211_ATTR_REG_RULES: a nested array of regulatory domain regulatory
* rules.
*
* @NL80211_ATTR_BSS_CTS_PROT: whether CTS protection is enabled (u8, 0 or 1)
* @NL80211_ATTR_BSS_SHORT_PREAMBLE: whether short preamble is enabled
* (u8, 0 or 1)
......@@ -265,6 +293,9 @@ enum nl80211_attrs {
NL80211_ATTR_SUPPORTED_IFTYPES,
NL80211_ATTR_REG_ALPHA2,
NL80211_ATTR_REG_RULES,
/* add attributes here, update the policy in nl80211.c */
__NL80211_ATTR_AFTER_LAST,
......@@ -278,6 +309,7 @@ enum nl80211_attrs {
#define NL80211_ATTR_HT_CAPABILITY NL80211_ATTR_HT_CAPABILITY
#define NL80211_MAX_SUPP_RATES 32
#define NL80211_MAX_SUPP_REG_RULES 32
#define NL80211_TKIP_DATA_OFFSET_ENCR_KEY 0
#define NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY 16
#define NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY 24
......@@ -472,6 +504,66 @@ enum nl80211_bitrate_attr {
NL80211_BITRATE_ATTR_MAX = __NL80211_BITRATE_ATTR_AFTER_LAST - 1
};
/**
* enum nl80211_reg_rule_attr - regulatory rule attributes
* @NL80211_ATTR_REG_RULE_FLAGS: a set of flags which specify additional
* considerations for a given frequency range. These are the
* &enum nl80211_reg_rule_flags.
* @NL80211_ATTR_FREQ_RANGE_START: starting frequencry for the regulatory
* rule in KHz. This is not a center of frequency but an actual regulatory
* band edge.
* @NL80211_ATTR_FREQ_RANGE_END: ending frequency for the regulatory rule
* in KHz. This is not a center a frequency but an actual regulatory
* band edge.
* @NL80211_ATTR_FREQ_RANGE_MAX_BW: maximum allowed bandwidth for this
* frequency range, in KHz.
* @NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN: the maximum allowed antenna gain
* for a given frequency range. The value is in mBi (100 * dBi).
* If you don't have one then don't send this.
* @NL80211_ATTR_POWER_RULE_MAX_EIRP: the maximum allowed EIRP for
* a given frequency range. The value is in mBm (100 * dBm).
*/
enum nl80211_reg_rule_attr {
__NL80211_REG_RULE_ATTR_INVALID,
NL80211_ATTR_REG_RULE_FLAGS,
NL80211_ATTR_FREQ_RANGE_START,
NL80211_ATTR_FREQ_RANGE_END,
NL80211_ATTR_FREQ_RANGE_MAX_BW,
NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN,
NL80211_ATTR_POWER_RULE_MAX_EIRP,
/* keep last */
__NL80211_REG_RULE_ATTR_AFTER_LAST,
NL80211_REG_RULE_ATTR_MAX = __NL80211_REG_RULE_ATTR_AFTER_LAST - 1
};
/**
* enum nl80211_reg_rule_flags - regulatory rule flags
*
* @NL80211_RRF_NO_OFDM: OFDM modulation not allowed
* @NL80211_RRF_NO_CCK: CCK modulation not allowed
* @NL80211_RRF_NO_INDOOR: indoor operation not allowed
* @NL80211_RRF_NO_OUTDOOR: outdoor operation not allowed
* @NL80211_RRF_DFS: DFS support is required to be used
* @NL80211_RRF_PTP_ONLY: this is only for Point To Point links
* @NL80211_RRF_PTMP_ONLY: this is only for Point To Multi Point links
* @NL80211_RRF_PASSIVE_SCAN: passive scan is required
* @NL80211_RRF_NO_IBSS: no IBSS is allowed
*/
enum nl80211_reg_rule_flags {
NL80211_RRF_NO_OFDM = 1<<0,
NL80211_RRF_NO_CCK = 1<<1,
NL80211_RRF_NO_INDOOR = 1<<2,
NL80211_RRF_NO_OUTDOOR = 1<<3,
NL80211_RRF_DFS = 1<<4,
NL80211_RRF_PTP_ONLY = 1<<5,
NL80211_RRF_PTMP_ONLY = 1<<6,
NL80211_RRF_PASSIVE_SCAN = 1<<7,
NL80211_RRF_NO_IBSS = 1<<8,
};
/**
* enum nl80211_mntr_flags - monitor configuration flags
*
......
......@@ -287,6 +287,66 @@ struct bss_parameters {
int use_short_slot_time;
};
/**
* enum reg_set_by - Indicates who is trying to set the regulatory domain
* @REGDOM_SET_BY_INIT: regulatory domain was set by initialization. We will be
* using a static world regulatory domain by default.
* @REGDOM_SET_BY_CORE: Core queried CRDA for a dynamic world regulatory domain.
* @REGDOM_SET_BY_USER: User asked the wireless core to set the
* regulatory domain.
* @REGDOM_SET_BY_DRIVER: a wireless drivers has hinted to the wireless core
* it thinks its knows the regulatory domain we should be in.
* @REGDOM_SET_BY_COUNTRY_IE: the wireless core has received an 802.11 country
* information element with regulatory information it thinks we
* should consider.
*/
enum reg_set_by {
REGDOM_SET_BY_INIT,
REGDOM_SET_BY_CORE,
REGDOM_SET_BY_USER,
REGDOM_SET_BY_DRIVER,
REGDOM_SET_BY_COUNTRY_IE,
};
struct ieee80211_freq_range {
u32 start_freq_khz;
u32 end_freq_khz;
u32 max_bandwidth_khz;
};
struct ieee80211_power_rule {
u32 max_antenna_gain;
u32 max_eirp;
};
struct ieee80211_reg_rule {
struct ieee80211_freq_range freq_range;
struct ieee80211_power_rule power_rule;
u32 flags;
};
struct ieee80211_regdomain {
u32 n_reg_rules;
char alpha2[2];
struct ieee80211_reg_rule reg_rules[];
};
#define MHZ_TO_KHZ(freq) (freq * 1000)
#define KHZ_TO_MHZ(freq) (freq / 1000)
#define DBI_TO_MBI(gain) (gain * 100)
#define MBI_TO_DBI(gain) (gain / 100)
#define DBM_TO_MBM(gain) (gain * 100)
#define MBM_TO_DBM(gain) (gain / 100)
#define REG_RULE(start, end, bw, gain, eirp, reg_flags) { \
.freq_range.start_freq_khz = (start) * 1000, \
.freq_range.end_freq_khz = (end) * 1000, \
.freq_range.max_bandwidth_khz = (bw) * 1000, \
.power_rule.max_antenna_gain = (gain) * 100, \
.power_rule.max_eirp = (eirp) * 100, \
.flags = reg_flags, \
}
/* from net/wireless.h */
struct wiphy;
......
......@@ -833,6 +833,8 @@ struct ieee80211_hw {
s8 max_signal;
};
struct ieee80211_hw *wiphy_to_hw(struct wiphy *wiphy);
/**
* SET_IEEE80211_DEV - set device for 802.11 hardware
*
......
......@@ -60,6 +60,7 @@ enum ieee80211_channel_flags {
* with cfg80211.
*
* @center_freq: center frequency in MHz
* @max_bandwidth: maximum allowed bandwidth for this channel, in MHz
* @hw_value: hardware-specific value for the channel
* @flags: channel flags from &enum ieee80211_channel_flags.
* @orig_flags: channel flags at registration time, used by regulatory
......@@ -73,6 +74,7 @@ enum ieee80211_channel_flags {
struct ieee80211_channel {
enum ieee80211_band band;
u16 center_freq;
u8 max_bandwidth;
u16 hw_value;
u32 flags;
int max_antenna_gain;
......@@ -178,6 +180,7 @@ struct ieee80211_supported_band {
* struct wiphy - wireless hardware description
* @idx: the wiphy index assigned to this item
* @class_dev: the class device representing /sys/class/ieee80211/<wiphy-name>
* @reg_notifier: the driver's regulatory notification callback
*/
struct wiphy {
/* assign these fields before you register the wiphy */
......@@ -197,6 +200,9 @@ struct wiphy {
struct ieee80211_supported_band *bands[IEEE80211_NUM_BANDS];
/* Lets us get back the wiphy on the callback */
int (*reg_notifier)(struct wiphy *wiphy, enum reg_set_by setby);
/* fields below are read-only, assigned by cfg80211 */
/* the item in /sys/class/ieee80211/ points to this,
......@@ -322,6 +328,58 @@ extern int ieee80211_frequency_to_channel(int freq);
*/
extern struct ieee80211_channel *__ieee80211_get_channel(struct wiphy *wiphy,
int freq);
/**
* __regulatory_hint - hint to the wireless core a regulatory domain
* @wiphy: if a driver is providing the hint this is the driver's very
* own &struct wiphy
* @alpha2: the ISO/IEC 3166 alpha2 being claimed the regulatory domain
* should be in. If @rd is set this should be NULL
* @rd: a complete regulatory domain, if passed the caller need not worry
* about freeing it
*
* The Wireless subsystem can use this function to hint to the wireless core
* what it believes should be the current regulatory domain by
* giving it an ISO/IEC 3166 alpha2 country code it knows its regulatory
* domain should be in or by providing a completely build regulatory domain.
*
* Returns -EALREADY if *a regulatory domain* has already been set. Note that
* this could be by another driver. It is safe for drivers to continue if
* -EALREADY is returned, if drivers are not capable of world roaming they
* should not register more channels than they support. Right now we only
* support listening to the first driver hint. If the driver is capable
* of world roaming but wants to respect its own EEPROM mappings for
* specific regulatory domains it should register the @reg_notifier callback
* on the &struct wiphy. Returns 0 if the hint went through fine or through an
* intersection operation. Otherwise a standard error code is returned.
*
*/
extern int __regulatory_hint(struct wiphy *wiphy, enum reg_set_by set_by,
const char *alpha2, struct ieee80211_regdomain *rd);
/**
* regulatory_hint - driver hint to the wireless core a regulatory domain
* @wiphy: the driver's very own &struct wiphy
* @alpha2: the ISO/IEC 3166 alpha2 the driver claims its regulatory domain
* should be in. If @rd is set this should be NULL. Note that if you
* set this to NULL you should still set rd->alpha2 to some accepted
* alpha2.
* @rd: a complete regulatory domain provided by the driver. If passed
* the driver does not need to worry about freeing it.
*
* Wireless drivers can use this function to hint to the wireless core
* what it believes should be the current regulatory domain by
* giving it an ISO/IEC 3166 alpha2 country code it knows its regulatory
* domain should be in or by providing a completely build regulatory domain.
* If the driver provides an ISO/IEC 3166 alpha2 userspace will be queried
* for a regulatory domain structure for the respective country. If
* a regulatory domain is build and passed you should set the alpha2
* if possible, otherwise set it to the special value of "99" which tells
* the wireless core it is unknown. If you pass a built regulatory domain
* and we return non zero you are in charge of kfree()'ing the structure.
*
* See __regulatory_hint() documentation for possible return values.
*/
extern int regulatory_hint(struct wiphy *wiphy,
const char *alpha2, struct ieee80211_regdomain *rd);
/**
* ieee80211_get_channel - get channel struct from wiphy for specified frequency
......
......@@ -17,6 +17,13 @@
#include "rate.h"
#include "mesh.h"
struct ieee80211_hw *wiphy_to_hw(struct wiphy *wiphy)
{
struct ieee80211_local *local = wiphy_priv(wiphy);
return &local->hw;
}
EXPORT_SYMBOL(wiphy_to_hw);
static enum ieee80211_if_types
nl80211_type_to_mac80211_type(enum nl80211_iftype type)
{
......
......@@ -14,6 +14,38 @@ config NL80211
If unsure, say Y.
config WIRELESS_OLD_REGULATORY
bool "Old wireless static regulatory defintions"
default n
---help---
This option enables the old static regulatory information
and uses it within the new framework. This is available
temporarily as an option to help prevent immediate issues
due to the switch to the new regulatory framework which
does require a new userspace application which has the
database of regulatory information (CRDA) and another for
setting regulatory domains (iw).
For more information see:
http://wireless.kernel.org/en/developers/Regulatory/CRDA
http://wireless.kernel.org/en/users/Documentation/iw
It is important to note though that if you *do* have CRDA present
and if this option is enabled CRDA *will* be called to update the
regulatory domain (for US and JP only). Support for letting the user
set the regulatory domain through iw is also supported. This option
mainly exists to leave around for a kernel release some old static
regulatory domains that were defined and to keep around the old
ieee80211_regdom module parameter. This is being phased out and you
should stop using them ASAP.
Say N unless you cannot install a new userspace application
or have one currently depending on the ieee80211_regdom module
parameter and cannot port it to use the new userspace interfaces.
This is scheduled for removal for 2.6.29.
config WIRELESS_EXT
bool "Wireless extensions"
default n
......
......@@ -13,12 +13,14 @@
#include <linux/debugfs.h>
#include <linux/notifier.h>
#include <linux/device.h>
#include <linux/list.h>
#include <net/genetlink.h>
#include <net/cfg80211.h>
#include <net/wireless.h>
#include "nl80211.h"
#include "core.h"
#include "sysfs.h"
#include "reg.h"
/* name for sysfs, %d is appended */
#define PHY_NAME "phy"
......@@ -27,6 +29,107 @@ MODULE_AUTHOR("Johannes Berg");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("wireless configuration support");
struct list_head regulatory_requests;
/* Central wireless core regulatory domains, we only need two,
* the current one and a world regulatory domain in case we have no
* information to give us an alpha2 */
struct ieee80211_regdomain *cfg80211_regdomain;
/* We keep a static world regulatory domain in case of the absence of CRDA */
const struct ieee80211_regdomain world_regdom = {
.n_reg_rules = 1,
.alpha2 = "00",
.reg_rules = {
REG_RULE(2402, 2472, 40, 6, 20,
NL80211_RRF_PASSIVE_SCAN |
NL80211_RRF_NO_IBSS),
}
};
#ifdef CONFIG_WIRELESS_OLD_REGULATORY
/* All this fucking static junk will be removed soon, so
* don't fucking count on it !@#$ */
static char *ieee80211_regdom = "US";
module_param(ieee80211_regdom, charp, 0444);
MODULE_PARM_DESC(ieee80211_regdom, "IEEE 802.11 regulatory domain code");
/* We assume 40 MHz bandwidth for the old regulatory work.
* We make emphasis we are using the exact same frequencies
* as before */
const struct ieee80211_regdomain us_regdom = {
.n_reg_rules = 6,
.alpha2 = "US",
.reg_rules = {
/* IEEE 802.11b/g, channels 1..11 */
REG_RULE(2412-20, 2462+20, 40, 6, 27, 0),
/* IEEE 802.11a, channel 36 */
REG_RULE(5180-20, 5180+20, 40, 6, 23, 0),
/* IEEE 802.11a, channel 40 */
REG_RULE(5200-20, 5200+20, 40, 6, 23, 0),
/* IEEE 802.11a, channel 44 */
REG_RULE(5220-20, 5220+20, 40, 6, 23, 0),
/* IEEE 802.11a, channels 48..64 */
REG_RULE(5240-20, 5320+20, 40, 6, 23, 0),
/* IEEE 802.11a, channels 149..165, outdoor */
REG_RULE(5745-20, 5825+20, 40, 6, 30, 0),
}
};
const struct ieee80211_regdomain jp_regdom = {
.n_reg_rules = 3,
.alpha2 = "JP",
.reg_rules = {
/* IEEE 802.11b/g, channels 1..14 */
REG_RULE(2412-20, 2484+20, 40, 6, 20, 0),
/* IEEE 802.11a, channels 34..48 */
REG_RULE(5170-20, 5240+20, 40, 6, 20,
NL80211_RRF_PASSIVE_SCAN),
/* IEEE 802.11a, channels 52..64 */
REG_RULE(5260-20, 5320+20, 40, 6, 20,
NL80211_RRF_NO_IBSS |
NL80211_RRF_DFS),
}
};
const struct ieee80211_regdomain eu_regdom = {
.n_reg_rules = 6,
/* This alpha2 is bogus, we leave it here just for stupid
* backward compatibility */
.alpha2 = "EU",
.reg_rules = {
/* IEEE 802.11b/g, channels 1..13 */
REG_RULE(2412-20, 2472+20, 40, 6, 20, 0),
/* IEEE 802.11a, channel 36 */
REG_RULE(5180-20, 5180+20, 40, 6, 23,
NL80211_RRF_PASSIVE_SCAN),
/* IEEE 802.11a, channel 40 */
REG_RULE(5200-20, 5200+20, 40, 6, 23,
NL80211_RRF_PASSIVE_SCAN),
/* IEEE 802.11a, channel 44 */
REG_RULE(5220-20, 5220+20, 40, 6, 23,
NL80211_RRF_PASSIVE_SCAN),
/* IEEE 802.11a, channels 48..64 */
REG_RULE(5240-20, 5320+20, 40, 6, 20,
NL80211_RRF_NO_IBSS |
NL80211_RRF_DFS),
/* IEEE 802.11a, channels 100..140 */
REG_RULE(5500-20, 5700+20, 40, 6, 30,
NL80211_RRF_NO_IBSS |
NL80211_RRF_DFS),
}
};
#endif
struct ieee80211_regdomain *cfg80211_world_regdom =
(struct ieee80211_regdomain *) &world_regdom;
LIST_HEAD(regulatory_requests);
DEFINE_MUTEX(cfg80211_reg_mutex);
/* RCU might be appropriate here since we usually
* only read the list, and that can happen quite
* often because we need to do it for each command */
......@@ -302,7 +405,9 @@ int wiphy_register(struct wiphy *wiphy)
ieee80211_set_bitrate_flags(wiphy);
/* set up regulatory info */
wiphy_update_regulatory(wiphy);
mutex_lock(&cfg80211_reg_mutex);
wiphy_update_regulatory(wiphy, REGDOM_SET_BY_CORE);
mutex_unlock(&cfg80211_reg_mutex);
mutex_lock(&cfg80211_drv_mutex);
......@@ -409,9 +514,35 @@ static struct notifier_block cfg80211_netdev_notifier = {
.notifier_call = cfg80211_netdev_notifier_call,
};
#ifdef CONFIG_WIRELESS_OLD_REGULATORY
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;
}
#endif
static int cfg80211_init(void)
{
int err = wiphy_sysfs_init();
int err;
#ifdef CONFIG_WIRELESS_OLD_REGULATORY
cfg80211_regdomain =
(struct ieee80211_regdomain *) static_regdom(ieee80211_regdom);
/* Used during reset_regdomains_static() */
cfg80211_world_regdom = cfg80211_regdomain;
#else
cfg80211_regdomain =
(struct ieee80211_regdomain *) cfg80211_world_regdom;
#endif
err = wiphy_sysfs_init();
if (err)
goto out_fail_sysfs;
......@@ -425,8 +556,33 @@ static int cfg80211_init(void)
ieee80211_debugfs_dir = debugfs_create_dir("ieee80211", NULL);
err = regulatory_init();
if (err)
goto out_fail_reg;
#ifdef CONFIG_WIRELESS_OLD_REGULATORY
printk(KERN_INFO "cfg80211: Using old static regulatory domain:\n");
print_regdomain_info(cfg80211_regdomain);
/* The old code still requests for a new regdomain and if
* you have CRDA you get it updated, otherwise you get
* stuck with the static values. We ignore "EU" code as
* that is not a valid ISO / IEC 3166 alpha2 */
if (ieee80211_regdom[0] != 'E' &&
ieee80211_regdom[1] != 'U')
err = __regulatory_hint(NULL, REGDOM_SET_BY_CORE,
ieee80211_regdom, NULL);
#else
err = __regulatory_hint(NULL, REGDOM_SET_BY_CORE, "00", NULL);
if (err)
printk(KERN_ERR "cfg80211: calling CRDA failed - "
"unable to update world regulatory domain, "
"using static definition\n");
#endif
return 0;
out_fail_reg:
debugfs_remove(ieee80211_debugfs_dir);
out_fail_nl80211:
unregister_netdevice_notifier(&cfg80211_netdev_notifier);
out_fail_notifier:
......@@ -434,6 +590,7 @@ static int cfg80211_init(void)
out_fail_sysfs:
return err;
}
subsys_initcall(cfg80211_init);
static void cfg80211_exit(void)
......@@ -442,5 +599,6 @@ static void cfg80211_exit(void)
nl80211_exit();
unregister_netdevice_notifier(&cfg80211_netdev_notifier);
wiphy_sysfs_exit();
regulatory_exit();
}
module_exit(cfg80211_exit);
......@@ -79,6 +79,6 @@ extern int cfg80211_dev_rename(struct cfg80211_registered_device *drv,
char *newname);
void ieee80211_set_bitrate_flags(struct wiphy *wiphy);
void wiphy_update_regulatory(struct wiphy *wiphy);
void wiphy_update_regulatory(struct wiphy *wiphy, enum reg_set_by setby);
#endif /* __NET_WIRELESS_CORE_H */
......@@ -18,6 +18,7 @@
#include <net/cfg80211.h>
#include "core.h"
#include "nl80211.h"
#include "reg.h"
/* the netlink family */
static struct genl_family nl80211_fam = {
......@@ -88,6 +89,9 @@ static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = {
.len = IEEE80211_MAX_MESH_ID_LEN },
[NL80211_ATTR_MPATH_NEXT_HOP] = { .type = NLA_U32 },
[NL80211_ATTR_REG_ALPHA2] = { .type = NLA_STRING, .len = 2 },
[NL80211_ATTR_REG_RULES] = { .type = NLA_NESTED },
[NL80211_ATTR_BSS_CTS_PROT] = { .type = NLA_U8 },
[NL80211_ATTR_BSS_SHORT_PREAMBLE] = { .type = NLA_U8 },
[NL80211_ATTR_BSS_SHORT_SLOT_TIME] = { .type = NLA_U8 },
......@@ -1599,6 +1603,141 @@ static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info)
return err;
}
static const struct nla_policy
reg_rule_policy[NL80211_REG_RULE_ATTR_MAX + 1] = {
[NL80211_ATTR_REG_RULE_FLAGS] = { .type = NLA_U32 },
[NL80211_ATTR_FREQ_RANGE_START] = { .type = NLA_U32 },
[NL80211_ATTR_FREQ_RANGE_END] = { .type = NLA_U32 },
[NL80211_ATTR_FREQ_RANGE_MAX_BW] = { .type = NLA_U32 },
[NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN] = { .type = NLA_U32 },
[NL80211_ATTR_POWER_RULE_MAX_EIRP] = { .type = NLA_U32 },
};
static int parse_reg_rule(struct nlattr *tb[],
struct ieee80211_reg_rule *reg_rule)
{
struct ieee80211_freq_range *freq_range = &reg_rule->freq_range;
struct ieee80211_power_rule *power_rule = &reg_rule->power_rule;
if (!tb[NL80211_ATTR_REG_RULE_FLAGS])
return -EINVAL;
if (!tb[NL80211_ATTR_FREQ_RANGE_START])
return -EINVAL;
if (!tb[NL80211_ATTR_FREQ_RANGE_END])
return -EINVAL;
if (!tb[NL80211_ATTR_FREQ_RANGE_MAX_BW])
return -EINVAL;
if (!tb[NL80211_ATTR_POWER_RULE_MAX_EIRP])
return -EINVAL;
reg_rule->flags = nla_get_u32(tb[NL80211_ATTR_REG_RULE_FLAGS]);
freq_range->start_freq_khz =
nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_START]);
freq_range->end_freq_khz =
nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_END]);
freq_range->max_bandwidth_khz =
nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_MAX_BW]);
power_rule->max_eirp =
nla_get_u32(tb[NL80211_ATTR_POWER_RULE_MAX_EIRP]);
if (tb[NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN])
power_rule->max_antenna_gain =
nla_get_u32(tb[NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN]);
return 0;
}
static int nl80211_req_set_reg(struct sk_buff *skb, struct genl_info *info)
{
int r;
char *data = NULL;
if (!info->attrs[NL80211_ATTR_REG_ALPHA2])
return -EINVAL;
data = nla_data(info->attrs[NL80211_ATTR_REG_ALPHA2]);
#ifdef CONFIG_WIRELESS_OLD_REGULATORY
/* We ignore world regdom requests with the old regdom setup */
if (is_world_regdom(data))
return -EINVAL;
#endif
mutex_lock(&cfg80211_drv_mutex);
r = __regulatory_hint(NULL, REGDOM_SET_BY_USER, data, NULL);
mutex_unlock(&cfg80211_drv_mutex);
return r;
}
static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info)
{
struct nlattr *tb[NL80211_REG_RULE_ATTR_MAX + 1];
struct nlattr *nl_reg_rule;
char *alpha2 = NULL;
int rem_reg_rules = 0, r = 0;
u32 num_rules = 0, rule_idx = 0, size_of_regd;
struct ieee80211_regdomain *rd = NULL;
if (!info->attrs[NL80211_ATTR_REG_ALPHA2])
return -EINVAL;
if (!info->attrs[NL80211_ATTR_REG_RULES])
return -EINVAL;
alpha2 = nla_data(info->attrs[NL80211_ATTR_REG_ALPHA2]);
nla_for_each_nested(nl_reg_rule, info->attrs[NL80211_ATTR_REG_RULES],
rem_reg_rules) {
num_rules++;
if (num_rules > NL80211_MAX_SUPP_REG_RULES)
goto bad_reg;
}
if (!reg_is_valid_request(alpha2))
return -EINVAL;
size_of_regd = sizeof(struct ieee80211_regdomain) +
(num_rules * sizeof(struct ieee80211_reg_rule));
rd = kzalloc(size_of_regd, GFP_KERNEL);
if (!rd)
return -ENOMEM;
rd->n_reg_rules = num_rules;
rd->alpha2[0] = alpha2[0];
rd->alpha2[1] = alpha2[1];
nla_for_each_nested(nl_reg_rule, info->attrs[NL80211_ATTR_REG_RULES],
rem_reg_rules) {
nla_parse(tb, NL80211_REG_RULE_ATTR_MAX,
nla_data(nl_reg_rule), nla_len(nl_reg_rule),
reg_rule_policy);
r = parse_reg_rule(tb, &rd->reg_rules[rule_idx]);
if (r)
goto bad_reg;
rule_idx++;
if (rule_idx > NL80211_MAX_SUPP_REG_RULES)
goto bad_reg;
}
BUG_ON(rule_idx != num_rules);
mutex_lock(&cfg80211_drv_mutex);
r = set_regdom(rd);
mutex_unlock(&cfg80211_drv_mutex);
if (r)
goto bad_reg;
return r;
bad_reg:
kfree(rd);
return -EINVAL;
}
static struct genl_ops nl80211_ops[] = {
{
.cmd = NL80211_CMD_GET_WIPHY,
......@@ -1736,6 +1875,18 @@ static struct genl_ops nl80211_ops[] = {
.policy = nl80211_policy,
.flags = GENL_ADMIN_PERM,
},
{
.cmd = NL80211_CMD_SET_REG,
.doit = nl80211_set_reg,
.policy = nl80211_policy,
.flags = GENL_ADMIN_PERM,
},
{
.cmd = NL80211_CMD_REQ_SET_REG,
.doit = nl80211_req_set_reg,
.policy = nl80211_policy,
.flags = GENL_ADMIN_PERM,
},
};
/* multicast groups */
......
This diff is collapsed.
#ifndef __NET_WIRELESS_REG_H
#define __NET_WIRELESS_REG_H
extern const struct ieee80211_regdomain world_regdom;
#ifdef CONFIG_WIRELESS_OLD_REGULATORY
extern const struct ieee80211_regdomain us_regdom;
extern const struct ieee80211_regdomain jp_regdom;
extern const struct ieee80211_regdomain eu_regdom;
#endif
extern struct ieee80211_regdomain *cfg80211_regdomain;
extern struct ieee80211_regdomain *cfg80211_world_regdom;
extern struct list_head regulatory_requests;
struct regdom_last_setby {
struct wiphy *wiphy;
u8 initiator;
};
/* wiphy is set if this request's initiator is REGDOM_SET_BY_DRIVER */
struct regulatory_request {
struct list_head list;
struct wiphy *wiphy;
int granted;
enum reg_set_by initiator;
char alpha2[2];
};
bool is_world_regdom(char *alpha2);
bool reg_is_valid_request(char *alpha2);
int set_regdom(struct ieee80211_regdomain *rd);
int __regulatory_hint_alpha2(struct wiphy *wiphy, enum reg_set_by set_by,
const char *alpha2);
int regulatory_init(void);
void regulatory_exit(void);
void print_regdomain_info(struct ieee80211_regdomain *);
/* If a char is A-Z */
#define IS_ALPHA(letter) (letter >= 65 && letter <= 90)
#endif /* __NET_WIRELESS_REG_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment