aboutsummaryrefslogtreecommitdiffstats
path: root/net/wireless
diff options
context:
space:
mode:
Diffstat (limited to 'net/wireless')
-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.c2
-rw-r--r--net/wireless/reg.c479
-rw-r--r--net/wireless/reg.h21
6 files changed, 507 insertions, 24 deletions
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..00121ceddb14 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -1760,7 +1760,7 @@ static int nl80211_req_set_reg(struct sk_buff *skb, struct genl_info *info)
1760 return -EINVAL; 1760 return -EINVAL;
1761#endif 1761#endif
1762 mutex_lock(&cfg80211_drv_mutex); 1762 mutex_lock(&cfg80211_drv_mutex);
1763 r = __regulatory_hint(NULL, REGDOM_SET_BY_USER, data); 1763 r = __regulatory_hint(NULL, REGDOM_SET_BY_USER, data, 0, ENVIRON_ANY);
1764 mutex_unlock(&cfg80211_drv_mutex); 1764 mutex_unlock(&cfg80211_drv_mutex);
1765 return r; 1765 return r;
1766} 1766}
diff --git a/net/wireless/reg.c b/net/wireless/reg.c
index f0ff3d1779da..4dab993ea488 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)
@@ -379,6 +421,174 @@ static u32 freq_max_bandwidth(const struct ieee80211_freq_range *freq_range,
379 return 0; 421 return 0;
380} 422}
381 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
382/* Helper for regdom_intersect(), this does the real 592/* Helper for regdom_intersect(), this does the real
383 * mathematical intersection fun */ 593 * mathematical intersection fun */
384static int reg_rules_intersect( 594static int reg_rules_intersect(
@@ -663,16 +873,14 @@ static int ignore_request(struct wiphy *wiphy, enum reg_set_by set_by,
663 return -EOPNOTSUPP; 873 return -EOPNOTSUPP;
664 return -EALREADY; 874 return -EALREADY;
665 } 875 }
666 /* Two consecutive Country IE hints on the same wiphy */ 876 /* Two consecutive Country IE hints on the same wiphy.
667 if (!alpha2_equal(cfg80211_regdomain->alpha2, alpha2)) 877 * This should be picked up early by the driver/stack */
878 if (WARN_ON(!alpha2_equal(cfg80211_regdomain->alpha2,
879 alpha2)))
668 return 0; 880 return 0;
669 return -EALREADY; 881 return -EALREADY;
670 } 882 }
671 /* 883 return REG_INTERSECT;
672 * Ignore Country IE hints for now, need to think about
673 * what we need to do to support multi-domain operation.
674 */
675 return -EOPNOTSUPP;
676 case REGDOM_SET_BY_DRIVER: 884 case REGDOM_SET_BY_DRIVER:
677 if (last_request->initiator == REGDOM_SET_BY_DRIVER) 885 if (last_request->initiator == REGDOM_SET_BY_DRIVER)
678 return -EALREADY; 886 return -EALREADY;
@@ -680,6 +888,11 @@ static int ignore_request(struct wiphy *wiphy, enum reg_set_by set_by,
680 case REGDOM_SET_BY_USER: 888 case REGDOM_SET_BY_USER:
681 if (last_request->initiator == REGDOM_SET_BY_COUNTRY_IE) 889 if (last_request->initiator == REGDOM_SET_BY_COUNTRY_IE)
682 return REG_INTERSECT; 890 return REG_INTERSECT;
891 /* If the user knows better the user should set the regdom
892 * to their country before the IE is picked up */
893 if (last_request->initiator == REGDOM_SET_BY_USER &&
894 last_request->intersect)
895 return -EOPNOTSUPP;
683 return 0; 896 return 0;
684 } 897 }
685 898
@@ -688,7 +901,9 @@ static int ignore_request(struct wiphy *wiphy, enum reg_set_by set_by,
688 901
689/* Caller must hold &cfg80211_drv_mutex */ 902/* Caller must hold &cfg80211_drv_mutex */
690int __regulatory_hint(struct wiphy *wiphy, enum reg_set_by set_by, 903int __regulatory_hint(struct wiphy *wiphy, enum reg_set_by set_by,
691 const char *alpha2) 904 const char *alpha2,
905 u32 country_ie_checksum,
906 enum environment_cap env)
692{ 907{
693 struct regulatory_request *request; 908 struct regulatory_request *request;
694 bool intersect = false; 909 bool intersect = false;
@@ -711,9 +926,21 @@ int __regulatory_hint(struct wiphy *wiphy, enum reg_set_by set_by,
711 request->initiator = set_by; 926 request->initiator = set_by;
712 request->wiphy = wiphy; 927 request->wiphy = wiphy;
713 request->intersect = intersect; 928 request->intersect = intersect;
929 request->country_ie_checksum = country_ie_checksum;
930 request->country_ie_env = env;
714 931
715 kfree(last_request); 932 kfree(last_request);
716 last_request = request; 933 last_request = request;
934 /*
935 * Note: When CONFIG_WIRELESS_OLD_REGULATORY is enabled
936 * AND if CRDA is NOT present nothing will happen, if someone
937 * wants to bother with 11d with OLD_REG you can add a timer.
938 * If after x amount of time nothing happens you can call:
939 *
940 * return set_regdom(country_ie_regdomain);
941 *
942 * to intersect with the static rd
943 */
717 return call_crda(alpha2); 944 return call_crda(alpha2);
718} 945}
719 946
@@ -722,11 +949,120 @@ void regulatory_hint(struct wiphy *wiphy, const char *alpha2)
722 BUG_ON(!alpha2); 949 BUG_ON(!alpha2);
723 950
724 mutex_lock(&cfg80211_drv_mutex); 951 mutex_lock(&cfg80211_drv_mutex);
725 __regulatory_hint(wiphy, REGDOM_SET_BY_DRIVER, alpha2); 952 __regulatory_hint(wiphy, REGDOM_SET_BY_DRIVER, alpha2, 0, ENVIRON_ANY);
726 mutex_unlock(&cfg80211_drv_mutex); 953 mutex_unlock(&cfg80211_drv_mutex);
727} 954}
728EXPORT_SYMBOL(regulatory_hint); 955EXPORT_SYMBOL(regulatory_hint);
729 956
957static bool reg_same_country_ie_hint(struct wiphy *wiphy,
958 u32 country_ie_checksum)
959{
960 if (!last_request->wiphy)
961 return false;
962 if (likely(last_request->wiphy != wiphy))
963 return !country_ie_integrity_changes(country_ie_checksum);
964 /* We should not have let these through at this point, they
965 * should have been picked up earlier by the first alpha2 check
966 * on the device */
967 if (WARN_ON(!country_ie_integrity_changes(country_ie_checksum)))
968 return true;
969 return false;
970}
971
972void regulatory_hint_11d(struct wiphy *wiphy,
973 u8 *country_ie,
974 u8 country_ie_len)
975{
976 struct ieee80211_regdomain *rd = NULL;
977 char alpha2[2];
978 u32 checksum = 0;
979 enum environment_cap env = ENVIRON_ANY;
980
981 mutex_lock(&cfg80211_drv_mutex);
982
983 /* IE len must be evenly divisible by 2 */
984 if (country_ie_len & 0x01)
985 goto out;
986
987 if (country_ie_len < IEEE80211_COUNTRY_IE_MIN_LEN)
988 goto out;
989
990 /* Pending country IE processing, this can happen after we
991 * call CRDA and wait for a response if a beacon was received before
992 * we were able to process the last regulatory_hint_11d() call */
993 if (country_ie_regdomain)
994 goto out;
995
996 alpha2[0] = country_ie[0];
997 alpha2[1] = country_ie[1];
998
999 if (country_ie[2] == 'I')
1000 env = ENVIRON_INDOOR;
1001 else if (country_ie[2] == 'O')
1002 env = ENVIRON_OUTDOOR;
1003
1004 /* We will run this for *every* beacon processed for the BSSID, so
1005 * we optimize an early check to exit out early if we don't have to
1006 * do anything */
1007 if (likely(last_request->wiphy)) {
1008 struct cfg80211_registered_device *drv_last_ie;
1009
1010 drv_last_ie = wiphy_to_dev(last_request->wiphy);
1011
1012 /* Lets keep this simple -- we trust the first AP
1013 * after we intersect with CRDA */
1014 if (likely(last_request->wiphy == wiphy)) {
1015 /* Ignore IEs coming in on this wiphy with
1016 * the same alpha2 and environment cap */
1017 if (likely(alpha2_equal(drv_last_ie->country_ie_alpha2,
1018 alpha2) &&
1019 env == drv_last_ie->env)) {
1020 goto out;
1021 }
1022 /* the wiphy moved on to another BSSID or the AP
1023 * was reconfigured. XXX: We need to deal with the
1024 * case where the user suspends and goes to goes
1025 * to another country, and then gets IEs from an
1026 * AP with different settings */
1027 goto out;
1028 } else {
1029 /* Ignore IEs coming in on two separate wiphys with
1030 * the same alpha2 and environment cap */
1031 if (likely(alpha2_equal(drv_last_ie->country_ie_alpha2,
1032 alpha2) &&
1033 env == drv_last_ie->env)) {
1034 goto out;
1035 }
1036 /* We could potentially intersect though */
1037 goto out;
1038 }
1039 }
1040
1041 rd = country_ie_2_rd(country_ie, country_ie_len, &checksum);
1042 if (!rd)
1043 goto out;
1044
1045 /* This will not happen right now but we leave it here for the
1046 * the future when we want to add suspend/resume support and having
1047 * the user move to another country after doing so, or having the user
1048 * move to another AP. Right now we just trust the first AP. This is why
1049 * this is marked as likley(). If we hit this before we add this support
1050 * we want to be informed of it as it would indicate a mistake in the
1051 * current design */
1052 if (likely(WARN_ON(reg_same_country_ie_hint(wiphy, checksum))))
1053 goto out;
1054
1055 /* We keep this around for when CRDA comes back with a response so
1056 * we can intersect with that */
1057 country_ie_regdomain = rd;
1058
1059 __regulatory_hint(wiphy, REGDOM_SET_BY_COUNTRY_IE,
1060 country_ie_regdomain->alpha2, checksum, env);
1061
1062out:
1063 mutex_unlock(&cfg80211_drv_mutex);
1064}
1065EXPORT_SYMBOL(regulatory_hint_11d);
730 1066
731static void print_rd_rules(const struct ieee80211_regdomain *rd) 1067static void print_rd_rules(const struct ieee80211_regdomain *rd)
732{ 1068{
@@ -766,7 +1102,25 @@ static void print_rd_rules(const struct ieee80211_regdomain *rd)
766static void print_regdomain(const struct ieee80211_regdomain *rd) 1102static void print_regdomain(const struct ieee80211_regdomain *rd)
767{ 1103{
768 1104
769 if (is_world_regdom(rd->alpha2)) 1105 if (is_intersected_alpha2(rd->alpha2)) {
1106 struct wiphy *wiphy = NULL;
1107 struct cfg80211_registered_device *drv;
1108
1109 if (last_request->initiator == REGDOM_SET_BY_COUNTRY_IE) {
1110 if (last_request->wiphy) {
1111 wiphy = last_request->wiphy;
1112 drv = wiphy_to_dev(wiphy);
1113 printk(KERN_INFO "cfg80211: Current regulatory "
1114 "domain updated by AP to: %c%c\n",
1115 drv->country_ie_alpha2[0],
1116 drv->country_ie_alpha2[1]);
1117 } else
1118 printk(KERN_INFO "cfg80211: Current regulatory "
1119 "domain intersected: \n");
1120 } else
1121 printk(KERN_INFO "cfg80211: Current regulatory "
1122 "intersected: \n");
1123 } else if (is_world_regdom(rd->alpha2))
770 printk(KERN_INFO "cfg80211: World regulatory " 1124 printk(KERN_INFO "cfg80211: World regulatory "
771 "domain updated:\n"); 1125 "domain updated:\n");
772 else { 1126 else {
@@ -789,10 +1143,39 @@ static void print_regdomain_info(const struct ieee80211_regdomain *rd)
789 print_rd_rules(rd); 1143 print_rd_rules(rd);
790} 1144}
791 1145
1146#ifdef CONFIG_CFG80211_REG_DEBUG
1147static void reg_country_ie_process_debug(
1148 const struct ieee80211_regdomain *rd,
1149 const struct ieee80211_regdomain *country_ie_regdomain,
1150 const struct ieee80211_regdomain *intersected_rd)
1151{
1152 printk(KERN_DEBUG "cfg80211: Received country IE:\n");
1153 print_regdomain_info(country_ie_regdomain);
1154 printk(KERN_DEBUG "cfg80211: CRDA thinks this should applied:\n");
1155 print_regdomain_info(rd);
1156 if (intersected_rd) {
1157 printk(KERN_DEBUG "cfg80211: We intersect both of these "
1158 "and get:\n");
1159 print_regdomain_info(rd);
1160 return;
1161 }
1162 printk(KERN_DEBUG "cfg80211: Intersection between both failed\n");
1163}
1164#else
1165static inline void reg_country_ie_process_debug(
1166 const struct ieee80211_regdomain *rd,
1167 const struct ieee80211_regdomain *country_ie_regdomain,
1168 const struct ieee80211_regdomain *intersected_rd)
1169{
1170}
1171#endif
1172
792/* Takes ownership of rd only if it doesn't fail */ 1173/* Takes ownership of rd only if it doesn't fail */
793static int __set_regdom(const struct ieee80211_regdomain *rd) 1174static int __set_regdom(const struct ieee80211_regdomain *rd)
794{ 1175{
795 const struct ieee80211_regdomain *intersected_rd = NULL; 1176 const struct ieee80211_regdomain *intersected_rd = NULL;
1177 struct cfg80211_registered_device *drv = NULL;
1178 struct wiphy *wiphy = NULL;
796 /* Some basic sanity checks first */ 1179 /* Some basic sanity checks first */
797 1180
798 if (is_world_regdom(rd->alpha2)) { 1181 if (is_world_regdom(rd->alpha2)) {
@@ -809,10 +1192,18 @@ static int __set_regdom(const struct ieee80211_regdomain *rd)
809 if (!last_request) 1192 if (!last_request)
810 return -EINVAL; 1193 return -EINVAL;
811 1194
812 /* allow overriding the static definitions if CRDA is present */ 1195 /* Lets only bother proceeding on the same alpha2 if the current
813 if (!is_old_static_regdom(cfg80211_regdomain) && 1196 * rd is non static (it means CRDA was present and was used last)
814 !regdom_changed(rd->alpha2)) 1197 * and the pending request came in from a country IE */
815 return -EINVAL; 1198 if (last_request->initiator != REGDOM_SET_BY_COUNTRY_IE) {
1199 /* If someone else asked us to change the rd lets only bother
1200 * checking if the alpha2 changes if CRDA was already called */
1201 if (!is_old_static_regdom(cfg80211_regdomain) &&
1202 !regdom_changed(rd->alpha2))
1203 return -EINVAL;
1204 }
1205
1206 wiphy = last_request->wiphy;
816 1207
817 /* Now lets set the regulatory domain, update all driver channels 1208 /* Now lets set the regulatory domain, update all driver channels
818 * and finally inform them of what we have done, in case they want 1209 * and finally inform them of what we have done, in case they want
@@ -853,9 +1244,47 @@ static int __set_regdom(const struct ieee80211_regdomain *rd)
853 return 0; 1244 return 0;
854 } 1245 }
855 1246
856 /* Country IE parsing coming soon */ 1247 /*
1248 * Country IE requests are handled a bit differently, we intersect
1249 * the country IE rd with what CRDA believes that country should have
1250 */
1251
1252 BUG_ON(!country_ie_regdomain);
1253
1254 if (rd != country_ie_regdomain) {
1255 /* Intersect what CRDA returned and our what we
1256 * had built from the Country IE received */
1257
1258 intersected_rd = regdom_intersect(rd, country_ie_regdomain);
1259
1260 reg_country_ie_process_debug(rd, country_ie_regdomain,
1261 intersected_rd);
1262
1263 kfree(country_ie_regdomain);
1264 country_ie_regdomain = NULL;
1265 } else {
1266 /* This would happen when CRDA was not present and
1267 * OLD_REGULATORY was enabled. We intersect our Country
1268 * IE rd and what was set on cfg80211 originally */
1269 intersected_rd = regdom_intersect(rd, cfg80211_regdomain);
1270 }
1271
1272 if (!intersected_rd)
1273 return -EINVAL;
1274
1275 drv = wiphy_to_dev(wiphy);
1276
1277 drv->country_ie_alpha2[0] = rd->alpha2[0];
1278 drv->country_ie_alpha2[1] = rd->alpha2[1];
1279 drv->env = last_request->country_ie_env;
1280
1281 BUG_ON(intersected_rd == rd);
1282
1283 kfree(rd);
1284 rd = NULL;
1285
857 reset_regdomains(); 1286 reset_regdomains();
858 WARN_ON(1); 1287 cfg80211_regdomain = intersected_rd;
859 1288
860 return 0; 1289 return 0;
861} 1290}
@@ -887,6 +1316,17 @@ int set_regdom(const struct ieee80211_regdomain *rd)
887 return r; 1316 return r;
888} 1317}
889 1318
1319/* Caller must hold cfg80211_drv_mutex */
1320void reg_device_remove(struct wiphy *wiphy)
1321{
1322 if (!last_request->wiphy)
1323 return;
1324 if (last_request->wiphy != wiphy)
1325 return;
1326 last_request->wiphy = NULL;
1327 last_request->country_ie_env = ENVIRON_ANY;
1328}
1329
890int regulatory_init(void) 1330int regulatory_init(void)
891{ 1331{
892 int err; 1332 int err;
@@ -906,11 +1346,11 @@ int regulatory_init(void)
906 * that is not a valid ISO / IEC 3166 alpha2 */ 1346 * that is not a valid ISO / IEC 3166 alpha2 */
907 if (ieee80211_regdom[0] != 'E' || ieee80211_regdom[1] != 'U') 1347 if (ieee80211_regdom[0] != 'E' || ieee80211_regdom[1] != 'U')
908 err = __regulatory_hint(NULL, REGDOM_SET_BY_CORE, 1348 err = __regulatory_hint(NULL, REGDOM_SET_BY_CORE,
909 ieee80211_regdom); 1349 ieee80211_regdom, 0, ENVIRON_ANY);
910#else 1350#else
911 cfg80211_regdomain = cfg80211_world_regdom; 1351 cfg80211_regdomain = cfg80211_world_regdom;
912 1352
913 err = __regulatory_hint(NULL, REGDOM_SET_BY_CORE, "00"); 1353 err = __regulatory_hint(NULL, REGDOM_SET_BY_CORE, "00", 0, ENVIRON_ANY);
914 if (err) 1354 if (err)
915 printk(KERN_ERR "cfg80211: calling CRDA failed - " 1355 printk(KERN_ERR "cfg80211: calling CRDA failed - "
916 "unable to update world regulatory domain, " 1356 "unable to update world regulatory domain, "
@@ -926,6 +1366,9 @@ void regulatory_exit(void)
926 1366
927 reset_regdomains(); 1367 reset_regdomains();
928 1368
1369 kfree(country_ie_regdomain);
1370 country_ie_regdomain = NULL;
1371
929 kfree(last_request); 1372 kfree(last_request);
930 1373
931 platform_device_unregister(reg_pdev); 1374 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 */