aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorLuis R. Rodriguez <lrodriguez@atheros.com>2008-09-10 02:19:48 -0400
committerJohn W. Linville <linville@tuxdriver.com>2008-09-15 16:48:19 -0400
commitb2e1b30290539b344cbaff0d9da38012e03aa347 (patch)
tree8d021d078c12f3d7b47da4b52a54eff4509daa98 /net
parent63f2c0464875b6ef2132cecb19b2a5abbf061227 (diff)
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: Luis R. Rodriguez <lrodriguez@atheros.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'net')
-rw-r--r--net/mac80211/cfg.c7
-rw-r--r--net/wireless/Kconfig32
-rw-r--r--net/wireless/core.c162
-rw-r--r--net/wireless/core.h2
-rw-r--r--net/wireless/nl80211.c151
-rw-r--r--net/wireless/reg.c805
-rw-r--r--net/wireless/reg.h44
7 files changed, 1087 insertions, 116 deletions
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index 928813ce08e2..5a3bdaad6c19 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -17,6 +17,13 @@
17#include "rate.h" 17#include "rate.h"
18#include "mesh.h" 18#include "mesh.h"
19 19
20struct ieee80211_hw *wiphy_to_hw(struct wiphy *wiphy)
21{
22 struct ieee80211_local *local = wiphy_priv(wiphy);
23 return &local->hw;
24}
25EXPORT_SYMBOL(wiphy_to_hw);
26
20static enum ieee80211_if_types 27static enum ieee80211_if_types
21nl80211_type_to_mac80211_type(enum nl80211_iftype type) 28nl80211_type_to_mac80211_type(enum nl80211_iftype type)
22{ 29{
diff --git a/net/wireless/Kconfig b/net/wireless/Kconfig
index 833b024f8f66..b97bd9fe6b79 100644
--- a/net/wireless/Kconfig
+++ b/net/wireless/Kconfig
@@ -14,6 +14,38 @@ config NL80211
14 14
15 If unsure, say Y. 15 If unsure, say Y.
16 16
17config WIRELESS_OLD_REGULATORY
18 bool "Old wireless static regulatory defintions"
19 default n
20 ---help---
21 This option enables the old static regulatory information
22 and uses it within the new framework. This is available
23 temporarily as an option to help prevent immediate issues
24 due to the switch to the new regulatory framework which
25 does require a new userspace application which has the
26 database of regulatory information (CRDA) and another for
27 setting regulatory domains (iw).
28
29 For more information see:
30
31 http://wireless.kernel.org/en/developers/Regulatory/CRDA
32 http://wireless.kernel.org/en/users/Documentation/iw
33
34 It is important to note though that if you *do* have CRDA present
35 and if this option is enabled CRDA *will* be called to update the
36 regulatory domain (for US and JP only). Support for letting the user
37 set the regulatory domain through iw is also supported. This option
38 mainly exists to leave around for a kernel release some old static
39 regulatory domains that were defined and to keep around the old
40 ieee80211_regdom module parameter. This is being phased out and you
41 should stop using them ASAP.
42
43 Say N unless you cannot install a new userspace application
44 or have one currently depending on the ieee80211_regdom module
45 parameter and cannot port it to use the new userspace interfaces.
46
47 This is scheduled for removal for 2.6.29.
48
17config WIRELESS_EXT 49config WIRELESS_EXT
18 bool "Wireless extensions" 50 bool "Wireless extensions"
19 default n 51 default n
diff --git a/net/wireless/core.c b/net/wireless/core.c
index 7e995ac06a0c..a910cd2d0fd1 100644
--- a/net/wireless/core.c
+++ b/net/wireless/core.c
@@ -13,12 +13,14 @@
13#include <linux/debugfs.h> 13#include <linux/debugfs.h>
14#include <linux/notifier.h> 14#include <linux/notifier.h>
15#include <linux/device.h> 15#include <linux/device.h>
16#include <linux/list.h>
16#include <net/genetlink.h> 17#include <net/genetlink.h>
17#include <net/cfg80211.h> 18#include <net/cfg80211.h>
18#include <net/wireless.h> 19#include <net/wireless.h>
19#include "nl80211.h" 20#include "nl80211.h"
20#include "core.h" 21#include "core.h"
21#include "sysfs.h" 22#include "sysfs.h"
23#include "reg.h"
22 24
23/* name for sysfs, %d is appended */ 25/* name for sysfs, %d is appended */
24#define PHY_NAME "phy" 26#define PHY_NAME "phy"
@@ -27,6 +29,107 @@ MODULE_AUTHOR("Johannes Berg");
27MODULE_LICENSE("GPL"); 29MODULE_LICENSE("GPL");
28MODULE_DESCRIPTION("wireless configuration support"); 30MODULE_DESCRIPTION("wireless configuration support");
29 31
32struct list_head regulatory_requests;
33
34/* Central wireless core regulatory domains, we only need two,
35 * the current one and a world regulatory domain in case we have no
36 * information to give us an alpha2 */
37struct ieee80211_regdomain *cfg80211_regdomain;
38
39/* We keep a static world regulatory domain in case of the absence of CRDA */
40const struct ieee80211_regdomain world_regdom = {
41 .n_reg_rules = 1,
42 .alpha2 = "00",
43 .reg_rules = {
44 REG_RULE(2402, 2472, 40, 6, 20,
45 NL80211_RRF_PASSIVE_SCAN |
46 NL80211_RRF_NO_IBSS),
47 }
48};
49
50#ifdef CONFIG_WIRELESS_OLD_REGULATORY
51/* All this fucking static junk will be removed soon, so
52 * don't fucking count on it !@#$ */
53
54static char *ieee80211_regdom = "US";
55module_param(ieee80211_regdom, charp, 0444);
56MODULE_PARM_DESC(ieee80211_regdom, "IEEE 802.11 regulatory domain code");
57
58/* We assume 40 MHz bandwidth for the old regulatory work.
59 * We make emphasis we are using the exact same frequencies
60 * as before */
61
62const struct ieee80211_regdomain us_regdom = {
63 .n_reg_rules = 6,
64 .alpha2 = "US",
65 .reg_rules = {
66 /* IEEE 802.11b/g, channels 1..11 */
67 REG_RULE(2412-20, 2462+20, 40, 6, 27, 0),
68 /* IEEE 802.11a, channel 36 */
69 REG_RULE(5180-20, 5180+20, 40, 6, 23, 0),
70 /* IEEE 802.11a, channel 40 */
71 REG_RULE(5200-20, 5200+20, 40, 6, 23, 0),
72 /* IEEE 802.11a, channel 44 */
73 REG_RULE(5220-20, 5220+20, 40, 6, 23, 0),
74 /* IEEE 802.11a, channels 48..64 */
75 REG_RULE(5240-20, 5320+20, 40, 6, 23, 0),
76 /* IEEE 802.11a, channels 149..165, outdoor */
77 REG_RULE(5745-20, 5825+20, 40, 6, 30, 0),
78 }
79};
80
81const struct ieee80211_regdomain jp_regdom = {
82 .n_reg_rules = 3,
83 .alpha2 = "JP",
84 .reg_rules = {
85 /* IEEE 802.11b/g, channels 1..14 */
86 REG_RULE(2412-20, 2484+20, 40, 6, 20, 0),
87 /* IEEE 802.11a, channels 34..48 */
88 REG_RULE(5170-20, 5240+20, 40, 6, 20,
89 NL80211_RRF_PASSIVE_SCAN),
90 /* IEEE 802.11a, channels 52..64 */
91 REG_RULE(5260-20, 5320+20, 40, 6, 20,
92 NL80211_RRF_NO_IBSS |
93 NL80211_RRF_DFS),
94 }
95};
96
97const struct ieee80211_regdomain eu_regdom = {
98 .n_reg_rules = 6,
99 /* This alpha2 is bogus, we leave it here just for stupid
100 * backward compatibility */
101 .alpha2 = "EU",
102 .reg_rules = {
103 /* IEEE 802.11b/g, channels 1..13 */
104 REG_RULE(2412-20, 2472+20, 40, 6, 20, 0),
105 /* IEEE 802.11a, channel 36 */
106 REG_RULE(5180-20, 5180+20, 40, 6, 23,
107 NL80211_RRF_PASSIVE_SCAN),
108 /* IEEE 802.11a, channel 40 */
109 REG_RULE(5200-20, 5200+20, 40, 6, 23,
110 NL80211_RRF_PASSIVE_SCAN),
111 /* IEEE 802.11a, channel 44 */
112 REG_RULE(5220-20, 5220+20, 40, 6, 23,
113 NL80211_RRF_PASSIVE_SCAN),
114 /* IEEE 802.11a, channels 48..64 */
115 REG_RULE(5240-20, 5320+20, 40, 6, 20,
116 NL80211_RRF_NO_IBSS |
117 NL80211_RRF_DFS),
118 /* IEEE 802.11a, channels 100..140 */
119 REG_RULE(5500-20, 5700+20, 40, 6, 30,
120 NL80211_RRF_NO_IBSS |
121 NL80211_RRF_DFS),
122 }
123};
124
125#endif
126
127struct ieee80211_regdomain *cfg80211_world_regdom =
128 (struct ieee80211_regdomain *) &world_regdom;
129
130LIST_HEAD(regulatory_requests);
131DEFINE_MUTEX(cfg80211_reg_mutex);
132
30/* RCU might be appropriate here since we usually 133/* RCU might be appropriate here since we usually
31 * only read the list, and that can happen quite 134 * only read the list, and that can happen quite
32 * often because we need to do it for each command */ 135 * often because we need to do it for each command */
@@ -302,7 +405,9 @@ int wiphy_register(struct wiphy *wiphy)
302 ieee80211_set_bitrate_flags(wiphy); 405 ieee80211_set_bitrate_flags(wiphy);
303 406
304 /* set up regulatory info */ 407 /* set up regulatory info */
305 wiphy_update_regulatory(wiphy); 408 mutex_lock(&cfg80211_reg_mutex);
409 wiphy_update_regulatory(wiphy, REGDOM_SET_BY_CORE);
410 mutex_unlock(&cfg80211_reg_mutex);
306 411
307 mutex_lock(&cfg80211_drv_mutex); 412 mutex_lock(&cfg80211_drv_mutex);
308 413
@@ -409,9 +514,35 @@ static struct notifier_block cfg80211_netdev_notifier = {
409 .notifier_call = cfg80211_netdev_notifier_call, 514 .notifier_call = cfg80211_netdev_notifier_call,
410}; 515};
411 516
517#ifdef CONFIG_WIRELESS_OLD_REGULATORY
518const struct ieee80211_regdomain *static_regdom(char *alpha2)
519{
520 if (alpha2[0] == 'U' && alpha2[1] == 'S')
521 return &us_regdom;
522 if (alpha2[0] == 'J' && alpha2[1] == 'P')
523 return &jp_regdom;
524 if (alpha2[0] == 'E' && alpha2[1] == 'U')
525 return &eu_regdom;
526 /* Default, as per the old rules */
527 return &us_regdom;
528}
529#endif
530
412static int cfg80211_init(void) 531static int cfg80211_init(void)
413{ 532{
414 int err = wiphy_sysfs_init(); 533 int err;
534
535#ifdef CONFIG_WIRELESS_OLD_REGULATORY
536 cfg80211_regdomain =
537 (struct ieee80211_regdomain *) static_regdom(ieee80211_regdom);
538 /* Used during reset_regdomains_static() */
539 cfg80211_world_regdom = cfg80211_regdomain;
540#else
541 cfg80211_regdomain =
542 (struct ieee80211_regdomain *) cfg80211_world_regdom;
543#endif
544
545 err = wiphy_sysfs_init();
415 if (err) 546 if (err)
416 goto out_fail_sysfs; 547 goto out_fail_sysfs;
417 548
@@ -425,8 +556,33 @@ static int cfg80211_init(void)
425 556
426 ieee80211_debugfs_dir = debugfs_create_dir("ieee80211", NULL); 557 ieee80211_debugfs_dir = debugfs_create_dir("ieee80211", NULL);
427 558
559 err = regulatory_init();
560 if (err)
561 goto out_fail_reg;
562
563#ifdef CONFIG_WIRELESS_OLD_REGULATORY
564 printk(KERN_INFO "cfg80211: Using old static regulatory domain:\n");
565 print_regdomain_info(cfg80211_regdomain);
566 /* The old code still requests for a new regdomain and if
567 * you have CRDA you get it updated, otherwise you get
568 * stuck with the static values. We ignore "EU" code as
569 * that is not a valid ISO / IEC 3166 alpha2 */
570 if (ieee80211_regdom[0] != 'E' &&
571 ieee80211_regdom[1] != 'U')
572 err = __regulatory_hint(NULL, REGDOM_SET_BY_CORE,
573 ieee80211_regdom, NULL);
574#else
575 err = __regulatory_hint(NULL, REGDOM_SET_BY_CORE, "00", NULL);
576 if (err)
577 printk(KERN_ERR "cfg80211: calling CRDA failed - "
578 "unable to update world regulatory domain, "
579 "using static definition\n");
580#endif
581
428 return 0; 582 return 0;
429 583
584out_fail_reg:
585 debugfs_remove(ieee80211_debugfs_dir);
430out_fail_nl80211: 586out_fail_nl80211:
431 unregister_netdevice_notifier(&cfg80211_netdev_notifier); 587 unregister_netdevice_notifier(&cfg80211_netdev_notifier);
432out_fail_notifier: 588out_fail_notifier:
@@ -434,6 +590,7 @@ out_fail_notifier:
434out_fail_sysfs: 590out_fail_sysfs:
435 return err; 591 return err;
436} 592}
593
437subsys_initcall(cfg80211_init); 594subsys_initcall(cfg80211_init);
438 595
439static void cfg80211_exit(void) 596static void cfg80211_exit(void)
@@ -442,5 +599,6 @@ static void cfg80211_exit(void)
442 nl80211_exit(); 599 nl80211_exit();
443 unregister_netdevice_notifier(&cfg80211_netdev_notifier); 600 unregister_netdevice_notifier(&cfg80211_netdev_notifier);
444 wiphy_sysfs_exit(); 601 wiphy_sysfs_exit();
602 regulatory_exit();
445} 603}
446module_exit(cfg80211_exit); 604module_exit(cfg80211_exit);
diff --git a/net/wireless/core.h b/net/wireless/core.h
index 7a02c356d63d..771cc5cc7658 100644
--- a/net/wireless/core.h
+++ b/net/wireless/core.h
@@ -79,6 +79,6 @@ extern int cfg80211_dev_rename(struct cfg80211_registered_device *drv,
79 char *newname); 79 char *newname);
80 80
81void ieee80211_set_bitrate_flags(struct wiphy *wiphy); 81void ieee80211_set_bitrate_flags(struct wiphy *wiphy);
82void wiphy_update_regulatory(struct wiphy *wiphy); 82void wiphy_update_regulatory(struct wiphy *wiphy, enum reg_set_by setby);
83 83
84#endif /* __NET_WIRELESS_CORE_H */ 84#endif /* __NET_WIRELESS_CORE_H */
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 77880ba8b619..1221d726ed50 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -18,6 +18,7 @@
18#include <net/cfg80211.h> 18#include <net/cfg80211.h>
19#include "core.h" 19#include "core.h"
20#include "nl80211.h" 20#include "nl80211.h"
21#include "reg.h"
21 22
22/* the netlink family */ 23/* the netlink family */
23static struct genl_family nl80211_fam = { 24static struct genl_family nl80211_fam = {
@@ -88,6 +89,9 @@ static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = {
88 .len = IEEE80211_MAX_MESH_ID_LEN }, 89 .len = IEEE80211_MAX_MESH_ID_LEN },
89 [NL80211_ATTR_MPATH_NEXT_HOP] = { .type = NLA_U32 }, 90 [NL80211_ATTR_MPATH_NEXT_HOP] = { .type = NLA_U32 },
90 91
92 [NL80211_ATTR_REG_ALPHA2] = { .type = NLA_STRING, .len = 2 },
93 [NL80211_ATTR_REG_RULES] = { .type = NLA_NESTED },
94
91 [NL80211_ATTR_BSS_CTS_PROT] = { .type = NLA_U8 }, 95 [NL80211_ATTR_BSS_CTS_PROT] = { .type = NLA_U8 },
92 [NL80211_ATTR_BSS_SHORT_PREAMBLE] = { .type = NLA_U8 }, 96 [NL80211_ATTR_BSS_SHORT_PREAMBLE] = { .type = NLA_U8 },
93 [NL80211_ATTR_BSS_SHORT_SLOT_TIME] = { .type = NLA_U8 }, 97 [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)
1599 return err; 1603 return err;
1600} 1604}
1601 1605
1606static const struct nla_policy
1607 reg_rule_policy[NL80211_REG_RULE_ATTR_MAX + 1] = {
1608 [NL80211_ATTR_REG_RULE_FLAGS] = { .type = NLA_U32 },
1609 [NL80211_ATTR_FREQ_RANGE_START] = { .type = NLA_U32 },
1610 [NL80211_ATTR_FREQ_RANGE_END] = { .type = NLA_U32 },
1611 [NL80211_ATTR_FREQ_RANGE_MAX_BW] = { .type = NLA_U32 },
1612 [NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN] = { .type = NLA_U32 },
1613 [NL80211_ATTR_POWER_RULE_MAX_EIRP] = { .type = NLA_U32 },
1614};
1615
1616static int parse_reg_rule(struct nlattr *tb[],
1617 struct ieee80211_reg_rule *reg_rule)
1618{
1619 struct ieee80211_freq_range *freq_range = &reg_rule->freq_range;
1620 struct ieee80211_power_rule *power_rule = &reg_rule->power_rule;
1621
1622 if (!tb[NL80211_ATTR_REG_RULE_FLAGS])
1623 return -EINVAL;
1624 if (!tb[NL80211_ATTR_FREQ_RANGE_START])
1625 return -EINVAL;
1626 if (!tb[NL80211_ATTR_FREQ_RANGE_END])
1627 return -EINVAL;
1628 if (!tb[NL80211_ATTR_FREQ_RANGE_MAX_BW])
1629 return -EINVAL;
1630 if (!tb[NL80211_ATTR_POWER_RULE_MAX_EIRP])
1631 return -EINVAL;
1632
1633 reg_rule->flags = nla_get_u32(tb[NL80211_ATTR_REG_RULE_FLAGS]);
1634
1635 freq_range->start_freq_khz =
1636 nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_START]);
1637 freq_range->end_freq_khz =
1638 nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_END]);
1639 freq_range->max_bandwidth_khz =
1640 nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_MAX_BW]);
1641
1642 power_rule->max_eirp =
1643 nla_get_u32(tb[NL80211_ATTR_POWER_RULE_MAX_EIRP]);
1644
1645 if (tb[NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN])
1646 power_rule->max_antenna_gain =
1647 nla_get_u32(tb[NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN]);
1648
1649 return 0;
1650}
1651
1652static int nl80211_req_set_reg(struct sk_buff *skb, struct genl_info *info)
1653{
1654 int r;
1655 char *data = NULL;
1656
1657 if (!info->attrs[NL80211_ATTR_REG_ALPHA2])
1658 return -EINVAL;
1659
1660 data = nla_data(info->attrs[NL80211_ATTR_REG_ALPHA2]);
1661
1662#ifdef CONFIG_WIRELESS_OLD_REGULATORY
1663 /* We ignore world regdom requests with the old regdom setup */
1664 if (is_world_regdom(data))
1665 return -EINVAL;
1666#endif
1667 mutex_lock(&cfg80211_drv_mutex);
1668 r = __regulatory_hint(NULL, REGDOM_SET_BY_USER, data, NULL);
1669 mutex_unlock(&cfg80211_drv_mutex);
1670 return r;
1671}
1672
1673static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info)
1674{
1675 struct nlattr *tb[NL80211_REG_RULE_ATTR_MAX + 1];
1676 struct nlattr *nl_reg_rule;
1677 char *alpha2 = NULL;
1678 int rem_reg_rules = 0, r = 0;
1679 u32 num_rules = 0, rule_idx = 0, size_of_regd;
1680 struct ieee80211_regdomain *rd = NULL;
1681
1682 if (!info->attrs[NL80211_ATTR_REG_ALPHA2])
1683 return -EINVAL;
1684
1685 if (!info->attrs[NL80211_ATTR_REG_RULES])
1686 return -EINVAL;
1687
1688 alpha2 = nla_data(info->attrs[NL80211_ATTR_REG_ALPHA2]);
1689
1690 nla_for_each_nested(nl_reg_rule, info->attrs[NL80211_ATTR_REG_RULES],
1691 rem_reg_rules) {
1692 num_rules++;
1693 if (num_rules > NL80211_MAX_SUPP_REG_RULES)
1694 goto bad_reg;
1695 }
1696
1697 if (!reg_is_valid_request(alpha2))
1698 return -EINVAL;
1699
1700 size_of_regd = sizeof(struct ieee80211_regdomain) +
1701 (num_rules * sizeof(struct ieee80211_reg_rule));
1702
1703 rd = kzalloc(size_of_regd, GFP_KERNEL);
1704 if (!rd)
1705 return -ENOMEM;
1706
1707 rd->n_reg_rules = num_rules;
1708 rd->alpha2[0] = alpha2[0];
1709 rd->alpha2[1] = alpha2[1];
1710
1711 nla_for_each_nested(nl_reg_rule, info->attrs[NL80211_ATTR_REG_RULES],
1712 rem_reg_rules) {
1713 nla_parse(tb, NL80211_REG_RULE_ATTR_MAX,
1714 nla_data(nl_reg_rule), nla_len(nl_reg_rule),
1715 reg_rule_policy);
1716 r = parse_reg_rule(tb, &rd->reg_rules[rule_idx]);
1717 if (r)
1718 goto bad_reg;
1719
1720 rule_idx++;
1721
1722 if (rule_idx > NL80211_MAX_SUPP_REG_RULES)
1723 goto bad_reg;
1724 }
1725
1726 BUG_ON(rule_idx != num_rules);
1727
1728 mutex_lock(&cfg80211_drv_mutex);
1729 r = set_regdom(rd);
1730 mutex_unlock(&cfg80211_drv_mutex);
1731 if (r)
1732 goto bad_reg;
1733
1734 return r;
1735
1736bad_reg:
1737 kfree(rd);
1738 return -EINVAL;
1739}
1740
1602static struct genl_ops nl80211_ops[] = { 1741static struct genl_ops nl80211_ops[] = {
1603 { 1742 {
1604 .cmd = NL80211_CMD_GET_WIPHY, 1743 .cmd = NL80211_CMD_GET_WIPHY,
@@ -1736,6 +1875,18 @@ static struct genl_ops nl80211_ops[] = {
1736 .policy = nl80211_policy, 1875 .policy = nl80211_policy,
1737 .flags = GENL_ADMIN_PERM, 1876 .flags = GENL_ADMIN_PERM,
1738 }, 1877 },
1878 {
1879 .cmd = NL80211_CMD_SET_REG,
1880 .doit = nl80211_set_reg,
1881 .policy = nl80211_policy,
1882 .flags = GENL_ADMIN_PERM,
1883 },
1884 {
1885 .cmd = NL80211_CMD_REQ_SET_REG,
1886 .doit = nl80211_req_set_reg,
1887 .policy = nl80211_policy,
1888 .flags = GENL_ADMIN_PERM,
1889 },
1739}; 1890};
1740 1891
1741/* multicast groups */ 1892/* multicast groups */
diff --git a/net/wireless/reg.c b/net/wireless/reg.c
index 855bff4b3250..592b2e391d42 100644
--- a/net/wireless/reg.c
+++ b/net/wireless/reg.c
@@ -2,179 +2,758 @@
2 * Copyright 2002-2005, Instant802 Networks, Inc. 2 * Copyright 2002-2005, Instant802 Networks, Inc.
3 * Copyright 2005-2006, Devicescape Software, Inc. 3 * Copyright 2005-2006, Devicescape Software, Inc.
4 * Copyright 2007 Johannes Berg <johannes@sipsolutions.net> 4 * Copyright 2007 Johannes Berg <johannes@sipsolutions.net>
5 * Copyright 2008 Luis R. Rodriguez <lrodriguz@atheros.com>
5 * 6 *
6 * This program is free software; you can redistribute it and/or modify 7 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as 8 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation. 9 * published by the Free Software Foundation.
9 */ 10 */
10 11
11/* 12/**
12 * This regulatory domain control implementation is highly incomplete, it 13 * DOC: Wireless regulatory infrastructure
13 * only exists for the purpose of not regressing mac80211.
14 *
15 * For now, drivers can restrict the set of allowed channels by either
16 * not registering those channels or setting the IEEE80211_CHAN_DISABLED
17 * flag; that flag will only be *set* by this code, never *cleared.
18 * 14 *
19 * The usual implementation is for a driver to read a device EEPROM to 15 * The usual implementation is for a driver to read a device EEPROM to
20 * determine which regulatory domain it should be operating under, then 16 * determine which regulatory domain it should be operating under, then
21 * looking up the allowable channels in a driver-local table and finally 17 * looking up the allowable channels in a driver-local table and finally
22 * registering those channels in the wiphy structure. 18 * registering those channels in the wiphy structure.
23 * 19 *
24 * Alternatively, drivers that trust the regulatory domain control here 20 * Another set of compliance enforcement is for drivers to use their
25 * will register a complete set of capabilities and the control code 21 * own compliance limits which can be stored on the EEPROM. The host
26 * will restrict the set by setting the IEEE80211_CHAN_* flags. 22 * driver or firmware may ensure these are used.
23 *
24 * In addition to all this we provide an extra layer of regulatory
25 * conformance. For drivers which do not have any regulatory
26 * information CRDA provides the complete regulatory solution.
27 * For others it provides a community effort on further restrictions
28 * to enhance compliance.
29 *
30 * Note: When number of rules --> infinity we will not be able to
31 * index on alpha2 any more, instead we'll probably have to
32 * rely on some SHA1 checksum of the regdomain for example.
33 *
27 */ 34 */
28#include <linux/kernel.h> 35#include <linux/kernel.h>
36#include <linux/list.h>
37#include <linux/random.h>
38#include <linux/nl80211.h>
39#include <linux/platform_device.h>
29#include <net/wireless.h> 40#include <net/wireless.h>
41#include <net/cfg80211.h>
30#include "core.h" 42#include "core.h"
43#include "reg.h"
31 44
32static char *ieee80211_regdom = "US"; 45/* To trigger userspace events */
33module_param(ieee80211_regdom, charp, 0444); 46static struct platform_device *reg_pdev;
34MODULE_PARM_DESC(ieee80211_regdom, "IEEE 802.11 regulatory domain code");
35 47
36struct ieee80211_channel_range { 48/* Keep the ordering from large to small */
37 short start_freq; 49static u32 supported_bandwidths[] = {
38 short end_freq; 50 MHZ_TO_KHZ(40),
39 int max_power; 51 MHZ_TO_KHZ(20),
40 int max_antenna_gain;
41 u32 flags;
42}; 52};
43 53
44struct ieee80211_regdomain { 54bool is_world_regdom(char *alpha2)
45 const char *code; 55{
46 const struct ieee80211_channel_range *ranges; 56 if (!alpha2)
47 int n_ranges; 57 return false;
48}; 58 if (alpha2[0] == '0' && alpha2[1] == '0')
59 return true;
60 return false;
61}
49 62
50#define RANGE_PWR(_start, _end, _pwr, _ag, _flags) \ 63static bool is_alpha2_set(char *alpha2)
51 { _start, _end, _pwr, _ag, _flags } 64{
65 if (!alpha2)
66 return false;
67 if (alpha2[0] != 0 && alpha2[1] != 0)
68 return true;
69 return false;
70}
52 71
72static bool is_alpha_upper(char letter)
73{
74 /* ASCII A - Z */
75 if (letter >= 65 && letter <= 90)
76 return true;
77 return false;
78}
53 79
54/* 80static bool is_unknown_alpha2(char *alpha2)
55 * Ideally, in the future, these definitions will be loaded from a 81{
56 * userspace table via some daemon. 82 if (!alpha2)
57 */ 83 return false;
58static const struct ieee80211_channel_range ieee80211_US_channels[] = { 84 /* Special case where regulatory domain was built by driver
59 /* IEEE 802.11b/g, channels 1..11 */ 85 * but a specific alpha2 cannot be determined */
60 RANGE_PWR(2412, 2462, 27, 6, 0), 86 if (alpha2[0] == '9' && alpha2[1] == '9')
61 /* IEEE 802.11a, channel 36*/ 87 return true;
62 RANGE_PWR(5180, 5180, 23, 6, 0), 88 return false;
63 /* IEEE 802.11a, channel 40*/ 89}
64 RANGE_PWR(5200, 5200, 23, 6, 0),
65 /* IEEE 802.11a, channel 44*/
66 RANGE_PWR(5220, 5220, 23, 6, 0),
67 /* IEEE 802.11a, channels 48..64 */
68 RANGE_PWR(5240, 5320, 23, 6, 0),
69 /* IEEE 802.11a, channels 149..165, outdoor */
70 RANGE_PWR(5745, 5825, 30, 6, 0),
71};
72 90
73static const struct ieee80211_channel_range ieee80211_JP_channels[] = { 91static bool is_an_alpha2(char *alpha2)
74 /* IEEE 802.11b/g, channels 1..14 */ 92{
75 RANGE_PWR(2412, 2484, 20, 6, 0), 93 if (!alpha2)
76 /* IEEE 802.11a, channels 34..48 */ 94 return false;
77 RANGE_PWR(5170, 5240, 20, 6, IEEE80211_CHAN_PASSIVE_SCAN), 95 if (is_alpha_upper(alpha2[0]) && is_alpha_upper(alpha2[1]))
78 /* IEEE 802.11a, channels 52..64 */ 96 return true;
79 RANGE_PWR(5260, 5320, 20, 6, IEEE80211_CHAN_NO_IBSS | 97 return false;
80 IEEE80211_CHAN_RADAR), 98}
81};
82 99
83static const struct ieee80211_channel_range ieee80211_EU_channels[] = { 100static bool alpha2_equal(char *alpha2_x, char *alpha2_y)
84 /* IEEE 802.11b/g, channels 1..13 */ 101{
85 RANGE_PWR(2412, 2472, 20, 6, 0), 102 if (!alpha2_x || !alpha2_y)
86 /* IEEE 802.11a, channel 36*/ 103 return false;
87 RANGE_PWR(5180, 5180, 23, 6, IEEE80211_CHAN_PASSIVE_SCAN), 104 if (alpha2_x[0] == alpha2_y[0] &&
88 /* IEEE 802.11a, channel 40*/ 105 alpha2_x[1] == alpha2_y[1])
89 RANGE_PWR(5200, 5200, 23, 6, IEEE80211_CHAN_PASSIVE_SCAN), 106 return true;
90 /* IEEE 802.11a, channel 44*/ 107 return false;
91 RANGE_PWR(5220, 5220, 23, 6, IEEE80211_CHAN_PASSIVE_SCAN), 108}
92 /* IEEE 802.11a, channels 48..64 */ 109
93 RANGE_PWR(5240, 5320, 23, 6, IEEE80211_CHAN_NO_IBSS | 110static bool regdom_changed(char *alpha2)
94 IEEE80211_CHAN_RADAR), 111{
95 /* IEEE 802.11a, channels 100..140 */ 112 if (!cfg80211_regdomain)
96 RANGE_PWR(5500, 5700, 30, 6, IEEE80211_CHAN_NO_IBSS | 113 return true;
97 IEEE80211_CHAN_RADAR), 114 if (alpha2_equal(cfg80211_regdomain->alpha2, alpha2))
98}; 115 return false;
116 return true;
117}
118
119/* This lets us keep regulatory code which is updated on a regulatory
120 * basis in userspace. */
121static int call_crda(const char *alpha2)
122{
123 char country_env[9 + 2] = "COUNTRY=";
124 char *envp[] = {
125 country_env,
126 NULL
127 };
128
129 if (!is_world_regdom((char *) alpha2))
130 printk(KERN_INFO "cfg80211: Calling CRDA for country: %c%c\n",
131 alpha2[0], alpha2[1]);
132 else
133#ifdef CONFIG_WIRELESS_OLD_REGULATORY
134 return -EINVAL;
135#else
136 printk(KERN_INFO "cfg80211: Calling CRDA to update world "
137 "regulatory domain\n");
138#endif
139
140 country_env[8] = alpha2[0];
141 country_env[9] = alpha2[1];
142
143 return kobject_uevent_env(&reg_pdev->dev.kobj, KOBJ_CHANGE, envp);
144}
145
146/* This has the logic which determines when a new request
147 * should be ignored. */
148static int ignore_request(struct wiphy *wiphy, enum reg_set_by set_by,
149 char *alpha2, struct ieee80211_regdomain *rd)
150{
151 struct regulatory_request *last_request = NULL;
99 152
100#define REGDOM(_code) \ 153 /* All initial requests are respected */
101 { \ 154 if (list_empty(&regulatory_requests))
102 .code = __stringify(_code), \ 155 return 0;
103 .ranges = ieee80211_ ##_code## _channels, \ 156
104 .n_ranges = ARRAY_SIZE(ieee80211_ ##_code## _channels), \ 157 last_request = list_first_entry(&regulatory_requests,
158 struct regulatory_request, list);
159
160 switch (set_by) {
161 case REGDOM_SET_BY_INIT:
162 return -EINVAL;
163 case REGDOM_SET_BY_CORE:
164 /* Always respect new wireless core hints, should only
165 * come in for updating the world regulatory domain at init
166 * anyway */
167 return 0;
168 case REGDOM_SET_BY_COUNTRY_IE:
169 if (last_request->initiator == set_by) {
170 if (last_request->wiphy != wiphy) {
171 /* Two cards with two APs claiming different
172 * different Country IE alpha2s!
173 * You're special!! */
174 if (!alpha2_equal(last_request->alpha2,
175 cfg80211_regdomain->alpha2)) {
176 /* XXX: Deal with conflict, consider
177 * building a new one out of the
178 * intersection */
179 WARN_ON(1);
180 return -EOPNOTSUPP;
181 }
182 return -EALREADY;
183 }
184 /* Two consecutive Country IE hints on the same wiphy */
185 if (!alpha2_equal(cfg80211_regdomain->alpha2, alpha2))
186 return 0;
187 return -EALREADY;
188 }
189 if (WARN_ON(!is_alpha2_set(alpha2) || !is_an_alpha2(alpha2)),
190 "Invalid Country IE regulatory hint passed "
191 "to the wireless core\n")
192 return -EINVAL;
193 /* We ignore Country IE hints for now, as we haven't yet
194 * added the dot11MultiDomainCapabilityEnabled flag
195 * for wiphys */
196 return 1;
197 case REGDOM_SET_BY_DRIVER:
198 BUG_ON(!wiphy);
199 if (last_request->initiator == set_by) {
200 /* Two separate drivers hinting different things,
201 * this is possible if you have two devices present
202 * on a system with different EEPROM regulatory
203 * readings. XXX: Do intersection, we support only
204 * the first regulatory hint for now */
205 if (last_request->wiphy != wiphy)
206 return -EALREADY;
207 if (rd)
208 return -EALREADY;
209 /* Driver should not be trying to hint different
210 * regulatory domains! */
211 BUG_ON(!alpha2_equal(alpha2,
212 cfg80211_regdomain->alpha2));
213 return -EALREADY;
214 }
215 if (last_request->initiator == REGDOM_SET_BY_CORE)
216 return 0;
217 /* XXX: Handle intersection, and add the
218 * dot11MultiDomainCapabilityEnabled flag to wiphy. For now
219 * we assume the driver has this set to false, following the
220 * 802.11d dot11MultiDomainCapabilityEnabled documentation */
221 if (last_request->initiator == REGDOM_SET_BY_COUNTRY_IE)
222 return 0;
223 return 0;
224 case REGDOM_SET_BY_USER:
225 if (last_request->initiator == set_by ||
226 last_request->initiator == REGDOM_SET_BY_CORE)
227 return 0;
228 /* Drivers can use their wiphy's reg_notifier()
229 * to override any information */
230 if (last_request->initiator == REGDOM_SET_BY_DRIVER)
231 return 0;
232 /* XXX: Handle intersection */
233 if (last_request->initiator == REGDOM_SET_BY_COUNTRY_IE)
234 return -EOPNOTSUPP;
235 return 0;
236 default:
237 return -EINVAL;
105 } 238 }
239}
106 240
107static const struct ieee80211_regdomain ieee80211_regdoms[] = { 241static bool __reg_is_valid_request(char *alpha2,
108 REGDOM(US), 242 struct regulatory_request **request)
109 REGDOM(JP), 243{
110 REGDOM(EU), 244 struct regulatory_request *req;
111}; 245 if (list_empty(&regulatory_requests))
246 return false;
247 list_for_each_entry(req, &regulatory_requests, list) {
248 if (alpha2_equal(req->alpha2, alpha2)) {
249 *request = req;
250 return true;
251 }
252 }
253 return false;
254}
112 255
256/* Used by nl80211 before kmalloc'ing our regulatory domain */
257bool reg_is_valid_request(char *alpha2)
258{
259 struct regulatory_request *request = NULL;
260 return __reg_is_valid_request(alpha2, &request);
261}
113 262
114static const struct ieee80211_regdomain *get_regdom(void) 263/* Sanity check on a regulatory rule */
264static bool is_valid_reg_rule(struct ieee80211_reg_rule *rule)
115{ 265{
116 static const struct ieee80211_channel_range 266 struct ieee80211_freq_range *freq_range = &rule->freq_range;
117 ieee80211_world_channels[] = { 267 u32 freq_diff;
118 /* IEEE 802.11b/g, channels 1..11 */ 268
119 RANGE_PWR(2412, 2462, 27, 6, 0), 269 if (freq_range->start_freq_khz == 0 || freq_range->end_freq_khz == 0)
120 }; 270 return false;
121 static const struct ieee80211_regdomain regdom_world = REGDOM(world); 271
122 int i; 272 if (freq_range->start_freq_khz > freq_range->end_freq_khz)
273 return false;
274
275 freq_diff = freq_range->end_freq_khz - freq_range->start_freq_khz;
276
277 if (freq_range->max_bandwidth_khz > freq_diff)
278 return false;
279
280 return true;
281}
282
283static bool is_valid_rd(struct ieee80211_regdomain *rd)
284{
285 struct ieee80211_reg_rule *reg_rule = NULL;
286 unsigned int i;
123 287
124 for (i = 0; i < ARRAY_SIZE(ieee80211_regdoms); i++) 288 if (!rd->n_reg_rules)
125 if (strcmp(ieee80211_regdom, ieee80211_regdoms[i].code) == 0) 289 return false;
126 return &ieee80211_regdoms[i];
127 290
128 return &regdom_world; 291 for (i = 0; i < rd->n_reg_rules; i++) {
292 reg_rule = &rd->reg_rules[i];
293 if (!is_valid_reg_rule(reg_rule))
294 return false;
295 }
296
297 return true;
129} 298}
130 299
300/* Returns value in KHz */
301static u32 freq_max_bandwidth(const struct ieee80211_freq_range *freq_range,
302 u32 freq)
303{
304 unsigned int i;
305 for (i = 0; i < ARRAY_SIZE(supported_bandwidths); i++) {
306 u32 start_freq_khz = freq - supported_bandwidths[i]/2;
307 u32 end_freq_khz = freq + supported_bandwidths[i]/2;
308 if (start_freq_khz >= freq_range->start_freq_khz &&
309 end_freq_khz <= freq_range->end_freq_khz)
310 return supported_bandwidths[i];
311 }
312 return 0;
313}
131 314
132static void handle_channel(struct ieee80211_channel *chan, 315/* XXX: add support for the rest of enum nl80211_reg_rule_flags, we may
133 const struct ieee80211_regdomain *rd) 316 * want to just have the channel structure use these */
317static u32 map_regdom_flags(u32 rd_flags)
318{
319 u32 channel_flags = 0;
320 if (rd_flags & NL80211_RRF_PASSIVE_SCAN)
321 channel_flags |= IEEE80211_CHAN_PASSIVE_SCAN;
322 if (rd_flags & NL80211_RRF_NO_IBSS)
323 channel_flags |= IEEE80211_CHAN_NO_IBSS;
324 if (rd_flags & NL80211_RRF_DFS)
325 channel_flags |= IEEE80211_CHAN_RADAR;
326 return channel_flags;
327}
328
329/**
330 * freq_reg_info - get regulatory information for the given frequency
331 * @center_freq: Frequency in KHz for which we want regulatory information for
332 * @bandwidth: the bandwidth requirement you have in KHz, if you do not have one
333 * you can set this to 0. If this frequency is allowed we then set
334 * this value to the maximum allowed bandwidth.
335 * @reg_rule: the regulatory rule which we have for this frequency
336 *
337 * Use this function to get the regulatory rule for a specific frequency.
338 */
339static int freq_reg_info(u32 center_freq, u32 *bandwidth,
340 const struct ieee80211_reg_rule **reg_rule)
134{ 341{
135 int i; 342 int i;
136 u32 flags = chan->orig_flags; 343 u32 max_bandwidth = 0;
137 const struct ieee80211_channel_range *rg = NULL;
138 344
139 for (i = 0; i < rd->n_ranges; i++) { 345 if (!cfg80211_regdomain)
140 if (rd->ranges[i].start_freq <= chan->center_freq && 346 return -EINVAL;
141 chan->center_freq <= rd->ranges[i].end_freq) { 347
142 rg = &rd->ranges[i]; 348 for (i = 0; i < cfg80211_regdomain->n_reg_rules; i++) {
349 const struct ieee80211_reg_rule *rr;
350 const struct ieee80211_freq_range *fr = NULL;
351 const struct ieee80211_power_rule *pr = NULL;
352
353 rr = &cfg80211_regdomain->reg_rules[i];
354 fr = &rr->freq_range;
355 pr = &rr->power_rule;
356 max_bandwidth = freq_max_bandwidth(fr, center_freq);
357 if (max_bandwidth && *bandwidth <= max_bandwidth) {
358 *reg_rule = rr;
359 *bandwidth = max_bandwidth;
143 break; 360 break;
144 } 361 }
145 } 362 }
146 363
147 if (!rg) { 364 return !max_bandwidth;
148 /* not found */ 365}
366
367static void handle_channel(struct ieee80211_channel *chan)
368{
369 int r;
370 u32 flags = chan->orig_flags;
371 u32 max_bandwidth = 0;
372 const struct ieee80211_reg_rule *reg_rule = NULL;
373 const struct ieee80211_power_rule *power_rule = NULL;
374
375 r = freq_reg_info(MHZ_TO_KHZ(chan->center_freq),
376 &max_bandwidth, &reg_rule);
377
378 if (r) {
149 flags |= IEEE80211_CHAN_DISABLED; 379 flags |= IEEE80211_CHAN_DISABLED;
150 chan->flags = flags; 380 chan->flags = flags;
151 return; 381 return;
152 } 382 }
153 383
154 chan->flags = flags; 384 power_rule = &reg_rule->power_rule;
385
386 chan->flags = flags | map_regdom_flags(reg_rule->flags);
155 chan->max_antenna_gain = min(chan->orig_mag, 387 chan->max_antenna_gain = min(chan->orig_mag,
156 rg->max_antenna_gain); 388 (int) MBI_TO_DBI(power_rule->max_antenna_gain));
389 chan->max_bandwidth = KHZ_TO_MHZ(max_bandwidth);
157 if (chan->orig_mpwr) 390 if (chan->orig_mpwr)
158 chan->max_power = min(chan->orig_mpwr, rg->max_power); 391 chan->max_power = min(chan->orig_mpwr,
392 (int) MBM_TO_DBM(power_rule->max_eirp));
159 else 393 else
160 chan->max_power = rg->max_power; 394 chan->max_power = (int) MBM_TO_DBM(power_rule->max_eirp);
161} 395}
162 396
163static void handle_band(struct ieee80211_supported_band *sband, 397static void handle_band(struct ieee80211_supported_band *sband)
164 const struct ieee80211_regdomain *rd)
165{ 398{
166 int i; 399 int i;
167 400
168 for (i = 0; i < sband->n_channels; i++) 401 for (i = 0; i < sband->n_channels; i++)
169 handle_channel(&sband->channels[i], rd); 402 handle_channel(&sband->channels[i]);
170} 403}
171 404
172void wiphy_update_regulatory(struct wiphy *wiphy) 405static void update_all_wiphy_regulatory(enum reg_set_by setby)
173{ 406{
174 enum ieee80211_band band; 407 struct cfg80211_registered_device *drv;
175 const struct ieee80211_regdomain *rd = get_regdom();
176 408
177 for (band = 0; band < IEEE80211_NUM_BANDS; band++) 409 list_for_each_entry(drv, &cfg80211_drv_list, list)
410 wiphy_update_regulatory(&drv->wiphy, setby);
411}
412
413void wiphy_update_regulatory(struct wiphy *wiphy, enum reg_set_by setby)
414{
415 enum ieee80211_band band;
416 for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
178 if (wiphy->bands[band]) 417 if (wiphy->bands[band])
179 handle_band(wiphy->bands[band], rd); 418 handle_band(wiphy->bands[band]);
419 if (wiphy->reg_notifier)
420 wiphy->reg_notifier(wiphy, setby);
421 }
422}
423
424/* Caller must hold &cfg80211_drv_mutex */
425int __regulatory_hint(struct wiphy *wiphy, enum reg_set_by set_by,
426 const char *alpha2, struct ieee80211_regdomain *rd)
427{
428 struct regulatory_request *request;
429 char *rd_alpha2;
430 int r = 0;
431
432 r = ignore_request(wiphy, set_by, (char *) alpha2, rd);
433 if (r)
434 return r;
435
436 if (rd)
437 rd_alpha2 = rd->alpha2;
438 else
439 rd_alpha2 = (char *) alpha2;
440
441 switch (set_by) {
442 case REGDOM_SET_BY_CORE:
443 case REGDOM_SET_BY_COUNTRY_IE:
444 case REGDOM_SET_BY_DRIVER:
445 case REGDOM_SET_BY_USER:
446 request = kzalloc(sizeof(struct regulatory_request),
447 GFP_KERNEL);
448 if (!request)
449 return -ENOMEM;
450
451 request->alpha2[0] = rd_alpha2[0];
452 request->alpha2[1] = rd_alpha2[1];
453 request->initiator = set_by;
454 request->wiphy = wiphy;
455
456 list_add_tail(&request->list, &regulatory_requests);
457 if (rd)
458 break;
459 r = call_crda(alpha2);
460#ifndef CONFIG_WIRELESS_OLD_REGULATORY
461 if (r)
462 printk(KERN_ERR "cfg80211: Failed calling CRDA\n");
463#endif
464 break;
465 default:
466 r = -ENOTSUPP;
467 break;
468 }
469
470 return r;
471}
472
473/* If rd is not NULL and if this call fails the caller must free it */
474int regulatory_hint(struct wiphy *wiphy, const char *alpha2,
475 struct ieee80211_regdomain *rd)
476{
477 int r;
478 BUG_ON(!rd && !alpha2);
479
480 mutex_lock(&cfg80211_drv_mutex);
481
482 r = __regulatory_hint(wiphy, REGDOM_SET_BY_DRIVER, alpha2, rd);
483 if (r || !rd)
484 goto unlock_and_exit;
485
486 /* If the driver passed a regulatory domain we skipped asking
487 * userspace for one so we can now go ahead and set it */
488 r = set_regdom(rd);
489
490unlock_and_exit:
491 mutex_unlock(&cfg80211_drv_mutex);
492 return r;
493}
494EXPORT_SYMBOL(regulatory_hint);
495
496
497static void print_rd_rules(struct ieee80211_regdomain *rd)
498{
499 unsigned int i;
500 struct ieee80211_reg_rule *reg_rule = NULL;
501 struct ieee80211_freq_range *freq_range = NULL;
502 struct ieee80211_power_rule *power_rule = NULL;
503
504 printk(KERN_INFO "\t(start_freq - end_freq @ bandwidth), "
505 "(max_antenna_gain, max_eirp)\n");
506
507 for (i = 0; i < rd->n_reg_rules; i++) {
508 reg_rule = &rd->reg_rules[i];
509 freq_range = &reg_rule->freq_range;
510 power_rule = &reg_rule->power_rule;
511
512 /* There may not be documentation for max antenna gain
513 * in certain regions */
514 if (power_rule->max_antenna_gain)
515 printk(KERN_INFO "\t(%d KHz - %d KHz @ %d KHz), "
516 "(%d mBi, %d mBm)\n",
517 freq_range->start_freq_khz,
518 freq_range->end_freq_khz,
519 freq_range->max_bandwidth_khz,
520 power_rule->max_antenna_gain,
521 power_rule->max_eirp);
522 else
523 printk(KERN_INFO "\t(%d KHz - %d KHz @ %d KHz), "
524 "(N/A, %d mBm)\n",
525 freq_range->start_freq_khz,
526 freq_range->end_freq_khz,
527 freq_range->max_bandwidth_khz,
528 power_rule->max_eirp);
529 }
530}
531
532static void print_regdomain(struct ieee80211_regdomain *rd)
533{
534
535 if (is_world_regdom(rd->alpha2))
536 printk(KERN_INFO "cfg80211: World regulatory "
537 "domain updated:\n");
538 else {
539 if (is_unknown_alpha2(rd->alpha2))
540 printk(KERN_INFO "cfg80211: Regulatory domain "
541 "changed to driver built-in settings "
542 "(unknown country)\n");
543 else
544 printk(KERN_INFO "cfg80211: Regulatory domain "
545 "changed to country: %c%c\n",
546 rd->alpha2[0], rd->alpha2[1]);
547 }
548 print_rd_rules(rd);
549}
550
551void print_regdomain_info(struct ieee80211_regdomain *rd)
552{
553 printk(KERN_INFO "cfg80211: Regulatory domain: %c%c\n",
554 rd->alpha2[0], rd->alpha2[1]);
555 print_rd_rules(rd);
556}
557
558#ifdef CONFIG_WIRELESS_OLD_REGULATORY
559
560static bool is_old_static_regdom(struct ieee80211_regdomain *rd)
561{
562 if (rd == &us_regdom || rd == &jp_regdom || rd == &eu_regdom)
563 return true;
564 return false;
565}
566
567/* The old crap never deals with a world regulatory domain, it only
568 * deals with the static regulatory domain passed and if possible
569 * an updated "US" or "JP" regulatory domain. We do however store the
570 * old static regulatory domain in cfg80211_world_regdom for convenience
571 * of use here */
572static void reset_regdomains_static(void)
573{
574 if (!is_old_static_regdom(cfg80211_regdomain))
575 kfree(cfg80211_regdomain);
576 /* This is setting the regdom to the old static regdom */
577 cfg80211_regdomain =
578 (struct ieee80211_regdomain *) cfg80211_world_regdom;
579}
580#else
581static void reset_regdomains(void)
582{
583 if (cfg80211_world_regdom && cfg80211_world_regdom != &world_regdom) {
584 if (cfg80211_world_regdom == cfg80211_regdomain) {
585 kfree(cfg80211_regdomain);
586 } else {
587 kfree(cfg80211_world_regdom);
588 kfree(cfg80211_regdomain);
589 }
590 } else if (cfg80211_regdomain && cfg80211_regdomain != &world_regdom)
591 kfree(cfg80211_regdomain);
592
593 cfg80211_world_regdom = (struct ieee80211_regdomain *) &world_regdom;
594 cfg80211_regdomain = NULL;
595}
596
597/* Dynamic world regulatory domain requested by the wireless
598 * core upon initialization */
599static void update_world_regdomain(struct ieee80211_regdomain *rd)
600{
601 BUG_ON(list_empty(&regulatory_requests));
602
603 reset_regdomains();
604
605 cfg80211_world_regdom = rd;
606 cfg80211_regdomain = rd;
607}
608#endif
609
610static int __set_regdom(struct ieee80211_regdomain *rd)
611{
612 struct regulatory_request *request = NULL;
613
614 /* Some basic sanity checks first */
615
616#ifdef CONFIG_WIRELESS_OLD_REGULATORY
617 /* We ignore the world regdom with the old static regdomains setup
618 * as there is no point to it with satic regulatory definitions :(
619 * Don't worry this shit will be removed soon... */
620 if (is_world_regdom(rd->alpha2))
621 return -EINVAL;
622#else
623 if (is_world_regdom(rd->alpha2)) {
624 if (WARN_ON(!__reg_is_valid_request(rd->alpha2, &request)))
625 return -EINVAL;
626 update_world_regdomain(rd);
627 return 0;
628 }
629#endif
630
631 if (!is_alpha2_set(rd->alpha2) && !is_an_alpha2(rd->alpha2) &&
632 !is_unknown_alpha2(rd->alpha2))
633 return -EINVAL;
634
635 if (list_empty(&regulatory_requests))
636 return -EINVAL;
637
638#ifdef CONFIG_WIRELESS_OLD_REGULATORY
639 /* Static "US" and "JP" will be overridden, but just once */
640 if (!is_old_static_regdom(cfg80211_regdomain) &&
641 !regdom_changed(rd->alpha2))
642 return -EINVAL;
643#else
644 if (!regdom_changed(rd->alpha2))
645 return -EINVAL;
646#endif
647
648 /* Now lets set the regulatory domain, update all driver channels
649 * and finally inform them of what we have done, in case they want
650 * to review or adjust their own settings based on their own
651 * internal EEPROM data */
652
653 if (WARN_ON(!__reg_is_valid_request(rd->alpha2, &request)))
654 return -EINVAL;
655
656#ifdef CONFIG_WIRELESS_OLD_REGULATORY
657 reset_regdomains_static();
658#else
659 reset_regdomains();
660#endif
661
662 /* Country IE parsing coming soon */
663 switch (request->initiator) {
664 case REGDOM_SET_BY_CORE:
665 case REGDOM_SET_BY_DRIVER:
666 case REGDOM_SET_BY_USER:
667 if (!is_valid_rd(rd)) {
668 printk(KERN_ERR "cfg80211: Invalid "
669 "regulatory domain detected:\n");
670 print_regdomain_info(rd);
671 return -EINVAL;
672 }
673 break;
674 case REGDOM_SET_BY_COUNTRY_IE: /* Not yet */
675 WARN_ON(1);
676 default:
677 return -EOPNOTSUPP;
678 }
679
680 /* Tada! */
681 cfg80211_regdomain = rd;
682 request->granted = 1;
683
684 return 0;
685}
686
687
688/* Use this call to set the current regulatory domain. Conflicts with
689 * multiple drivers can be ironed out later. Caller must've already
690 * kmalloc'd the rd structure. If this calls fails you should kfree()
691 * the passed rd. Caller must hold cfg80211_drv_mutex */
692int set_regdom(struct ieee80211_regdomain *rd)
693{
694 struct regulatory_request *this_request = NULL, *prev_request = NULL;
695 int r;
696
697 if (!list_empty(&regulatory_requests))
698 prev_request = list_first_entry(&regulatory_requests,
699 struct regulatory_request, list);
700
701 /* Note that this doesn't update the wiphys, this is done below */
702 r = __set_regdom(rd);
703 if (r)
704 return r;
705
706 BUG_ON((!__reg_is_valid_request(rd->alpha2, &this_request)));
707
708 /* The initial standard core update of the world regulatory domain, no
709 * need to keep that request info around if it didn't fail. */
710 if (is_world_regdom(rd->alpha2) &&
711 this_request->initiator == REGDOM_SET_BY_CORE &&
712 this_request->granted) {
713 list_del(&this_request->list);
714 kfree(this_request);
715 this_request = NULL;
716 }
717
718 /* Remove old requests, we only leave behind the last one */
719 if (prev_request) {
720 list_del(&prev_request->list);
721 kfree(prev_request);
722 prev_request = NULL;
723 }
724
725 /* This would make this whole thing pointless */
726 BUG_ON(rd != cfg80211_regdomain);
727
728 /* update all wiphys now with the new established regulatory domain */
729 update_all_wiphy_regulatory(this_request->initiator);
730
731 print_regdomain(rd);
732
733 return r;
734}
735
736int regulatory_init(void)
737{
738 reg_pdev = platform_device_register_simple("regulatory", 0, NULL, 0);
739 if (IS_ERR(reg_pdev))
740 return PTR_ERR(reg_pdev);
741 return 0;
742}
743
744void regulatory_exit(void)
745{
746 struct regulatory_request *req, *req_tmp;
747 mutex_lock(&cfg80211_drv_mutex);
748#ifdef CONFIG_WIRELESS_OLD_REGULATORY
749 reset_regdomains_static();
750#else
751 reset_regdomains();
752#endif
753 list_for_each_entry_safe(req, req_tmp, &regulatory_requests, list) {
754 list_del(&req->list);
755 kfree(req);
756 }
757 platform_device_unregister(reg_pdev);
758 mutex_unlock(&cfg80211_drv_mutex);
180} 759}
diff --git a/net/wireless/reg.h b/net/wireless/reg.h
new file mode 100644
index 000000000000..d75fd0232972
--- /dev/null
+++ b/net/wireless/reg.h
@@ -0,0 +1,44 @@
1#ifndef __NET_WIRELESS_REG_H
2#define __NET_WIRELESS_REG_H
3
4extern const struct ieee80211_regdomain world_regdom;
5#ifdef CONFIG_WIRELESS_OLD_REGULATORY
6extern const struct ieee80211_regdomain us_regdom;
7extern const struct ieee80211_regdomain jp_regdom;
8extern const struct ieee80211_regdomain eu_regdom;
9#endif
10
11extern struct ieee80211_regdomain *cfg80211_regdomain;
12extern struct ieee80211_regdomain *cfg80211_world_regdom;
13extern struct list_head regulatory_requests;
14
15struct regdom_last_setby {
16 struct wiphy *wiphy;
17 u8 initiator;
18};
19
20/* wiphy is set if this request's initiator is REGDOM_SET_BY_DRIVER */
21struct regulatory_request {
22 struct list_head list;
23 struct wiphy *wiphy;
24 int granted;
25 enum reg_set_by initiator;
26 char alpha2[2];
27};
28
29bool is_world_regdom(char *alpha2);
30bool reg_is_valid_request(char *alpha2);
31
32int set_regdom(struct ieee80211_regdomain *rd);
33int __regulatory_hint_alpha2(struct wiphy *wiphy, enum reg_set_by set_by,
34 const char *alpha2);
35
36int regulatory_init(void);
37void regulatory_exit(void);
38
39void print_regdomain_info(struct ieee80211_regdomain *);
40
41/* If a char is A-Z */
42#define IS_ALPHA(letter) (letter >= 65 && letter <= 90)
43
44#endif /* __NET_WIRELESS_REG_H */