diff options
author | David S. Miller <davem@davemloft.net> | 2008-11-10 16:24:44 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2008-11-10 16:24:44 -0500 |
commit | 23779897546c1effb546ff89b89803d9d955d517 (patch) | |
tree | d4b5d52b5d716a72755ba018382d4b87eae763a4 /net/wireless | |
parent | f574179b63e48f5285468b5ee40f3c480221f708 (diff) | |
parent | c4832467a5c8c2ae96d6dad882be4d4ab9eefad7 (diff) |
Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-next-2.6
Diffstat (limited to 'net/wireless')
-rw-r--r-- | net/wireless/nl80211.c | 75 | ||||
-rw-r--r-- | net/wireless/reg.c | 192 | ||||
-rw-r--r-- | net/wireless/util.c | 19 |
3 files changed, 268 insertions, 18 deletions
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 5e1d658a8b5a..e3e1494e769a 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c | |||
@@ -58,6 +58,7 @@ static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = { | |||
58 | [NL80211_ATTR_WIPHY] = { .type = NLA_U32 }, | 58 | [NL80211_ATTR_WIPHY] = { .type = NLA_U32 }, |
59 | [NL80211_ATTR_WIPHY_NAME] = { .type = NLA_NUL_STRING, | 59 | [NL80211_ATTR_WIPHY_NAME] = { .type = NLA_NUL_STRING, |
60 | .len = BUS_ID_SIZE-1 }, | 60 | .len = BUS_ID_SIZE-1 }, |
61 | [NL80211_ATTR_WIPHY_TXQ_PARAMS] = { .type = NLA_NESTED }, | ||
61 | 62 | ||
62 | [NL80211_ATTR_IFTYPE] = { .type = NLA_U32 }, | 63 | [NL80211_ATTR_IFTYPE] = { .type = NLA_U32 }, |
63 | [NL80211_ATTR_IFINDEX] = { .type = NLA_U32 }, | 64 | [NL80211_ATTR_IFINDEX] = { .type = NLA_U32 }, |
@@ -95,6 +96,8 @@ static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = { | |||
95 | [NL80211_ATTR_BSS_CTS_PROT] = { .type = NLA_U8 }, | 96 | [NL80211_ATTR_BSS_CTS_PROT] = { .type = NLA_U8 }, |
96 | [NL80211_ATTR_BSS_SHORT_PREAMBLE] = { .type = NLA_U8 }, | 97 | [NL80211_ATTR_BSS_SHORT_PREAMBLE] = { .type = NLA_U8 }, |
97 | [NL80211_ATTR_BSS_SHORT_SLOT_TIME] = { .type = NLA_U8 }, | 98 | [NL80211_ATTR_BSS_SHORT_SLOT_TIME] = { .type = NLA_U8 }, |
99 | [NL80211_ATTR_BSS_BASIC_RATES] = { .type = NLA_BINARY, | ||
100 | .len = NL80211_MAX_SUPP_RATES }, | ||
98 | 101 | ||
99 | [NL80211_ATTR_MESH_PARAMS] = { .type = NLA_NESTED }, | 102 | [NL80211_ATTR_MESH_PARAMS] = { .type = NLA_NESTED }, |
100 | 103 | ||
@@ -284,20 +287,76 @@ static int nl80211_get_wiphy(struct sk_buff *skb, struct genl_info *info) | |||
284 | return -ENOBUFS; | 287 | return -ENOBUFS; |
285 | } | 288 | } |
286 | 289 | ||
290 | static const struct nla_policy txq_params_policy[NL80211_TXQ_ATTR_MAX + 1] = { | ||
291 | [NL80211_TXQ_ATTR_QUEUE] = { .type = NLA_U8 }, | ||
292 | [NL80211_TXQ_ATTR_TXOP] = { .type = NLA_U16 }, | ||
293 | [NL80211_TXQ_ATTR_CWMIN] = { .type = NLA_U16 }, | ||
294 | [NL80211_TXQ_ATTR_CWMAX] = { .type = NLA_U16 }, | ||
295 | [NL80211_TXQ_ATTR_AIFS] = { .type = NLA_U8 }, | ||
296 | }; | ||
297 | |||
298 | static int parse_txq_params(struct nlattr *tb[], | ||
299 | struct ieee80211_txq_params *txq_params) | ||
300 | { | ||
301 | if (!tb[NL80211_TXQ_ATTR_QUEUE] || !tb[NL80211_TXQ_ATTR_TXOP] || | ||
302 | !tb[NL80211_TXQ_ATTR_CWMIN] || !tb[NL80211_TXQ_ATTR_CWMAX] || | ||
303 | !tb[NL80211_TXQ_ATTR_AIFS]) | ||
304 | return -EINVAL; | ||
305 | |||
306 | txq_params->queue = nla_get_u8(tb[NL80211_TXQ_ATTR_QUEUE]); | ||
307 | txq_params->txop = nla_get_u16(tb[NL80211_TXQ_ATTR_TXOP]); | ||
308 | txq_params->cwmin = nla_get_u16(tb[NL80211_TXQ_ATTR_CWMIN]); | ||
309 | txq_params->cwmax = nla_get_u16(tb[NL80211_TXQ_ATTR_CWMAX]); | ||
310 | txq_params->aifs = nla_get_u8(tb[NL80211_TXQ_ATTR_AIFS]); | ||
311 | |||
312 | return 0; | ||
313 | } | ||
314 | |||
287 | static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) | 315 | static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) |
288 | { | 316 | { |
289 | struct cfg80211_registered_device *rdev; | 317 | struct cfg80211_registered_device *rdev; |
290 | int result; | 318 | int result = 0, rem_txq_params = 0; |
291 | 319 | struct nlattr *nl_txq_params; | |
292 | if (!info->attrs[NL80211_ATTR_WIPHY_NAME]) | ||
293 | return -EINVAL; | ||
294 | 320 | ||
295 | rdev = cfg80211_get_dev_from_info(info); | 321 | rdev = cfg80211_get_dev_from_info(info); |
296 | if (IS_ERR(rdev)) | 322 | if (IS_ERR(rdev)) |
297 | return PTR_ERR(rdev); | 323 | return PTR_ERR(rdev); |
298 | 324 | ||
299 | result = cfg80211_dev_rename(rdev, nla_data(info->attrs[NL80211_ATTR_WIPHY_NAME])); | 325 | if (info->attrs[NL80211_ATTR_WIPHY_NAME]) { |
326 | result = cfg80211_dev_rename( | ||
327 | rdev, nla_data(info->attrs[NL80211_ATTR_WIPHY_NAME])); | ||
328 | if (result) | ||
329 | goto bad_res; | ||
330 | } | ||
331 | |||
332 | if (info->attrs[NL80211_ATTR_WIPHY_TXQ_PARAMS]) { | ||
333 | struct ieee80211_txq_params txq_params; | ||
334 | struct nlattr *tb[NL80211_TXQ_ATTR_MAX + 1]; | ||
335 | |||
336 | if (!rdev->ops->set_txq_params) { | ||
337 | result = -EOPNOTSUPP; | ||
338 | goto bad_res; | ||
339 | } | ||
300 | 340 | ||
341 | nla_for_each_nested(nl_txq_params, | ||
342 | info->attrs[NL80211_ATTR_WIPHY_TXQ_PARAMS], | ||
343 | rem_txq_params) { | ||
344 | nla_parse(tb, NL80211_TXQ_ATTR_MAX, | ||
345 | nla_data(nl_txq_params), | ||
346 | nla_len(nl_txq_params), | ||
347 | txq_params_policy); | ||
348 | result = parse_txq_params(tb, &txq_params); | ||
349 | if (result) | ||
350 | goto bad_res; | ||
351 | |||
352 | result = rdev->ops->set_txq_params(&rdev->wiphy, | ||
353 | &txq_params); | ||
354 | if (result) | ||
355 | goto bad_res; | ||
356 | } | ||
357 | } | ||
358 | |||
359 | bad_res: | ||
301 | cfg80211_put_dev(rdev); | 360 | cfg80211_put_dev(rdev); |
302 | return result; | 361 | return result; |
303 | } | 362 | } |
@@ -1613,6 +1672,12 @@ static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info) | |||
1613 | if (info->attrs[NL80211_ATTR_BSS_SHORT_SLOT_TIME]) | 1672 | if (info->attrs[NL80211_ATTR_BSS_SHORT_SLOT_TIME]) |
1614 | params.use_short_slot_time = | 1673 | params.use_short_slot_time = |
1615 | nla_get_u8(info->attrs[NL80211_ATTR_BSS_SHORT_SLOT_TIME]); | 1674 | nla_get_u8(info->attrs[NL80211_ATTR_BSS_SHORT_SLOT_TIME]); |
1675 | if (info->attrs[NL80211_ATTR_BSS_BASIC_RATES]) { | ||
1676 | params.basic_rates = | ||
1677 | nla_data(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]); | ||
1678 | params.basic_rates_len = | ||
1679 | nla_len(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]); | ||
1680 | } | ||
1616 | 1681 | ||
1617 | err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); | 1682 | err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); |
1618 | if (err) | 1683 | if (err) |
diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 9dff716d1b02..4c7e39d466c4 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c | |||
@@ -42,16 +42,33 @@ | |||
42 | #include "core.h" | 42 | #include "core.h" |
43 | #include "reg.h" | 43 | #include "reg.h" |
44 | 44 | ||
45 | /* | 45 | /** |
46 | * wiphy is set if this request's initiator is | 46 | * struct regulatory_request - receipt of last regulatory request |
47 | * REGDOM_SET_BY_COUNTRY_IE or _DRIVER | 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. | ||
48 | */ | 63 | */ |
49 | struct regulatory_request { | 64 | struct regulatory_request { |
50 | struct wiphy *wiphy; | 65 | struct wiphy *wiphy; |
51 | enum reg_set_by initiator; | 66 | enum reg_set_by initiator; |
52 | char alpha2[2]; | 67 | char alpha2[2]; |
68 | bool intersect; | ||
53 | }; | 69 | }; |
54 | 70 | ||
71 | /* Receipt of information from last regulatory request */ | ||
55 | static struct regulatory_request *last_request; | 72 | static struct regulatory_request *last_request; |
56 | 73 | ||
57 | /* To trigger userspace events */ | 74 | /* To trigger userspace events */ |
@@ -321,7 +338,7 @@ static bool is_valid_reg_rule(const struct ieee80211_reg_rule *rule) | |||
321 | 338 | ||
322 | 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; |
323 | 340 | ||
324 | if (freq_range->max_bandwidth_khz > freq_diff) | 341 | if (freq_diff <= 0 || freq_range->max_bandwidth_khz > freq_diff) |
325 | return false; | 342 | return false; |
326 | 343 | ||
327 | return true; | 344 | return true; |
@@ -359,6 +376,143 @@ static u32 freq_max_bandwidth(const struct ieee80211_freq_range *freq_range, | |||
359 | return 0; | 376 | return 0; |
360 | } | 377 | } |
361 | 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 | |||
362 | /* 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 |
363 | * want to just have the channel structure use these */ | 517 | * want to just have the channel structure use these */ |
364 | static u32 map_regdom_flags(u32 rd_flags) | 518 | static u32 map_regdom_flags(u32 rd_flags) |
@@ -468,6 +622,10 @@ void wiphy_update_regulatory(struct wiphy *wiphy, enum reg_set_by setby) | |||
468 | } | 622 | } |
469 | } | 623 | } |
470 | 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 | |||
471 | /* This has the logic which determines when a new request | 629 | /* This has the logic which determines when a new request |
472 | * should be ignored. */ | 630 | * should be ignored. */ |
473 | static int ignore_request(struct wiphy *wiphy, enum reg_set_by set_by, | 631 | static int ignore_request(struct wiphy *wiphy, enum reg_set_by set_by, |
@@ -517,14 +675,8 @@ static int ignore_request(struct wiphy *wiphy, enum reg_set_by set_by, | |||
517 | return -EALREADY; | 675 | return -EALREADY; |
518 | return 0; | 676 | return 0; |
519 | case REGDOM_SET_BY_USER: | 677 | 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) | 678 | if (last_request->initiator == REGDOM_SET_BY_COUNTRY_IE) |
527 | return -EOPNOTSUPP; | 679 | return REG_INTERSECT; |
528 | return 0; | 680 | return 0; |
529 | } | 681 | } |
530 | 682 | ||
@@ -536,10 +688,14 @@ int __regulatory_hint(struct wiphy *wiphy, enum reg_set_by set_by, | |||
536 | const char *alpha2) | 688 | const char *alpha2) |
537 | { | 689 | { |
538 | struct regulatory_request *request; | 690 | struct regulatory_request *request; |
691 | bool intersect = false; | ||
539 | int r = 0; | 692 | int r = 0; |
540 | 693 | ||
541 | r = ignore_request(wiphy, set_by, alpha2); | 694 | r = ignore_request(wiphy, set_by, alpha2); |
542 | if (r) | 695 | |
696 | if (r == REG_INTERSECT) | ||
697 | intersect = true; | ||
698 | else if (r) | ||
543 | return r; | 699 | return r; |
544 | 700 | ||
545 | switch (set_by) { | 701 | switch (set_by) { |
@@ -556,6 +712,7 @@ int __regulatory_hint(struct wiphy *wiphy, enum reg_set_by set_by, | |||
556 | request->alpha2[1] = alpha2[1]; | 712 | request->alpha2[1] = alpha2[1]; |
557 | request->initiator = set_by; | 713 | request->initiator = set_by; |
558 | request->wiphy = wiphy; | 714 | request->wiphy = wiphy; |
715 | request->intersect = intersect; | ||
559 | 716 | ||
560 | kfree(last_request); | 717 | kfree(last_request); |
561 | last_request = request; | 718 | last_request = request; |
@@ -638,7 +795,7 @@ static void print_regdomain(const struct ieee80211_regdomain *rd) | |||
638 | print_rd_rules(rd); | 795 | print_rd_rules(rd); |
639 | } | 796 | } |
640 | 797 | ||
641 | void print_regdomain_info(const struct ieee80211_regdomain *rd) | 798 | static void print_regdomain_info(const struct ieee80211_regdomain *rd) |
642 | { | 799 | { |
643 | printk(KERN_INFO "cfg80211: Regulatory domain: %c%c\n", | 800 | printk(KERN_INFO "cfg80211: Regulatory domain: %c%c\n", |
644 | rd->alpha2[0], rd->alpha2[1]); | 801 | rd->alpha2[0], rd->alpha2[1]); |
@@ -648,6 +805,7 @@ void print_regdomain_info(const struct ieee80211_regdomain *rd) | |||
648 | /* Takes ownership of rd only if it doesn't fail */ | 805 | /* Takes ownership of rd only if it doesn't fail */ |
649 | static int __set_regdom(const struct ieee80211_regdomain *rd) | 806 | static int __set_regdom(const struct ieee80211_regdomain *rd) |
650 | { | 807 | { |
808 | const struct ieee80211_regdomain *intersected_rd = NULL; | ||
651 | /* Some basic sanity checks first */ | 809 | /* Some basic sanity checks first */ |
652 | 810 | ||
653 | if (is_world_regdom(rd->alpha2)) { | 811 | if (is_world_regdom(rd->alpha2)) { |
@@ -697,6 +855,14 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) | |||
697 | return -EOPNOTSUPP; | 855 | return -EOPNOTSUPP; |
698 | } | 856 | } |
699 | 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 | |||
700 | /* Tada! */ | 866 | /* Tada! */ |
701 | cfg80211_regdomain = rd; | 867 | cfg80211_regdomain = rd; |
702 | 868 | ||
diff --git a/net/wireless/util.c b/net/wireless/util.c index f54424693a38..e76cc28b0345 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c | |||
@@ -7,6 +7,25 @@ | |||
7 | #include <asm/bitops.h> | 7 | #include <asm/bitops.h> |
8 | #include "core.h" | 8 | #include "core.h" |
9 | 9 | ||
10 | struct ieee80211_rate * | ||
11 | ieee80211_get_response_rate(struct ieee80211_supported_band *sband, | ||
12 | u64 basic_rates, int bitrate) | ||
13 | { | ||
14 | struct ieee80211_rate *result = &sband->bitrates[0]; | ||
15 | int i; | ||
16 | |||
17 | for (i = 0; i < sband->n_bitrates; i++) { | ||
18 | if (!(basic_rates & BIT(i))) | ||
19 | continue; | ||
20 | if (sband->bitrates[i].bitrate > bitrate) | ||
21 | continue; | ||
22 | result = &sband->bitrates[i]; | ||
23 | } | ||
24 | |||
25 | return result; | ||
26 | } | ||
27 | EXPORT_SYMBOL(ieee80211_get_response_rate); | ||
28 | |||
10 | int ieee80211_channel_to_frequency(int chan) | 29 | int ieee80211_channel_to_frequency(int chan) |
11 | { | 30 | { |
12 | if (chan < 14) | 31 | if (chan < 14) |