Linux 无线法规文档¶
本文档简要概述了 Linux 无线法规基础设施的工作原理。
更多最新信息可以在该项目的网页上获取
https://wireless.wiki.kernel.org/en/developers/Regulatory
在用户空间维护法规域¶
由于法规域的动态特性,我们将其保存在用户空间中,并提供一个框架,供用户空间上传一个法规域到内核,作为所有无线设备应遵守的中央核心法规域。
如何将法规域传递到内核¶
当首次设置法规域时,内核将请求一个包含所有法规规则的数据库文件 (regulatory.db)。然后,当需要查找给定国家/地区的规则时,它将使用该数据库。
如何将法规域传递到内核(旧的 CRDA 解决方案)¶
用户空间通过让用户空间代理构建法规域并通过 nl80211 发送它,从而在内核中获得法规域。内核只会遵守预期的法规域。
目前可用的用户空间代理 CRDA(中央法规域代理)可以完成此操作。其文档在此处:
https://wireless.wiki.kernel.org/en/developers/Regulatory/CRDA
本质上,当内核知道它需要新的法规域时,它会发送一个 udev 事件。可以设置一个 udev 规则,以触发 crda 为特定的 ISO/IEC 3166 alpha2 发送相应的法规域。
下面是一个可以使用的 udev 规则示例
# 示例文件,应放在 /etc/udev/rules.d/regulatory.rules KERNEL==”regulatory*”, ACTION==”change”, SUBSYSTEM==”platform”, RUN+=”/sbin/crda”
alpha2 作为环境变量在变量 COUNTRY 下传递。
谁请求法规域?¶
用户
用户可以使用 iw
https://wireless.wiki.kernel.org/en/users/Documentation/iw
一个例子
# set regulatory domain to "Costa Rica"
iw reg set CR
这将请求内核将法规域设置为指定的 alpha2。反过来,内核将通过发送 uevent 要求用户空间为用户指定的 alpha2 提供法规域。
用于国家/地区信息元素的无线子系统
内核将发送一个 uevent,以通知用户空间需要新的法规域。 随着其集成的添加,将添加更多内容。
驱动程序
如果驱动程序确定它们需要设置特定的法规域,它们可以使用 regulatory_hint()
通知无线核心。它们有两种选择 -- 它们要么提供一个 alpha2,以便 crda 可以提供该国家/地区的法规域,要么它们可以基于内部自定义知识构建自己的法规域,以便无线核心可以遵守它。
大多数 驱动程序将依赖于使用 alpha2 提供法规提示的第一种机制。对于这些驱动程序,可以使用额外的检查来确保基于自定义 EEPROM 法规数据的合规性。驱动程序可以通过在其 struct wiphy
上注册 reg_notifier() 回调来使用此附加检查。当核心的法规域发生更改时,将调用此通知器。驱动程序可以使用它来查看所做的更改,并查看谁进行了更改(驱动程序、用户、国家/地区 IE),并根据其内部 EEPROM 数据确定允许的内容。希望能够进行全球漫游的设备驱动程序应使用此回调。启用全球漫游支持后,将在此文档中添加更多内容。
提供自己构建的法规域的设备驱动程序不需要回调,因为它们注册的通道是唯一允许的通道,因此无法启用其他通道。
示例代码 - 驱动程序提示 alpha2:¶
此示例来自 zd1211rw 设备驱动程序。您可以首先将设备的 EEPROM 国家/地区/法规域值映射到特定的 alpha2,如下所示
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" },
然后,您可以定义一个例程,将您读取的 EEPROM 值映射到 alpha2,如下所示
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;
}
最后,如果找到匹配项,则可以提示核心您发现的 alpha2。您需要在注册您的 wiphy 后执行此操作。您应该在初始化期间执行此操作。
r = zd_reg2alpha2(mac->regdomain, alpha2);
if (!r)
regulatory_hint(hw->wiphy, alpha2);
示例代码 - 驱动程序提供内置法规域:¶
[注意:此 API 当前不可用,可以在需要时添加]
如果您可以从驱动程序中获取法规信息,并且您需要使用它,我们可以让您构建法规域结构并将其传递给无线核心。为此,您应该使用 kmalloc()
分配一个足够大的结构来保存您的法规域结构,然后您应该用您的数据填充它。最后,您只需调用 regulatory_hint()
,其中包含法规域结构。
下面是一个简单的示例,使用堆栈缓存法规域。您的实现可能会有所不同(例如,读取 EEPROM 缓存)。
一些法规域的示例缓存
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),
}
};
然后在您的代码的某些部分,在您的 wiphy 注册之后
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);
静态编译的法规数据库¶
当数据库应该被固定到内核中时,它可以作为固件文件在构建时提供,然后链接到内核中。