198661e0cSMauro Carvalho Chehab.. SPDX-License-Identifier: GPL-2.0 298661e0cSMauro Carvalho Chehab 398661e0cSMauro Carvalho Chehab======================================= 498661e0cSMauro Carvalho ChehabLinux wireless regulatory documentation 598661e0cSMauro Carvalho Chehab======================================= 698661e0cSMauro Carvalho Chehab 798661e0cSMauro Carvalho ChehabThis document gives a brief review over how the Linux wireless 898661e0cSMauro Carvalho Chehabregulatory infrastructure works. 998661e0cSMauro Carvalho Chehab 1098661e0cSMauro Carvalho ChehabMore up to date information can be obtained at the project's web page: 1198661e0cSMauro Carvalho Chehab 12327cdb98SFlavio Suligoihttps://wireless.wiki.kernel.org/en/developers/Regulatory 1398661e0cSMauro Carvalho Chehab 1498661e0cSMauro Carvalho ChehabKeeping regulatory domains in userspace 1598661e0cSMauro Carvalho Chehab--------------------------------------- 1698661e0cSMauro Carvalho Chehab 1798661e0cSMauro Carvalho ChehabDue to the dynamic nature of regulatory domains we keep them 1898661e0cSMauro Carvalho Chehabin userspace and provide a framework for userspace to upload 1998661e0cSMauro Carvalho Chehabto the kernel one regulatory domain to be used as the central 2098661e0cSMauro Carvalho Chehabcore regulatory domain all wireless devices should adhere to. 2198661e0cSMauro Carvalho Chehab 2298661e0cSMauro Carvalho ChehabHow to get regulatory domains to the kernel 2398661e0cSMauro Carvalho Chehab------------------------------------------- 2498661e0cSMauro Carvalho Chehab 2598661e0cSMauro Carvalho ChehabWhen the regulatory domain is first set up, the kernel will request a 2698661e0cSMauro Carvalho Chehabdatabase file (regulatory.db) containing all the regulatory rules. It 2798661e0cSMauro Carvalho Chehabwill then use that database when it needs to look up the rules for a 2898661e0cSMauro Carvalho Chehabgiven country. 2998661e0cSMauro Carvalho Chehab 3098661e0cSMauro Carvalho ChehabHow to get regulatory domains to the kernel (old CRDA solution) 3198661e0cSMauro Carvalho Chehab--------------------------------------------------------------- 3298661e0cSMauro Carvalho Chehab 3398661e0cSMauro Carvalho ChehabUserspace gets a regulatory domain in the kernel by having 3498661e0cSMauro Carvalho Chehaba userspace agent build it and send it via nl80211. Only 3598661e0cSMauro Carvalho Chehabexpected regulatory domains will be respected by the kernel. 3698661e0cSMauro Carvalho Chehab 3798661e0cSMauro Carvalho ChehabA currently available userspace agent which can accomplish this 3898661e0cSMauro Carvalho Chehabis CRDA - central regulatory domain agent. Its documented here: 3998661e0cSMauro Carvalho Chehab 40327cdb98SFlavio Suligoihttps://wireless.wiki.kernel.org/en/developers/Regulatory/CRDA 4198661e0cSMauro Carvalho Chehab 4298661e0cSMauro Carvalho ChehabEssentially the kernel will send a udev event when it knows 4398661e0cSMauro Carvalho Chehabit needs a new regulatory domain. A udev rule can be put in place 4498661e0cSMauro Carvalho Chehabto trigger crda to send the respective regulatory domain for a 4598661e0cSMauro Carvalho Chehabspecific ISO/IEC 3166 alpha2. 4698661e0cSMauro Carvalho Chehab 4798661e0cSMauro Carvalho ChehabBelow is an example udev rule which can be used: 4898661e0cSMauro Carvalho Chehab 4998661e0cSMauro Carvalho Chehab# Example file, should be put in /etc/udev/rules.d/regulatory.rules 5098661e0cSMauro Carvalho ChehabKERNEL=="regulatory*", ACTION=="change", SUBSYSTEM=="platform", RUN+="/sbin/crda" 5198661e0cSMauro Carvalho Chehab 5298661e0cSMauro Carvalho ChehabThe alpha2 is passed as an environment variable under the variable COUNTRY. 5398661e0cSMauro Carvalho Chehab 5498661e0cSMauro Carvalho ChehabWho asks for regulatory domains? 5598661e0cSMauro Carvalho Chehab-------------------------------- 5698661e0cSMauro Carvalho Chehab 5798661e0cSMauro Carvalho Chehab* Users 5898661e0cSMauro Carvalho Chehab 5998661e0cSMauro Carvalho ChehabUsers can use iw: 6098661e0cSMauro Carvalho Chehab 61327cdb98SFlavio Suligoihttps://wireless.wiki.kernel.org/en/users/Documentation/iw 6298661e0cSMauro Carvalho Chehab 6398661e0cSMauro Carvalho ChehabAn example:: 6498661e0cSMauro Carvalho Chehab 6598661e0cSMauro Carvalho Chehab # set regulatory domain to "Costa Rica" 6698661e0cSMauro Carvalho Chehab iw reg set CR 6798661e0cSMauro Carvalho Chehab 6898661e0cSMauro Carvalho ChehabThis will request the kernel to set the regulatory domain to 69*a266ef69SRandy Dunlapthe specified alpha2. The kernel in turn will then ask userspace 7098661e0cSMauro Carvalho Chehabto provide a regulatory domain for the alpha2 specified by the user 7198661e0cSMauro Carvalho Chehabby sending a uevent. 7298661e0cSMauro Carvalho Chehab 7398661e0cSMauro Carvalho Chehab* Wireless subsystems for Country Information elements 7498661e0cSMauro Carvalho Chehab 7598661e0cSMauro Carvalho ChehabThe kernel will send a uevent to inform userspace a new 7698661e0cSMauro Carvalho Chehabregulatory domain is required. More on this to be added 7798661e0cSMauro Carvalho Chehabas its integration is added. 7898661e0cSMauro Carvalho Chehab 7998661e0cSMauro Carvalho Chehab* Drivers 8098661e0cSMauro Carvalho Chehab 8198661e0cSMauro Carvalho ChehabIf drivers determine they need a specific regulatory domain 8298661e0cSMauro Carvalho Chehabset they can inform the wireless core using regulatory_hint(). 8398661e0cSMauro Carvalho ChehabThey have two options -- they either provide an alpha2 so that 8498661e0cSMauro Carvalho Chehabcrda can provide back a regulatory domain for that country or 8598661e0cSMauro Carvalho Chehabthey can build their own regulatory domain based on internal 8698661e0cSMauro Carvalho Chehabcustom knowledge so the wireless core can respect it. 8798661e0cSMauro Carvalho Chehab 8898661e0cSMauro Carvalho Chehab*Most* drivers will rely on the first mechanism of providing a 8998661e0cSMauro Carvalho Chehabregulatory hint with an alpha2. For these drivers there is an additional 9098661e0cSMauro Carvalho Chehabcheck that can be used to ensure compliance based on custom EEPROM 9198661e0cSMauro Carvalho Chehabregulatory data. This additional check can be used by drivers by 9298661e0cSMauro Carvalho Chehabregistering on its struct wiphy a reg_notifier() callback. This notifier 9398661e0cSMauro Carvalho Chehabis called when the core's regulatory domain has been changed. The driver 9498661e0cSMauro Carvalho Chehabcan use this to review the changes made and also review who made them 9598661e0cSMauro Carvalho Chehab(driver, user, country IE) and determine what to allow based on its 9698661e0cSMauro Carvalho Chehabinternal EEPROM data. Devices drivers wishing to be capable of world 9798661e0cSMauro Carvalho Chehabroaming should use this callback. More on world roaming will be 9898661e0cSMauro Carvalho Chehabadded to this document when its support is enabled. 9998661e0cSMauro Carvalho Chehab 10098661e0cSMauro Carvalho ChehabDevice drivers who provide their own built regulatory domain 10198661e0cSMauro Carvalho Chehabdo not need a callback as the channels registered by them are 10298661e0cSMauro Carvalho Chehabthe only ones that will be allowed and therefore *additional* 10398661e0cSMauro Carvalho Chehabchannels cannot be enabled. 10498661e0cSMauro Carvalho Chehab 10598661e0cSMauro Carvalho ChehabExample code - drivers hinting an alpha2: 10698661e0cSMauro Carvalho Chehab------------------------------------------ 10798661e0cSMauro Carvalho Chehab 10898661e0cSMauro Carvalho ChehabThis example comes from the zd1211rw device driver. You can start 10998661e0cSMauro Carvalho Chehabby having a mapping of your device's EEPROM country/regulatory 11098661e0cSMauro Carvalho Chehabdomain value to a specific alpha2 as follows:: 11198661e0cSMauro Carvalho Chehab 11298661e0cSMauro Carvalho Chehab static struct zd_reg_alpha2_map reg_alpha2_map[] = { 11398661e0cSMauro Carvalho Chehab { ZD_REGDOMAIN_FCC, "US" }, 11498661e0cSMauro Carvalho Chehab { ZD_REGDOMAIN_IC, "CA" }, 11598661e0cSMauro Carvalho Chehab { ZD_REGDOMAIN_ETSI, "DE" }, /* Generic ETSI, use most restrictive */ 11698661e0cSMauro Carvalho Chehab { ZD_REGDOMAIN_JAPAN, "JP" }, 11798661e0cSMauro Carvalho Chehab { ZD_REGDOMAIN_JAPAN_ADD, "JP" }, 11898661e0cSMauro Carvalho Chehab { ZD_REGDOMAIN_SPAIN, "ES" }, 11998661e0cSMauro Carvalho Chehab { ZD_REGDOMAIN_FRANCE, "FR" }, 12098661e0cSMauro Carvalho Chehab 12198661e0cSMauro Carvalho ChehabThen you can define a routine to map your read EEPROM value to an alpha2, 12298661e0cSMauro Carvalho Chehabas follows:: 12398661e0cSMauro Carvalho Chehab 12498661e0cSMauro Carvalho Chehab static int zd_reg2alpha2(u8 regdomain, char *alpha2) 12598661e0cSMauro Carvalho Chehab { 12698661e0cSMauro Carvalho Chehab unsigned int i; 12798661e0cSMauro Carvalho Chehab struct zd_reg_alpha2_map *reg_map; 12898661e0cSMauro Carvalho Chehab for (i = 0; i < ARRAY_SIZE(reg_alpha2_map); i++) { 12998661e0cSMauro Carvalho Chehab reg_map = ®_alpha2_map[i]; 13098661e0cSMauro Carvalho Chehab if (regdomain == reg_map->reg) { 13198661e0cSMauro Carvalho Chehab alpha2[0] = reg_map->alpha2[0]; 13298661e0cSMauro Carvalho Chehab alpha2[1] = reg_map->alpha2[1]; 13398661e0cSMauro Carvalho Chehab return 0; 13498661e0cSMauro Carvalho Chehab } 13598661e0cSMauro Carvalho Chehab } 13698661e0cSMauro Carvalho Chehab return 1; 13798661e0cSMauro Carvalho Chehab } 13898661e0cSMauro Carvalho Chehab 13998661e0cSMauro Carvalho ChehabLastly, you can then hint to the core of your discovered alpha2, if a match 14098661e0cSMauro Carvalho Chehabwas found. You need to do this after you have registered your wiphy. You 14198661e0cSMauro Carvalho Chehabare expected to do this during initialization. 14298661e0cSMauro Carvalho Chehab 14398661e0cSMauro Carvalho Chehab:: 14498661e0cSMauro Carvalho Chehab 14598661e0cSMauro Carvalho Chehab r = zd_reg2alpha2(mac->regdomain, alpha2); 14698661e0cSMauro Carvalho Chehab if (!r) 14798661e0cSMauro Carvalho Chehab regulatory_hint(hw->wiphy, alpha2); 14898661e0cSMauro Carvalho Chehab 14998661e0cSMauro Carvalho ChehabExample code - drivers providing a built in regulatory domain: 15098661e0cSMauro Carvalho Chehab-------------------------------------------------------------- 15198661e0cSMauro Carvalho Chehab 15298661e0cSMauro Carvalho Chehab[NOTE: This API is not currently available, it can be added when required] 15398661e0cSMauro Carvalho Chehab 15498661e0cSMauro Carvalho ChehabIf you have regulatory information you can obtain from your 15598661e0cSMauro Carvalho Chehabdriver and you *need* to use this we let you build a regulatory domain 15698661e0cSMauro Carvalho Chehabstructure and pass it to the wireless core. To do this you should 15798661e0cSMauro Carvalho Chehabkmalloc() a structure big enough to hold your regulatory domain 15898661e0cSMauro Carvalho Chehabstructure and you should then fill it with your data. Finally you simply 15998661e0cSMauro Carvalho Chehabcall regulatory_hint() with the regulatory domain structure in it. 16098661e0cSMauro Carvalho Chehab 161*a266ef69SRandy DunlapBelow is a simple example, with a regulatory domain cached using the stack. 16298661e0cSMauro Carvalho ChehabYour implementation may vary (read EEPROM cache instead, for example). 16398661e0cSMauro Carvalho Chehab 16498661e0cSMauro Carvalho ChehabExample cache of some regulatory domain:: 16598661e0cSMauro Carvalho Chehab 16698661e0cSMauro Carvalho Chehab struct ieee80211_regdomain mydriver_jp_regdom = { 16798661e0cSMauro Carvalho Chehab .n_reg_rules = 3, 16898661e0cSMauro Carvalho Chehab .alpha2 = "JP", 16998661e0cSMauro Carvalho Chehab //.alpha2 = "99", /* If I have no alpha2 to map it to */ 17098661e0cSMauro Carvalho Chehab .reg_rules = { 17198661e0cSMauro Carvalho Chehab /* IEEE 802.11b/g, channels 1..14 */ 17298661e0cSMauro Carvalho Chehab REG_RULE(2412-10, 2484+10, 40, 6, 20, 0), 17398661e0cSMauro Carvalho Chehab /* IEEE 802.11a, channels 34..48 */ 17498661e0cSMauro Carvalho Chehab REG_RULE(5170-10, 5240+10, 40, 6, 20, 17598661e0cSMauro Carvalho Chehab NL80211_RRF_NO_IR), 17698661e0cSMauro Carvalho Chehab /* IEEE 802.11a, channels 52..64 */ 17798661e0cSMauro Carvalho Chehab REG_RULE(5260-10, 5320+10, 40, 6, 20, 17898661e0cSMauro Carvalho Chehab NL80211_RRF_NO_IR| 17998661e0cSMauro Carvalho Chehab NL80211_RRF_DFS), 18098661e0cSMauro Carvalho Chehab } 18198661e0cSMauro Carvalho Chehab }; 18298661e0cSMauro Carvalho Chehab 18398661e0cSMauro Carvalho ChehabThen in some part of your code after your wiphy has been registered:: 18498661e0cSMauro Carvalho Chehab 18598661e0cSMauro Carvalho Chehab struct ieee80211_regdomain *rd; 18698661e0cSMauro Carvalho Chehab int size_of_regd; 18798661e0cSMauro Carvalho Chehab int num_rules = mydriver_jp_regdom.n_reg_rules; 18898661e0cSMauro Carvalho Chehab unsigned int i; 18998661e0cSMauro Carvalho Chehab 19098661e0cSMauro Carvalho Chehab size_of_regd = sizeof(struct ieee80211_regdomain) + 19198661e0cSMauro Carvalho Chehab (num_rules * sizeof(struct ieee80211_reg_rule)); 19298661e0cSMauro Carvalho Chehab 19398661e0cSMauro Carvalho Chehab rd = kzalloc(size_of_regd, GFP_KERNEL); 19498661e0cSMauro Carvalho Chehab if (!rd) 19598661e0cSMauro Carvalho Chehab return -ENOMEM; 19698661e0cSMauro Carvalho Chehab 19798661e0cSMauro Carvalho Chehab memcpy(rd, &mydriver_jp_regdom, sizeof(struct ieee80211_regdomain)); 19898661e0cSMauro Carvalho Chehab 19998661e0cSMauro Carvalho Chehab for (i=0; i < num_rules; i++) 20098661e0cSMauro Carvalho Chehab memcpy(&rd->reg_rules[i], 20198661e0cSMauro Carvalho Chehab &mydriver_jp_regdom.reg_rules[i], 20298661e0cSMauro Carvalho Chehab sizeof(struct ieee80211_reg_rule)); 20398661e0cSMauro Carvalho Chehab regulatory_struct_hint(rd); 20498661e0cSMauro Carvalho Chehab 20598661e0cSMauro Carvalho ChehabStatically compiled regulatory database 20698661e0cSMauro Carvalho Chehab--------------------------------------- 20798661e0cSMauro Carvalho Chehab 20898661e0cSMauro Carvalho ChehabWhen a database should be fixed into the kernel, it can be provided as a 20998661e0cSMauro Carvalho Chehabfirmware file at build time that is then linked into the kernel. 210