aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorDavid S. Miller <davem@davemloft.net>2008-11-26 18:28:40 -0500
committerDavid S. Miller <davem@davemloft.net>2008-11-26 18:28:40 -0500
commitb5ddedc9cc01b1d86015af08c5f1694191804530 (patch)
treedb08f24da9ef4dcec0976ee4de4d77e5e596057e /net
parent244e6c2d0724bc4908a1995804704bdee3b31528 (diff)
parentb235507cc5e552b9e75678d596727249e8fba01b (diff)
Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-next-2.6
Diffstat (limited to 'net')
-rw-r--r--net/mac80211/Kconfig8
-rw-r--r--net/mac80211/ht.c2
-rw-r--r--net/mac80211/main.c5
-rw-r--r--net/mac80211/mlme.c15
-rw-r--r--net/mac80211/rc80211_pid_algo.c2
-rw-r--r--net/mac80211/wep.c7
-rw-r--r--net/rfkill/rfkill.c17
-rw-r--r--net/wireless/Kconfig11
-rw-r--r--net/wireless/core.c5
-rw-r--r--net/wireless/core.h13
-rw-r--r--net/wireless/nl80211.c5
-rw-r--r--net/wireless/reg.c586
-rw-r--r--net/wireless/reg.h21
13 files changed, 606 insertions, 91 deletions
diff --git a/net/mac80211/Kconfig b/net/mac80211/Kconfig
index 7f710a27e91c..60c16162474c 100644
--- a/net/mac80211/Kconfig
+++ b/net/mac80211/Kconfig
@@ -16,20 +16,20 @@ menu "Rate control algorithm selection"
16 16
17config MAC80211_RC_PID 17config MAC80211_RC_PID
18 bool "PID controller based rate control algorithm" if EMBEDDED 18 bool "PID controller based rate control algorithm" if EMBEDDED
19 default y
20 ---help--- 19 ---help---
21 This option enables a TX rate control algorithm for 20 This option enables a TX rate control algorithm for
22 mac80211 that uses a PID controller to select the TX 21 mac80211 that uses a PID controller to select the TX
23 rate. 22 rate.
24 23
25config MAC80211_RC_MINSTREL 24config MAC80211_RC_MINSTREL
26 bool "Minstrel" 25 bool "Minstrel" if EMBEDDED
26 default y
27 ---help--- 27 ---help---
28 This option enables the 'minstrel' TX rate control algorithm 28 This option enables the 'minstrel' TX rate control algorithm
29 29
30choice 30choice
31 prompt "Default rate control algorithm" 31 prompt "Default rate control algorithm"
32 default MAC80211_RC_DEFAULT_PID 32 default MAC80211_RC_DEFAULT_MINSTREL
33 ---help--- 33 ---help---
34 This option selects the default rate control algorithm 34 This option selects the default rate control algorithm
35 mac80211 will use. Note that this default can still be 35 mac80211 will use. Note that this default can still be
@@ -55,8 +55,8 @@ endchoice
55 55
56config MAC80211_RC_DEFAULT 56config MAC80211_RC_DEFAULT
57 string 57 string
58 default "pid" if MAC80211_RC_DEFAULT_PID
59 default "minstrel" if MAC80211_RC_DEFAULT_MINSTREL 58 default "minstrel" if MAC80211_RC_DEFAULT_MINSTREL
59 default "pid" if MAC80211_RC_DEFAULT_PID
60 default "" 60 default ""
61 61
62endmenu 62endmenu
diff --git a/net/mac80211/ht.c b/net/mac80211/ht.c
index 3e231d756776..a1eed7032c9b 100644
--- a/net/mac80211/ht.c
+++ b/net/mac80211/ht.c
@@ -36,7 +36,7 @@ void ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_supported_band *sband,
36 36
37 ht_cap->ht_supported = true; 37 ht_cap->ht_supported = true;
38 38
39 ht_cap->cap = ht_cap->cap & sband->ht_cap.cap; 39 ht_cap->cap = le16_to_cpu(ht_cap_ie->cap_info) & sband->ht_cap.cap;
40 ht_cap->cap &= ~IEEE80211_HT_CAP_SM_PS; 40 ht_cap->cap &= ~IEEE80211_HT_CAP_SM_PS;
41 ht_cap->cap |= sband->ht_cap.cap & IEEE80211_HT_CAP_SM_PS; 41 ht_cap->cap |= sband->ht_cap.cap & IEEE80211_HT_CAP_SM_PS;
42 42
diff --git a/net/mac80211/main.c b/net/mac80211/main.c
index d631dc96c323..cec9b6d3e1ce 100644
--- a/net/mac80211/main.c
+++ b/net/mac80211/main.c
@@ -722,7 +722,6 @@ EXPORT_SYMBOL(ieee80211_alloc_hw);
722int ieee80211_register_hw(struct ieee80211_hw *hw) 722int ieee80211_register_hw(struct ieee80211_hw *hw)
723{ 723{
724 struct ieee80211_local *local = hw_to_local(hw); 724 struct ieee80211_local *local = hw_to_local(hw);
725 const char *name;
726 int result; 725 int result;
727 enum ieee80211_band band; 726 enum ieee80211_band band;
728 struct net_device *mdev; 727 struct net_device *mdev;
@@ -787,8 +786,8 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
787 mdev->header_ops = &ieee80211_header_ops; 786 mdev->header_ops = &ieee80211_header_ops;
788 mdev->set_multicast_list = ieee80211_master_set_multicast_list; 787 mdev->set_multicast_list = ieee80211_master_set_multicast_list;
789 788
790 name = wiphy_dev(local->hw.wiphy)->driver->name; 789 local->hw.workqueue =
791 local->hw.workqueue = create_freezeable_workqueue(name); 790 create_freezeable_workqueue(wiphy_name(local->hw.wiphy));
792 if (!local->hw.workqueue) { 791 if (!local->hw.workqueue) {
793 result = -ENOMEM; 792 result = -ENOMEM;
794 goto fail_workqueue; 793 goto fail_workqueue;
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index d81a4d2cd3aa..7600ac9b87fe 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -802,6 +802,10 @@ static void ieee80211_authenticate(struct ieee80211_sub_if_data *sdata,
802 mod_timer(&ifsta->timer, jiffies + IEEE80211_AUTH_TIMEOUT); 802 mod_timer(&ifsta->timer, jiffies + IEEE80211_AUTH_TIMEOUT);
803} 803}
804 804
805/*
806 * The disassoc 'reason' argument can be either our own reason
807 * if self disconnected or a reason code from the AP.
808 */
805static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, 809static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
806 struct ieee80211_if_sta *ifsta, bool deauth, 810 struct ieee80211_if_sta *ifsta, bool deauth,
807 bool self_disconnected, u16 reason) 811 bool self_disconnected, u16 reason)
@@ -848,7 +852,7 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
848 852
849 ieee80211_sta_send_apinfo(sdata, ifsta); 853 ieee80211_sta_send_apinfo(sdata, ifsta);
850 854
851 if (self_disconnected) 855 if (self_disconnected || reason == WLAN_REASON_DISASSOC_STA_HAS_LEFT)
852 ifsta->state = IEEE80211_STA_MLME_DISABLED; 856 ifsta->state = IEEE80211_STA_MLME_DISABLED;
853 857
854 sta_info_unlink(&sta); 858 sta_info_unlink(&sta);
@@ -1163,7 +1167,7 @@ static void ieee80211_rx_mgmt_disassoc(struct ieee80211_sub_if_data *sdata,
1163 IEEE80211_RETRY_AUTH_INTERVAL); 1167 IEEE80211_RETRY_AUTH_INTERVAL);
1164 } 1168 }
1165 1169
1166 ieee80211_set_disassoc(sdata, ifsta, false, false, 0); 1170 ieee80211_set_disassoc(sdata, ifsta, false, false, reason_code);
1167} 1171}
1168 1172
1169 1173
@@ -1736,6 +1740,13 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
1736 ap_ht_cap_flags); 1740 ap_ht_cap_flags);
1737 } 1741 }
1738 1742
1743 if (elems.country_elem) {
1744 /* Note we are only reviewing this on beacons
1745 * for the BSSID we are associated to */
1746 regulatory_hint_11d(local->hw.wiphy,
1747 elems.country_elem, elems.country_elem_len);
1748 }
1749
1739 ieee80211_bss_info_change_notify(sdata, changed); 1750 ieee80211_bss_info_change_notify(sdata, changed);
1740} 1751}
1741 1752
diff --git a/net/mac80211/rc80211_pid_algo.c b/net/mac80211/rc80211_pid_algo.c
index 96ceb7e86c5c..b16801cde06f 100644
--- a/net/mac80211/rc80211_pid_algo.c
+++ b/net/mac80211/rc80211_pid_algo.c
@@ -256,7 +256,7 @@ static void rate_control_pid_tx_status(void *priv, struct ieee80211_supported_ba
256 if (!(info->flags & IEEE80211_TX_STAT_ACK)) { 256 if (!(info->flags & IEEE80211_TX_STAT_ACK)) {
257 spinfo->tx_num_failed += 2; 257 spinfo->tx_num_failed += 2;
258 spinfo->tx_num_xmit++; 258 spinfo->tx_num_xmit++;
259 } else if (info->status.rates[0].count) { 259 } else if (info->status.rates[0].count > 1) {
260 spinfo->tx_num_failed++; 260 spinfo->tx_num_failed++;
261 spinfo->tx_num_xmit++; 261 spinfo->tx_num_xmit++;
262 } 262 }
diff --git a/net/mac80211/wep.c b/net/mac80211/wep.c
index 7bbb98e846a3..7043ddc75498 100644
--- a/net/mac80211/wep.c
+++ b/net/mac80211/wep.c
@@ -17,6 +17,7 @@
17#include <linux/err.h> 17#include <linux/err.h>
18#include <linux/mm.h> 18#include <linux/mm.h>
19#include <linux/scatterlist.h> 19#include <linux/scatterlist.h>
20#include <asm/unaligned.h>
20 21
21#include <net/mac80211.h> 22#include <net/mac80211.h>
22#include "ieee80211_i.h" 23#include "ieee80211_i.h"
@@ -125,10 +126,10 @@ void ieee80211_wep_encrypt_data(struct crypto_blkcipher *tfm, u8 *rc4key,
125{ 126{
126 struct blkcipher_desc desc = { .tfm = tfm }; 127 struct blkcipher_desc desc = { .tfm = tfm };
127 struct scatterlist sg; 128 struct scatterlist sg;
128 __le32 *icv; 129 __le32 icv;
129 130
130 icv = (__le32 *)(data + data_len); 131 icv = cpu_to_le32(~crc32_le(~0, data, data_len));
131 *icv = cpu_to_le32(~crc32_le(~0, data, data_len)); 132 put_unaligned(icv, (__le32 *)(data + data_len));
132 133
133 crypto_blkcipher_setkey(tfm, rc4key, klen); 134 crypto_blkcipher_setkey(tfm, rc4key, klen);
134 sg_init_one(&sg, data, data_len + WEP_ICV_LEN); 135 sg_init_one(&sg, data, data_len + WEP_ICV_LEN);
diff --git a/net/rfkill/rfkill.c b/net/rfkill/rfkill.c
index ec26eae8004d..051d2c9ea66b 100644
--- a/net/rfkill/rfkill.c
+++ b/net/rfkill/rfkill.c
@@ -565,16 +565,22 @@ static void rfkill_release(struct device *dev)
565#ifdef CONFIG_PM 565#ifdef CONFIG_PM
566static int rfkill_suspend(struct device *dev, pm_message_t state) 566static int rfkill_suspend(struct device *dev, pm_message_t state)
567{ 567{
568 struct rfkill *rfkill = to_rfkill(dev);
569
568 /* mark class device as suspended */ 570 /* mark class device as suspended */
569 if (dev->power.power_state.event != state.event) 571 if (dev->power.power_state.event != state.event)
570 dev->power.power_state = state; 572 dev->power.power_state = state;
571 573
574 /* store state for the resume handler */
575 rfkill->state_for_resume = rfkill->state;
576
572 return 0; 577 return 0;
573} 578}
574 579
575static int rfkill_resume(struct device *dev) 580static int rfkill_resume(struct device *dev)
576{ 581{
577 struct rfkill *rfkill = to_rfkill(dev); 582 struct rfkill *rfkill = to_rfkill(dev);
583 enum rfkill_state newstate;
578 584
579 if (dev->power.power_state.event != PM_EVENT_ON) { 585 if (dev->power.power_state.event != PM_EVENT_ON) {
580 mutex_lock(&rfkill->mutex); 586 mutex_lock(&rfkill->mutex);
@@ -582,6 +588,15 @@ static int rfkill_resume(struct device *dev)
582 dev->power.power_state.event = PM_EVENT_ON; 588 dev->power.power_state.event = PM_EVENT_ON;
583 589
584 /* 590 /*
591 * rfkill->state could have been modified before we got
592 * called, and won't be updated by rfkill_toggle_radio()
593 * in force mode. Sync it FIRST.
594 */
595 if (rfkill->get_state &&
596 !rfkill->get_state(rfkill->data, &newstate))
597 rfkill->state = newstate;
598
599 /*
585 * If we are under EPO, kick transmitter offline, 600 * If we are under EPO, kick transmitter offline,
586 * otherwise restore to pre-suspend state. 601 * otherwise restore to pre-suspend state.
587 * 602 *
@@ -590,7 +605,7 @@ static int rfkill_resume(struct device *dev)
590 rfkill_toggle_radio(rfkill, 605 rfkill_toggle_radio(rfkill,
591 rfkill_epo_lock_active ? 606 rfkill_epo_lock_active ?
592 RFKILL_STATE_SOFT_BLOCKED : 607 RFKILL_STATE_SOFT_BLOCKED :
593 rfkill->state, 608 rfkill->state_for_resume,
594 1); 609 1);
595 610
596 mutex_unlock(&rfkill->mutex); 611 mutex_unlock(&rfkill->mutex);
diff --git a/net/wireless/Kconfig b/net/wireless/Kconfig
index f7c64dbe86cc..e28e2b8fa436 100644
--- a/net/wireless/Kconfig
+++ b/net/wireless/Kconfig
@@ -1,6 +1,15 @@
1config CFG80211 1config CFG80211
2 tristate "Improved wireless configuration API" 2 tristate "Improved wireless configuration API"
3 3
4config CFG80211_REG_DEBUG
5 bool "cfg80211 regulatory debugging"
6 depends on CFG80211
7 default n
8 ---help---
9 You can enable this if you want to debug regulatory changes.
10
11 If unsure, say N.
12
4config NL80211 13config NL80211
5 bool "nl80211 new netlink interface support" 14 bool "nl80211 new netlink interface support"
6 depends on CFG80211 15 depends on CFG80211
@@ -40,6 +49,8 @@ config WIRELESS_OLD_REGULATORY
40 ieee80211_regdom module parameter. This is being phased out and you 49 ieee80211_regdom module parameter. This is being phased out and you
41 should stop using them ASAP. 50 should stop using them ASAP.
42 51
52 Note: You will need CRDA if you want 802.11d support
53
43 Say Y unless you have installed a new userspace application. 54 Say Y unless you have installed a new userspace application.
44 Also say Y if have one currently depending on the ieee80211_regdom 55 Also say Y if have one currently depending on the ieee80211_regdom
45 module parameter and cannot port it to use the new userspace 56 module parameter and cannot port it to use the new userspace
diff --git a/net/wireless/core.c b/net/wireless/core.c
index 39e3d10fccde..b96fc0c3f1c4 100644
--- a/net/wireless/core.c
+++ b/net/wireless/core.c
@@ -19,7 +19,6 @@
19#include "nl80211.h" 19#include "nl80211.h"
20#include "core.h" 20#include "core.h"
21#include "sysfs.h" 21#include "sysfs.h"
22#include "reg.h"
23 22
24/* name for sysfs, %d is appended */ 23/* name for sysfs, %d is appended */
25#define PHY_NAME "phy" 24#define PHY_NAME "phy"
@@ -348,6 +347,10 @@ void wiphy_unregister(struct wiphy *wiphy)
348 /* unlock again before freeing */ 347 /* unlock again before freeing */
349 mutex_unlock(&drv->mtx); 348 mutex_unlock(&drv->mtx);
350 349
350 /* If this device got a regulatory hint tell core its
351 * free to listen now to a new shiny device regulatory hint */
352 reg_device_remove(wiphy);
353
351 list_del(&drv->list); 354 list_del(&drv->list);
352 device_del(&drv->wiphy.dev); 355 device_del(&drv->wiphy.dev);
353 debugfs_remove(drv->wiphy.debugfsdir); 356 debugfs_remove(drv->wiphy.debugfsdir);
diff --git a/net/wireless/core.h b/net/wireless/core.h
index 771cc5cc7658..f7fb9f413028 100644
--- a/net/wireless/core.h
+++ b/net/wireless/core.h
@@ -11,6 +11,7 @@
11#include <net/genetlink.h> 11#include <net/genetlink.h>
12#include <net/wireless.h> 12#include <net/wireless.h>
13#include <net/cfg80211.h> 13#include <net/cfg80211.h>
14#include "reg.h"
14 15
15struct cfg80211_registered_device { 16struct cfg80211_registered_device {
16 struct cfg80211_ops *ops; 17 struct cfg80211_ops *ops;
@@ -21,6 +22,18 @@ struct cfg80211_registered_device {
21 * any call is in progress */ 22 * any call is in progress */
22 struct mutex mtx; 23 struct mutex mtx;
23 24
25 /* ISO / IEC 3166 alpha2 for which this device is receiving
26 * country IEs on, this can help disregard country IEs from APs
27 * on the same alpha2 quickly. The alpha2 may differ from
28 * cfg80211_regdomain's alpha2 when an intersection has occurred.
29 * If the AP is reconfigured this can also be used to tell us if
30 * the country on the country IE changed. */
31 char country_ie_alpha2[2];
32
33 /* If a Country IE has been received this tells us the environment
34 * which its telling us its in. This defaults to ENVIRON_ANY */
35 enum environment_cap env;
36
24 /* wiphy index, internal only */ 37 /* wiphy index, internal only */
25 int idx; 38 int idx;
26 39
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index e3e1494e769a..c9141e3df9ba 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -198,6 +198,9 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,
198 if (chan->flags & IEEE80211_CHAN_RADAR) 198 if (chan->flags & IEEE80211_CHAN_RADAR)
199 NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_RADAR); 199 NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_RADAR);
200 200
201 NLA_PUT_U32(msg, NL80211_FREQUENCY_ATTR_MAX_TX_POWER,
202 DBM_TO_MBM(chan->max_power));
203
201 nla_nest_end(msg, nl_freq); 204 nla_nest_end(msg, nl_freq);
202 } 205 }
203 206
@@ -1760,7 +1763,7 @@ static int nl80211_req_set_reg(struct sk_buff *skb, struct genl_info *info)
1760 return -EINVAL; 1763 return -EINVAL;
1761#endif 1764#endif
1762 mutex_lock(&cfg80211_drv_mutex); 1765 mutex_lock(&cfg80211_drv_mutex);
1763 r = __regulatory_hint(NULL, REGDOM_SET_BY_USER, data); 1766 r = __regulatory_hint(NULL, REGDOM_SET_BY_USER, data, 0, ENVIRON_ANY);
1764 mutex_unlock(&cfg80211_drv_mutex); 1767 mutex_unlock(&cfg80211_drv_mutex);
1765 return r; 1768 return r;
1766} 1769}
diff --git a/net/wireless/reg.c b/net/wireless/reg.c
index 4c7e39d466c4..0990059f7e48 100644
--- a/net/wireless/reg.c
+++ b/net/wireless/reg.c
@@ -60,12 +60,18 @@
60 * @intersect: indicates whether the wireless core should intersect 60 * @intersect: indicates whether the wireless core should intersect
61 * the requested regulatory domain with the presently set regulatory 61 * the requested regulatory domain with the presently set regulatory
62 * domain. 62 * domain.
63 * @country_ie_checksum: checksum of the last processed and accepted
64 * country IE
65 * @country_ie_env: lets us know if the AP is telling us we are outdoor,
66 * indoor, or if it doesn't matter
63 */ 67 */
64struct regulatory_request { 68struct regulatory_request {
65 struct wiphy *wiphy; 69 struct wiphy *wiphy;
66 enum reg_set_by initiator; 70 enum reg_set_by initiator;
67 char alpha2[2]; 71 char alpha2[2];
68 bool intersect; 72 bool intersect;
73 u32 country_ie_checksum;
74 enum environment_cap country_ie_env;
69}; 75};
70 76
71/* Receipt of information from last regulatory request */ 77/* Receipt of information from last regulatory request */
@@ -85,6 +91,11 @@ static u32 supported_bandwidths[] = {
85 * information to give us an alpha2 */ 91 * information to give us an alpha2 */
86static const struct ieee80211_regdomain *cfg80211_regdomain; 92static const struct ieee80211_regdomain *cfg80211_regdomain;
87 93
94/* We use this as a place for the rd structure built from the
95 * last parsed country IE to rest until CRDA gets back to us with
96 * what it thinks should apply for the same country */
97static const struct ieee80211_regdomain *country_ie_regdomain;
98
88/* We keep a static world regulatory domain in case of the absence of CRDA */ 99/* We keep a static world regulatory domain in case of the absence of CRDA */
89static const struct ieee80211_regdomain world_regdom = { 100static const struct ieee80211_regdomain world_regdom = {
90 .n_reg_rules = 1, 101 .n_reg_rules = 1,
@@ -264,6 +275,18 @@ static bool is_unknown_alpha2(const char *alpha2)
264 return false; 275 return false;
265} 276}
266 277
278static bool is_intersected_alpha2(const char *alpha2)
279{
280 if (!alpha2)
281 return false;
282 /* Special case where regulatory domain is the
283 * result of an intersection between two regulatory domain
284 * structures */
285 if (alpha2[0] == '9' && alpha2[1] == '8')
286 return true;
287 return false;
288}
289
267static bool is_an_alpha2(const char *alpha2) 290static bool is_an_alpha2(const char *alpha2)
268{ 291{
269 if (!alpha2) 292 if (!alpha2)
@@ -292,6 +315,25 @@ static bool regdom_changed(const char *alpha2)
292 return true; 315 return true;
293} 316}
294 317
318/**
319 * country_ie_integrity_changes - tells us if the country IE has changed
320 * @checksum: checksum of country IE of fields we are interested in
321 *
322 * If the country IE has not changed you can ignore it safely. This is
323 * useful to determine if two devices are seeing two different country IEs
324 * even on the same alpha2. Note that this will return false if no IE has
325 * been set on the wireless core yet.
326 */
327static bool country_ie_integrity_changes(u32 checksum)
328{
329 /* If no IE has been set then the checksum doesn't change */
330 if (unlikely(!last_request->country_ie_checksum))
331 return false;
332 if (unlikely(last_request->country_ie_checksum != checksum))
333 return true;
334 return false;
335}
336
295/* This lets us keep regulatory code which is updated on a regulatory 337/* This lets us keep regulatory code which is updated on a regulatory
296 * basis in userspace. */ 338 * basis in userspace. */
297static int call_crda(const char *alpha2) 339static int call_crda(const char *alpha2)
@@ -330,7 +372,7 @@ static bool is_valid_reg_rule(const struct ieee80211_reg_rule *rule)
330 const struct ieee80211_freq_range *freq_range = &rule->freq_range; 372 const struct ieee80211_freq_range *freq_range = &rule->freq_range;
331 u32 freq_diff; 373 u32 freq_diff;
332 374
333 if (freq_range->start_freq_khz == 0 || freq_range->end_freq_khz == 0) 375 if (freq_range->start_freq_khz <= 0 || freq_range->end_freq_khz <= 0)
334 return false; 376 return false;
335 377
336 if (freq_range->start_freq_khz > freq_range->end_freq_khz) 378 if (freq_range->start_freq_khz > freq_range->end_freq_khz)
@@ -352,6 +394,9 @@ static bool is_valid_rd(const struct ieee80211_regdomain *rd)
352 if (!rd->n_reg_rules) 394 if (!rd->n_reg_rules)
353 return false; 395 return false;
354 396
397 if (WARN_ON(rd->n_reg_rules > NL80211_MAX_SUPP_REG_RULES))
398 return false;
399
355 for (i = 0; i < rd->n_reg_rules; i++) { 400 for (i = 0; i < rd->n_reg_rules; i++) {
356 reg_rule = &rd->reg_rules[i]; 401 reg_rule = &rd->reg_rules[i];
357 if (!is_valid_reg_rule(reg_rule)) 402 if (!is_valid_reg_rule(reg_rule))
@@ -376,6 +421,174 @@ static u32 freq_max_bandwidth(const struct ieee80211_freq_range *freq_range,
376 return 0; 421 return 0;
377} 422}
378 423
424/* Converts a country IE to a regulatory domain. A regulatory domain
425 * structure has a lot of information which the IE doesn't yet have,
426 * so for the other values we use upper max values as we will intersect
427 * with our userspace regulatory agent to get lower bounds. */
428static struct ieee80211_regdomain *country_ie_2_rd(
429 u8 *country_ie,
430 u8 country_ie_len,
431 u32 *checksum)
432{
433 struct ieee80211_regdomain *rd = NULL;
434 unsigned int i = 0;
435 char alpha2[2];
436 u32 flags = 0;
437 u32 num_rules = 0, size_of_regd = 0;
438 u8 *triplets_start = NULL;
439 u8 len_at_triplet = 0;
440 /* the last channel we have registered in a subband (triplet) */
441 int last_sub_max_channel = 0;
442
443 *checksum = 0xDEADBEEF;
444
445 /* Country IE requirements */
446 BUG_ON(country_ie_len < IEEE80211_COUNTRY_IE_MIN_LEN ||
447 country_ie_len & 0x01);
448
449 alpha2[0] = country_ie[0];
450 alpha2[1] = country_ie[1];
451
452 /*
453 * Third octet can be:
454 * 'I' - Indoor
455 * 'O' - Outdoor
456 *
457 * anything else we assume is no restrictions
458 */
459 if (country_ie[2] == 'I')
460 flags = NL80211_RRF_NO_OUTDOOR;
461 else if (country_ie[2] == 'O')
462 flags = NL80211_RRF_NO_INDOOR;
463
464 country_ie += 3;
465 country_ie_len -= 3;
466
467 triplets_start = country_ie;
468 len_at_triplet = country_ie_len;
469
470 *checksum ^= ((flags ^ alpha2[0] ^ alpha2[1]) << 8);
471
472 /* We need to build a reg rule for each triplet, but first we must
473 * calculate the number of reg rules we will need. We will need one
474 * for each channel subband */
475 while (country_ie_len >= 3) {
476 struct ieee80211_country_ie_triplet *triplet =
477 (struct ieee80211_country_ie_triplet *) country_ie;
478 int cur_sub_max_channel = 0, cur_channel = 0;
479
480 if (triplet->ext.reg_extension_id >=
481 IEEE80211_COUNTRY_EXTENSION_ID) {
482 country_ie += 3;
483 country_ie_len -= 3;
484 continue;
485 }
486
487 cur_channel = triplet->chans.first_channel;
488 cur_sub_max_channel = ieee80211_channel_to_frequency(
489 cur_channel + triplet->chans.num_channels);
490
491 /* Basic sanity check */
492 if (cur_sub_max_channel < cur_channel)
493 return NULL;
494
495 /* Do not allow overlapping channels. Also channels
496 * passed in each subband must be monotonically
497 * increasing */
498 if (last_sub_max_channel) {
499 if (cur_channel <= last_sub_max_channel)
500 return NULL;
501 if (cur_sub_max_channel <= last_sub_max_channel)
502 return NULL;
503 }
504
505 /* When dot11RegulatoryClassesRequired is supported
506 * we can throw ext triplets as part of this soup,
507 * for now we don't care when those change as we
508 * don't support them */
509 *checksum ^= ((cur_channel ^ cur_sub_max_channel) << 8) |
510 ((cur_sub_max_channel ^ cur_sub_max_channel) << 16) |
511 ((triplet->chans.max_power ^ cur_sub_max_channel) << 24);
512
513 last_sub_max_channel = cur_sub_max_channel;
514
515 country_ie += 3;
516 country_ie_len -= 3;
517 num_rules++;
518
519 /* Note: this is not a IEEE requirement but
520 * simply a memory requirement */
521 if (num_rules > NL80211_MAX_SUPP_REG_RULES)
522 return NULL;
523 }
524
525 country_ie = triplets_start;
526 country_ie_len = len_at_triplet;
527
528 size_of_regd = sizeof(struct ieee80211_regdomain) +
529 (num_rules * sizeof(struct ieee80211_reg_rule));
530
531 rd = kzalloc(size_of_regd, GFP_KERNEL);
532 if (!rd)
533 return NULL;
534
535 rd->n_reg_rules = num_rules;
536 rd->alpha2[0] = alpha2[0];
537 rd->alpha2[1] = alpha2[1];
538
539 /* This time around we fill in the rd */
540 while (country_ie_len >= 3) {
541 struct ieee80211_country_ie_triplet *triplet =
542 (struct ieee80211_country_ie_triplet *) country_ie;
543 struct ieee80211_reg_rule *reg_rule = NULL;
544 struct ieee80211_freq_range *freq_range = NULL;
545 struct ieee80211_power_rule *power_rule = NULL;
546
547 /* Must parse if dot11RegulatoryClassesRequired is true,
548 * we don't support this yet */
549 if (triplet->ext.reg_extension_id >=
550 IEEE80211_COUNTRY_EXTENSION_ID) {
551 country_ie += 3;
552 country_ie_len -= 3;
553 continue;
554 }
555
556 reg_rule = &rd->reg_rules[i];
557 freq_range = &reg_rule->freq_range;
558 power_rule = &reg_rule->power_rule;
559
560 reg_rule->flags = flags;
561
562 /* The +10 is since the regulatory domain expects
563 * the actual band edge, not the center of freq for
564 * its start and end freqs, assuming 20 MHz bandwidth on
565 * the channels passed */
566 freq_range->start_freq_khz =
567 MHZ_TO_KHZ(ieee80211_channel_to_frequency(
568 triplet->chans.first_channel) - 10);
569 freq_range->end_freq_khz =
570 MHZ_TO_KHZ(ieee80211_channel_to_frequency(
571 triplet->chans.first_channel +
572 triplet->chans.num_channels) + 10);
573
574 /* Large arbitrary values, we intersect later */
575 /* Increment this if we ever support >= 40 MHz channels
576 * in IEEE 802.11 */
577 freq_range->max_bandwidth_khz = MHZ_TO_KHZ(40);
578 power_rule->max_antenna_gain = DBI_TO_MBI(100);
579 power_rule->max_eirp = DBM_TO_MBM(100);
580
581 country_ie += 3;
582 country_ie_len -= 3;
583 i++;
584
585 BUG_ON(i > NL80211_MAX_SUPP_REG_RULES);
586 }
587
588 return rd;
589}
590
591
379/* Helper for regdom_intersect(), this does the real 592/* Helper for regdom_intersect(), this does the real
380 * mathematical intersection fun */ 593 * mathematical intersection fun */
381static int reg_rules_intersect( 594static int reg_rules_intersect(
@@ -603,12 +816,23 @@ static void handle_band(struct ieee80211_supported_band *sband)
603 handle_channel(&sband->channels[i]); 816 handle_channel(&sband->channels[i]);
604} 817}
605 818
819static bool ignore_reg_update(struct wiphy *wiphy, enum reg_set_by setby)
820{
821 if (!last_request)
822 return true;
823 if (setby == REGDOM_SET_BY_CORE &&
824 wiphy->fw_handles_regulatory)
825 return true;
826 return false;
827}
828
606static void update_all_wiphy_regulatory(enum reg_set_by setby) 829static void update_all_wiphy_regulatory(enum reg_set_by setby)
607{ 830{
608 struct cfg80211_registered_device *drv; 831 struct cfg80211_registered_device *drv;
609 832
610 list_for_each_entry(drv, &cfg80211_drv_list, list) 833 list_for_each_entry(drv, &cfg80211_drv_list, list)
611 wiphy_update_regulatory(&drv->wiphy, setby); 834 if (!ignore_reg_update(&drv->wiphy, setby))
835 wiphy_update_regulatory(&drv->wiphy, setby);
612} 836}
613 837
614void wiphy_update_regulatory(struct wiphy *wiphy, enum reg_set_by setby) 838void wiphy_update_regulatory(struct wiphy *wiphy, enum reg_set_by setby)
@@ -660,16 +884,14 @@ static int ignore_request(struct wiphy *wiphy, enum reg_set_by set_by,
660 return -EOPNOTSUPP; 884 return -EOPNOTSUPP;
661 return -EALREADY; 885 return -EALREADY;
662 } 886 }
663 /* Two consecutive Country IE hints on the same wiphy */ 887 /* Two consecutive Country IE hints on the same wiphy.
664 if (!alpha2_equal(cfg80211_regdomain->alpha2, alpha2)) 888 * This should be picked up early by the driver/stack */
889 if (WARN_ON(!alpha2_equal(cfg80211_regdomain->alpha2,
890 alpha2)))
665 return 0; 891 return 0;
666 return -EALREADY; 892 return -EALREADY;
667 } 893 }
668 /* 894 return REG_INTERSECT;
669 * Ignore Country IE hints for now, need to think about
670 * what we need to do to support multi-domain operation.
671 */
672 return -EOPNOTSUPP;
673 case REGDOM_SET_BY_DRIVER: 895 case REGDOM_SET_BY_DRIVER:
674 if (last_request->initiator == REGDOM_SET_BY_DRIVER) 896 if (last_request->initiator == REGDOM_SET_BY_DRIVER)
675 return -EALREADY; 897 return -EALREADY;
@@ -677,6 +899,11 @@ static int ignore_request(struct wiphy *wiphy, enum reg_set_by set_by,
677 case REGDOM_SET_BY_USER: 899 case REGDOM_SET_BY_USER:
678 if (last_request->initiator == REGDOM_SET_BY_COUNTRY_IE) 900 if (last_request->initiator == REGDOM_SET_BY_COUNTRY_IE)
679 return REG_INTERSECT; 901 return REG_INTERSECT;
902 /* If the user knows better the user should set the regdom
903 * to their country before the IE is picked up */
904 if (last_request->initiator == REGDOM_SET_BY_USER &&
905 last_request->intersect)
906 return -EOPNOTSUPP;
680 return 0; 907 return 0;
681 } 908 }
682 909
@@ -685,7 +912,9 @@ static int ignore_request(struct wiphy *wiphy, enum reg_set_by set_by,
685 912
686/* Caller must hold &cfg80211_drv_mutex */ 913/* Caller must hold &cfg80211_drv_mutex */
687int __regulatory_hint(struct wiphy *wiphy, enum reg_set_by set_by, 914int __regulatory_hint(struct wiphy *wiphy, enum reg_set_by set_by,
688 const char *alpha2) 915 const char *alpha2,
916 u32 country_ie_checksum,
917 enum environment_cap env)
689{ 918{
690 struct regulatory_request *request; 919 struct regulatory_request *request;
691 bool intersect = false; 920 bool intersect = false;
@@ -698,36 +927,32 @@ int __regulatory_hint(struct wiphy *wiphy, enum reg_set_by set_by,
698 else if (r) 927 else if (r)
699 return r; 928 return r;
700 929
701 switch (set_by) { 930 request = kzalloc(sizeof(struct regulatory_request),
702 case REGDOM_SET_BY_CORE: 931 GFP_KERNEL);
703 case REGDOM_SET_BY_COUNTRY_IE: 932 if (!request)
704 case REGDOM_SET_BY_DRIVER: 933 return -ENOMEM;
705 case REGDOM_SET_BY_USER:
706 request = kzalloc(sizeof(struct regulatory_request),
707 GFP_KERNEL);
708 if (!request)
709 return -ENOMEM;
710
711 request->alpha2[0] = alpha2[0];
712 request->alpha2[1] = alpha2[1];
713 request->initiator = set_by;
714 request->wiphy = wiphy;
715 request->intersect = intersect;
716
717 kfree(last_request);
718 last_request = request;
719 r = call_crda(alpha2);
720#ifndef CONFIG_WIRELESS_OLD_REGULATORY
721 if (r)
722 printk(KERN_ERR "cfg80211: Failed calling CRDA\n");
723#endif
724 break;
725 default:
726 r = -ENOTSUPP;
727 break;
728 }
729 934
730 return r; 935 request->alpha2[0] = alpha2[0];
936 request->alpha2[1] = alpha2[1];
937 request->initiator = set_by;
938 request->wiphy = wiphy;
939 request->intersect = intersect;
940 request->country_ie_checksum = country_ie_checksum;
941 request->country_ie_env = env;
942
943 kfree(last_request);
944 last_request = request;
945 /*
946 * Note: When CONFIG_WIRELESS_OLD_REGULATORY is enabled
947 * AND if CRDA is NOT present nothing will happen, if someone
948 * wants to bother with 11d with OLD_REG you can add a timer.
949 * If after x amount of time nothing happens you can call:
950 *
951 * return set_regdom(country_ie_regdomain);
952 *
953 * to intersect with the static rd
954 */
955 return call_crda(alpha2);
731} 956}
732 957
733void regulatory_hint(struct wiphy *wiphy, const char *alpha2) 958void regulatory_hint(struct wiphy *wiphy, const char *alpha2)
@@ -735,11 +960,120 @@ void regulatory_hint(struct wiphy *wiphy, const char *alpha2)
735 BUG_ON(!alpha2); 960 BUG_ON(!alpha2);
736 961
737 mutex_lock(&cfg80211_drv_mutex); 962 mutex_lock(&cfg80211_drv_mutex);
738 __regulatory_hint(wiphy, REGDOM_SET_BY_DRIVER, alpha2); 963 __regulatory_hint(wiphy, REGDOM_SET_BY_DRIVER, alpha2, 0, ENVIRON_ANY);
739 mutex_unlock(&cfg80211_drv_mutex); 964 mutex_unlock(&cfg80211_drv_mutex);
740} 965}
741EXPORT_SYMBOL(regulatory_hint); 966EXPORT_SYMBOL(regulatory_hint);
742 967
968static bool reg_same_country_ie_hint(struct wiphy *wiphy,
969 u32 country_ie_checksum)
970{
971 if (!last_request->wiphy)
972 return false;
973 if (likely(last_request->wiphy != wiphy))
974 return !country_ie_integrity_changes(country_ie_checksum);
975 /* We should not have let these through at this point, they
976 * should have been picked up earlier by the first alpha2 check
977 * on the device */
978 if (WARN_ON(!country_ie_integrity_changes(country_ie_checksum)))
979 return true;
980 return false;
981}
982
983void regulatory_hint_11d(struct wiphy *wiphy,
984 u8 *country_ie,
985 u8 country_ie_len)
986{
987 struct ieee80211_regdomain *rd = NULL;
988 char alpha2[2];
989 u32 checksum = 0;
990 enum environment_cap env = ENVIRON_ANY;
991
992 mutex_lock(&cfg80211_drv_mutex);
993
994 /* IE len must be evenly divisible by 2 */
995 if (country_ie_len & 0x01)
996 goto out;
997
998 if (country_ie_len < IEEE80211_COUNTRY_IE_MIN_LEN)
999 goto out;
1000
1001 /* Pending country IE processing, this can happen after we
1002 * call CRDA and wait for a response if a beacon was received before
1003 * we were able to process the last regulatory_hint_11d() call */
1004 if (country_ie_regdomain)
1005 goto out;
1006
1007 alpha2[0] = country_ie[0];
1008 alpha2[1] = country_ie[1];
1009
1010 if (country_ie[2] == 'I')
1011 env = ENVIRON_INDOOR;
1012 else if (country_ie[2] == 'O')
1013 env = ENVIRON_OUTDOOR;
1014
1015 /* We will run this for *every* beacon processed for the BSSID, so
1016 * we optimize an early check to exit out early if we don't have to
1017 * do anything */
1018 if (likely(last_request->wiphy)) {
1019 struct cfg80211_registered_device *drv_last_ie;
1020
1021 drv_last_ie = wiphy_to_dev(last_request->wiphy);
1022
1023 /* Lets keep this simple -- we trust the first AP
1024 * after we intersect with CRDA */
1025 if (likely(last_request->wiphy == wiphy)) {
1026 /* Ignore IEs coming in on this wiphy with
1027 * the same alpha2 and environment cap */
1028 if (likely(alpha2_equal(drv_last_ie->country_ie_alpha2,
1029 alpha2) &&
1030 env == drv_last_ie->env)) {
1031 goto out;
1032 }
1033 /* the wiphy moved on to another BSSID or the AP
1034 * was reconfigured. XXX: We need to deal with the
1035 * case where the user suspends and goes to goes
1036 * to another country, and then gets IEs from an
1037 * AP with different settings */
1038 goto out;
1039 } else {
1040 /* Ignore IEs coming in on two separate wiphys with
1041 * the same alpha2 and environment cap */
1042 if (likely(alpha2_equal(drv_last_ie->country_ie_alpha2,
1043 alpha2) &&
1044 env == drv_last_ie->env)) {
1045 goto out;
1046 }
1047 /* We could potentially intersect though */
1048 goto out;
1049 }
1050 }
1051
1052 rd = country_ie_2_rd(country_ie, country_ie_len, &checksum);
1053 if (!rd)
1054 goto out;
1055
1056 /* This will not happen right now but we leave it here for the
1057 * the future when we want to add suspend/resume support and having
1058 * the user move to another country after doing so, or having the user
1059 * move to another AP. Right now we just trust the first AP. This is why
1060 * this is marked as likley(). If we hit this before we add this support
1061 * we want to be informed of it as it would indicate a mistake in the
1062 * current design */
1063 if (likely(WARN_ON(reg_same_country_ie_hint(wiphy, checksum))))
1064 goto out;
1065
1066 /* We keep this around for when CRDA comes back with a response so
1067 * we can intersect with that */
1068 country_ie_regdomain = rd;
1069
1070 __regulatory_hint(wiphy, REGDOM_SET_BY_COUNTRY_IE,
1071 country_ie_regdomain->alpha2, checksum, env);
1072
1073out:
1074 mutex_unlock(&cfg80211_drv_mutex);
1075}
1076EXPORT_SYMBOL(regulatory_hint_11d);
743 1077
744static void print_rd_rules(const struct ieee80211_regdomain *rd) 1078static void print_rd_rules(const struct ieee80211_regdomain *rd)
745{ 1079{
@@ -779,7 +1113,25 @@ static void print_rd_rules(const struct ieee80211_regdomain *rd)
779static void print_regdomain(const struct ieee80211_regdomain *rd) 1113static void print_regdomain(const struct ieee80211_regdomain *rd)
780{ 1114{
781 1115
782 if (is_world_regdom(rd->alpha2)) 1116 if (is_intersected_alpha2(rd->alpha2)) {
1117 struct wiphy *wiphy = NULL;
1118 struct cfg80211_registered_device *drv;
1119
1120 if (last_request->initiator == REGDOM_SET_BY_COUNTRY_IE) {
1121 if (last_request->wiphy) {
1122 wiphy = last_request->wiphy;
1123 drv = wiphy_to_dev(wiphy);
1124 printk(KERN_INFO "cfg80211: Current regulatory "
1125 "domain updated by AP to: %c%c\n",
1126 drv->country_ie_alpha2[0],
1127 drv->country_ie_alpha2[1]);
1128 } else
1129 printk(KERN_INFO "cfg80211: Current regulatory "
1130 "domain intersected: \n");
1131 } else
1132 printk(KERN_INFO "cfg80211: Current regulatory "
1133 "intersected: \n");
1134 } else if (is_world_regdom(rd->alpha2))
783 printk(KERN_INFO "cfg80211: World regulatory " 1135 printk(KERN_INFO "cfg80211: World regulatory "
784 "domain updated:\n"); 1136 "domain updated:\n");
785 else { 1137 else {
@@ -802,10 +1154,39 @@ static void print_regdomain_info(const struct ieee80211_regdomain *rd)
802 print_rd_rules(rd); 1154 print_rd_rules(rd);
803} 1155}
804 1156
1157#ifdef CONFIG_CFG80211_REG_DEBUG
1158static void reg_country_ie_process_debug(
1159 const struct ieee80211_regdomain *rd,
1160 const struct ieee80211_regdomain *country_ie_regdomain,
1161 const struct ieee80211_regdomain *intersected_rd)
1162{
1163 printk(KERN_DEBUG "cfg80211: Received country IE:\n");
1164 print_regdomain_info(country_ie_regdomain);
1165 printk(KERN_DEBUG "cfg80211: CRDA thinks this should applied:\n");
1166 print_regdomain_info(rd);
1167 if (intersected_rd) {
1168 printk(KERN_DEBUG "cfg80211: We intersect both of these "
1169 "and get:\n");
1170 print_regdomain_info(rd);
1171 return;
1172 }
1173 printk(KERN_DEBUG "cfg80211: Intersection between both failed\n");
1174}
1175#else
1176static inline void reg_country_ie_process_debug(
1177 const struct ieee80211_regdomain *rd,
1178 const struct ieee80211_regdomain *country_ie_regdomain,
1179 const struct ieee80211_regdomain *intersected_rd)
1180{
1181}
1182#endif
1183
805/* Takes ownership of rd only if it doesn't fail */ 1184/* Takes ownership of rd only if it doesn't fail */
806static int __set_regdom(const struct ieee80211_regdomain *rd) 1185static int __set_regdom(const struct ieee80211_regdomain *rd)
807{ 1186{
808 const struct ieee80211_regdomain *intersected_rd = NULL; 1187 const struct ieee80211_regdomain *intersected_rd = NULL;
1188 struct cfg80211_registered_device *drv = NULL;
1189 struct wiphy *wiphy = NULL;
809 /* Some basic sanity checks first */ 1190 /* Some basic sanity checks first */
810 1191
811 if (is_world_regdom(rd->alpha2)) { 1192 if (is_world_regdom(rd->alpha2)) {
@@ -822,10 +1203,18 @@ static int __set_regdom(const struct ieee80211_regdomain *rd)
822 if (!last_request) 1203 if (!last_request)
823 return -EINVAL; 1204 return -EINVAL;
824 1205
825 /* allow overriding the static definitions if CRDA is present */ 1206 /* Lets only bother proceeding on the same alpha2 if the current
826 if (!is_old_static_regdom(cfg80211_regdomain) && 1207 * rd is non static (it means CRDA was present and was used last)
827 !regdom_changed(rd->alpha2)) 1208 * and the pending request came in from a country IE */
828 return -EINVAL; 1209 if (last_request->initiator != REGDOM_SET_BY_COUNTRY_IE) {
1210 /* If someone else asked us to change the rd lets only bother
1211 * checking if the alpha2 changes if CRDA was already called */
1212 if (!is_old_static_regdom(cfg80211_regdomain) &&
1213 !regdom_changed(rd->alpha2))
1214 return -EINVAL;
1215 }
1216
1217 wiphy = last_request->wiphy;
829 1218
830 /* Now lets set the regulatory domain, update all driver channels 1219 /* Now lets set the regulatory domain, update all driver channels
831 * and finally inform them of what we have done, in case they want 1220 * and finally inform them of what we have done, in case they want
@@ -835,36 +1224,78 @@ static int __set_regdom(const struct ieee80211_regdomain *rd)
835 if (WARN_ON(!reg_is_valid_request(rd->alpha2))) 1224 if (WARN_ON(!reg_is_valid_request(rd->alpha2)))
836 return -EINVAL; 1225 return -EINVAL;
837 1226
838 reset_regdomains(); 1227 if (!is_valid_rd(rd)) {
1228 printk(KERN_ERR "cfg80211: Invalid "
1229 "regulatory domain detected:\n");
1230 print_regdomain_info(rd);
1231 return -EINVAL;
1232 }
839 1233
840 /* Country IE parsing coming soon */ 1234 if (!last_request->intersect) {
841 switch (last_request->initiator) { 1235 reset_regdomains();
842 case REGDOM_SET_BY_CORE: 1236 cfg80211_regdomain = rd;
843 case REGDOM_SET_BY_DRIVER: 1237 return 0;
844 case REGDOM_SET_BY_USER:
845 if (!is_valid_rd(rd)) {
846 printk(KERN_ERR "cfg80211: Invalid "
847 "regulatory domain detected:\n");
848 print_regdomain_info(rd);
849 return -EINVAL;
850 }
851 break;
852 case REGDOM_SET_BY_COUNTRY_IE: /* Not yet */
853 WARN_ON(1);
854 default:
855 return -EOPNOTSUPP;
856 } 1238 }
857 1239
858 if (unlikely(last_request->intersect)) { 1240 /* Intersection requires a bit more work */
1241
1242 if (last_request->initiator != REGDOM_SET_BY_COUNTRY_IE) {
1243
859 intersected_rd = regdom_intersect(rd, cfg80211_regdomain); 1244 intersected_rd = regdom_intersect(rd, cfg80211_regdomain);
860 if (!intersected_rd) 1245 if (!intersected_rd)
861 return -EINVAL; 1246 return -EINVAL;
1247
1248 /* We can trash what CRDA provided now */
862 kfree(rd); 1249 kfree(rd);
863 rd = intersected_rd; 1250 rd = NULL;
1251
1252 reset_regdomains();
1253 cfg80211_regdomain = intersected_rd;
1254
1255 return 0;
864 } 1256 }
865 1257
866 /* Tada! */ 1258 /*
867 cfg80211_regdomain = rd; 1259 * Country IE requests are handled a bit differently, we intersect
1260 * the country IE rd with what CRDA believes that country should have
1261 */
1262
1263 BUG_ON(!country_ie_regdomain);
1264
1265 if (rd != country_ie_regdomain) {
1266 /* Intersect what CRDA returned and our what we
1267 * had built from the Country IE received */
1268
1269 intersected_rd = regdom_intersect(rd, country_ie_regdomain);
1270
1271 reg_country_ie_process_debug(rd, country_ie_regdomain,
1272 intersected_rd);
1273
1274 kfree(country_ie_regdomain);
1275 country_ie_regdomain = NULL;
1276 } else {
1277 /* This would happen when CRDA was not present and
1278 * OLD_REGULATORY was enabled. We intersect our Country
1279 * IE rd and what was set on cfg80211 originally */
1280 intersected_rd = regdom_intersect(rd, cfg80211_regdomain);
1281 }
1282
1283 if (!intersected_rd)
1284 return -EINVAL;
1285
1286 drv = wiphy_to_dev(wiphy);
1287
1288 drv->country_ie_alpha2[0] = rd->alpha2[0];
1289 drv->country_ie_alpha2[1] = rd->alpha2[1];
1290 drv->env = last_request->country_ie_env;
1291
1292 BUG_ON(intersected_rd == rd);
1293
1294 kfree(rd);
1295 rd = NULL;
1296
1297 reset_regdomains();
1298 cfg80211_regdomain = intersected_rd;
868 1299
869 return 0; 1300 return 0;
870} 1301}
@@ -885,16 +1316,28 @@ int set_regdom(const struct ieee80211_regdomain *rd)
885 } 1316 }
886 1317
887 /* This would make this whole thing pointless */ 1318 /* This would make this whole thing pointless */
888 BUG_ON(rd != cfg80211_regdomain); 1319 if (!last_request->intersect)
1320 BUG_ON(rd != cfg80211_regdomain);
889 1321
890 /* update all wiphys now with the new established regulatory domain */ 1322 /* update all wiphys now with the new established regulatory domain */
891 update_all_wiphy_regulatory(last_request->initiator); 1323 update_all_wiphy_regulatory(last_request->initiator);
892 1324
893 print_regdomain(rd); 1325 print_regdomain(cfg80211_regdomain);
894 1326
895 return r; 1327 return r;
896} 1328}
897 1329
1330/* Caller must hold cfg80211_drv_mutex */
1331void reg_device_remove(struct wiphy *wiphy)
1332{
1333 if (!last_request->wiphy)
1334 return;
1335 if (last_request->wiphy != wiphy)
1336 return;
1337 last_request->wiphy = NULL;
1338 last_request->country_ie_env = ENVIRON_ANY;
1339}
1340
898int regulatory_init(void) 1341int regulatory_init(void)
899{ 1342{
900 int err; 1343 int err;
@@ -914,11 +1357,11 @@ int regulatory_init(void)
914 * that is not a valid ISO / IEC 3166 alpha2 */ 1357 * that is not a valid ISO / IEC 3166 alpha2 */
915 if (ieee80211_regdom[0] != 'E' || ieee80211_regdom[1] != 'U') 1358 if (ieee80211_regdom[0] != 'E' || ieee80211_regdom[1] != 'U')
916 err = __regulatory_hint(NULL, REGDOM_SET_BY_CORE, 1359 err = __regulatory_hint(NULL, REGDOM_SET_BY_CORE,
917 ieee80211_regdom); 1360 ieee80211_regdom, 0, ENVIRON_ANY);
918#else 1361#else
919 cfg80211_regdomain = cfg80211_world_regdom; 1362 cfg80211_regdomain = cfg80211_world_regdom;
920 1363
921 err = __regulatory_hint(NULL, REGDOM_SET_BY_CORE, "00"); 1364 err = __regulatory_hint(NULL, REGDOM_SET_BY_CORE, "00", 0, ENVIRON_ANY);
922 if (err) 1365 if (err)
923 printk(KERN_ERR "cfg80211: calling CRDA failed - " 1366 printk(KERN_ERR "cfg80211: calling CRDA failed - "
924 "unable to update world regulatory domain, " 1367 "unable to update world regulatory domain, "
@@ -934,6 +1377,9 @@ void regulatory_exit(void)
934 1377
935 reset_regdomains(); 1378 reset_regdomains();
936 1379
1380 kfree(country_ie_regdomain);
1381 country_ie_regdomain = NULL;
1382
937 kfree(last_request); 1383 kfree(last_request);
938 1384
939 platform_device_unregister(reg_pdev); 1385 platform_device_unregister(reg_pdev);
diff --git a/net/wireless/reg.h b/net/wireless/reg.h
index c9b6b6358bbe..a76ea3ff7cd6 100644
--- a/net/wireless/reg.h
+++ b/net/wireless/reg.h
@@ -4,28 +4,41 @@
4bool is_world_regdom(const char *alpha2); 4bool is_world_regdom(const char *alpha2);
5bool reg_is_valid_request(const char *alpha2); 5bool reg_is_valid_request(const char *alpha2);
6 6
7void reg_device_remove(struct wiphy *wiphy);
8
7int regulatory_init(void); 9int regulatory_init(void);
8void regulatory_exit(void); 10void regulatory_exit(void);
9 11
10int set_regdom(const struct ieee80211_regdomain *rd); 12int set_regdom(const struct ieee80211_regdomain *rd);
11 13
14enum environment_cap {
15 ENVIRON_ANY,
16 ENVIRON_INDOOR,
17 ENVIRON_OUTDOOR,
18};
19
20
12/** 21/**
13 * __regulatory_hint - hint to the wireless core a regulatory domain 22 * __regulatory_hint - hint to the wireless core a regulatory domain
14 * @wiphy: if the hint comes from country information from an AP, this 23 * @wiphy: if the hint comes from country information from an AP, this
15 * is required to be set to the wiphy that received the information 24 * is required to be set to the wiphy that received the information
16 * @alpha2: the ISO/IEC 3166 alpha2 being claimed the regulatory domain 25 * @alpha2: the ISO/IEC 3166 alpha2 being claimed the regulatory domain
17 * should be in. 26 * should be in.
27 * @country_ie_checksum: checksum of processed country IE, set this to 0
28 * if the hint did not come from a country IE
29 * @country_ie_env: the environment the IE told us we are in, %ENVIRON_*
18 * 30 *
19 * The Wireless subsystem can use this function to hint to the wireless core 31 * The Wireless subsystem can use this function to hint to the wireless core
20 * what it believes should be the current regulatory domain by 32 * what it believes should be the current regulatory domain by giving it an
21 * giving it an ISO/IEC 3166 alpha2 country code it knows its regulatory 33 * ISO/IEC 3166 alpha2 country code it knows its regulatory domain should be
22 * domain should be in. 34 * in.
23 * 35 *
24 * Returns zero if all went fine, %-EALREADY if a regulatory domain had 36 * Returns zero if all went fine, %-EALREADY if a regulatory domain had
25 * already been set or other standard error codes. 37 * already been set or other standard error codes.
26 * 38 *
27 */ 39 */
28extern int __regulatory_hint(struct wiphy *wiphy, enum reg_set_by set_by, 40extern int __regulatory_hint(struct wiphy *wiphy, enum reg_set_by set_by,
29 const char *alpha2); 41 const char *alpha2, u32 country_ie_checksum,
42 enum environment_cap country_ie_env);
30 43
31#endif /* __NET_WIRELESS_REG_H */ 44#endif /* __NET_WIRELESS_REG_H */