diff options
Diffstat (limited to 'net/wireless/reg.c')
-rw-r--r-- | net/wireless/reg.c | 166 |
1 files changed, 158 insertions, 8 deletions
diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 34983a825ac1..3f18a1d03a55 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c | |||
@@ -50,6 +50,7 @@ struct regulatory_request { | |||
50 | struct wiphy *wiphy; | 50 | struct wiphy *wiphy; |
51 | enum reg_set_by initiator; | 51 | enum reg_set_by initiator; |
52 | char alpha2[2]; | 52 | char alpha2[2]; |
53 | bool intersect; | ||
53 | }; | 54 | }; |
54 | 55 | ||
55 | static struct regulatory_request *last_request; | 56 | static struct regulatory_request *last_request; |
@@ -359,6 +360,143 @@ static u32 freq_max_bandwidth(const struct ieee80211_freq_range *freq_range, | |||
359 | return 0; | 360 | return 0; |
360 | } | 361 | } |
361 | 362 | ||
363 | /* Helper for regdom_intersect(), this does the real | ||
364 | * mathematical intersection fun */ | ||
365 | static int reg_rules_intersect( | ||
366 | const struct ieee80211_reg_rule *rule1, | ||
367 | const struct ieee80211_reg_rule *rule2, | ||
368 | struct ieee80211_reg_rule *intersected_rule) | ||
369 | { | ||
370 | const struct ieee80211_freq_range *freq_range1, *freq_range2; | ||
371 | struct ieee80211_freq_range *freq_range; | ||
372 | const struct ieee80211_power_rule *power_rule1, *power_rule2; | ||
373 | struct ieee80211_power_rule *power_rule; | ||
374 | u32 freq_diff; | ||
375 | |||
376 | freq_range1 = &rule1->freq_range; | ||
377 | freq_range2 = &rule2->freq_range; | ||
378 | freq_range = &intersected_rule->freq_range; | ||
379 | |||
380 | power_rule1 = &rule1->power_rule; | ||
381 | power_rule2 = &rule2->power_rule; | ||
382 | power_rule = &intersected_rule->power_rule; | ||
383 | |||
384 | freq_range->start_freq_khz = max(freq_range1->start_freq_khz, | ||
385 | freq_range2->start_freq_khz); | ||
386 | freq_range->end_freq_khz = min(freq_range1->end_freq_khz, | ||
387 | freq_range2->end_freq_khz); | ||
388 | freq_range->max_bandwidth_khz = min(freq_range1->max_bandwidth_khz, | ||
389 | freq_range2->max_bandwidth_khz); | ||
390 | |||
391 | freq_diff = freq_range->end_freq_khz - freq_range->start_freq_khz; | ||
392 | if (freq_range->max_bandwidth_khz > freq_diff) | ||
393 | freq_range->max_bandwidth_khz = freq_diff; | ||
394 | |||
395 | power_rule->max_eirp = min(power_rule1->max_eirp, | ||
396 | power_rule2->max_eirp); | ||
397 | power_rule->max_antenna_gain = min(power_rule1->max_antenna_gain, | ||
398 | power_rule2->max_antenna_gain); | ||
399 | |||
400 | intersected_rule->flags = (rule1->flags | rule2->flags); | ||
401 | |||
402 | if (!is_valid_reg_rule(intersected_rule)) | ||
403 | return -EINVAL; | ||
404 | |||
405 | return 0; | ||
406 | } | ||
407 | |||
408 | /** | ||
409 | * regdom_intersect - do the intersection between two regulatory domains | ||
410 | * @rd1: first regulatory domain | ||
411 | * @rd2: second regulatory domain | ||
412 | * | ||
413 | * Use this function to get the intersection between two regulatory domains. | ||
414 | * Once completed we will mark the alpha2 for the rd as intersected, "98", | ||
415 | * as no one single alpha2 can represent this regulatory domain. | ||
416 | * | ||
417 | * Returns a pointer to the regulatory domain structure which will hold the | ||
418 | * resulting intersection of rules between rd1 and rd2. We will | ||
419 | * kzalloc() this structure for you. | ||
420 | */ | ||
421 | static struct ieee80211_regdomain *regdom_intersect( | ||
422 | const struct ieee80211_regdomain *rd1, | ||
423 | const struct ieee80211_regdomain *rd2) | ||
424 | { | ||
425 | int r, size_of_regd; | ||
426 | unsigned int x, y; | ||
427 | unsigned int num_rules = 0, rule_idx = 0; | ||
428 | const struct ieee80211_reg_rule *rule1, *rule2; | ||
429 | struct ieee80211_reg_rule *intersected_rule; | ||
430 | struct ieee80211_regdomain *rd; | ||
431 | /* This is just a dummy holder to help us count */ | ||
432 | struct ieee80211_reg_rule irule; | ||
433 | |||
434 | /* Uses the stack temporarily for counter arithmetic */ | ||
435 | intersected_rule = &irule; | ||
436 | |||
437 | memset(intersected_rule, 0, sizeof(struct ieee80211_reg_rule)); | ||
438 | |||
439 | if (!rd1 || !rd2) | ||
440 | return NULL; | ||
441 | |||
442 | /* First we get a count of the rules we'll need, then we actually | ||
443 | * build them. This is to so we can malloc() and free() a | ||
444 | * regdomain once. The reason we use reg_rules_intersect() here | ||
445 | * is it will return -EINVAL if the rule computed makes no sense. | ||
446 | * All rules that do check out OK are valid. */ | ||
447 | |||
448 | for (x = 0; x < rd1->n_reg_rules; x++) { | ||
449 | rule1 = &rd1->reg_rules[x]; | ||
450 | for (y = 0; y < rd2->n_reg_rules; y++) { | ||
451 | rule2 = &rd2->reg_rules[y]; | ||
452 | if (!reg_rules_intersect(rule1, rule2, | ||
453 | intersected_rule)) | ||
454 | num_rules++; | ||
455 | memset(intersected_rule, 0, | ||
456 | sizeof(struct ieee80211_reg_rule)); | ||
457 | } | ||
458 | } | ||
459 | |||
460 | if (!num_rules) | ||
461 | return NULL; | ||
462 | |||
463 | size_of_regd = sizeof(struct ieee80211_regdomain) + | ||
464 | ((num_rules + 1) * sizeof(struct ieee80211_reg_rule)); | ||
465 | |||
466 | rd = kzalloc(size_of_regd, GFP_KERNEL); | ||
467 | if (!rd) | ||
468 | return NULL; | ||
469 | |||
470 | for (x = 0; x < rd1->n_reg_rules; x++) { | ||
471 | rule1 = &rd1->reg_rules[x]; | ||
472 | for (y = 0; y < rd2->n_reg_rules; y++) { | ||
473 | rule2 = &rd2->reg_rules[y]; | ||
474 | /* This time around instead of using the stack lets | ||
475 | * write to the target rule directly saving ourselves | ||
476 | * a memcpy() */ | ||
477 | intersected_rule = &rd->reg_rules[rule_idx]; | ||
478 | r = reg_rules_intersect(rule1, rule2, | ||
479 | intersected_rule); | ||
480 | /* No need to memset here the intersected rule here as | ||
481 | * we're not using the stack anymore */ | ||
482 | if (r) | ||
483 | continue; | ||
484 | rule_idx++; | ||
485 | } | ||
486 | } | ||
487 | |||
488 | if (rule_idx != num_rules) { | ||
489 | kfree(rd); | ||
490 | return NULL; | ||
491 | } | ||
492 | |||
493 | rd->n_reg_rules = num_rules; | ||
494 | rd->alpha2[0] = '9'; | ||
495 | rd->alpha2[1] = '8'; | ||
496 | |||
497 | return rd; | ||
498 | } | ||
499 | |||
362 | /* XXX: add support for the rest of enum nl80211_reg_rule_flags, we may | 500 | /* XXX: add support for the rest of enum nl80211_reg_rule_flags, we may |
363 | * want to just have the channel structure use these */ | 501 | * want to just have the channel structure use these */ |
364 | static u32 map_regdom_flags(u32 rd_flags) | 502 | static u32 map_regdom_flags(u32 rd_flags) |
@@ -468,6 +606,10 @@ void wiphy_update_regulatory(struct wiphy *wiphy, enum reg_set_by setby) | |||
468 | } | 606 | } |
469 | } | 607 | } |
470 | 608 | ||
609 | /* Return value which can be used by ignore_request() to indicate | ||
610 | * it has been determined we should intersect two regulatory domains */ | ||
611 | #define REG_INTERSECT 1 | ||
612 | |||
471 | /* This has the logic which determines when a new request | 613 | /* This has the logic which determines when a new request |
472 | * should be ignored. */ | 614 | * should be ignored. */ |
473 | static int ignore_request(struct wiphy *wiphy, enum reg_set_by set_by, | 615 | static int ignore_request(struct wiphy *wiphy, enum reg_set_by set_by, |
@@ -517,14 +659,8 @@ static int ignore_request(struct wiphy *wiphy, enum reg_set_by set_by, | |||
517 | return -EALREADY; | 659 | return -EALREADY; |
518 | return 0; | 660 | return 0; |
519 | case REGDOM_SET_BY_USER: | 661 | case REGDOM_SET_BY_USER: |
520 | /* | ||
521 | * If the user wants to override the AP's hint, we may | ||
522 | * need to follow both and use the intersection. For now, | ||
523 | * reject any such attempt (but we don't support country | ||
524 | * IEs right now anyway.) | ||
525 | */ | ||
526 | if (last_request->initiator == REGDOM_SET_BY_COUNTRY_IE) | 662 | if (last_request->initiator == REGDOM_SET_BY_COUNTRY_IE) |
527 | return -EOPNOTSUPP; | 663 | return REG_INTERSECT; |
528 | return 0; | 664 | return 0; |
529 | } | 665 | } |
530 | 666 | ||
@@ -536,10 +672,14 @@ int __regulatory_hint(struct wiphy *wiphy, enum reg_set_by set_by, | |||
536 | const char *alpha2) | 672 | const char *alpha2) |
537 | { | 673 | { |
538 | struct regulatory_request *request; | 674 | struct regulatory_request *request; |
675 | bool intersect = false; | ||
539 | int r = 0; | 676 | int r = 0; |
540 | 677 | ||
541 | r = ignore_request(wiphy, set_by, alpha2); | 678 | r = ignore_request(wiphy, set_by, alpha2); |
542 | if (r) | 679 | |
680 | if (r == REG_INTERSECT) | ||
681 | intersect = true; | ||
682 | else if (r) | ||
543 | return r; | 683 | return r; |
544 | 684 | ||
545 | switch (set_by) { | 685 | switch (set_by) { |
@@ -556,6 +696,7 @@ int __regulatory_hint(struct wiphy *wiphy, enum reg_set_by set_by, | |||
556 | request->alpha2[1] = alpha2[1]; | 696 | request->alpha2[1] = alpha2[1]; |
557 | request->initiator = set_by; | 697 | request->initiator = set_by; |
558 | request->wiphy = wiphy; | 698 | request->wiphy = wiphy; |
699 | request->intersect = intersect; | ||
559 | 700 | ||
560 | kfree(last_request); | 701 | kfree(last_request); |
561 | last_request = request; | 702 | last_request = request; |
@@ -648,6 +789,7 @@ static void print_regdomain_info(const struct ieee80211_regdomain *rd) | |||
648 | /* Takes ownership of rd only if it doesn't fail */ | 789 | /* Takes ownership of rd only if it doesn't fail */ |
649 | static int __set_regdom(const struct ieee80211_regdomain *rd) | 790 | static int __set_regdom(const struct ieee80211_regdomain *rd) |
650 | { | 791 | { |
792 | const struct ieee80211_regdomain *intersected_rd = NULL; | ||
651 | /* Some basic sanity checks first */ | 793 | /* Some basic sanity checks first */ |
652 | 794 | ||
653 | if (is_world_regdom(rd->alpha2)) { | 795 | if (is_world_regdom(rd->alpha2)) { |
@@ -697,6 +839,14 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) | |||
697 | return -EOPNOTSUPP; | 839 | return -EOPNOTSUPP; |
698 | } | 840 | } |
699 | 841 | ||
842 | if (unlikely(last_request->intersect)) { | ||
843 | intersected_rd = regdom_intersect(rd, cfg80211_regdomain); | ||
844 | if (!intersected_rd) | ||
845 | return -EINVAL; | ||
846 | kfree(rd); | ||
847 | rd = intersected_rd; | ||
848 | } | ||
849 | |||
700 | /* Tada! */ | 850 | /* Tada! */ |
701 | cfg80211_regdomain = rd; | 851 | cfg80211_regdomain = rd; |
702 | 852 | ||