aboutsummaryrefslogtreecommitdiffstats
path: root/net/wireless/reg.c
diff options
context:
space:
mode:
authorLuis R. Rodriguez <lrodriguez@atheros.com>2008-10-30 16:33:53 -0400
committerJohn W. Linville <linville@tuxdriver.com>2008-11-10 15:17:41 -0500
commit9c96477d10763d4314012425ecc8a3cff9e60ea9 (patch)
tree8e49cd68ea62824667b80550ee6fe3a730693033 /net/wireless/reg.c
parentd71aaf6053e380996e40c9ecfe39a8dced52694c (diff)
cfg80211: Add regulatory domain intersection capability
There are certain scenerios where we require intersecting two regulatory domains. This adds intersection support. When we enable 802.11d support we will use this to intersect the regulatory domain from the AP's country IE and what our regulatory agent believes is correct for a country. This patch enables intersection for now in the case where the last regdomain was set by a country IE which was parsed and the user then wants to set the regulatory domain. Since we don't support country IE parsing yet this code path will not be hit, however this allows us to pave the way for 11d support. Intersection code has been tested in userspace with CRDA. Signed-off-by: Luis R. Rodriguez <lrodriguez@atheros.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'net/wireless/reg.c')
-rw-r--r--net/wireless/reg.c166
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
55static struct regulatory_request *last_request; 56static 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 */
365static 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 */
421static 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 */
364static u32 map_regdom_flags(u32 rd_flags) 502static 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. */
473static int ignore_request(struct wiphy *wiphy, enum reg_set_by set_by, 615static 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 */
649static int __set_regdom(const struct ieee80211_regdomain *rd) 790static 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