diff options
Diffstat (limited to 'net/wireless/reg.c')
-rw-r--r-- | net/wireless/reg.c | 462 |
1 files changed, 266 insertions, 196 deletions
diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 626dbb688499..4c7e39d466c4 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c | |||
@@ -42,17 +42,34 @@ | |||
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 | */ | ||
46 | struct regulatory_request { | 64 | struct regulatory_request { |
47 | struct list_head list; | ||
48 | struct wiphy *wiphy; | 65 | struct wiphy *wiphy; |
49 | int granted; | ||
50 | enum reg_set_by initiator; | 66 | enum reg_set_by initiator; |
51 | char alpha2[2]; | 67 | char alpha2[2]; |
68 | bool intersect; | ||
52 | }; | 69 | }; |
53 | 70 | ||
54 | static LIST_HEAD(regulatory_requests); | 71 | /* Receipt of information from last regulatory request */ |
55 | DEFINE_MUTEX(cfg80211_reg_mutex); | 72 | static struct regulatory_request *last_request; |
56 | 73 | ||
57 | /* To trigger userspace events */ | 74 | /* To trigger userspace events */ |
58 | static struct platform_device *reg_pdev; | 75 | static struct platform_device *reg_pdev; |
@@ -63,8 +80,6 @@ static u32 supported_bandwidths[] = { | |||
63 | MHZ_TO_KHZ(20), | 80 | MHZ_TO_KHZ(20), |
64 | }; | 81 | }; |
65 | 82 | ||
66 | static struct list_head regulatory_requests; | ||
67 | |||
68 | /* Central wireless core regulatory domains, we only need two, | 83 | /* Central wireless core regulatory domains, we only need two, |
69 | * the current one and a world regulatory domain in case we have no | 84 | * the current one and a world regulatory domain in case we have no |
70 | * information to give us an alpha2 */ | 85 | * information to give us an alpha2 */ |
@@ -204,7 +219,7 @@ static void reset_regdomains(void) | |||
204 | * core upon initialization */ | 219 | * core upon initialization */ |
205 | static void update_world_regdomain(const struct ieee80211_regdomain *rd) | 220 | static void update_world_regdomain(const struct ieee80211_regdomain *rd) |
206 | { | 221 | { |
207 | BUG_ON(list_empty(®ulatory_requests)); | 222 | BUG_ON(!last_request); |
208 | 223 | ||
209 | reset_regdomains(); | 224 | reset_regdomains(); |
210 | 225 | ||
@@ -300,121 +315,13 @@ static int call_crda(const char *alpha2) | |||
300 | return kobject_uevent_env(®_pdev->dev.kobj, KOBJ_CHANGE, envp); | 315 | return kobject_uevent_env(®_pdev->dev.kobj, KOBJ_CHANGE, envp); |
301 | } | 316 | } |
302 | 317 | ||
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_ON(!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 */ | 318 | /* Used by nl80211 before kmalloc'ing our regulatory domain */ |
414 | bool reg_is_valid_request(const char *alpha2) | 319 | bool reg_is_valid_request(const char *alpha2) |
415 | { | 320 | { |
416 | struct regulatory_request *request = NULL; | 321 | if (!last_request) |
417 | return __reg_is_valid_request(alpha2, &request); | 322 | return false; |
323 | |||
324 | return alpha2_equal(last_request->alpha2, alpha2); | ||
418 | } | 325 | } |
419 | 326 | ||
420 | /* Sanity check on a regulatory rule */ | 327 | /* Sanity check on a regulatory rule */ |
@@ -431,7 +338,7 @@ static bool is_valid_reg_rule(const struct ieee80211_reg_rule *rule) | |||
431 | 338 | ||
432 | freq_diff = freq_range->end_freq_khz - freq_range->start_freq_khz; | 339 | freq_diff = freq_range->end_freq_khz - freq_range->start_freq_khz; |
433 | 340 | ||
434 | if (freq_range->max_bandwidth_khz > freq_diff) | 341 | if (freq_diff <= 0 || freq_range->max_bandwidth_khz > freq_diff) |
435 | return false; | 342 | return false; |
436 | 343 | ||
437 | return true; | 344 | return true; |
@@ -469,6 +376,143 @@ static u32 freq_max_bandwidth(const struct ieee80211_freq_range *freq_range, | |||
469 | return 0; | 376 | return 0; |
470 | } | 377 | } |
471 | 378 | ||
379 | /* Helper for regdom_intersect(), this does the real | ||
380 | * mathematical intersection fun */ | ||
381 | static int reg_rules_intersect( | ||
382 | const struct ieee80211_reg_rule *rule1, | ||
383 | const struct ieee80211_reg_rule *rule2, | ||
384 | struct ieee80211_reg_rule *intersected_rule) | ||
385 | { | ||
386 | const struct ieee80211_freq_range *freq_range1, *freq_range2; | ||
387 | struct ieee80211_freq_range *freq_range; | ||
388 | const struct ieee80211_power_rule *power_rule1, *power_rule2; | ||
389 | struct ieee80211_power_rule *power_rule; | ||
390 | u32 freq_diff; | ||
391 | |||
392 | freq_range1 = &rule1->freq_range; | ||
393 | freq_range2 = &rule2->freq_range; | ||
394 | freq_range = &intersected_rule->freq_range; | ||
395 | |||
396 | power_rule1 = &rule1->power_rule; | ||
397 | power_rule2 = &rule2->power_rule; | ||
398 | power_rule = &intersected_rule->power_rule; | ||
399 | |||
400 | freq_range->start_freq_khz = max(freq_range1->start_freq_khz, | ||
401 | freq_range2->start_freq_khz); | ||
402 | freq_range->end_freq_khz = min(freq_range1->end_freq_khz, | ||
403 | freq_range2->end_freq_khz); | ||
404 | freq_range->max_bandwidth_khz = min(freq_range1->max_bandwidth_khz, | ||
405 | freq_range2->max_bandwidth_khz); | ||
406 | |||
407 | freq_diff = freq_range->end_freq_khz - freq_range->start_freq_khz; | ||
408 | if (freq_range->max_bandwidth_khz > freq_diff) | ||
409 | freq_range->max_bandwidth_khz = freq_diff; | ||
410 | |||
411 | power_rule->max_eirp = min(power_rule1->max_eirp, | ||
412 | power_rule2->max_eirp); | ||
413 | power_rule->max_antenna_gain = min(power_rule1->max_antenna_gain, | ||
414 | power_rule2->max_antenna_gain); | ||
415 | |||
416 | intersected_rule->flags = (rule1->flags | rule2->flags); | ||
417 | |||
418 | if (!is_valid_reg_rule(intersected_rule)) | ||
419 | return -EINVAL; | ||
420 | |||
421 | return 0; | ||
422 | } | ||
423 | |||
424 | /** | ||
425 | * regdom_intersect - do the intersection between two regulatory domains | ||
426 | * @rd1: first regulatory domain | ||
427 | * @rd2: second regulatory domain | ||
428 | * | ||
429 | * Use this function to get the intersection between two regulatory domains. | ||
430 | * Once completed we will mark the alpha2 for the rd as intersected, "98", | ||
431 | * as no one single alpha2 can represent this regulatory domain. | ||
432 | * | ||
433 | * Returns a pointer to the regulatory domain structure which will hold the | ||
434 | * resulting intersection of rules between rd1 and rd2. We will | ||
435 | * kzalloc() this structure for you. | ||
436 | */ | ||
437 | static struct ieee80211_regdomain *regdom_intersect( | ||
438 | const struct ieee80211_regdomain *rd1, | ||
439 | const struct ieee80211_regdomain *rd2) | ||
440 | { | ||
441 | int r, size_of_regd; | ||
442 | unsigned int x, y; | ||
443 | unsigned int num_rules = 0, rule_idx = 0; | ||
444 | const struct ieee80211_reg_rule *rule1, *rule2; | ||
445 | struct ieee80211_reg_rule *intersected_rule; | ||
446 | struct ieee80211_regdomain *rd; | ||
447 | /* This is just a dummy holder to help us count */ | ||
448 | struct ieee80211_reg_rule irule; | ||
449 | |||
450 | /* Uses the stack temporarily for counter arithmetic */ | ||
451 | intersected_rule = &irule; | ||
452 | |||
453 | memset(intersected_rule, 0, sizeof(struct ieee80211_reg_rule)); | ||
454 | |||
455 | if (!rd1 || !rd2) | ||
456 | return NULL; | ||
457 | |||
458 | /* First we get a count of the rules we'll need, then we actually | ||
459 | * build them. This is to so we can malloc() and free() a | ||
460 | * regdomain once. The reason we use reg_rules_intersect() here | ||
461 | * is it will return -EINVAL if the rule computed makes no sense. | ||
462 | * All rules that do check out OK are valid. */ | ||
463 | |||
464 | for (x = 0; x < rd1->n_reg_rules; x++) { | ||
465 | rule1 = &rd1->reg_rules[x]; | ||
466 | for (y = 0; y < rd2->n_reg_rules; y++) { | ||
467 | rule2 = &rd2->reg_rules[y]; | ||
468 | if (!reg_rules_intersect(rule1, rule2, | ||
469 | intersected_rule)) | ||
470 | num_rules++; | ||
471 | memset(intersected_rule, 0, | ||
472 | sizeof(struct ieee80211_reg_rule)); | ||
473 | } | ||
474 | } | ||
475 | |||
476 | if (!num_rules) | ||
477 | return NULL; | ||
478 | |||
479 | size_of_regd = sizeof(struct ieee80211_regdomain) + | ||
480 | ((num_rules + 1) * sizeof(struct ieee80211_reg_rule)); | ||
481 | |||
482 | rd = kzalloc(size_of_regd, GFP_KERNEL); | ||
483 | if (!rd) | ||
484 | return NULL; | ||
485 | |||
486 | for (x = 0; x < rd1->n_reg_rules; x++) { | ||
487 | rule1 = &rd1->reg_rules[x]; | ||
488 | for (y = 0; y < rd2->n_reg_rules; y++) { | ||
489 | rule2 = &rd2->reg_rules[y]; | ||
490 | /* This time around instead of using the stack lets | ||
491 | * write to the target rule directly saving ourselves | ||
492 | * a memcpy() */ | ||
493 | intersected_rule = &rd->reg_rules[rule_idx]; | ||
494 | r = reg_rules_intersect(rule1, rule2, | ||
495 | intersected_rule); | ||
496 | /* No need to memset here the intersected rule here as | ||
497 | * we're not using the stack anymore */ | ||
498 | if (r) | ||
499 | continue; | ||
500 | rule_idx++; | ||
501 | } | ||
502 | } | ||
503 | |||
504 | if (rule_idx != num_rules) { | ||
505 | kfree(rd); | ||
506 | return NULL; | ||
507 | } | ||
508 | |||
509 | rd->n_reg_rules = num_rules; | ||
510 | rd->alpha2[0] = '9'; | ||
511 | rd->alpha2[1] = '8'; | ||
512 | |||
513 | return rd; | ||
514 | } | ||
515 | |||
472 | /* XXX: add support for the rest of enum nl80211_reg_rule_flags, we may | 516 | /* XXX: add support for the rest of enum nl80211_reg_rule_flags, we may |
473 | * want to just have the channel structure use these */ | 517 | * want to just have the channel structure use these */ |
474 | static u32 map_regdom_flags(u32 rd_flags) | 518 | static u32 map_regdom_flags(u32 rd_flags) |
@@ -578,22 +622,81 @@ void wiphy_update_regulatory(struct wiphy *wiphy, enum reg_set_by setby) | |||
578 | } | 622 | } |
579 | } | 623 | } |
580 | 624 | ||
625 | /* Return value which can be used by ignore_request() to indicate | ||
626 | * it has been determined we should intersect two regulatory domains */ | ||
627 | #define REG_INTERSECT 1 | ||
628 | |||
629 | /* This has the logic which determines when a new request | ||
630 | * should be ignored. */ | ||
631 | static int ignore_request(struct wiphy *wiphy, enum reg_set_by set_by, | ||
632 | const char *alpha2) | ||
633 | { | ||
634 | /* All initial requests are respected */ | ||
635 | if (!last_request) | ||
636 | return 0; | ||
637 | |||
638 | switch (set_by) { | ||
639 | case REGDOM_SET_BY_INIT: | ||
640 | return -EINVAL; | ||
641 | case REGDOM_SET_BY_CORE: | ||
642 | /* | ||
643 | * Always respect new wireless core hints, should only happen | ||
644 | * when updating the world regulatory domain at init. | ||
645 | */ | ||
646 | return 0; | ||
647 | case REGDOM_SET_BY_COUNTRY_IE: | ||
648 | if (unlikely(!is_an_alpha2(alpha2))) | ||
649 | return -EINVAL; | ||
650 | if (last_request->initiator == REGDOM_SET_BY_COUNTRY_IE) { | ||
651 | if (last_request->wiphy != wiphy) { | ||
652 | /* | ||
653 | * Two cards with two APs claiming different | ||
654 | * different Country IE alpha2s. We could | ||
655 | * intersect them, but that seems unlikely | ||
656 | * to be correct. Reject second one for now. | ||
657 | */ | ||
658 | if (!alpha2_equal(alpha2, | ||
659 | cfg80211_regdomain->alpha2)) | ||
660 | return -EOPNOTSUPP; | ||
661 | return -EALREADY; | ||
662 | } | ||
663 | /* Two consecutive Country IE hints on the same wiphy */ | ||
664 | if (!alpha2_equal(cfg80211_regdomain->alpha2, alpha2)) | ||
665 | return 0; | ||
666 | return -EALREADY; | ||
667 | } | ||
668 | /* | ||
669 | * Ignore Country IE hints for now, need to think about | ||
670 | * what we need to do to support multi-domain operation. | ||
671 | */ | ||
672 | return -EOPNOTSUPP; | ||
673 | case REGDOM_SET_BY_DRIVER: | ||
674 | if (last_request->initiator == REGDOM_SET_BY_DRIVER) | ||
675 | return -EALREADY; | ||
676 | return 0; | ||
677 | case REGDOM_SET_BY_USER: | ||
678 | if (last_request->initiator == REGDOM_SET_BY_COUNTRY_IE) | ||
679 | return REG_INTERSECT; | ||
680 | return 0; | ||
681 | } | ||
682 | |||
683 | return -EINVAL; | ||
684 | } | ||
685 | |||
581 | /* Caller must hold &cfg80211_drv_mutex */ | 686 | /* Caller must hold &cfg80211_drv_mutex */ |
582 | int __regulatory_hint(struct wiphy *wiphy, enum reg_set_by set_by, | 687 | int __regulatory_hint(struct wiphy *wiphy, enum reg_set_by set_by, |
583 | const char *alpha2, struct ieee80211_regdomain *rd) | 688 | const char *alpha2) |
584 | { | 689 | { |
585 | struct regulatory_request *request; | 690 | struct regulatory_request *request; |
586 | char *rd_alpha2; | 691 | bool intersect = false; |
587 | int r = 0; | 692 | int r = 0; |
588 | 693 | ||
589 | r = ignore_request(wiphy, set_by, (char *) alpha2, rd); | 694 | r = ignore_request(wiphy, set_by, alpha2); |
590 | if (r) | ||
591 | return r; | ||
592 | 695 | ||
593 | if (rd) | 696 | if (r == REG_INTERSECT) |
594 | rd_alpha2 = rd->alpha2; | 697 | intersect = true; |
595 | else | 698 | else if (r) |
596 | rd_alpha2 = (char *) alpha2; | 699 | return r; |
597 | 700 | ||
598 | switch (set_by) { | 701 | switch (set_by) { |
599 | case REGDOM_SET_BY_CORE: | 702 | case REGDOM_SET_BY_CORE: |
@@ -601,18 +704,18 @@ int __regulatory_hint(struct wiphy *wiphy, enum reg_set_by set_by, | |||
601 | case REGDOM_SET_BY_DRIVER: | 704 | case REGDOM_SET_BY_DRIVER: |
602 | case REGDOM_SET_BY_USER: | 705 | case REGDOM_SET_BY_USER: |
603 | request = kzalloc(sizeof(struct regulatory_request), | 706 | request = kzalloc(sizeof(struct regulatory_request), |
604 | GFP_KERNEL); | 707 | GFP_KERNEL); |
605 | if (!request) | 708 | if (!request) |
606 | return -ENOMEM; | 709 | return -ENOMEM; |
607 | 710 | ||
608 | request->alpha2[0] = rd_alpha2[0]; | 711 | request->alpha2[0] = alpha2[0]; |
609 | request->alpha2[1] = rd_alpha2[1]; | 712 | request->alpha2[1] = alpha2[1]; |
610 | request->initiator = set_by; | 713 | request->initiator = set_by; |
611 | request->wiphy = wiphy; | 714 | request->wiphy = wiphy; |
715 | request->intersect = intersect; | ||
612 | 716 | ||
613 | list_add_tail(&request->list, ®ulatory_requests); | 717 | kfree(last_request); |
614 | if (rd) | 718 | last_request = request; |
615 | break; | ||
616 | r = call_crda(alpha2); | 719 | r = call_crda(alpha2); |
617 | #ifndef CONFIG_WIRELESS_OLD_REGULATORY | 720 | #ifndef CONFIG_WIRELESS_OLD_REGULATORY |
618 | if (r) | 721 | if (r) |
@@ -627,26 +730,13 @@ int __regulatory_hint(struct wiphy *wiphy, enum reg_set_by set_by, | |||
627 | return r; | 730 | return r; |
628 | } | 731 | } |
629 | 732 | ||
630 | /* If rd is not NULL and if this call fails the caller must free it */ | 733 | void regulatory_hint(struct wiphy *wiphy, const char *alpha2) |
631 | int regulatory_hint(struct wiphy *wiphy, const char *alpha2, | ||
632 | struct ieee80211_regdomain *rd) | ||
633 | { | 734 | { |
634 | int r; | 735 | BUG_ON(!alpha2); |
635 | BUG_ON(!rd && !alpha2); | ||
636 | 736 | ||
637 | mutex_lock(&cfg80211_drv_mutex); | 737 | mutex_lock(&cfg80211_drv_mutex); |
638 | 738 | __regulatory_hint(wiphy, REGDOM_SET_BY_DRIVER, alpha2); | |
639 | r = __regulatory_hint(wiphy, REGDOM_SET_BY_DRIVER, alpha2, rd); | ||
640 | if (r || !rd) | ||
641 | goto unlock_and_exit; | ||
642 | |||
643 | /* If the driver passed a regulatory domain we skipped asking | ||
644 | * userspace for one so we can now go ahead and set it */ | ||
645 | r = set_regdom(rd); | ||
646 | |||
647 | unlock_and_exit: | ||
648 | mutex_unlock(&cfg80211_drv_mutex); | 739 | mutex_unlock(&cfg80211_drv_mutex); |
649 | return r; | ||
650 | } | 740 | } |
651 | EXPORT_SYMBOL(regulatory_hint); | 741 | EXPORT_SYMBOL(regulatory_hint); |
652 | 742 | ||
@@ -705,21 +795,21 @@ static void print_regdomain(const struct ieee80211_regdomain *rd) | |||
705 | print_rd_rules(rd); | 795 | print_rd_rules(rd); |
706 | } | 796 | } |
707 | 797 | ||
708 | void print_regdomain_info(const struct ieee80211_regdomain *rd) | 798 | static void print_regdomain_info(const struct ieee80211_regdomain *rd) |
709 | { | 799 | { |
710 | printk(KERN_INFO "cfg80211: Regulatory domain: %c%c\n", | 800 | printk(KERN_INFO "cfg80211: Regulatory domain: %c%c\n", |
711 | rd->alpha2[0], rd->alpha2[1]); | 801 | rd->alpha2[0], rd->alpha2[1]); |
712 | print_rd_rules(rd); | 802 | print_rd_rules(rd); |
713 | } | 803 | } |
714 | 804 | ||
805 | /* Takes ownership of rd only if it doesn't fail */ | ||
715 | static int __set_regdom(const struct ieee80211_regdomain *rd) | 806 | static int __set_regdom(const struct ieee80211_regdomain *rd) |
716 | { | 807 | { |
717 | struct regulatory_request *request = NULL; | 808 | const struct ieee80211_regdomain *intersected_rd = NULL; |
718 | |||
719 | /* Some basic sanity checks first */ | 809 | /* Some basic sanity checks first */ |
720 | 810 | ||
721 | if (is_world_regdom(rd->alpha2)) { | 811 | if (is_world_regdom(rd->alpha2)) { |
722 | if (WARN_ON(!__reg_is_valid_request(rd->alpha2, &request))) | 812 | if (WARN_ON(!reg_is_valid_request(rd->alpha2))) |
723 | return -EINVAL; | 813 | return -EINVAL; |
724 | update_world_regdomain(rd); | 814 | update_world_regdomain(rd); |
725 | return 0; | 815 | return 0; |
@@ -729,7 +819,7 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) | |||
729 | !is_unknown_alpha2(rd->alpha2)) | 819 | !is_unknown_alpha2(rd->alpha2)) |
730 | return -EINVAL; | 820 | return -EINVAL; |
731 | 821 | ||
732 | if (list_empty(®ulatory_requests)) | 822 | if (!last_request) |
733 | return -EINVAL; | 823 | return -EINVAL; |
734 | 824 | ||
735 | /* allow overriding the static definitions if CRDA is present */ | 825 | /* allow overriding the static definitions if CRDA is present */ |
@@ -742,13 +832,13 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) | |||
742 | * to review or adjust their own settings based on their own | 832 | * to review or adjust their own settings based on their own |
743 | * internal EEPROM data */ | 833 | * internal EEPROM data */ |
744 | 834 | ||
745 | if (WARN_ON(!__reg_is_valid_request(rd->alpha2, &request))) | 835 | if (WARN_ON(!reg_is_valid_request(rd->alpha2))) |
746 | return -EINVAL; | 836 | return -EINVAL; |
747 | 837 | ||
748 | reset_regdomains(); | 838 | reset_regdomains(); |
749 | 839 | ||
750 | /* Country IE parsing coming soon */ | 840 | /* Country IE parsing coming soon */ |
751 | switch (request->initiator) { | 841 | switch (last_request->initiator) { |
752 | case REGDOM_SET_BY_CORE: | 842 | case REGDOM_SET_BY_CORE: |
753 | case REGDOM_SET_BY_DRIVER: | 843 | case REGDOM_SET_BY_DRIVER: |
754 | case REGDOM_SET_BY_USER: | 844 | case REGDOM_SET_BY_USER: |
@@ -765,9 +855,16 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) | |||
765 | return -EOPNOTSUPP; | 855 | return -EOPNOTSUPP; |
766 | } | 856 | } |
767 | 857 | ||
858 | if (unlikely(last_request->intersect)) { | ||
859 | intersected_rd = regdom_intersect(rd, cfg80211_regdomain); | ||
860 | if (!intersected_rd) | ||
861 | return -EINVAL; | ||
862 | kfree(rd); | ||
863 | rd = intersected_rd; | ||
864 | } | ||
865 | |||
768 | /* Tada! */ | 866 | /* Tada! */ |
769 | cfg80211_regdomain = rd; | 867 | cfg80211_regdomain = rd; |
770 | request->granted = 1; | ||
771 | 868 | ||
772 | return 0; | 869 | return 0; |
773 | } | 870 | } |
@@ -775,46 +872,23 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) | |||
775 | 872 | ||
776 | /* Use this call to set the current regulatory domain. Conflicts with | 873 | /* Use this call to set the current regulatory domain. Conflicts with |
777 | * multiple drivers can be ironed out later. Caller must've already | 874 | * multiple drivers can be ironed out later. Caller must've already |
778 | * kmalloc'd the rd structure. If this calls fails you should kfree() | 875 | * 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) | 876 | int set_regdom(const struct ieee80211_regdomain *rd) |
781 | { | 877 | { |
782 | struct regulatory_request *this_request = NULL, *prev_request = NULL; | ||
783 | int r; | 878 | int r; |
784 | 879 | ||
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 */ | 880 | /* Note that this doesn't update the wiphys, this is done below */ |
790 | r = __set_regdom(rd); | 881 | r = __set_regdom(rd); |
791 | if (r) | 882 | if (r) { |
883 | kfree(rd); | ||
792 | return r; | 884 | 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 | } | 885 | } |
812 | 886 | ||
813 | /* This would make this whole thing pointless */ | 887 | /* This would make this whole thing pointless */ |
814 | BUG_ON(rd != cfg80211_regdomain); | 888 | BUG_ON(rd != cfg80211_regdomain); |
815 | 889 | ||
816 | /* update all wiphys now with the new established regulatory domain */ | 890 | /* update all wiphys now with the new established regulatory domain */ |
817 | update_all_wiphy_regulatory(this_request->initiator); | 891 | update_all_wiphy_regulatory(last_request->initiator); |
818 | 892 | ||
819 | print_regdomain(rd); | 893 | print_regdomain(rd); |
820 | 894 | ||
@@ -838,13 +912,13 @@ int regulatory_init(void) | |||
838 | * you have CRDA you get it updated, otherwise you get | 912 | * you have CRDA you get it updated, otherwise you get |
839 | * stuck with the static values. We ignore "EU" code as | 913 | * stuck with the static values. We ignore "EU" code as |
840 | * that is not a valid ISO / IEC 3166 alpha2 */ | 914 | * that is not a valid ISO / IEC 3166 alpha2 */ |
841 | if (ieee80211_regdom[0] != 'E' && ieee80211_regdom[1] != 'U') | 915 | if (ieee80211_regdom[0] != 'E' || ieee80211_regdom[1] != 'U') |
842 | err = __regulatory_hint(NULL, REGDOM_SET_BY_CORE, | 916 | err = __regulatory_hint(NULL, REGDOM_SET_BY_CORE, |
843 | ieee80211_regdom, NULL); | 917 | ieee80211_regdom); |
844 | #else | 918 | #else |
845 | cfg80211_regdomain = cfg80211_world_regdom; | 919 | cfg80211_regdomain = cfg80211_world_regdom; |
846 | 920 | ||
847 | err = __regulatory_hint(NULL, REGDOM_SET_BY_CORE, "00", NULL); | 921 | err = __regulatory_hint(NULL, REGDOM_SET_BY_CORE, "00"); |
848 | if (err) | 922 | if (err) |
849 | printk(KERN_ERR "cfg80211: calling CRDA failed - " | 923 | printk(KERN_ERR "cfg80211: calling CRDA failed - " |
850 | "unable to update world regulatory domain, " | 924 | "unable to update world regulatory domain, " |
@@ -856,16 +930,12 @@ int regulatory_init(void) | |||
856 | 930 | ||
857 | void regulatory_exit(void) | 931 | void regulatory_exit(void) |
858 | { | 932 | { |
859 | struct regulatory_request *req, *req_tmp; | ||
860 | |||
861 | mutex_lock(&cfg80211_drv_mutex); | 933 | mutex_lock(&cfg80211_drv_mutex); |
862 | 934 | ||
863 | reset_regdomains(); | 935 | reset_regdomains(); |
864 | 936 | ||
865 | list_for_each_entry_safe(req, req_tmp, ®ulatory_requests, list) { | 937 | kfree(last_request); |
866 | list_del(&req->list); | 938 | |
867 | kfree(req); | ||
868 | } | ||
869 | platform_device_unregister(reg_pdev); | 939 | platform_device_unregister(reg_pdev); |
870 | 940 | ||
871 | mutex_unlock(&cfg80211_drv_mutex); | 941 | mutex_unlock(&cfg80211_drv_mutex); |