diff options
Diffstat (limited to 'net/wireless/reg.c')
-rw-r--r-- | net/wireless/reg.c | 732 |
1 files changed, 553 insertions, 179 deletions
diff --git a/net/wireless/reg.c b/net/wireless/reg.c index f256dfffbf46..422da20d1e5b 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c | |||
@@ -33,6 +33,7 @@ | |||
33 | * | 33 | * |
34 | */ | 34 | */ |
35 | #include <linux/kernel.h> | 35 | #include <linux/kernel.h> |
36 | #include <linux/slab.h> | ||
36 | #include <linux/list.h> | 37 | #include <linux/list.h> |
37 | #include <linux/random.h> | 38 | #include <linux/random.h> |
38 | #include <linux/nl80211.h> | 39 | #include <linux/nl80211.h> |
@@ -40,8 +41,18 @@ | |||
40 | #include <net/cfg80211.h> | 41 | #include <net/cfg80211.h> |
41 | #include "core.h" | 42 | #include "core.h" |
42 | #include "reg.h" | 43 | #include "reg.h" |
44 | #include "regdb.h" | ||
43 | #include "nl80211.h" | 45 | #include "nl80211.h" |
44 | 46 | ||
47 | #ifdef CONFIG_CFG80211_REG_DEBUG | ||
48 | #define REG_DBG_PRINT(format, args...) \ | ||
49 | do { \ | ||
50 | printk(KERN_DEBUG format , ## args); \ | ||
51 | } while (0) | ||
52 | #else | ||
53 | #define REG_DBG_PRINT(args...) | ||
54 | #endif | ||
55 | |||
45 | /* Receipt of information from last regulatory request */ | 56 | /* Receipt of information from last regulatory request */ |
46 | static struct regulatory_request *last_request; | 57 | static struct regulatory_request *last_request; |
47 | 58 | ||
@@ -124,107 +135,11 @@ static const struct ieee80211_regdomain *cfg80211_world_regdom = | |||
124 | &world_regdom; | 135 | &world_regdom; |
125 | 136 | ||
126 | static char *ieee80211_regdom = "00"; | 137 | static char *ieee80211_regdom = "00"; |
138 | static char user_alpha2[2]; | ||
127 | 139 | ||
128 | module_param(ieee80211_regdom, charp, 0444); | 140 | module_param(ieee80211_regdom, charp, 0444); |
129 | MODULE_PARM_DESC(ieee80211_regdom, "IEEE 802.11 regulatory domain code"); | 141 | MODULE_PARM_DESC(ieee80211_regdom, "IEEE 802.11 regulatory domain code"); |
130 | 142 | ||
131 | #ifdef CONFIG_WIRELESS_OLD_REGULATORY | ||
132 | /* | ||
133 | * We assume 40 MHz bandwidth for the old regulatory work. | ||
134 | * We make emphasis we are using the exact same frequencies | ||
135 | * as before | ||
136 | */ | ||
137 | |||
138 | static const struct ieee80211_regdomain us_regdom = { | ||
139 | .n_reg_rules = 6, | ||
140 | .alpha2 = "US", | ||
141 | .reg_rules = { | ||
142 | /* IEEE 802.11b/g, channels 1..11 */ | ||
143 | REG_RULE(2412-10, 2462+10, 40, 6, 27, 0), | ||
144 | /* IEEE 802.11a, channel 36 */ | ||
145 | REG_RULE(5180-10, 5180+10, 40, 6, 23, 0), | ||
146 | /* IEEE 802.11a, channel 40 */ | ||
147 | REG_RULE(5200-10, 5200+10, 40, 6, 23, 0), | ||
148 | /* IEEE 802.11a, channel 44 */ | ||
149 | REG_RULE(5220-10, 5220+10, 40, 6, 23, 0), | ||
150 | /* IEEE 802.11a, channels 48..64 */ | ||
151 | REG_RULE(5240-10, 5320+10, 40, 6, 23, 0), | ||
152 | /* IEEE 802.11a, channels 149..165, outdoor */ | ||
153 | REG_RULE(5745-10, 5825+10, 40, 6, 30, 0), | ||
154 | } | ||
155 | }; | ||
156 | |||
157 | static const struct ieee80211_regdomain jp_regdom = { | ||
158 | .n_reg_rules = 3, | ||
159 | .alpha2 = "JP", | ||
160 | .reg_rules = { | ||
161 | /* IEEE 802.11b/g, channels 1..14 */ | ||
162 | REG_RULE(2412-10, 2484+10, 40, 6, 20, 0), | ||
163 | /* IEEE 802.11a, channels 34..48 */ | ||
164 | REG_RULE(5170-10, 5240+10, 40, 6, 20, | ||
165 | NL80211_RRF_PASSIVE_SCAN), | ||
166 | /* IEEE 802.11a, channels 52..64 */ | ||
167 | REG_RULE(5260-10, 5320+10, 40, 6, 20, | ||
168 | NL80211_RRF_NO_IBSS | | ||
169 | NL80211_RRF_DFS), | ||
170 | } | ||
171 | }; | ||
172 | |||
173 | static const struct ieee80211_regdomain eu_regdom = { | ||
174 | .n_reg_rules = 6, | ||
175 | /* | ||
176 | * This alpha2 is bogus, we leave it here just for stupid | ||
177 | * backward compatibility | ||
178 | */ | ||
179 | .alpha2 = "EU", | ||
180 | .reg_rules = { | ||
181 | /* IEEE 802.11b/g, channels 1..13 */ | ||
182 | REG_RULE(2412-10, 2472+10, 40, 6, 20, 0), | ||
183 | /* IEEE 802.11a, channel 36 */ | ||
184 | REG_RULE(5180-10, 5180+10, 40, 6, 23, | ||
185 | NL80211_RRF_PASSIVE_SCAN), | ||
186 | /* IEEE 802.11a, channel 40 */ | ||
187 | REG_RULE(5200-10, 5200+10, 40, 6, 23, | ||
188 | NL80211_RRF_PASSIVE_SCAN), | ||
189 | /* IEEE 802.11a, channel 44 */ | ||
190 | REG_RULE(5220-10, 5220+10, 40, 6, 23, | ||
191 | NL80211_RRF_PASSIVE_SCAN), | ||
192 | /* IEEE 802.11a, channels 48..64 */ | ||
193 | REG_RULE(5240-10, 5320+10, 40, 6, 20, | ||
194 | NL80211_RRF_NO_IBSS | | ||
195 | NL80211_RRF_DFS), | ||
196 | /* IEEE 802.11a, channels 100..140 */ | ||
197 | REG_RULE(5500-10, 5700+10, 40, 6, 30, | ||
198 | NL80211_RRF_NO_IBSS | | ||
199 | NL80211_RRF_DFS), | ||
200 | } | ||
201 | }; | ||
202 | |||
203 | static const struct ieee80211_regdomain *static_regdom(char *alpha2) | ||
204 | { | ||
205 | if (alpha2[0] == 'U' && alpha2[1] == 'S') | ||
206 | return &us_regdom; | ||
207 | if (alpha2[0] == 'J' && alpha2[1] == 'P') | ||
208 | return &jp_regdom; | ||
209 | if (alpha2[0] == 'E' && alpha2[1] == 'U') | ||
210 | return &eu_regdom; | ||
211 | /* Default, as per the old rules */ | ||
212 | return &us_regdom; | ||
213 | } | ||
214 | |||
215 | static bool is_old_static_regdom(const struct ieee80211_regdomain *rd) | ||
216 | { | ||
217 | if (rd == &us_regdom || rd == &jp_regdom || rd == &eu_regdom) | ||
218 | return true; | ||
219 | return false; | ||
220 | } | ||
221 | #else | ||
222 | static inline bool is_old_static_regdom(const struct ieee80211_regdomain *rd) | ||
223 | { | ||
224 | return false; | ||
225 | } | ||
226 | #endif | ||
227 | |||
228 | static void reset_regdomains(void) | 143 | static void reset_regdomains(void) |
229 | { | 144 | { |
230 | /* avoid freeing static information or freeing something twice */ | 145 | /* avoid freeing static information or freeing something twice */ |
@@ -234,8 +149,6 @@ static void reset_regdomains(void) | |||
234 | cfg80211_world_regdom = NULL; | 149 | cfg80211_world_regdom = NULL; |
235 | if (cfg80211_regdomain == &world_regdom) | 150 | if (cfg80211_regdomain == &world_regdom) |
236 | cfg80211_regdomain = NULL; | 151 | cfg80211_regdomain = NULL; |
237 | if (is_old_static_regdom(cfg80211_regdomain)) | ||
238 | cfg80211_regdomain = NULL; | ||
239 | 152 | ||
240 | kfree(cfg80211_regdomain); | 153 | kfree(cfg80211_regdomain); |
241 | kfree(cfg80211_world_regdom); | 154 | kfree(cfg80211_world_regdom); |
@@ -341,6 +254,27 @@ static bool regdom_changes(const char *alpha2) | |||
341 | return true; | 254 | return true; |
342 | } | 255 | } |
343 | 256 | ||
257 | /* | ||
258 | * The NL80211_REGDOM_SET_BY_USER regdom alpha2 is cached, this lets | ||
259 | * you know if a valid regulatory hint with NL80211_REGDOM_SET_BY_USER | ||
260 | * has ever been issued. | ||
261 | */ | ||
262 | static bool is_user_regdom_saved(void) | ||
263 | { | ||
264 | if (user_alpha2[0] == '9' && user_alpha2[1] == '7') | ||
265 | return false; | ||
266 | |||
267 | /* This would indicate a mistake on the design */ | ||
268 | if (WARN((!is_world_regdom(user_alpha2) && | ||
269 | !is_an_alpha2(user_alpha2)), | ||
270 | "Unexpected user alpha2: %c%c\n", | ||
271 | user_alpha2[0], | ||
272 | user_alpha2[1])) | ||
273 | return false; | ||
274 | |||
275 | return true; | ||
276 | } | ||
277 | |||
344 | /** | 278 | /** |
345 | * country_ie_integrity_changes - tells us if the country IE has changed | 279 | * country_ie_integrity_changes - tells us if the country IE has changed |
346 | * @checksum: checksum of country IE of fields we are interested in | 280 | * @checksum: checksum of country IE of fields we are interested in |
@@ -360,6 +294,96 @@ static bool country_ie_integrity_changes(u32 checksum) | |||
360 | return false; | 294 | return false; |
361 | } | 295 | } |
362 | 296 | ||
297 | static int reg_copy_regd(const struct ieee80211_regdomain **dst_regd, | ||
298 | const struct ieee80211_regdomain *src_regd) | ||
299 | { | ||
300 | struct ieee80211_regdomain *regd; | ||
301 | int size_of_regd = 0; | ||
302 | unsigned int i; | ||
303 | |||
304 | size_of_regd = sizeof(struct ieee80211_regdomain) + | ||
305 | ((src_regd->n_reg_rules + 1) * sizeof(struct ieee80211_reg_rule)); | ||
306 | |||
307 | regd = kzalloc(size_of_regd, GFP_KERNEL); | ||
308 | if (!regd) | ||
309 | return -ENOMEM; | ||
310 | |||
311 | memcpy(regd, src_regd, sizeof(struct ieee80211_regdomain)); | ||
312 | |||
313 | for (i = 0; i < src_regd->n_reg_rules; i++) | ||
314 | memcpy(®d->reg_rules[i], &src_regd->reg_rules[i], | ||
315 | sizeof(struct ieee80211_reg_rule)); | ||
316 | |||
317 | *dst_regd = regd; | ||
318 | return 0; | ||
319 | } | ||
320 | |||
321 | #ifdef CONFIG_CFG80211_INTERNAL_REGDB | ||
322 | struct reg_regdb_search_request { | ||
323 | char alpha2[2]; | ||
324 | struct list_head list; | ||
325 | }; | ||
326 | |||
327 | static LIST_HEAD(reg_regdb_search_list); | ||
328 | static DEFINE_MUTEX(reg_regdb_search_mutex); | ||
329 | |||
330 | static void reg_regdb_search(struct work_struct *work) | ||
331 | { | ||
332 | struct reg_regdb_search_request *request; | ||
333 | const struct ieee80211_regdomain *curdom, *regdom; | ||
334 | int i, r; | ||
335 | |||
336 | mutex_lock(®_regdb_search_mutex); | ||
337 | while (!list_empty(®_regdb_search_list)) { | ||
338 | request = list_first_entry(®_regdb_search_list, | ||
339 | struct reg_regdb_search_request, | ||
340 | list); | ||
341 | list_del(&request->list); | ||
342 | |||
343 | for (i=0; i<reg_regdb_size; i++) { | ||
344 | curdom = reg_regdb[i]; | ||
345 | |||
346 | if (!memcmp(request->alpha2, curdom->alpha2, 2)) { | ||
347 | r = reg_copy_regd(®dom, curdom); | ||
348 | if (r) | ||
349 | break; | ||
350 | mutex_lock(&cfg80211_mutex); | ||
351 | set_regdom(regdom); | ||
352 | mutex_unlock(&cfg80211_mutex); | ||
353 | break; | ||
354 | } | ||
355 | } | ||
356 | |||
357 | kfree(request); | ||
358 | } | ||
359 | mutex_unlock(®_regdb_search_mutex); | ||
360 | } | ||
361 | |||
362 | static DECLARE_WORK(reg_regdb_work, reg_regdb_search); | ||
363 | |||
364 | static void reg_regdb_query(const char *alpha2) | ||
365 | { | ||
366 | struct reg_regdb_search_request *request; | ||
367 | |||
368 | if (!alpha2) | ||
369 | return; | ||
370 | |||
371 | request = kzalloc(sizeof(struct reg_regdb_search_request), GFP_KERNEL); | ||
372 | if (!request) | ||
373 | return; | ||
374 | |||
375 | memcpy(request->alpha2, alpha2, 2); | ||
376 | |||
377 | mutex_lock(®_regdb_search_mutex); | ||
378 | list_add_tail(&request->list, ®_regdb_search_list); | ||
379 | mutex_unlock(®_regdb_search_mutex); | ||
380 | |||
381 | schedule_work(®_regdb_work); | ||
382 | } | ||
383 | #else | ||
384 | static inline void reg_regdb_query(const char *alpha2) {} | ||
385 | #endif /* CONFIG_CFG80211_INTERNAL_REGDB */ | ||
386 | |||
363 | /* | 387 | /* |
364 | * This lets us keep regulatory code which is updated on a regulatory | 388 | * This lets us keep regulatory code which is updated on a regulatory |
365 | * basis in userspace. | 389 | * basis in userspace. |
@@ -379,6 +403,9 @@ static int call_crda(const char *alpha2) | |||
379 | printk(KERN_INFO "cfg80211: Calling CRDA to update world " | 403 | printk(KERN_INFO "cfg80211: Calling CRDA to update world " |
380 | "regulatory domain\n"); | 404 | "regulatory domain\n"); |
381 | 405 | ||
406 | /* query internal regulatory database (if it exists) */ | ||
407 | reg_regdb_query(alpha2); | ||
408 | |||
382 | country_env[8] = alpha2[0]; | 409 | country_env[8] = alpha2[0]; |
383 | country_env[9] = alpha2[1]; | 410 | country_env[9] = alpha2[1]; |
384 | 411 | ||
@@ -479,12 +506,212 @@ static bool freq_in_rule_band(const struct ieee80211_freq_range *freq_range, | |||
479 | } | 506 | } |
480 | 507 | ||
481 | /* | 508 | /* |
509 | * This is a work around for sanity checking ieee80211_channel_to_frequency()'s | ||
510 | * work. ieee80211_channel_to_frequency() can for example currently provide a | ||
511 | * 2 GHz channel when in fact a 5 GHz channel was desired. An example would be | ||
512 | * an AP providing channel 8 on a country IE triplet when it sent this on the | ||
513 | * 5 GHz band, that channel is designed to be channel 8 on 5 GHz, not a 2 GHz | ||
514 | * channel. | ||
515 | * | ||
516 | * This can be removed once ieee80211_channel_to_frequency() takes in a band. | ||
517 | */ | ||
518 | static bool chan_in_band(int chan, enum ieee80211_band band) | ||
519 | { | ||
520 | int center_freq = ieee80211_channel_to_frequency(chan); | ||
521 | |||
522 | switch (band) { | ||
523 | case IEEE80211_BAND_2GHZ: | ||
524 | if (center_freq <= 2484) | ||
525 | return true; | ||
526 | return false; | ||
527 | case IEEE80211_BAND_5GHZ: | ||
528 | if (center_freq >= 5005) | ||
529 | return true; | ||
530 | return false; | ||
531 | default: | ||
532 | return false; | ||
533 | } | ||
534 | } | ||
535 | |||
536 | /* | ||
537 | * Some APs may send a country IE triplet for each channel they | ||
538 | * support and while this is completely overkill and silly we still | ||
539 | * need to support it. We avoid making a single rule for each channel | ||
540 | * though and to help us with this we use this helper to find the | ||
541 | * actual subband end channel. These type of country IE triplet | ||
542 | * scenerios are handled then, all yielding two regulaotry rules from | ||
543 | * parsing a country IE: | ||
544 | * | ||
545 | * [1] | ||
546 | * [2] | ||
547 | * [36] | ||
548 | * [40] | ||
549 | * | ||
550 | * [1] | ||
551 | * [2-4] | ||
552 | * [5-12] | ||
553 | * [36] | ||
554 | * [40-44] | ||
555 | * | ||
556 | * [1-4] | ||
557 | * [5-7] | ||
558 | * [36-44] | ||
559 | * [48-64] | ||
560 | * | ||
561 | * [36-36] | ||
562 | * [40-40] | ||
563 | * [44-44] | ||
564 | * [48-48] | ||
565 | * [52-52] | ||
566 | * [56-56] | ||
567 | * [60-60] | ||
568 | * [64-64] | ||
569 | * [100-100] | ||
570 | * [104-104] | ||
571 | * [108-108] | ||
572 | * [112-112] | ||
573 | * [116-116] | ||
574 | * [120-120] | ||
575 | * [124-124] | ||
576 | * [128-128] | ||
577 | * [132-132] | ||
578 | * [136-136] | ||
579 | * [140-140] | ||
580 | * | ||
581 | * Returns 0 if the IE has been found to be invalid in the middle | ||
582 | * somewhere. | ||
583 | */ | ||
584 | static int max_subband_chan(enum ieee80211_band band, | ||
585 | int orig_cur_chan, | ||
586 | int orig_end_channel, | ||
587 | s8 orig_max_power, | ||
588 | u8 **country_ie, | ||
589 | u8 *country_ie_len) | ||
590 | { | ||
591 | u8 *triplets_start = *country_ie; | ||
592 | u8 len_at_triplet = *country_ie_len; | ||
593 | int end_subband_chan = orig_end_channel; | ||
594 | |||
595 | /* | ||
596 | * We'll deal with padding for the caller unless | ||
597 | * its not immediate and we don't process any channels | ||
598 | */ | ||
599 | if (*country_ie_len == 1) { | ||
600 | *country_ie += 1; | ||
601 | *country_ie_len -= 1; | ||
602 | return orig_end_channel; | ||
603 | } | ||
604 | |||
605 | /* Move to the next triplet and then start search */ | ||
606 | *country_ie += 3; | ||
607 | *country_ie_len -= 3; | ||
608 | |||
609 | if (!chan_in_band(orig_cur_chan, band)) | ||
610 | return 0; | ||
611 | |||
612 | while (*country_ie_len >= 3) { | ||
613 | int end_channel = 0; | ||
614 | struct ieee80211_country_ie_triplet *triplet = | ||
615 | (struct ieee80211_country_ie_triplet *) *country_ie; | ||
616 | int cur_channel = 0, next_expected_chan; | ||
617 | |||
618 | /* means last triplet is completely unrelated to this one */ | ||
619 | if (triplet->ext.reg_extension_id >= | ||
620 | IEEE80211_COUNTRY_EXTENSION_ID) { | ||
621 | *country_ie -= 3; | ||
622 | *country_ie_len += 3; | ||
623 | break; | ||
624 | } | ||
625 | |||
626 | if (triplet->chans.first_channel == 0) { | ||
627 | *country_ie += 1; | ||
628 | *country_ie_len -= 1; | ||
629 | if (*country_ie_len != 0) | ||
630 | return 0; | ||
631 | break; | ||
632 | } | ||
633 | |||
634 | if (triplet->chans.num_channels == 0) | ||
635 | return 0; | ||
636 | |||
637 | /* Monitonically increasing channel order */ | ||
638 | if (triplet->chans.first_channel <= end_subband_chan) | ||
639 | return 0; | ||
640 | |||
641 | if (!chan_in_band(triplet->chans.first_channel, band)) | ||
642 | return 0; | ||
643 | |||
644 | /* 2 GHz */ | ||
645 | if (triplet->chans.first_channel <= 14) { | ||
646 | end_channel = triplet->chans.first_channel + | ||
647 | triplet->chans.num_channels - 1; | ||
648 | } | ||
649 | else { | ||
650 | end_channel = triplet->chans.first_channel + | ||
651 | (4 * (triplet->chans.num_channels - 1)); | ||
652 | } | ||
653 | |||
654 | if (!chan_in_band(end_channel, band)) | ||
655 | return 0; | ||
656 | |||
657 | if (orig_max_power != triplet->chans.max_power) { | ||
658 | *country_ie -= 3; | ||
659 | *country_ie_len += 3; | ||
660 | break; | ||
661 | } | ||
662 | |||
663 | cur_channel = triplet->chans.first_channel; | ||
664 | |||
665 | /* The key is finding the right next expected channel */ | ||
666 | if (band == IEEE80211_BAND_2GHZ) | ||
667 | next_expected_chan = end_subband_chan + 1; | ||
668 | else | ||
669 | next_expected_chan = end_subband_chan + 4; | ||
670 | |||
671 | if (cur_channel != next_expected_chan) { | ||
672 | *country_ie -= 3; | ||
673 | *country_ie_len += 3; | ||
674 | break; | ||
675 | } | ||
676 | |||
677 | end_subband_chan = end_channel; | ||
678 | |||
679 | /* Move to the next one */ | ||
680 | *country_ie += 3; | ||
681 | *country_ie_len -= 3; | ||
682 | |||
683 | /* | ||
684 | * Padding needs to be dealt with if we processed | ||
685 | * some channels. | ||
686 | */ | ||
687 | if (*country_ie_len == 1) { | ||
688 | *country_ie += 1; | ||
689 | *country_ie_len -= 1; | ||
690 | break; | ||
691 | } | ||
692 | |||
693 | /* If seen, the IE is invalid */ | ||
694 | if (*country_ie_len == 2) | ||
695 | return 0; | ||
696 | } | ||
697 | |||
698 | if (end_subband_chan == orig_end_channel) { | ||
699 | *country_ie = triplets_start; | ||
700 | *country_ie_len = len_at_triplet; | ||
701 | return orig_end_channel; | ||
702 | } | ||
703 | |||
704 | return end_subband_chan; | ||
705 | } | ||
706 | |||
707 | /* | ||
482 | * Converts a country IE to a regulatory domain. A regulatory domain | 708 | * Converts a country IE to a regulatory domain. A regulatory domain |
483 | * structure has a lot of information which the IE doesn't yet have, | 709 | * structure has a lot of information which the IE doesn't yet have, |
484 | * so for the other values we use upper max values as we will intersect | 710 | * so for the other values we use upper max values as we will intersect |
485 | * with our userspace regulatory agent to get lower bounds. | 711 | * with our userspace regulatory agent to get lower bounds. |
486 | */ | 712 | */ |
487 | static struct ieee80211_regdomain *country_ie_2_rd( | 713 | static struct ieee80211_regdomain *country_ie_2_rd( |
714 | enum ieee80211_band band, | ||
488 | u8 *country_ie, | 715 | u8 *country_ie, |
489 | u8 country_ie_len, | 716 | u8 country_ie_len, |
490 | u32 *checksum) | 717 | u32 *checksum) |
@@ -546,10 +773,29 @@ static struct ieee80211_regdomain *country_ie_2_rd( | |||
546 | continue; | 773 | continue; |
547 | } | 774 | } |
548 | 775 | ||
776 | /* | ||
777 | * APs can add padding to make length divisible | ||
778 | * by two, required by the spec. | ||
779 | */ | ||
780 | if (triplet->chans.first_channel == 0) { | ||
781 | country_ie++; | ||
782 | country_ie_len--; | ||
783 | /* This is expected to be at the very end only */ | ||
784 | if (country_ie_len != 0) | ||
785 | return NULL; | ||
786 | break; | ||
787 | } | ||
788 | |||
789 | if (triplet->chans.num_channels == 0) | ||
790 | return NULL; | ||
791 | |||
792 | if (!chan_in_band(triplet->chans.first_channel, band)) | ||
793 | return NULL; | ||
794 | |||
549 | /* 2 GHz */ | 795 | /* 2 GHz */ |
550 | if (triplet->chans.first_channel <= 14) | 796 | if (band == IEEE80211_BAND_2GHZ) |
551 | end_channel = triplet->chans.first_channel + | 797 | end_channel = triplet->chans.first_channel + |
552 | triplet->chans.num_channels; | 798 | triplet->chans.num_channels - 1; |
553 | else | 799 | else |
554 | /* | 800 | /* |
555 | * 5 GHz -- For example in country IEs if the first | 801 | * 5 GHz -- For example in country IEs if the first |
@@ -564,6 +810,24 @@ static struct ieee80211_regdomain *country_ie_2_rd( | |||
564 | (4 * (triplet->chans.num_channels - 1)); | 810 | (4 * (triplet->chans.num_channels - 1)); |
565 | 811 | ||
566 | cur_channel = triplet->chans.first_channel; | 812 | cur_channel = triplet->chans.first_channel; |
813 | |||
814 | /* | ||
815 | * Enhancement for APs that send a triplet for every channel | ||
816 | * or for whatever reason sends triplets with multiple channels | ||
817 | * separated when in fact they should be together. | ||
818 | */ | ||
819 | end_channel = max_subband_chan(band, | ||
820 | cur_channel, | ||
821 | end_channel, | ||
822 | triplet->chans.max_power, | ||
823 | &country_ie, | ||
824 | &country_ie_len); | ||
825 | if (!end_channel) | ||
826 | return NULL; | ||
827 | |||
828 | if (!chan_in_band(end_channel, band)) | ||
829 | return NULL; | ||
830 | |||
567 | cur_sub_max_channel = end_channel; | 831 | cur_sub_max_channel = end_channel; |
568 | 832 | ||
569 | /* Basic sanity check */ | 833 | /* Basic sanity check */ |
@@ -594,10 +858,13 @@ static struct ieee80211_regdomain *country_ie_2_rd( | |||
594 | 858 | ||
595 | last_sub_max_channel = cur_sub_max_channel; | 859 | last_sub_max_channel = cur_sub_max_channel; |
596 | 860 | ||
597 | country_ie += 3; | ||
598 | country_ie_len -= 3; | ||
599 | num_rules++; | 861 | num_rules++; |
600 | 862 | ||
863 | if (country_ie_len >= 3) { | ||
864 | country_ie += 3; | ||
865 | country_ie_len -= 3; | ||
866 | } | ||
867 | |||
601 | /* | 868 | /* |
602 | * Note: this is not a IEEE requirement but | 869 | * Note: this is not a IEEE requirement but |
603 | * simply a memory requirement | 870 | * simply a memory requirement |
@@ -640,6 +907,12 @@ static struct ieee80211_regdomain *country_ie_2_rd( | |||
640 | continue; | 907 | continue; |
641 | } | 908 | } |
642 | 909 | ||
910 | if (triplet->chans.first_channel == 0) { | ||
911 | country_ie++; | ||
912 | country_ie_len--; | ||
913 | break; | ||
914 | } | ||
915 | |||
643 | reg_rule = &rd->reg_rules[i]; | 916 | reg_rule = &rd->reg_rules[i]; |
644 | freq_range = ®_rule->freq_range; | 917 | freq_range = ®_rule->freq_range; |
645 | power_rule = ®_rule->power_rule; | 918 | power_rule = ®_rule->power_rule; |
@@ -647,13 +920,20 @@ static struct ieee80211_regdomain *country_ie_2_rd( | |||
647 | reg_rule->flags = flags; | 920 | reg_rule->flags = flags; |
648 | 921 | ||
649 | /* 2 GHz */ | 922 | /* 2 GHz */ |
650 | if (triplet->chans.first_channel <= 14) | 923 | if (band == IEEE80211_BAND_2GHZ) |
651 | end_channel = triplet->chans.first_channel + | 924 | end_channel = triplet->chans.first_channel + |
652 | triplet->chans.num_channels; | 925 | triplet->chans.num_channels -1; |
653 | else | 926 | else |
654 | end_channel = triplet->chans.first_channel + | 927 | end_channel = triplet->chans.first_channel + |
655 | (4 * (triplet->chans.num_channels - 1)); | 928 | (4 * (triplet->chans.num_channels - 1)); |
656 | 929 | ||
930 | end_channel = max_subband_chan(band, | ||
931 | triplet->chans.first_channel, | ||
932 | end_channel, | ||
933 | triplet->chans.max_power, | ||
934 | &country_ie, | ||
935 | &country_ie_len); | ||
936 | |||
657 | /* | 937 | /* |
658 | * The +10 is since the regulatory domain expects | 938 | * The +10 is since the regulatory domain expects |
659 | * the actual band edge, not the center of freq for | 939 | * the actual band edge, not the center of freq for |
@@ -674,12 +954,15 @@ static struct ieee80211_regdomain *country_ie_2_rd( | |||
674 | */ | 954 | */ |
675 | freq_range->max_bandwidth_khz = MHZ_TO_KHZ(40); | 955 | freq_range->max_bandwidth_khz = MHZ_TO_KHZ(40); |
676 | power_rule->max_antenna_gain = DBI_TO_MBI(100); | 956 | power_rule->max_antenna_gain = DBI_TO_MBI(100); |
677 | power_rule->max_eirp = DBM_TO_MBM(100); | 957 | power_rule->max_eirp = DBM_TO_MBM(triplet->chans.max_power); |
678 | 958 | ||
679 | country_ie += 3; | ||
680 | country_ie_len -= 3; | ||
681 | i++; | 959 | i++; |
682 | 960 | ||
961 | if (country_ie_len >= 3) { | ||
962 | country_ie += 3; | ||
963 | country_ie_len -= 3; | ||
964 | } | ||
965 | |||
683 | BUG_ON(i > NL80211_MAX_SUPP_REG_RULES); | 966 | BUG_ON(i > NL80211_MAX_SUPP_REG_RULES); |
684 | } | 967 | } |
685 | 968 | ||
@@ -975,25 +1258,21 @@ static void handle_channel(struct wiphy *wiphy, enum ieee80211_band band, | |||
975 | if (r == -ERANGE && | 1258 | if (r == -ERANGE && |
976 | last_request->initiator == | 1259 | last_request->initiator == |
977 | NL80211_REGDOM_SET_BY_COUNTRY_IE) { | 1260 | NL80211_REGDOM_SET_BY_COUNTRY_IE) { |
978 | #ifdef CONFIG_CFG80211_REG_DEBUG | 1261 | REG_DBG_PRINT("cfg80211: Leaving channel %d MHz " |
979 | printk(KERN_DEBUG "cfg80211: Leaving channel %d MHz " | ||
980 | "intact on %s - no rule found in band on " | 1262 | "intact on %s - no rule found in band on " |
981 | "Country IE\n", | 1263 | "Country IE\n", |
982 | chan->center_freq, wiphy_name(wiphy)); | 1264 | chan->center_freq, wiphy_name(wiphy)); |
983 | #endif | ||
984 | } else { | 1265 | } else { |
985 | /* | 1266 | /* |
986 | * In this case we know the country IE has at least one reg rule | 1267 | * In this case we know the country IE has at least one reg rule |
987 | * for the band so we respect its band definitions | 1268 | * for the band so we respect its band definitions |
988 | */ | 1269 | */ |
989 | #ifdef CONFIG_CFG80211_REG_DEBUG | ||
990 | if (last_request->initiator == | 1270 | if (last_request->initiator == |
991 | NL80211_REGDOM_SET_BY_COUNTRY_IE) | 1271 | NL80211_REGDOM_SET_BY_COUNTRY_IE) |
992 | printk(KERN_DEBUG "cfg80211: Disabling " | 1272 | REG_DBG_PRINT("cfg80211: Disabling " |
993 | "channel %d MHz on %s due to " | 1273 | "channel %d MHz on %s due to " |
994 | "Country IE\n", | 1274 | "Country IE\n", |
995 | chan->center_freq, wiphy_name(wiphy)); | 1275 | chan->center_freq, wiphy_name(wiphy)); |
996 | #endif | ||
997 | flags |= IEEE80211_CHAN_DISABLED; | 1276 | flags |= IEEE80211_CHAN_DISABLED; |
998 | chan->flags = flags; | 1277 | chan->flags = flags; |
999 | } | 1278 | } |
@@ -1008,7 +1287,7 @@ static void handle_channel(struct wiphy *wiphy, enum ieee80211_band band, | |||
1008 | 1287 | ||
1009 | if (last_request->initiator == NL80211_REGDOM_SET_BY_DRIVER && | 1288 | if (last_request->initiator == NL80211_REGDOM_SET_BY_DRIVER && |
1010 | request_wiphy && request_wiphy == wiphy && | 1289 | request_wiphy && request_wiphy == wiphy && |
1011 | request_wiphy->strict_regulatory) { | 1290 | request_wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY) { |
1012 | /* | 1291 | /* |
1013 | * This gaurantees the driver's requested regulatory domain | 1292 | * This gaurantees the driver's requested regulatory domain |
1014 | * will always be used as a base for further regulatory | 1293 | * will always be used as a base for further regulatory |
@@ -1051,13 +1330,13 @@ static bool ignore_reg_update(struct wiphy *wiphy, | |||
1051 | if (!last_request) | 1330 | if (!last_request) |
1052 | return true; | 1331 | return true; |
1053 | if (initiator == NL80211_REGDOM_SET_BY_CORE && | 1332 | if (initiator == NL80211_REGDOM_SET_BY_CORE && |
1054 | wiphy->custom_regulatory) | 1333 | wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY) |
1055 | return true; | 1334 | return true; |
1056 | /* | 1335 | /* |
1057 | * wiphy->regd will be set once the device has its own | 1336 | * wiphy->regd will be set once the device has its own |
1058 | * desired regulatory domain set | 1337 | * desired regulatory domain set |
1059 | */ | 1338 | */ |
1060 | if (wiphy->strict_regulatory && !wiphy->regd && | 1339 | if (wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY && !wiphy->regd && |
1061 | !is_world_regdom(last_request->alpha2)) | 1340 | !is_world_regdom(last_request->alpha2)) |
1062 | return true; | 1341 | return true; |
1063 | return false; | 1342 | return false; |
@@ -1093,7 +1372,7 @@ static void handle_reg_beacon(struct wiphy *wiphy, | |||
1093 | 1372 | ||
1094 | chan->beacon_found = true; | 1373 | chan->beacon_found = true; |
1095 | 1374 | ||
1096 | if (wiphy->disable_beacon_hints) | 1375 | if (wiphy->flags & WIPHY_FLAG_DISABLE_BEACON_HINTS) |
1097 | return; | 1376 | return; |
1098 | 1377 | ||
1099 | chan_before.center_freq = chan->center_freq; | 1378 | chan_before.center_freq = chan->center_freq; |
@@ -1164,7 +1443,7 @@ static bool reg_is_world_roaming(struct wiphy *wiphy) | |||
1164 | return true; | 1443 | return true; |
1165 | if (last_request && | 1444 | if (last_request && |
1166 | last_request->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE && | 1445 | last_request->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE && |
1167 | wiphy->custom_regulatory) | 1446 | wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY) |
1168 | return true; | 1447 | return true; |
1169 | return false; | 1448 | return false; |
1170 | } | 1449 | } |
@@ -1367,30 +1646,6 @@ void wiphy_apply_custom_regulatory(struct wiphy *wiphy, | |||
1367 | } | 1646 | } |
1368 | EXPORT_SYMBOL(wiphy_apply_custom_regulatory); | 1647 | EXPORT_SYMBOL(wiphy_apply_custom_regulatory); |
1369 | 1648 | ||
1370 | static int reg_copy_regd(const struct ieee80211_regdomain **dst_regd, | ||
1371 | const struct ieee80211_regdomain *src_regd) | ||
1372 | { | ||
1373 | struct ieee80211_regdomain *regd; | ||
1374 | int size_of_regd = 0; | ||
1375 | unsigned int i; | ||
1376 | |||
1377 | size_of_regd = sizeof(struct ieee80211_regdomain) + | ||
1378 | ((src_regd->n_reg_rules + 1) * sizeof(struct ieee80211_reg_rule)); | ||
1379 | |||
1380 | regd = kzalloc(size_of_regd, GFP_KERNEL); | ||
1381 | if (!regd) | ||
1382 | return -ENOMEM; | ||
1383 | |||
1384 | memcpy(regd, src_regd, sizeof(struct ieee80211_regdomain)); | ||
1385 | |||
1386 | for (i = 0; i < src_regd->n_reg_rules; i++) | ||
1387 | memcpy(®d->reg_rules[i], &src_regd->reg_rules[i], | ||
1388 | sizeof(struct ieee80211_reg_rule)); | ||
1389 | |||
1390 | *dst_regd = regd; | ||
1391 | return 0; | ||
1392 | } | ||
1393 | |||
1394 | /* | 1649 | /* |
1395 | * Return value which can be used by ignore_request() to indicate | 1650 | * Return value which can be used by ignore_request() to indicate |
1396 | * it has been determined we should intersect two regulatory domains | 1651 | * it has been determined we should intersect two regulatory domains |
@@ -1412,7 +1667,7 @@ static int ignore_request(struct wiphy *wiphy, | |||
1412 | 1667 | ||
1413 | switch (pending_request->initiator) { | 1668 | switch (pending_request->initiator) { |
1414 | case NL80211_REGDOM_SET_BY_CORE: | 1669 | case NL80211_REGDOM_SET_BY_CORE: |
1415 | return -EINVAL; | 1670 | return 0; |
1416 | case NL80211_REGDOM_SET_BY_COUNTRY_IE: | 1671 | case NL80211_REGDOM_SET_BY_COUNTRY_IE: |
1417 | 1672 | ||
1418 | last_wiphy = wiphy_idx_to_wiphy(last_request->wiphy_idx); | 1673 | last_wiphy = wiphy_idx_to_wiphy(last_request->wiphy_idx); |
@@ -1443,8 +1698,6 @@ static int ignore_request(struct wiphy *wiphy, | |||
1443 | return REG_INTERSECT; | 1698 | return REG_INTERSECT; |
1444 | case NL80211_REGDOM_SET_BY_DRIVER: | 1699 | case NL80211_REGDOM_SET_BY_DRIVER: |
1445 | if (last_request->initiator == NL80211_REGDOM_SET_BY_CORE) { | 1700 | if (last_request->initiator == NL80211_REGDOM_SET_BY_CORE) { |
1446 | if (is_old_static_regdom(cfg80211_regdomain)) | ||
1447 | return 0; | ||
1448 | if (regdom_changes(pending_request->alpha2)) | 1701 | if (regdom_changes(pending_request->alpha2)) |
1449 | return 0; | 1702 | return 0; |
1450 | return -EALREADY; | 1703 | return -EALREADY; |
@@ -1481,8 +1734,7 @@ static int ignore_request(struct wiphy *wiphy, | |||
1481 | return -EAGAIN; | 1734 | return -EAGAIN; |
1482 | } | 1735 | } |
1483 | 1736 | ||
1484 | if (!is_old_static_regdom(cfg80211_regdomain) && | 1737 | if (!regdom_changes(pending_request->alpha2)) |
1485 | !regdom_changes(pending_request->alpha2)) | ||
1486 | return -EALREADY; | 1738 | return -EALREADY; |
1487 | 1739 | ||
1488 | return 0; | 1740 | return 0; |
@@ -1554,6 +1806,11 @@ new_request: | |||
1554 | 1806 | ||
1555 | pending_request = NULL; | 1807 | pending_request = NULL; |
1556 | 1808 | ||
1809 | if (last_request->initiator == NL80211_REGDOM_SET_BY_USER) { | ||
1810 | user_alpha2[0] = last_request->alpha2[0]; | ||
1811 | user_alpha2[1] = last_request->alpha2[1]; | ||
1812 | } | ||
1813 | |||
1557 | /* When r == REG_INTERSECT we do need to call CRDA */ | 1814 | /* When r == REG_INTERSECT we do need to call CRDA */ |
1558 | if (r < 0) { | 1815 | if (r < 0) { |
1559 | /* | 1816 | /* |
@@ -1591,7 +1848,8 @@ static void reg_process_hint(struct regulatory_request *reg_request) | |||
1591 | 1848 | ||
1592 | r = __regulatory_hint(wiphy, reg_request); | 1849 | r = __regulatory_hint(wiphy, reg_request); |
1593 | /* This is required so that the orig_* parameters are saved */ | 1850 | /* This is required so that the orig_* parameters are saved */ |
1594 | if (r == -EALREADY && wiphy && wiphy->strict_regulatory) | 1851 | if (r == -EALREADY && wiphy && |
1852 | wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY) | ||
1595 | wiphy_update_regulatory(wiphy, reg_request->initiator); | 1853 | wiphy_update_regulatory(wiphy, reg_request->initiator); |
1596 | out: | 1854 | out: |
1597 | mutex_unlock(®_mutex); | 1855 | mutex_unlock(®_mutex); |
@@ -1672,12 +1930,16 @@ static void queue_regulatory_request(struct regulatory_request *request) | |||
1672 | schedule_work(®_work); | 1930 | schedule_work(®_work); |
1673 | } | 1931 | } |
1674 | 1932 | ||
1675 | /* Core regulatory hint -- happens once during cfg80211_init() */ | 1933 | /* |
1934 | * Core regulatory hint -- happens during cfg80211_init() | ||
1935 | * and when we restore regulatory settings. | ||
1936 | */ | ||
1676 | static int regulatory_hint_core(const char *alpha2) | 1937 | static int regulatory_hint_core(const char *alpha2) |
1677 | { | 1938 | { |
1678 | struct regulatory_request *request; | 1939 | struct regulatory_request *request; |
1679 | 1940 | ||
1680 | BUG_ON(last_request); | 1941 | kfree(last_request); |
1942 | last_request = NULL; | ||
1681 | 1943 | ||
1682 | request = kzalloc(sizeof(struct regulatory_request), | 1944 | request = kzalloc(sizeof(struct regulatory_request), |
1683 | GFP_KERNEL); | 1945 | GFP_KERNEL); |
@@ -1688,14 +1950,12 @@ static int regulatory_hint_core(const char *alpha2) | |||
1688 | request->alpha2[1] = alpha2[1]; | 1950 | request->alpha2[1] = alpha2[1]; |
1689 | request->initiator = NL80211_REGDOM_SET_BY_CORE; | 1951 | request->initiator = NL80211_REGDOM_SET_BY_CORE; |
1690 | 1952 | ||
1691 | queue_regulatory_request(request); | ||
1692 | |||
1693 | /* | 1953 | /* |
1694 | * This ensures last_request is populated once modules | 1954 | * This ensures last_request is populated once modules |
1695 | * come swinging in and calling regulatory hints and | 1955 | * come swinging in and calling regulatory hints and |
1696 | * wiphy_apply_custom_regulatory(). | 1956 | * wiphy_apply_custom_regulatory(). |
1697 | */ | 1957 | */ |
1698 | flush_scheduled_work(); | 1958 | reg_process_hint(request); |
1699 | 1959 | ||
1700 | return 0; | 1960 | return 0; |
1701 | } | 1961 | } |
@@ -1714,7 +1974,7 @@ int regulatory_hint_user(const char *alpha2) | |||
1714 | request->wiphy_idx = WIPHY_IDX_STALE; | 1974 | request->wiphy_idx = WIPHY_IDX_STALE; |
1715 | request->alpha2[0] = alpha2[0]; | 1975 | request->alpha2[0] = alpha2[0]; |
1716 | request->alpha2[1] = alpha2[1]; | 1976 | request->alpha2[1] = alpha2[1]; |
1717 | request->initiator = NL80211_REGDOM_SET_BY_USER, | 1977 | request->initiator = NL80211_REGDOM_SET_BY_USER; |
1718 | 1978 | ||
1719 | queue_regulatory_request(request); | 1979 | queue_regulatory_request(request); |
1720 | 1980 | ||
@@ -1782,8 +2042,9 @@ static bool reg_same_country_ie_hint(struct wiphy *wiphy, | |||
1782 | * therefore cannot iterate over the rdev list here. | 2042 | * therefore cannot iterate over the rdev list here. |
1783 | */ | 2043 | */ |
1784 | void regulatory_hint_11d(struct wiphy *wiphy, | 2044 | void regulatory_hint_11d(struct wiphy *wiphy, |
1785 | u8 *country_ie, | 2045 | enum ieee80211_band band, |
1786 | u8 country_ie_len) | 2046 | u8 *country_ie, |
2047 | u8 country_ie_len) | ||
1787 | { | 2048 | { |
1788 | struct ieee80211_regdomain *rd = NULL; | 2049 | struct ieee80211_regdomain *rd = NULL; |
1789 | char alpha2[2]; | 2050 | char alpha2[2]; |
@@ -1829,9 +2090,11 @@ void regulatory_hint_11d(struct wiphy *wiphy, | |||
1829 | wiphy_idx_valid(last_request->wiphy_idx))) | 2090 | wiphy_idx_valid(last_request->wiphy_idx))) |
1830 | goto out; | 2091 | goto out; |
1831 | 2092 | ||
1832 | rd = country_ie_2_rd(country_ie, country_ie_len, &checksum); | 2093 | rd = country_ie_2_rd(band, country_ie, country_ie_len, &checksum); |
1833 | if (!rd) | 2094 | if (!rd) { |
2095 | REG_DBG_PRINT("cfg80211: Ignoring bogus country IE\n"); | ||
1834 | goto out; | 2096 | goto out; |
2097 | } | ||
1835 | 2098 | ||
1836 | /* | 2099 | /* |
1837 | * This will not happen right now but we leave it here for the | 2100 | * This will not happen right now but we leave it here for the |
@@ -1874,6 +2137,123 @@ out: | |||
1874 | mutex_unlock(®_mutex); | 2137 | mutex_unlock(®_mutex); |
1875 | } | 2138 | } |
1876 | 2139 | ||
2140 | static void restore_alpha2(char *alpha2, bool reset_user) | ||
2141 | { | ||
2142 | /* indicates there is no alpha2 to consider for restoration */ | ||
2143 | alpha2[0] = '9'; | ||
2144 | alpha2[1] = '7'; | ||
2145 | |||
2146 | /* The user setting has precedence over the module parameter */ | ||
2147 | if (is_user_regdom_saved()) { | ||
2148 | /* Unless we're asked to ignore it and reset it */ | ||
2149 | if (reset_user) { | ||
2150 | REG_DBG_PRINT("cfg80211: Restoring regulatory settings " | ||
2151 | "including user preference\n"); | ||
2152 | user_alpha2[0] = '9'; | ||
2153 | user_alpha2[1] = '7'; | ||
2154 | |||
2155 | /* | ||
2156 | * If we're ignoring user settings, we still need to | ||
2157 | * check the module parameter to ensure we put things | ||
2158 | * back as they were for a full restore. | ||
2159 | */ | ||
2160 | if (!is_world_regdom(ieee80211_regdom)) { | ||
2161 | REG_DBG_PRINT("cfg80211: Keeping preference on " | ||
2162 | "module parameter ieee80211_regdom: %c%c\n", | ||
2163 | ieee80211_regdom[0], | ||
2164 | ieee80211_regdom[1]); | ||
2165 | alpha2[0] = ieee80211_regdom[0]; | ||
2166 | alpha2[1] = ieee80211_regdom[1]; | ||
2167 | } | ||
2168 | } else { | ||
2169 | REG_DBG_PRINT("cfg80211: Restoring regulatory settings " | ||
2170 | "while preserving user preference for: %c%c\n", | ||
2171 | user_alpha2[0], | ||
2172 | user_alpha2[1]); | ||
2173 | alpha2[0] = user_alpha2[0]; | ||
2174 | alpha2[1] = user_alpha2[1]; | ||
2175 | } | ||
2176 | } else if (!is_world_regdom(ieee80211_regdom)) { | ||
2177 | REG_DBG_PRINT("cfg80211: Keeping preference on " | ||
2178 | "module parameter ieee80211_regdom: %c%c\n", | ||
2179 | ieee80211_regdom[0], | ||
2180 | ieee80211_regdom[1]); | ||
2181 | alpha2[0] = ieee80211_regdom[0]; | ||
2182 | alpha2[1] = ieee80211_regdom[1]; | ||
2183 | } else | ||
2184 | REG_DBG_PRINT("cfg80211: Restoring regulatory settings\n"); | ||
2185 | } | ||
2186 | |||
2187 | /* | ||
2188 | * Restoring regulatory settings involves ingoring any | ||
2189 | * possibly stale country IE information and user regulatory | ||
2190 | * settings if so desired, this includes any beacon hints | ||
2191 | * learned as we could have traveled outside to another country | ||
2192 | * after disconnection. To restore regulatory settings we do | ||
2193 | * exactly what we did at bootup: | ||
2194 | * | ||
2195 | * - send a core regulatory hint | ||
2196 | * - send a user regulatory hint if applicable | ||
2197 | * | ||
2198 | * Device drivers that send a regulatory hint for a specific country | ||
2199 | * keep their own regulatory domain on wiphy->regd so that does does | ||
2200 | * not need to be remembered. | ||
2201 | */ | ||
2202 | static void restore_regulatory_settings(bool reset_user) | ||
2203 | { | ||
2204 | char alpha2[2]; | ||
2205 | struct reg_beacon *reg_beacon, *btmp; | ||
2206 | |||
2207 | mutex_lock(&cfg80211_mutex); | ||
2208 | mutex_lock(®_mutex); | ||
2209 | |||
2210 | reset_regdomains(); | ||
2211 | restore_alpha2(alpha2, reset_user); | ||
2212 | |||
2213 | /* Clear beacon hints */ | ||
2214 | spin_lock_bh(®_pending_beacons_lock); | ||
2215 | if (!list_empty(®_pending_beacons)) { | ||
2216 | list_for_each_entry_safe(reg_beacon, btmp, | ||
2217 | ®_pending_beacons, list) { | ||
2218 | list_del(®_beacon->list); | ||
2219 | kfree(reg_beacon); | ||
2220 | } | ||
2221 | } | ||
2222 | spin_unlock_bh(®_pending_beacons_lock); | ||
2223 | |||
2224 | if (!list_empty(®_beacon_list)) { | ||
2225 | list_for_each_entry_safe(reg_beacon, btmp, | ||
2226 | ®_beacon_list, list) { | ||
2227 | list_del(®_beacon->list); | ||
2228 | kfree(reg_beacon); | ||
2229 | } | ||
2230 | } | ||
2231 | |||
2232 | /* First restore to the basic regulatory settings */ | ||
2233 | cfg80211_regdomain = cfg80211_world_regdom; | ||
2234 | |||
2235 | mutex_unlock(®_mutex); | ||
2236 | mutex_unlock(&cfg80211_mutex); | ||
2237 | |||
2238 | regulatory_hint_core(cfg80211_regdomain->alpha2); | ||
2239 | |||
2240 | /* | ||
2241 | * This restores the ieee80211_regdom module parameter | ||
2242 | * preference or the last user requested regulatory | ||
2243 | * settings, user regulatory settings takes precedence. | ||
2244 | */ | ||
2245 | if (is_an_alpha2(alpha2)) | ||
2246 | regulatory_hint_user(user_alpha2); | ||
2247 | } | ||
2248 | |||
2249 | |||
2250 | void regulatory_hint_disconnect(void) | ||
2251 | { | ||
2252 | REG_DBG_PRINT("cfg80211: All devices are disconnected, going to " | ||
2253 | "restore regulatory settings\n"); | ||
2254 | restore_regulatory_settings(false); | ||
2255 | } | ||
2256 | |||
1877 | static bool freq_is_chan_12_13_14(u16 freq) | 2257 | static bool freq_is_chan_12_13_14(u16 freq) |
1878 | { | 2258 | { |
1879 | if (freq == ieee80211_channel_to_frequency(12) || | 2259 | if (freq == ieee80211_channel_to_frequency(12) || |
@@ -1899,13 +2279,12 @@ int regulatory_hint_found_beacon(struct wiphy *wiphy, | |||
1899 | if (!reg_beacon) | 2279 | if (!reg_beacon) |
1900 | return -ENOMEM; | 2280 | return -ENOMEM; |
1901 | 2281 | ||
1902 | #ifdef CONFIG_CFG80211_REG_DEBUG | 2282 | REG_DBG_PRINT("cfg80211: Found new beacon on " |
1903 | printk(KERN_DEBUG "cfg80211: Found new beacon on " | 2283 | "frequency: %d MHz (Ch %d) on %s\n", |
1904 | "frequency: %d MHz (Ch %d) on %s\n", | 2284 | beacon_chan->center_freq, |
1905 | beacon_chan->center_freq, | 2285 | ieee80211_frequency_to_channel(beacon_chan->center_freq), |
1906 | ieee80211_frequency_to_channel(beacon_chan->center_freq), | 2286 | wiphy_name(wiphy)); |
1907 | wiphy_name(wiphy)); | 2287 | |
1908 | #endif | ||
1909 | memcpy(®_beacon->chan, beacon_chan, | 2288 | memcpy(®_beacon->chan, beacon_chan, |
1910 | sizeof(struct ieee80211_channel)); | 2289 | sizeof(struct ieee80211_channel)); |
1911 | 2290 | ||
@@ -1930,7 +2309,7 @@ static void print_rd_rules(const struct ieee80211_regdomain *rd) | |||
1930 | const struct ieee80211_freq_range *freq_range = NULL; | 2309 | const struct ieee80211_freq_range *freq_range = NULL; |
1931 | const struct ieee80211_power_rule *power_rule = NULL; | 2310 | const struct ieee80211_power_rule *power_rule = NULL; |
1932 | 2311 | ||
1933 | printk(KERN_INFO "\t(start_freq - end_freq @ bandwidth), " | 2312 | printk(KERN_INFO " (start_freq - end_freq @ bandwidth), " |
1934 | "(max_antenna_gain, max_eirp)\n"); | 2313 | "(max_antenna_gain, max_eirp)\n"); |
1935 | 2314 | ||
1936 | for (i = 0; i < rd->n_reg_rules; i++) { | 2315 | for (i = 0; i < rd->n_reg_rules; i++) { |
@@ -1943,7 +2322,7 @@ static void print_rd_rules(const struct ieee80211_regdomain *rd) | |||
1943 | * in certain regions | 2322 | * in certain regions |
1944 | */ | 2323 | */ |
1945 | if (power_rule->max_antenna_gain) | 2324 | if (power_rule->max_antenna_gain) |
1946 | printk(KERN_INFO "\t(%d KHz - %d KHz @ %d KHz), " | 2325 | printk(KERN_INFO " (%d KHz - %d KHz @ %d KHz), " |
1947 | "(%d mBi, %d mBm)\n", | 2326 | "(%d mBi, %d mBm)\n", |
1948 | freq_range->start_freq_khz, | 2327 | freq_range->start_freq_khz, |
1949 | freq_range->end_freq_khz, | 2328 | freq_range->end_freq_khz, |
@@ -1951,7 +2330,7 @@ static void print_rd_rules(const struct ieee80211_regdomain *rd) | |||
1951 | power_rule->max_antenna_gain, | 2330 | power_rule->max_antenna_gain, |
1952 | power_rule->max_eirp); | 2331 | power_rule->max_eirp); |
1953 | else | 2332 | else |
1954 | printk(KERN_INFO "\t(%d KHz - %d KHz @ %d KHz), " | 2333 | printk(KERN_INFO " (%d KHz - %d KHz @ %d KHz), " |
1955 | "(N/A, %d mBm)\n", | 2334 | "(N/A, %d mBm)\n", |
1956 | freq_range->start_freq_khz, | 2335 | freq_range->start_freq_khz, |
1957 | freq_range->end_freq_khz, | 2336 | freq_range->end_freq_khz, |
@@ -2063,8 +2442,7 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) | |||
2063 | * If someone else asked us to change the rd lets only bother | 2442 | * If someone else asked us to change the rd lets only bother |
2064 | * checking if the alpha2 changes if CRDA was already called | 2443 | * checking if the alpha2 changes if CRDA was already called |
2065 | */ | 2444 | */ |
2066 | if (!is_old_static_regdom(cfg80211_regdomain) && | 2445 | if (!regdom_changes(rd->alpha2)) |
2067 | !regdom_changes(rd->alpha2)) | ||
2068 | return -EINVAL; | 2446 | return -EINVAL; |
2069 | } | 2447 | } |
2070 | 2448 | ||
@@ -2263,15 +2641,11 @@ int regulatory_init(void) | |||
2263 | spin_lock_init(®_requests_lock); | 2641 | spin_lock_init(®_requests_lock); |
2264 | spin_lock_init(®_pending_beacons_lock); | 2642 | spin_lock_init(®_pending_beacons_lock); |
2265 | 2643 | ||
2266 | #ifdef CONFIG_WIRELESS_OLD_REGULATORY | ||
2267 | cfg80211_regdomain = static_regdom(ieee80211_regdom); | ||
2268 | |||
2269 | printk(KERN_INFO "cfg80211: Using static regulatory domain info\n"); | ||
2270 | print_regdomain_info(cfg80211_regdomain); | ||
2271 | #else | ||
2272 | cfg80211_regdomain = cfg80211_world_regdom; | 2644 | cfg80211_regdomain = cfg80211_world_regdom; |
2273 | 2645 | ||
2274 | #endif | 2646 | user_alpha2[0] = '9'; |
2647 | user_alpha2[1] = '7'; | ||
2648 | |||
2275 | /* We always try to get an update for the static regdomain */ | 2649 | /* We always try to get an update for the static regdomain */ |
2276 | err = regulatory_hint_core(cfg80211_regdomain->alpha2); | 2650 | err = regulatory_hint_core(cfg80211_regdomain->alpha2); |
2277 | if (err) { | 2651 | if (err) { |