aboutsummaryrefslogtreecommitdiffstats
path: root/net/wireless/reg.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/wireless/reg.c')
-rw-r--r--net/wireless/reg.c479
1 files changed, 461 insertions, 18 deletions
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);