Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Luis R. Rodriguez and committed by
John W. Linville
9c96477d d71aaf60

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