Linux wireless regulatory documentation¶
This document gives a brief review over how the Linux wirelessregulatory infrastructure works.
More up to date information can be obtained at the project’s web page:
https://wireless.wiki.kernel.org/en/developers/Regulatory
Keeping regulatory domains in userspace¶
Due to the dynamic nature of regulatory domains we keep themin userspace and provide a framework for userspace to uploadto the kernel one regulatory domain to be used as the centralcore regulatory domain all wireless devices should adhere to.
How to get regulatory domains to the kernel¶
When the regulatory domain is first set up, the kernel will request adatabase file (regulatory.db) containing all the regulatory rules. Itwill then use that database when it needs to look up the rules for agiven country.
How to get regulatory domains to the kernel (old CRDA solution)¶
Userspace gets a regulatory domain in the kernel by havinga userspace agent build it and send it via nl80211. Onlyexpected regulatory domains will be respected by the kernel.
A currently available userspace agent which can accomplish thisis CRDA - central regulatory domain agent. Its documented here:
https://wireless.wiki.kernel.org/en/developers/Regulatory/CRDA
Essentially the kernel will send a udev event when it knowsit needs a new regulatory domain. A udev rule can be put in placeto trigger crda to send the respective regulatory domain for aspecific 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.rulesKERNEL==”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:
https://wireless.wiki.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 tothe specificied alpha2. The kernel in turn will then ask userspaceto provide a regulatory domain for the alpha2 specified by the userby sending a uevent.
- Wireless subsystems for Country Information elements
The kernel will send a uevent to inform userspace a newregulatory domain is required. More on this to be addedas its integration is added.
- Drivers
If drivers determine they need a specific regulatory domainset they can inform the wireless core usingregulatory_hint().They have two options – they either provide an alpha2 so thatcrda can provide back a regulatory domain for that country orthey can build their own regulatory domain based on internalcustom knowledge so the wireless core can respect it.
Most drivers will rely on the first mechanism of providing aregulatory hint with an alpha2. For these drivers there is an additionalcheck that can be used to ensure compliance based on custom EEPROMregulatory data. This additional check can be used by drivers byregistering on its struct wiphy a reg_notifier() callback. This notifieris called when the core’s regulatory domain has been changed. The drivercan use this to review the changes made and also review who made them(driver, user, country IE) and determine what to allow based on itsinternal EEPROM data. Devices drivers wishing to be capable of worldroaming should use this callback. More on world roaming will beadded to this document when its support is enabled.
Device drivers who provide their own built regulatory domaindo not need a callback as the channels registered by them arethe only ones that will be allowed and thereforeadditionalchannels cannot be enabled.
Example code - drivers hinting an alpha2:¶
This example comes from the zd1211rw device driver. You can startby having a mapping of your device’s EEPROM country/regulatorydomain value 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 = ®_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 matchwas found. You need to do this after you have registered your wiphy. Youare expected to do this during initialization.
r = zd_reg2alpha2(mac->regdomain, alpha2);if (!r) regulatory_hint(hw->wiphy, alpha2);
Example code - drivers providing a built in regulatory domain:¶
[NOTE: This API is not currently available, it can be added when required]
If you have regulatory information you can obtain from yourdriver and youneed to use this we let you build a regulatory domainstructure and pass it to the wireless core. To do this you shouldkmalloc() a structure big enough to hold your regulatory domainstructure and you should then fill it with your data. Finally you simplycallregulatory_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-10, 2484+10, 40, 6, 20, 0), /* IEEE 802.11a, channels 34..48 */ REG_RULE(5170-10, 5240+10, 40, 6, 20, NL80211_RRF_NO_IR), /* IEEE 802.11a, channels 52..64 */ REG_RULE(5260-10, 5320+10, 40, 6, 20, NL80211_RRF_NO_IR| NL80211_RRF_DFS), }};Then in some part of your code after your wiphy has been registered:
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));regulatory_struct_hint(rd);
Statically compiled regulatory database¶
When a database should be fixed into the kernel, it can be provided as afirmware file at build time that is then linked into the kernel.