diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2008-12-28 15:49:40 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2008-12-28 15:49:40 -0500 |
commit | 0191b625ca5a46206d2fb862bb08f36f2fcb3b31 (patch) | |
tree | 454d1842b1833d976da62abcbd5c47521ebe9bd7 /net/wireless/reg.c | |
parent | 54a696bd07c14d3b1192d03ce7269bc59b45209a (diff) | |
parent | eb56092fc168bf5af199d47af50c0d84a96db898 (diff) |
Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next-2.6
* git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next-2.6: (1429 commits)
net: Allow dependancies of FDDI & Tokenring to be modular.
igb: Fix build warning when DCA is disabled.
net: Fix warning fallout from recent NAPI interface changes.
gro: Fix potential use after free
sfc: If AN is enabled, always read speed/duplex from the AN advertising bits
sfc: When disabling the NIC, close the device rather than unregistering it
sfc: SFT9001: Add cable diagnostics
sfc: Add support for multiple PHY self-tests
sfc: Merge top-level functions for self-tests
sfc: Clean up PHY mode management in loopback self-test
sfc: Fix unreliable link detection in some loopback modes
sfc: Generate unique names for per-NIC workqueues
802.3ad: use standard ethhdr instead of ad_header
802.3ad: generalize out mac address initializer
802.3ad: initialize ports LACPDU from const initializer
802.3ad: remove typedef around ad_system
802.3ad: turn ports is_individual into a bool
802.3ad: turn ports is_enabled into a bool
802.3ad: make ntt bool
ixgbe: Fix set_ringparam in ixgbe to use the same memory pools.
...
Fixed trivial IPv4/6 address printing conflicts in fs/cifs/connect.c due
to the conversion to %pI (in this networking merge) and the addition of
doing IPv6 addresses (from the earlier merge of CIFS).
Diffstat (limited to 'net/wireless/reg.c')
-rw-r--r-- | net/wireless/reg.c | 1005 |
1 files changed, 762 insertions, 243 deletions
diff --git a/net/wireless/reg.c b/net/wireless/reg.c index eb3b1a9f9b12..4f877535e666 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c | |||
@@ -42,17 +42,40 @@ | |||
42 | #include "core.h" | 42 | #include "core.h" |
43 | #include "reg.h" | 43 | #include "reg.h" |
44 | 44 | ||
45 | /* wiphy is set if this request's initiator is REGDOM_SET_BY_DRIVER */ | 45 | /** |
46 | * struct regulatory_request - receipt of last regulatory request | ||
47 | * | ||
48 | * @wiphy: this is set if this request's initiator is | ||
49 | * %REGDOM_SET_BY_COUNTRY_IE or %REGDOM_SET_BY_DRIVER. This | ||
50 | * can be used by the wireless core to deal with conflicts | ||
51 | * and potentially inform users of which devices specifically | ||
52 | * cased the conflicts. | ||
53 | * @initiator: indicates who sent this request, could be any of | ||
54 | * of those set in reg_set_by, %REGDOM_SET_BY_* | ||
55 | * @alpha2: the ISO / IEC 3166 alpha2 country code of the requested | ||
56 | * regulatory domain. We have a few special codes: | ||
57 | * 00 - World regulatory domain | ||
58 | * 99 - built by driver but a specific alpha2 cannot be determined | ||
59 | * 98 - result of an intersection between two regulatory domains | ||
60 | * @intersect: indicates whether the wireless core should intersect | ||
61 | * the requested regulatory domain with the presently set regulatory | ||
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 | ||
67 | */ | ||
46 | struct regulatory_request { | 68 | struct regulatory_request { |
47 | struct list_head list; | ||
48 | struct wiphy *wiphy; | 69 | struct wiphy *wiphy; |
49 | int granted; | ||
50 | enum reg_set_by initiator; | 70 | enum reg_set_by initiator; |
51 | char alpha2[2]; | 71 | char alpha2[2]; |
72 | bool intersect; | ||
73 | u32 country_ie_checksum; | ||
74 | enum environment_cap country_ie_env; | ||
52 | }; | 75 | }; |
53 | 76 | ||
54 | static LIST_HEAD(regulatory_requests); | 77 | /* Receipt of information from last regulatory request */ |
55 | DEFINE_MUTEX(cfg80211_reg_mutex); | 78 | static struct regulatory_request *last_request; |
56 | 79 | ||
57 | /* To trigger userspace events */ | 80 | /* To trigger userspace events */ |
58 | static struct platform_device *reg_pdev; | 81 | static struct platform_device *reg_pdev; |
@@ -63,13 +86,16 @@ static u32 supported_bandwidths[] = { | |||
63 | MHZ_TO_KHZ(20), | 86 | MHZ_TO_KHZ(20), |
64 | }; | 87 | }; |
65 | 88 | ||
66 | static struct list_head regulatory_requests; | ||
67 | |||
68 | /* Central wireless core regulatory domains, we only need two, | 89 | /* Central wireless core regulatory domains, we only need two, |
69 | * the current one and a world regulatory domain in case we have no | 90 | * the current one and a world regulatory domain in case we have no |
70 | * information to give us an alpha2 */ | 91 | * information to give us an alpha2 */ |
71 | static const struct ieee80211_regdomain *cfg80211_regdomain; | 92 | static const struct ieee80211_regdomain *cfg80211_regdomain; |
72 | 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 */ | ||
97 | static const struct ieee80211_regdomain *country_ie_regdomain; | ||
98 | |||
73 | /* 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 */ |
74 | static const struct ieee80211_regdomain world_regdom = { | 100 | static const struct ieee80211_regdomain world_regdom = { |
75 | .n_reg_rules = 1, | 101 | .n_reg_rules = 1, |
@@ -204,7 +230,7 @@ static void reset_regdomains(void) | |||
204 | * core upon initialization */ | 230 | * core upon initialization */ |
205 | static void update_world_regdomain(const struct ieee80211_regdomain *rd) | 231 | static void update_world_regdomain(const struct ieee80211_regdomain *rd) |
206 | { | 232 | { |
207 | BUG_ON(list_empty(®ulatory_requests)); | 233 | BUG_ON(!last_request); |
208 | 234 | ||
209 | reset_regdomains(); | 235 | reset_regdomains(); |
210 | 236 | ||
@@ -249,6 +275,18 @@ static bool is_unknown_alpha2(const char *alpha2) | |||
249 | return false; | 275 | return false; |
250 | } | 276 | } |
251 | 277 | ||
278 | static 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 | |||
252 | static bool is_an_alpha2(const char *alpha2) | 290 | static bool is_an_alpha2(const char *alpha2) |
253 | { | 291 | { |
254 | if (!alpha2) | 292 | if (!alpha2) |
@@ -277,6 +315,25 @@ static bool regdom_changed(const char *alpha2) | |||
277 | return true; | 315 | return true; |
278 | } | 316 | } |
279 | 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 | */ | ||
327 | static 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 | |||
280 | /* 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 |
281 | * basis in userspace. */ | 338 | * basis in userspace. */ |
282 | static int call_crda(const char *alpha2) | 339 | static int call_crda(const char *alpha2) |
@@ -300,121 +357,13 @@ static int call_crda(const char *alpha2) | |||
300 | return kobject_uevent_env(®_pdev->dev.kobj, KOBJ_CHANGE, envp); | 357 | return kobject_uevent_env(®_pdev->dev.kobj, KOBJ_CHANGE, envp); |
301 | } | 358 | } |
302 | 359 | ||
303 | /* This has the logic which determines when a new request | ||
304 | * should be ignored. */ | ||
305 | static int ignore_request(struct wiphy *wiphy, enum reg_set_by set_by, | ||
306 | char *alpha2, struct ieee80211_regdomain *rd) | ||
307 | { | ||
308 | struct regulatory_request *last_request = NULL; | ||
309 | |||
310 | /* All initial requests are respected */ | ||
311 | if (list_empty(®ulatory_requests)) | ||
312 | return 0; | ||
313 | |||
314 | last_request = list_first_entry(®ulatory_requests, | ||
315 | struct regulatory_request, list); | ||
316 | |||
317 | switch (set_by) { | ||
318 | case REGDOM_SET_BY_INIT: | ||
319 | return -EINVAL; | ||
320 | case REGDOM_SET_BY_CORE: | ||
321 | /* Always respect new wireless core hints, should only | ||
322 | * come in for updating the world regulatory domain at init | ||
323 | * anyway */ | ||
324 | return 0; | ||
325 | case REGDOM_SET_BY_COUNTRY_IE: | ||
326 | if (last_request->initiator == set_by) { | ||
327 | if (last_request->wiphy != wiphy) { | ||
328 | /* Two cards with two APs claiming different | ||
329 | * different Country IE alpha2s! | ||
330 | * You're special!! */ | ||
331 | if (!alpha2_equal(last_request->alpha2, | ||
332 | cfg80211_regdomain->alpha2)) { | ||
333 | /* XXX: Deal with conflict, consider | ||
334 | * building a new one out of the | ||
335 | * intersection */ | ||
336 | WARN_ON(1); | ||
337 | return -EOPNOTSUPP; | ||
338 | } | ||
339 | return -EALREADY; | ||
340 | } | ||
341 | /* Two consecutive Country IE hints on the same wiphy */ | ||
342 | if (!alpha2_equal(cfg80211_regdomain->alpha2, alpha2)) | ||
343 | return 0; | ||
344 | return -EALREADY; | ||
345 | } | ||
346 | if (WARN(!is_alpha2_set(alpha2) || !is_an_alpha2(alpha2), | ||
347 | "Invalid Country IE regulatory hint passed " | ||
348 | "to the wireless core\n")) | ||
349 | return -EINVAL; | ||
350 | /* We ignore Country IE hints for now, as we haven't yet | ||
351 | * added the dot11MultiDomainCapabilityEnabled flag | ||
352 | * for wiphys */ | ||
353 | return 1; | ||
354 | case REGDOM_SET_BY_DRIVER: | ||
355 | BUG_ON(!wiphy); | ||
356 | if (last_request->initiator == set_by) { | ||
357 | /* Two separate drivers hinting different things, | ||
358 | * this is possible if you have two devices present | ||
359 | * on a system with different EEPROM regulatory | ||
360 | * readings. XXX: Do intersection, we support only | ||
361 | * the first regulatory hint for now */ | ||
362 | if (last_request->wiphy != wiphy) | ||
363 | return -EALREADY; | ||
364 | if (rd) | ||
365 | return -EALREADY; | ||
366 | /* Driver should not be trying to hint different | ||
367 | * regulatory domains! */ | ||
368 | BUG_ON(!alpha2_equal(alpha2, | ||
369 | cfg80211_regdomain->alpha2)); | ||
370 | return -EALREADY; | ||
371 | } | ||
372 | if (last_request->initiator == REGDOM_SET_BY_CORE) | ||
373 | return 0; | ||
374 | /* XXX: Handle intersection, and add the | ||
375 | * dot11MultiDomainCapabilityEnabled flag to wiphy. For now | ||
376 | * we assume the driver has this set to false, following the | ||
377 | * 802.11d dot11MultiDomainCapabilityEnabled documentation */ | ||
378 | if (last_request->initiator == REGDOM_SET_BY_COUNTRY_IE) | ||
379 | return 0; | ||
380 | return 0; | ||
381 | case REGDOM_SET_BY_USER: | ||
382 | if (last_request->initiator == set_by || | ||
383 | last_request->initiator == REGDOM_SET_BY_CORE) | ||
384 | return 0; | ||
385 | /* Drivers can use their wiphy's reg_notifier() | ||
386 | * to override any information */ | ||
387 | if (last_request->initiator == REGDOM_SET_BY_DRIVER) | ||
388 | return 0; | ||
389 | /* XXX: Handle intersection */ | ||
390 | if (last_request->initiator == REGDOM_SET_BY_COUNTRY_IE) | ||
391 | return -EOPNOTSUPP; | ||
392 | return 0; | ||
393 | default: | ||
394 | return -EINVAL; | ||
395 | } | ||
396 | } | ||
397 | |||
398 | static bool __reg_is_valid_request(const char *alpha2, | ||
399 | struct regulatory_request **request) | ||
400 | { | ||
401 | struct regulatory_request *req; | ||
402 | if (list_empty(®ulatory_requests)) | ||
403 | return false; | ||
404 | list_for_each_entry(req, ®ulatory_requests, list) { | ||
405 | if (alpha2_equal(req->alpha2, alpha2)) { | ||
406 | *request = req; | ||
407 | return true; | ||
408 | } | ||
409 | } | ||
410 | return false; | ||
411 | } | ||
412 | |||
413 | /* Used by nl80211 before kmalloc'ing our regulatory domain */ | 360 | /* Used by nl80211 before kmalloc'ing our regulatory domain */ |
414 | bool reg_is_valid_request(const char *alpha2) | 361 | bool reg_is_valid_request(const char *alpha2) |
415 | { | 362 | { |
416 | struct regulatory_request *request = NULL; | 363 | if (!last_request) |
417 | return __reg_is_valid_request(alpha2, &request); | 364 | return false; |
365 | |||
366 | return alpha2_equal(last_request->alpha2, alpha2); | ||
418 | } | 367 | } |
419 | 368 | ||
420 | /* Sanity check on a regulatory rule */ | 369 | /* Sanity check on a regulatory rule */ |
@@ -423,7 +372,7 @@ static bool is_valid_reg_rule(const struct ieee80211_reg_rule *rule) | |||
423 | const struct ieee80211_freq_range *freq_range = &rule->freq_range; | 372 | const struct ieee80211_freq_range *freq_range = &rule->freq_range; |
424 | u32 freq_diff; | 373 | u32 freq_diff; |
425 | 374 | ||
426 | if (freq_range->start_freq_khz == 0 || freq_range->end_freq_khz == 0) | 375 | if (freq_range->start_freq_khz <= 0 || freq_range->end_freq_khz <= 0) |
427 | return false; | 376 | return false; |
428 | 377 | ||
429 | if (freq_range->start_freq_khz > freq_range->end_freq_khz) | 378 | if (freq_range->start_freq_khz > freq_range->end_freq_khz) |
@@ -431,7 +380,7 @@ static bool is_valid_reg_rule(const struct ieee80211_reg_rule *rule) | |||
431 | 380 | ||
432 | freq_diff = freq_range->end_freq_khz - freq_range->start_freq_khz; | 381 | freq_diff = freq_range->end_freq_khz - freq_range->start_freq_khz; |
433 | 382 | ||
434 | if (freq_range->max_bandwidth_khz > freq_diff) | 383 | if (freq_diff <= 0 || freq_range->max_bandwidth_khz > freq_diff) |
435 | return false; | 384 | return false; |
436 | 385 | ||
437 | return true; | 386 | return true; |
@@ -445,6 +394,9 @@ static bool is_valid_rd(const struct ieee80211_regdomain *rd) | |||
445 | if (!rd->n_reg_rules) | 394 | if (!rd->n_reg_rules) |
446 | return false; | 395 | return false; |
447 | 396 | ||
397 | if (WARN_ON(rd->n_reg_rules > NL80211_MAX_SUPP_REG_RULES)) | ||
398 | return false; | ||
399 | |||
448 | for (i = 0; i < rd->n_reg_rules; i++) { | 400 | for (i = 0; i < rd->n_reg_rules; i++) { |
449 | reg_rule = &rd->reg_rules[i]; | 401 | reg_rule = &rd->reg_rules[i]; |
450 | if (!is_valid_reg_rule(reg_rule)) | 402 | if (!is_valid_reg_rule(reg_rule)) |
@@ -469,6 +421,311 @@ static u32 freq_max_bandwidth(const struct ieee80211_freq_range *freq_range, | |||
469 | return 0; | 421 | return 0; |
470 | } | 422 | } |
471 | 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. */ | ||
428 | static 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 = ®_rule->freq_range; | ||
558 | power_rule = ®_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 | |||
592 | /* Helper for regdom_intersect(), this does the real | ||
593 | * mathematical intersection fun */ | ||
594 | static int reg_rules_intersect( | ||
595 | const struct ieee80211_reg_rule *rule1, | ||
596 | const struct ieee80211_reg_rule *rule2, | ||
597 | struct ieee80211_reg_rule *intersected_rule) | ||
598 | { | ||
599 | const struct ieee80211_freq_range *freq_range1, *freq_range2; | ||
600 | struct ieee80211_freq_range *freq_range; | ||
601 | const struct ieee80211_power_rule *power_rule1, *power_rule2; | ||
602 | struct ieee80211_power_rule *power_rule; | ||
603 | u32 freq_diff; | ||
604 | |||
605 | freq_range1 = &rule1->freq_range; | ||
606 | freq_range2 = &rule2->freq_range; | ||
607 | freq_range = &intersected_rule->freq_range; | ||
608 | |||
609 | power_rule1 = &rule1->power_rule; | ||
610 | power_rule2 = &rule2->power_rule; | ||
611 | power_rule = &intersected_rule->power_rule; | ||
612 | |||
613 | freq_range->start_freq_khz = max(freq_range1->start_freq_khz, | ||
614 | freq_range2->start_freq_khz); | ||
615 | freq_range->end_freq_khz = min(freq_range1->end_freq_khz, | ||
616 | freq_range2->end_freq_khz); | ||
617 | freq_range->max_bandwidth_khz = min(freq_range1->max_bandwidth_khz, | ||
618 | freq_range2->max_bandwidth_khz); | ||
619 | |||
620 | freq_diff = freq_range->end_freq_khz - freq_range->start_freq_khz; | ||
621 | if (freq_range->max_bandwidth_khz > freq_diff) | ||
622 | freq_range->max_bandwidth_khz = freq_diff; | ||
623 | |||
624 | power_rule->max_eirp = min(power_rule1->max_eirp, | ||
625 | power_rule2->max_eirp); | ||
626 | power_rule->max_antenna_gain = min(power_rule1->max_antenna_gain, | ||
627 | power_rule2->max_antenna_gain); | ||
628 | |||
629 | intersected_rule->flags = (rule1->flags | rule2->flags); | ||
630 | |||
631 | if (!is_valid_reg_rule(intersected_rule)) | ||
632 | return -EINVAL; | ||
633 | |||
634 | return 0; | ||
635 | } | ||
636 | |||
637 | /** | ||
638 | * regdom_intersect - do the intersection between two regulatory domains | ||
639 | * @rd1: first regulatory domain | ||
640 | * @rd2: second regulatory domain | ||
641 | * | ||
642 | * Use this function to get the intersection between two regulatory domains. | ||
643 | * Once completed we will mark the alpha2 for the rd as intersected, "98", | ||
644 | * as no one single alpha2 can represent this regulatory domain. | ||
645 | * | ||
646 | * Returns a pointer to the regulatory domain structure which will hold the | ||
647 | * resulting intersection of rules between rd1 and rd2. We will | ||
648 | * kzalloc() this structure for you. | ||
649 | */ | ||
650 | static struct ieee80211_regdomain *regdom_intersect( | ||
651 | const struct ieee80211_regdomain *rd1, | ||
652 | const struct ieee80211_regdomain *rd2) | ||
653 | { | ||
654 | int r, size_of_regd; | ||
655 | unsigned int x, y; | ||
656 | unsigned int num_rules = 0, rule_idx = 0; | ||
657 | const struct ieee80211_reg_rule *rule1, *rule2; | ||
658 | struct ieee80211_reg_rule *intersected_rule; | ||
659 | struct ieee80211_regdomain *rd; | ||
660 | /* This is just a dummy holder to help us count */ | ||
661 | struct ieee80211_reg_rule irule; | ||
662 | |||
663 | /* Uses the stack temporarily for counter arithmetic */ | ||
664 | intersected_rule = &irule; | ||
665 | |||
666 | memset(intersected_rule, 0, sizeof(struct ieee80211_reg_rule)); | ||
667 | |||
668 | if (!rd1 || !rd2) | ||
669 | return NULL; | ||
670 | |||
671 | /* First we get a count of the rules we'll need, then we actually | ||
672 | * build them. This is to so we can malloc() and free() a | ||
673 | * regdomain once. The reason we use reg_rules_intersect() here | ||
674 | * is it will return -EINVAL if the rule computed makes no sense. | ||
675 | * All rules that do check out OK are valid. */ | ||
676 | |||
677 | for (x = 0; x < rd1->n_reg_rules; x++) { | ||
678 | rule1 = &rd1->reg_rules[x]; | ||
679 | for (y = 0; y < rd2->n_reg_rules; y++) { | ||
680 | rule2 = &rd2->reg_rules[y]; | ||
681 | if (!reg_rules_intersect(rule1, rule2, | ||
682 | intersected_rule)) | ||
683 | num_rules++; | ||
684 | memset(intersected_rule, 0, | ||
685 | sizeof(struct ieee80211_reg_rule)); | ||
686 | } | ||
687 | } | ||
688 | |||
689 | if (!num_rules) | ||
690 | return NULL; | ||
691 | |||
692 | size_of_regd = sizeof(struct ieee80211_regdomain) + | ||
693 | ((num_rules + 1) * sizeof(struct ieee80211_reg_rule)); | ||
694 | |||
695 | rd = kzalloc(size_of_regd, GFP_KERNEL); | ||
696 | if (!rd) | ||
697 | return NULL; | ||
698 | |||
699 | for (x = 0; x < rd1->n_reg_rules; x++) { | ||
700 | rule1 = &rd1->reg_rules[x]; | ||
701 | for (y = 0; y < rd2->n_reg_rules; y++) { | ||
702 | rule2 = &rd2->reg_rules[y]; | ||
703 | /* This time around instead of using the stack lets | ||
704 | * write to the target rule directly saving ourselves | ||
705 | * a memcpy() */ | ||
706 | intersected_rule = &rd->reg_rules[rule_idx]; | ||
707 | r = reg_rules_intersect(rule1, rule2, | ||
708 | intersected_rule); | ||
709 | /* No need to memset here the intersected rule here as | ||
710 | * we're not using the stack anymore */ | ||
711 | if (r) | ||
712 | continue; | ||
713 | rule_idx++; | ||
714 | } | ||
715 | } | ||
716 | |||
717 | if (rule_idx != num_rules) { | ||
718 | kfree(rd); | ||
719 | return NULL; | ||
720 | } | ||
721 | |||
722 | rd->n_reg_rules = num_rules; | ||
723 | rd->alpha2[0] = '9'; | ||
724 | rd->alpha2[1] = '8'; | ||
725 | |||
726 | return rd; | ||
727 | } | ||
728 | |||
472 | /* XXX: add support for the rest of enum nl80211_reg_rule_flags, we may | 729 | /* XXX: add support for the rest of enum nl80211_reg_rule_flags, we may |
473 | * want to just have the channel structure use these */ | 730 | * want to just have the channel structure use these */ |
474 | static u32 map_regdom_flags(u32 rd_flags) | 731 | static u32 map_regdom_flags(u32 rd_flags) |
@@ -559,12 +816,23 @@ static void handle_band(struct ieee80211_supported_band *sband) | |||
559 | handle_channel(&sband->channels[i]); | 816 | handle_channel(&sband->channels[i]); |
560 | } | 817 | } |
561 | 818 | ||
819 | static bool ignore_reg_update(struct wiphy *wiphy, enum reg_set_by setby) | ||
820 | { | ||
821 | if (!last_request) | ||
822 | return true; | ||
823 | if (setby == REGDOM_SET_BY_CORE && | ||
824 | wiphy->fw_handles_regulatory) | ||
825 | return true; | ||
826 | return false; | ||
827 | } | ||
828 | |||
562 | static void update_all_wiphy_regulatory(enum reg_set_by setby) | 829 | static void update_all_wiphy_regulatory(enum reg_set_by setby) |
563 | { | 830 | { |
564 | struct cfg80211_registered_device *drv; | 831 | struct cfg80211_registered_device *drv; |
565 | 832 | ||
566 | list_for_each_entry(drv, &cfg80211_drv_list, list) | 833 | list_for_each_entry(drv, &cfg80211_drv_list, list) |
567 | wiphy_update_regulatory(&drv->wiphy, setby); | 834 | if (!ignore_reg_update(&drv->wiphy, setby)) |
835 | wiphy_update_regulatory(&drv->wiphy, setby); | ||
568 | } | 836 | } |
569 | 837 | ||
570 | void wiphy_update_regulatory(struct wiphy *wiphy, enum reg_set_by setby) | 838 | void wiphy_update_regulatory(struct wiphy *wiphy, enum reg_set_by setby) |
@@ -578,78 +846,237 @@ void wiphy_update_regulatory(struct wiphy *wiphy, enum reg_set_by setby) | |||
578 | } | 846 | } |
579 | } | 847 | } |
580 | 848 | ||
581 | /* Caller must hold &cfg80211_drv_mutex */ | 849 | /* Return value which can be used by ignore_request() to indicate |
582 | int __regulatory_hint(struct wiphy *wiphy, enum reg_set_by set_by, | 850 | * it has been determined we should intersect two regulatory domains */ |
583 | const char *alpha2, struct ieee80211_regdomain *rd) | 851 | #define REG_INTERSECT 1 |
584 | { | ||
585 | struct regulatory_request *request; | ||
586 | char *rd_alpha2; | ||
587 | int r = 0; | ||
588 | |||
589 | r = ignore_request(wiphy, set_by, (char *) alpha2, rd); | ||
590 | if (r) | ||
591 | return r; | ||
592 | 852 | ||
593 | if (rd) | 853 | /* This has the logic which determines when a new request |
594 | rd_alpha2 = rd->alpha2; | 854 | * should be ignored. */ |
595 | else | 855 | static int ignore_request(struct wiphy *wiphy, enum reg_set_by set_by, |
596 | rd_alpha2 = (char *) alpha2; | 856 | const char *alpha2) |
857 | { | ||
858 | /* All initial requests are respected */ | ||
859 | if (!last_request) | ||
860 | return 0; | ||
597 | 861 | ||
598 | switch (set_by) { | 862 | switch (set_by) { |
863 | case REGDOM_SET_BY_INIT: | ||
864 | return -EINVAL; | ||
599 | case REGDOM_SET_BY_CORE: | 865 | case REGDOM_SET_BY_CORE: |
866 | /* | ||
867 | * Always respect new wireless core hints, should only happen | ||
868 | * when updating the world regulatory domain at init. | ||
869 | */ | ||
870 | return 0; | ||
600 | case REGDOM_SET_BY_COUNTRY_IE: | 871 | case REGDOM_SET_BY_COUNTRY_IE: |
872 | if (unlikely(!is_an_alpha2(alpha2))) | ||
873 | return -EINVAL; | ||
874 | if (last_request->initiator == REGDOM_SET_BY_COUNTRY_IE) { | ||
875 | if (last_request->wiphy != wiphy) { | ||
876 | /* | ||
877 | * Two cards with two APs claiming different | ||
878 | * different Country IE alpha2s. We could | ||
879 | * intersect them, but that seems unlikely | ||
880 | * to be correct. Reject second one for now. | ||
881 | */ | ||
882 | if (!alpha2_equal(alpha2, | ||
883 | cfg80211_regdomain->alpha2)) | ||
884 | return -EOPNOTSUPP; | ||
885 | return -EALREADY; | ||
886 | } | ||
887 | /* Two consecutive Country IE hints on the same wiphy. | ||
888 | * This should be picked up early by the driver/stack */ | ||
889 | if (WARN_ON(!alpha2_equal(cfg80211_regdomain->alpha2, | ||
890 | alpha2))) | ||
891 | return 0; | ||
892 | return -EALREADY; | ||
893 | } | ||
894 | return REG_INTERSECT; | ||
601 | case REGDOM_SET_BY_DRIVER: | 895 | case REGDOM_SET_BY_DRIVER: |
896 | if (last_request->initiator == REGDOM_SET_BY_DRIVER) | ||
897 | return -EALREADY; | ||
898 | return 0; | ||
602 | case REGDOM_SET_BY_USER: | 899 | case REGDOM_SET_BY_USER: |
603 | request = kzalloc(sizeof(struct regulatory_request), | 900 | if (last_request->initiator == REGDOM_SET_BY_COUNTRY_IE) |
604 | GFP_KERNEL); | 901 | return REG_INTERSECT; |
605 | if (!request) | 902 | /* If the user knows better the user should set the regdom |
606 | return -ENOMEM; | 903 | * to their country before the IE is picked up */ |
607 | 904 | if (last_request->initiator == REGDOM_SET_BY_USER && | |
608 | request->alpha2[0] = rd_alpha2[0]; | 905 | last_request->intersect) |
609 | request->alpha2[1] = rd_alpha2[1]; | 906 | return -EOPNOTSUPP; |
610 | request->initiator = set_by; | 907 | return 0; |
611 | request->wiphy = wiphy; | ||
612 | |||
613 | list_add_tail(&request->list, ®ulatory_requests); | ||
614 | if (rd) | ||
615 | break; | ||
616 | r = call_crda(alpha2); | ||
617 | #ifndef CONFIG_WIRELESS_OLD_REGULATORY | ||
618 | if (r) | ||
619 | printk(KERN_ERR "cfg80211: Failed calling CRDA\n"); | ||
620 | #endif | ||
621 | break; | ||
622 | default: | ||
623 | r = -ENOTSUPP; | ||
624 | break; | ||
625 | } | 908 | } |
626 | 909 | ||
627 | return r; | 910 | return -EINVAL; |
628 | } | 911 | } |
629 | 912 | ||
630 | /* If rd is not NULL and if this call fails the caller must free it */ | 913 | /* Caller must hold &cfg80211_drv_mutex */ |
631 | int regulatory_hint(struct wiphy *wiphy, const char *alpha2, | 914 | int __regulatory_hint(struct wiphy *wiphy, enum reg_set_by set_by, |
632 | struct ieee80211_regdomain *rd) | 915 | const char *alpha2, |
916 | u32 country_ie_checksum, | ||
917 | enum environment_cap env) | ||
633 | { | 918 | { |
634 | int r; | 919 | struct regulatory_request *request; |
635 | BUG_ON(!rd && !alpha2); | 920 | bool intersect = false; |
921 | int r = 0; | ||
636 | 922 | ||
637 | mutex_lock(&cfg80211_drv_mutex); | 923 | r = ignore_request(wiphy, set_by, alpha2); |
638 | 924 | ||
639 | r = __regulatory_hint(wiphy, REGDOM_SET_BY_DRIVER, alpha2, rd); | 925 | if (r == REG_INTERSECT) |
640 | if (r || !rd) | 926 | intersect = true; |
641 | goto unlock_and_exit; | 927 | else if (r) |
928 | return r; | ||
642 | 929 | ||
643 | /* If the driver passed a regulatory domain we skipped asking | 930 | request = kzalloc(sizeof(struct regulatory_request), |
644 | * userspace for one so we can now go ahead and set it */ | 931 | GFP_KERNEL); |
645 | r = set_regdom(rd); | 932 | if (!request) |
933 | return -ENOMEM; | ||
934 | |||
935 | request->alpha2[0] = alpha2[0]; | ||
936 | request->alpha2[1] = alpha2[1]; | ||
937 | request->initiator = set_by; | ||
938 | request->wiphy = wiphy; | ||
939 | request->intersect = intersect; | ||
940 | request->country_ie_checksum = country_ie_checksum; | ||
941 | request->country_ie_env = env; | ||
942 | |||
943 | kfree(last_request); | ||
944 | last_request = request; | ||
945 | /* | ||
946 | * Note: When CONFIG_WIRELESS_OLD_REGULATORY is enabled | ||
947 | * AND if CRDA is NOT present nothing will happen, if someone | ||
948 | * wants to bother with 11d with OLD_REG you can add a timer. | ||
949 | * If after x amount of time nothing happens you can call: | ||
950 | * | ||
951 | * return set_regdom(country_ie_regdomain); | ||
952 | * | ||
953 | * to intersect with the static rd | ||
954 | */ | ||
955 | return call_crda(alpha2); | ||
956 | } | ||
957 | |||
958 | void regulatory_hint(struct wiphy *wiphy, const char *alpha2) | ||
959 | { | ||
960 | BUG_ON(!alpha2); | ||
646 | 961 | ||
647 | unlock_and_exit: | 962 | mutex_lock(&cfg80211_drv_mutex); |
963 | __regulatory_hint(wiphy, REGDOM_SET_BY_DRIVER, alpha2, 0, ENVIRON_ANY); | ||
648 | mutex_unlock(&cfg80211_drv_mutex); | 964 | mutex_unlock(&cfg80211_drv_mutex); |
649 | return r; | ||
650 | } | 965 | } |
651 | EXPORT_SYMBOL(regulatory_hint); | 966 | EXPORT_SYMBOL(regulatory_hint); |
652 | 967 | ||
968 | static bool reg_same_country_ie_hint(struct wiphy *wiphy, | ||
969 | u32 country_ie_checksum) | ||
970 | { | ||
971 | if (!last_request->wiphy) | ||
972 | return false; | ||
973 | if (likely(last_request->wiphy != wiphy)) | ||
974 | return !country_ie_integrity_changes(country_ie_checksum); | ||
975 | /* We should not have let these through at this point, they | ||
976 | * should have been picked up earlier by the first alpha2 check | ||
977 | * on the device */ | ||
978 | if (WARN_ON(!country_ie_integrity_changes(country_ie_checksum))) | ||
979 | return true; | ||
980 | return false; | ||
981 | } | ||
982 | |||
983 | void regulatory_hint_11d(struct wiphy *wiphy, | ||
984 | u8 *country_ie, | ||
985 | u8 country_ie_len) | ||
986 | { | ||
987 | struct ieee80211_regdomain *rd = NULL; | ||
988 | char alpha2[2]; | ||
989 | u32 checksum = 0; | ||
990 | enum environment_cap env = ENVIRON_ANY; | ||
991 | |||
992 | if (!last_request) | ||
993 | return; | ||
994 | |||
995 | mutex_lock(&cfg80211_drv_mutex); | ||
996 | |||
997 | /* IE len must be evenly divisible by 2 */ | ||
998 | if (country_ie_len & 0x01) | ||
999 | goto out; | ||
1000 | |||
1001 | if (country_ie_len < IEEE80211_COUNTRY_IE_MIN_LEN) | ||
1002 | goto out; | ||
1003 | |||
1004 | /* Pending country IE processing, this can happen after we | ||
1005 | * call CRDA and wait for a response if a beacon was received before | ||
1006 | * we were able to process the last regulatory_hint_11d() call */ | ||
1007 | if (country_ie_regdomain) | ||
1008 | goto out; | ||
1009 | |||
1010 | alpha2[0] = country_ie[0]; | ||
1011 | alpha2[1] = country_ie[1]; | ||
1012 | |||
1013 | if (country_ie[2] == 'I') | ||
1014 | env = ENVIRON_INDOOR; | ||
1015 | else if (country_ie[2] == 'O') | ||
1016 | env = ENVIRON_OUTDOOR; | ||
1017 | |||
1018 | /* We will run this for *every* beacon processed for the BSSID, so | ||
1019 | * we optimize an early check to exit out early if we don't have to | ||
1020 | * do anything */ | ||
1021 | if (likely(last_request->wiphy)) { | ||
1022 | struct cfg80211_registered_device *drv_last_ie; | ||
1023 | |||
1024 | drv_last_ie = wiphy_to_dev(last_request->wiphy); | ||
1025 | |||
1026 | /* Lets keep this simple -- we trust the first AP | ||
1027 | * after we intersect with CRDA */ | ||
1028 | if (likely(last_request->wiphy == wiphy)) { | ||
1029 | /* Ignore IEs coming in on this wiphy 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 | /* the wiphy moved on to another BSSID or the AP | ||
1037 | * was reconfigured. XXX: We need to deal with the | ||
1038 | * case where the user suspends and goes to goes | ||
1039 | * to another country, and then gets IEs from an | ||
1040 | * AP with different settings */ | ||
1041 | goto out; | ||
1042 | } else { | ||
1043 | /* Ignore IEs coming in on two separate wiphys with | ||
1044 | * the same alpha2 and environment cap */ | ||
1045 | if (likely(alpha2_equal(drv_last_ie->country_ie_alpha2, | ||
1046 | alpha2) && | ||
1047 | env == drv_last_ie->env)) { | ||
1048 | goto out; | ||
1049 | } | ||
1050 | /* We could potentially intersect though */ | ||
1051 | goto out; | ||
1052 | } | ||
1053 | } | ||
1054 | |||
1055 | rd = country_ie_2_rd(country_ie, country_ie_len, &checksum); | ||
1056 | if (!rd) | ||
1057 | goto out; | ||
1058 | |||
1059 | /* This will not happen right now but we leave it here for the | ||
1060 | * the future when we want to add suspend/resume support and having | ||
1061 | * the user move to another country after doing so, or having the user | ||
1062 | * move to another AP. Right now we just trust the first AP. This is why | ||
1063 | * this is marked as likley(). If we hit this before we add this support | ||
1064 | * we want to be informed of it as it would indicate a mistake in the | ||
1065 | * current design */ | ||
1066 | if (likely(WARN_ON(reg_same_country_ie_hint(wiphy, checksum)))) | ||
1067 | goto out; | ||
1068 | |||
1069 | /* We keep this around for when CRDA comes back with a response so | ||
1070 | * we can intersect with that */ | ||
1071 | country_ie_regdomain = rd; | ||
1072 | |||
1073 | __regulatory_hint(wiphy, REGDOM_SET_BY_COUNTRY_IE, | ||
1074 | country_ie_regdomain->alpha2, checksum, env); | ||
1075 | |||
1076 | out: | ||
1077 | mutex_unlock(&cfg80211_drv_mutex); | ||
1078 | } | ||
1079 | EXPORT_SYMBOL(regulatory_hint_11d); | ||
653 | 1080 | ||
654 | static void print_rd_rules(const struct ieee80211_regdomain *rd) | 1081 | static void print_rd_rules(const struct ieee80211_regdomain *rd) |
655 | { | 1082 | { |
@@ -689,7 +1116,25 @@ static void print_rd_rules(const struct ieee80211_regdomain *rd) | |||
689 | static void print_regdomain(const struct ieee80211_regdomain *rd) | 1116 | static void print_regdomain(const struct ieee80211_regdomain *rd) |
690 | { | 1117 | { |
691 | 1118 | ||
692 | if (is_world_regdom(rd->alpha2)) | 1119 | if (is_intersected_alpha2(rd->alpha2)) { |
1120 | struct wiphy *wiphy = NULL; | ||
1121 | struct cfg80211_registered_device *drv; | ||
1122 | |||
1123 | if (last_request->initiator == REGDOM_SET_BY_COUNTRY_IE) { | ||
1124 | if (last_request->wiphy) { | ||
1125 | wiphy = last_request->wiphy; | ||
1126 | drv = wiphy_to_dev(wiphy); | ||
1127 | printk(KERN_INFO "cfg80211: Current regulatory " | ||
1128 | "domain updated by AP to: %c%c\n", | ||
1129 | drv->country_ie_alpha2[0], | ||
1130 | drv->country_ie_alpha2[1]); | ||
1131 | } else | ||
1132 | printk(KERN_INFO "cfg80211: Current regulatory " | ||
1133 | "domain intersected: \n"); | ||
1134 | } else | ||
1135 | printk(KERN_INFO "cfg80211: Current regulatory " | ||
1136 | "intersected: \n"); | ||
1137 | } else if (is_world_regdom(rd->alpha2)) | ||
693 | printk(KERN_INFO "cfg80211: World regulatory " | 1138 | printk(KERN_INFO "cfg80211: World regulatory " |
694 | "domain updated:\n"); | 1139 | "domain updated:\n"); |
695 | else { | 1140 | else { |
@@ -705,21 +1150,50 @@ static void print_regdomain(const struct ieee80211_regdomain *rd) | |||
705 | print_rd_rules(rd); | 1150 | print_rd_rules(rd); |
706 | } | 1151 | } |
707 | 1152 | ||
708 | void print_regdomain_info(const struct ieee80211_regdomain *rd) | 1153 | static void print_regdomain_info(const struct ieee80211_regdomain *rd) |
709 | { | 1154 | { |
710 | printk(KERN_INFO "cfg80211: Regulatory domain: %c%c\n", | 1155 | printk(KERN_INFO "cfg80211: Regulatory domain: %c%c\n", |
711 | rd->alpha2[0], rd->alpha2[1]); | 1156 | rd->alpha2[0], rd->alpha2[1]); |
712 | print_rd_rules(rd); | 1157 | print_rd_rules(rd); |
713 | } | 1158 | } |
714 | 1159 | ||
715 | static int __set_regdom(const struct ieee80211_regdomain *rd) | 1160 | #ifdef CONFIG_CFG80211_REG_DEBUG |
1161 | static void reg_country_ie_process_debug( | ||
1162 | const struct ieee80211_regdomain *rd, | ||
1163 | const struct ieee80211_regdomain *country_ie_regdomain, | ||
1164 | const struct ieee80211_regdomain *intersected_rd) | ||
716 | { | 1165 | { |
717 | struct regulatory_request *request = NULL; | 1166 | printk(KERN_DEBUG "cfg80211: Received country IE:\n"); |
1167 | print_regdomain_info(country_ie_regdomain); | ||
1168 | printk(KERN_DEBUG "cfg80211: CRDA thinks this should applied:\n"); | ||
1169 | print_regdomain_info(rd); | ||
1170 | if (intersected_rd) { | ||
1171 | printk(KERN_DEBUG "cfg80211: We intersect both of these " | ||
1172 | "and get:\n"); | ||
1173 | print_regdomain_info(rd); | ||
1174 | return; | ||
1175 | } | ||
1176 | printk(KERN_DEBUG "cfg80211: Intersection between both failed\n"); | ||
1177 | } | ||
1178 | #else | ||
1179 | static inline void reg_country_ie_process_debug( | ||
1180 | const struct ieee80211_regdomain *rd, | ||
1181 | const struct ieee80211_regdomain *country_ie_regdomain, | ||
1182 | const struct ieee80211_regdomain *intersected_rd) | ||
1183 | { | ||
1184 | } | ||
1185 | #endif | ||
718 | 1186 | ||
1187 | /* Takes ownership of rd only if it doesn't fail */ | ||
1188 | static int __set_regdom(const struct ieee80211_regdomain *rd) | ||
1189 | { | ||
1190 | const struct ieee80211_regdomain *intersected_rd = NULL; | ||
1191 | struct cfg80211_registered_device *drv = NULL; | ||
1192 | struct wiphy *wiphy = NULL; | ||
719 | /* Some basic sanity checks first */ | 1193 | /* Some basic sanity checks first */ |
720 | 1194 | ||
721 | if (is_world_regdom(rd->alpha2)) { | 1195 | if (is_world_regdom(rd->alpha2)) { |
722 | if (WARN_ON(!__reg_is_valid_request(rd->alpha2, &request))) | 1196 | if (WARN_ON(!reg_is_valid_request(rd->alpha2))) |
723 | return -EINVAL; | 1197 | return -EINVAL; |
724 | update_world_regdomain(rd); | 1198 | update_world_regdomain(rd); |
725 | return 0; | 1199 | return 0; |
@@ -729,45 +1203,102 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) | |||
729 | !is_unknown_alpha2(rd->alpha2)) | 1203 | !is_unknown_alpha2(rd->alpha2)) |
730 | return -EINVAL; | 1204 | return -EINVAL; |
731 | 1205 | ||
732 | if (list_empty(®ulatory_requests)) | 1206 | if (!last_request) |
733 | return -EINVAL; | 1207 | return -EINVAL; |
734 | 1208 | ||
735 | /* allow overriding the static definitions if CRDA is present */ | 1209 | /* Lets only bother proceeding on the same alpha2 if the current |
736 | if (!is_old_static_regdom(cfg80211_regdomain) && | 1210 | * rd is non static (it means CRDA was present and was used last) |
737 | !regdom_changed(rd->alpha2)) | 1211 | * and the pending request came in from a country IE */ |
738 | return -EINVAL; | 1212 | if (last_request->initiator != REGDOM_SET_BY_COUNTRY_IE) { |
1213 | /* If someone else asked us to change the rd lets only bother | ||
1214 | * checking if the alpha2 changes if CRDA was already called */ | ||
1215 | if (!is_old_static_regdom(cfg80211_regdomain) && | ||
1216 | !regdom_changed(rd->alpha2)) | ||
1217 | return -EINVAL; | ||
1218 | } | ||
1219 | |||
1220 | wiphy = last_request->wiphy; | ||
739 | 1221 | ||
740 | /* Now lets set the regulatory domain, update all driver channels | 1222 | /* Now lets set the regulatory domain, update all driver channels |
741 | * and finally inform them of what we have done, in case they want | 1223 | * and finally inform them of what we have done, in case they want |
742 | * to review or adjust their own settings based on their own | 1224 | * to review or adjust their own settings based on their own |
743 | * internal EEPROM data */ | 1225 | * internal EEPROM data */ |
744 | 1226 | ||
745 | if (WARN_ON(!__reg_is_valid_request(rd->alpha2, &request))) | 1227 | if (WARN_ON(!reg_is_valid_request(rd->alpha2))) |
746 | return -EINVAL; | 1228 | return -EINVAL; |
747 | 1229 | ||
748 | reset_regdomains(); | 1230 | if (!is_valid_rd(rd)) { |
1231 | printk(KERN_ERR "cfg80211: Invalid " | ||
1232 | "regulatory domain detected:\n"); | ||
1233 | print_regdomain_info(rd); | ||
1234 | return -EINVAL; | ||
1235 | } | ||
749 | 1236 | ||
750 | /* Country IE parsing coming soon */ | 1237 | if (!last_request->intersect) { |
751 | switch (request->initiator) { | 1238 | reset_regdomains(); |
752 | case REGDOM_SET_BY_CORE: | 1239 | cfg80211_regdomain = rd; |
753 | case REGDOM_SET_BY_DRIVER: | 1240 | return 0; |
754 | case REGDOM_SET_BY_USER: | 1241 | } |
755 | if (!is_valid_rd(rd)) { | 1242 | |
756 | printk(KERN_ERR "cfg80211: Invalid " | 1243 | /* Intersection requires a bit more work */ |
757 | "regulatory domain detected:\n"); | 1244 | |
758 | print_regdomain_info(rd); | 1245 | if (last_request->initiator != REGDOM_SET_BY_COUNTRY_IE) { |
1246 | |||
1247 | intersected_rd = regdom_intersect(rd, cfg80211_regdomain); | ||
1248 | if (!intersected_rd) | ||
759 | return -EINVAL; | 1249 | return -EINVAL; |
760 | } | 1250 | |
761 | break; | 1251 | /* We can trash what CRDA provided now */ |
762 | case REGDOM_SET_BY_COUNTRY_IE: /* Not yet */ | 1252 | kfree(rd); |
763 | WARN_ON(1); | 1253 | rd = NULL; |
764 | default: | 1254 | |
765 | return -EOPNOTSUPP; | 1255 | reset_regdomains(); |
1256 | cfg80211_regdomain = intersected_rd; | ||
1257 | |||
1258 | return 0; | ||
766 | } | 1259 | } |
767 | 1260 | ||
768 | /* Tada! */ | 1261 | /* |
769 | cfg80211_regdomain = rd; | 1262 | * Country IE requests are handled a bit differently, we intersect |
770 | request->granted = 1; | 1263 | * the country IE rd with what CRDA believes that country should have |
1264 | */ | ||
1265 | |||
1266 | BUG_ON(!country_ie_regdomain); | ||
1267 | |||
1268 | if (rd != country_ie_regdomain) { | ||
1269 | /* Intersect what CRDA returned and our what we | ||
1270 | * had built from the Country IE received */ | ||
1271 | |||
1272 | intersected_rd = regdom_intersect(rd, country_ie_regdomain); | ||
1273 | |||
1274 | reg_country_ie_process_debug(rd, country_ie_regdomain, | ||
1275 | intersected_rd); | ||
1276 | |||
1277 | kfree(country_ie_regdomain); | ||
1278 | country_ie_regdomain = NULL; | ||
1279 | } else { | ||
1280 | /* This would happen when CRDA was not present and | ||
1281 | * OLD_REGULATORY was enabled. We intersect our Country | ||
1282 | * IE rd and what was set on cfg80211 originally */ | ||
1283 | intersected_rd = regdom_intersect(rd, cfg80211_regdomain); | ||
1284 | } | ||
1285 | |||
1286 | if (!intersected_rd) | ||
1287 | return -EINVAL; | ||
1288 | |||
1289 | drv = wiphy_to_dev(wiphy); | ||
1290 | |||
1291 | drv->country_ie_alpha2[0] = rd->alpha2[0]; | ||
1292 | drv->country_ie_alpha2[1] = rd->alpha2[1]; | ||
1293 | drv->env = last_request->country_ie_env; | ||
1294 | |||
1295 | BUG_ON(intersected_rd == rd); | ||
1296 | |||
1297 | kfree(rd); | ||
1298 | rd = NULL; | ||
1299 | |||
1300 | reset_regdomains(); | ||
1301 | cfg80211_regdomain = intersected_rd; | ||
771 | 1302 | ||
772 | return 0; | 1303 | return 0; |
773 | } | 1304 | } |
@@ -775,52 +1306,41 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) | |||
775 | 1306 | ||
776 | /* Use this call to set the current regulatory domain. Conflicts with | 1307 | /* Use this call to set the current regulatory domain. Conflicts with |
777 | * multiple drivers can be ironed out later. Caller must've already | 1308 | * multiple drivers can be ironed out later. Caller must've already |
778 | * kmalloc'd the rd structure. If this calls fails you should kfree() | 1309 | * kmalloc'd the rd structure. Caller must hold cfg80211_drv_mutex */ |
779 | * the passed rd. Caller must hold cfg80211_drv_mutex */ | ||
780 | int set_regdom(const struct ieee80211_regdomain *rd) | 1310 | int set_regdom(const struct ieee80211_regdomain *rd) |
781 | { | 1311 | { |
782 | struct regulatory_request *this_request = NULL, *prev_request = NULL; | ||
783 | int r; | 1312 | int r; |
784 | 1313 | ||
785 | if (!list_empty(®ulatory_requests)) | ||
786 | prev_request = list_first_entry(®ulatory_requests, | ||
787 | struct regulatory_request, list); | ||
788 | |||
789 | /* Note that this doesn't update the wiphys, this is done below */ | 1314 | /* Note that this doesn't update the wiphys, this is done below */ |
790 | r = __set_regdom(rd); | 1315 | r = __set_regdom(rd); |
791 | if (r) | 1316 | if (r) { |
1317 | kfree(rd); | ||
792 | return r; | 1318 | return r; |
793 | |||
794 | BUG_ON((!__reg_is_valid_request(rd->alpha2, &this_request))); | ||
795 | |||
796 | /* The initial standard core update of the world regulatory domain, no | ||
797 | * need to keep that request info around if it didn't fail. */ | ||
798 | if (is_world_regdom(rd->alpha2) && | ||
799 | this_request->initiator == REGDOM_SET_BY_CORE && | ||
800 | this_request->granted) { | ||
801 | list_del(&this_request->list); | ||
802 | kfree(this_request); | ||
803 | this_request = NULL; | ||
804 | } | ||
805 | |||
806 | /* Remove old requests, we only leave behind the last one */ | ||
807 | if (prev_request) { | ||
808 | list_del(&prev_request->list); | ||
809 | kfree(prev_request); | ||
810 | prev_request = NULL; | ||
811 | } | 1319 | } |
812 | 1320 | ||
813 | /* This would make this whole thing pointless */ | 1321 | /* This would make this whole thing pointless */ |
814 | BUG_ON(rd != cfg80211_regdomain); | 1322 | if (!last_request->intersect) |
1323 | BUG_ON(rd != cfg80211_regdomain); | ||
815 | 1324 | ||
816 | /* update all wiphys now with the new established regulatory domain */ | 1325 | /* update all wiphys now with the new established regulatory domain */ |
817 | update_all_wiphy_regulatory(this_request->initiator); | 1326 | update_all_wiphy_regulatory(last_request->initiator); |
818 | 1327 | ||
819 | print_regdomain(rd); | 1328 | print_regdomain(cfg80211_regdomain); |
820 | 1329 | ||
821 | return r; | 1330 | return r; |
822 | } | 1331 | } |
823 | 1332 | ||
1333 | /* Caller must hold cfg80211_drv_mutex */ | ||
1334 | void reg_device_remove(struct wiphy *wiphy) | ||
1335 | { | ||
1336 | if (!last_request || !last_request->wiphy) | ||
1337 | return; | ||
1338 | if (last_request->wiphy != wiphy) | ||
1339 | return; | ||
1340 | last_request->wiphy = NULL; | ||
1341 | last_request->country_ie_env = ENVIRON_ANY; | ||
1342 | } | ||
1343 | |||
824 | int regulatory_init(void) | 1344 | int regulatory_init(void) |
825 | { | 1345 | { |
826 | int err; | 1346 | int err; |
@@ -838,13 +1358,13 @@ int regulatory_init(void) | |||
838 | * you have CRDA you get it updated, otherwise you get | 1358 | * you have CRDA you get it updated, otherwise you get |
839 | * stuck with the static values. We ignore "EU" code as | 1359 | * stuck with the static values. We ignore "EU" code as |
840 | * that is not a valid ISO / IEC 3166 alpha2 */ | 1360 | * that is not a valid ISO / IEC 3166 alpha2 */ |
841 | if (ieee80211_regdom[0] != 'E' && ieee80211_regdom[1] != 'U') | 1361 | if (ieee80211_regdom[0] != 'E' || ieee80211_regdom[1] != 'U') |
842 | err = __regulatory_hint(NULL, REGDOM_SET_BY_CORE, | 1362 | err = __regulatory_hint(NULL, REGDOM_SET_BY_CORE, |
843 | ieee80211_regdom, NULL); | 1363 | ieee80211_regdom, 0, ENVIRON_ANY); |
844 | #else | 1364 | #else |
845 | cfg80211_regdomain = cfg80211_world_regdom; | 1365 | cfg80211_regdomain = cfg80211_world_regdom; |
846 | 1366 | ||
847 | err = __regulatory_hint(NULL, REGDOM_SET_BY_CORE, "00", NULL); | 1367 | err = __regulatory_hint(NULL, REGDOM_SET_BY_CORE, "00", 0, ENVIRON_ANY); |
848 | if (err) | 1368 | if (err) |
849 | printk(KERN_ERR "cfg80211: calling CRDA failed - " | 1369 | printk(KERN_ERR "cfg80211: calling CRDA failed - " |
850 | "unable to update world regulatory domain, " | 1370 | "unable to update world regulatory domain, " |
@@ -856,16 +1376,15 @@ int regulatory_init(void) | |||
856 | 1376 | ||
857 | void regulatory_exit(void) | 1377 | void regulatory_exit(void) |
858 | { | 1378 | { |
859 | struct regulatory_request *req, *req_tmp; | ||
860 | |||
861 | mutex_lock(&cfg80211_drv_mutex); | 1379 | mutex_lock(&cfg80211_drv_mutex); |
862 | 1380 | ||
863 | reset_regdomains(); | 1381 | reset_regdomains(); |
864 | 1382 | ||
865 | list_for_each_entry_safe(req, req_tmp, ®ulatory_requests, list) { | 1383 | kfree(country_ie_regdomain); |
866 | list_del(&req->list); | 1384 | country_ie_regdomain = NULL; |
867 | kfree(req); | 1385 | |
868 | } | 1386 | kfree(last_request); |
1387 | |||
869 | platform_device_unregister(reg_pdev); | 1388 | platform_device_unregister(reg_pdev); |
870 | 1389 | ||
871 | mutex_unlock(&cfg80211_drv_mutex); | 1390 | mutex_unlock(&cfg80211_drv_mutex); |