diff options
Diffstat (limited to 'net/wireless/reg.c')
| -rw-r--r-- | net/wireless/reg.c | 806 |
1 files changed, 278 insertions, 528 deletions
diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 7a0754c92df4..f180db0de66c 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c | |||
| @@ -33,6 +33,7 @@ | |||
| 33 | * | 33 | * |
| 34 | */ | 34 | */ |
| 35 | #include <linux/kernel.h> | 35 | #include <linux/kernel.h> |
| 36 | #include <linux/slab.h> | ||
| 36 | #include <linux/list.h> | 37 | #include <linux/list.h> |
| 37 | #include <linux/random.h> | 38 | #include <linux/random.h> |
| 38 | #include <linux/nl80211.h> | 39 | #include <linux/nl80211.h> |
| @@ -40,8 +41,18 @@ | |||
| 40 | #include <net/cfg80211.h> | 41 | #include <net/cfg80211.h> |
| 41 | #include "core.h" | 42 | #include "core.h" |
| 42 | #include "reg.h" | 43 | #include "reg.h" |
| 44 | #include "regdb.h" | ||
| 43 | #include "nl80211.h" | 45 | #include "nl80211.h" |
| 44 | 46 | ||
| 47 | #ifdef CONFIG_CFG80211_REG_DEBUG | ||
| 48 | #define REG_DBG_PRINT(format, args...) \ | ||
| 49 | do { \ | ||
| 50 | printk(KERN_DEBUG format , ## args); \ | ||
| 51 | } while (0) | ||
| 52 | #else | ||
| 53 | #define REG_DBG_PRINT(args...) | ||
| 54 | #endif | ||
| 55 | |||
| 45 | /* Receipt of information from last regulatory request */ | 56 | /* Receipt of information from last regulatory request */ |
| 46 | static struct regulatory_request *last_request; | 57 | static struct regulatory_request *last_request; |
| 47 | 58 | ||
| @@ -56,20 +67,12 @@ static struct platform_device *reg_pdev; | |||
| 56 | const struct ieee80211_regdomain *cfg80211_regdomain; | 67 | const struct ieee80211_regdomain *cfg80211_regdomain; |
| 57 | 68 | ||
| 58 | /* | 69 | /* |
| 59 | * We use this as a place for the rd structure built from the | ||
| 60 | * last parsed country IE to rest until CRDA gets back to us with | ||
| 61 | * what it thinks should apply for the same country | ||
| 62 | */ | ||
| 63 | static const struct ieee80211_regdomain *country_ie_regdomain; | ||
| 64 | |||
| 65 | /* | ||
| 66 | * Protects static reg.c components: | 70 | * Protects static reg.c components: |
| 67 | * - cfg80211_world_regdom | 71 | * - cfg80211_world_regdom |
| 68 | * - cfg80211_regdom | 72 | * - cfg80211_regdom |
| 69 | * - country_ie_regdomain | ||
| 70 | * - last_request | 73 | * - last_request |
| 71 | */ | 74 | */ |
| 72 | DEFINE_MUTEX(reg_mutex); | 75 | static DEFINE_MUTEX(reg_mutex); |
| 73 | #define assert_reg_lock() WARN_ON(!mutex_is_locked(®_mutex)) | 76 | #define assert_reg_lock() WARN_ON(!mutex_is_locked(®_mutex)) |
| 74 | 77 | ||
| 75 | /* Used to queue up regulatory hints */ | 78 | /* Used to queue up regulatory hints */ |
| @@ -124,82 +127,11 @@ static const struct ieee80211_regdomain *cfg80211_world_regdom = | |||
| 124 | &world_regdom; | 127 | &world_regdom; |
| 125 | 128 | ||
| 126 | static char *ieee80211_regdom = "00"; | 129 | static char *ieee80211_regdom = "00"; |
| 130 | static char user_alpha2[2]; | ||
| 127 | 131 | ||
| 128 | module_param(ieee80211_regdom, charp, 0444); | 132 | module_param(ieee80211_regdom, charp, 0444); |
| 129 | MODULE_PARM_DESC(ieee80211_regdom, "IEEE 802.11 regulatory domain code"); | 133 | MODULE_PARM_DESC(ieee80211_regdom, "IEEE 802.11 regulatory domain code"); |
| 130 | 134 | ||
| 131 | #ifdef CONFIG_WIRELESS_OLD_REGULATORY | ||
| 132 | /* | ||
| 133 | * We assume 40 MHz bandwidth for the old regulatory work. | ||
| 134 | * We make emphasis we are using the exact same frequencies | ||
| 135 | * as before | ||
| 136 | */ | ||
| 137 | |||
| 138 | static const struct ieee80211_regdomain us_regdom = { | ||
| 139 | .n_reg_rules = 6, | ||
| 140 | .alpha2 = "US", | ||
| 141 | .reg_rules = { | ||
| 142 | /* IEEE 802.11b/g, channels 1..11 */ | ||
| 143 | REG_RULE(2412-10, 2462+10, 40, 6, 27, 0), | ||
| 144 | /* IEEE 802.11a, channel 36..48 */ | ||
| 145 | REG_RULE(5180-10, 5240+10, 40, 6, 17, 0), | ||
| 146 | /* IEEE 802.11a, channels 48..64 */ | ||
| 147 | REG_RULE(5260-10, 5320+10, 40, 6, 20, NL80211_RRF_DFS), | ||
| 148 | /* IEEE 802.11a, channels 100..124 */ | ||
| 149 | REG_RULE(5500-10, 5590+10, 40, 6, 20, NL80211_RRF_DFS), | ||
| 150 | /* IEEE 802.11a, channels 132..144 */ | ||
| 151 | REG_RULE(5660-10, 5700+10, 40, 6, 20, NL80211_RRF_DFS), | ||
| 152 | /* IEEE 802.11a, channels 149..165, outdoor */ | ||
| 153 | REG_RULE(5745-10, 5825+10, 40, 6, 30, 0), | ||
| 154 | } | ||
| 155 | }; | ||
| 156 | |||
| 157 | static const struct ieee80211_regdomain jp_regdom = { | ||
| 158 | .n_reg_rules = 6, | ||
| 159 | .alpha2 = "JP", | ||
| 160 | .reg_rules = { | ||
| 161 | /* IEEE 802.11b/g, channels 1..11 */ | ||
| 162 | REG_RULE(2412-10, 2462+10, 40, 6, 20, 0), | ||
| 163 | /* IEEE 802.11b/g, channels 12..13 */ | ||
| 164 | REG_RULE(2467-10, 2472+10, 20, 6, 20, 0), | ||
| 165 | /* IEEE 802.11b/g, channel 14 */ | ||
| 166 | REG_RULE(2484-10, 2484+10, 20, 6, 20, NL80211_RRF_NO_OFDM), | ||
| 167 | /* IEEE 802.11a, channels 36..48 */ | ||
| 168 | REG_RULE(5180-10, 5240+10, 40, 6, 20, 0), | ||
| 169 | /* IEEE 802.11a, channels 52..64 */ | ||
| 170 | REG_RULE(5260-10, 5320+10, 40, 6, 20, NL80211_RRF_DFS), | ||
| 171 | /* IEEE 802.11a, channels 100..144 */ | ||
| 172 | REG_RULE(5500-10, 5700+10, 40, 6, 23, NL80211_RRF_DFS), | ||
| 173 | } | ||
| 174 | }; | ||
| 175 | |||
| 176 | static const struct ieee80211_regdomain *static_regdom(char *alpha2) | ||
| 177 | { | ||
| 178 | if (alpha2[0] == 'U' && alpha2[1] == 'S') | ||
| 179 | return &us_regdom; | ||
| 180 | if (alpha2[0] == 'J' && alpha2[1] == 'P') | ||
| 181 | return &jp_regdom; | ||
| 182 | /* Use world roaming rules for "EU", since it was a pseudo | ||
| 183 | domain anyway... */ | ||
| 184 | if (alpha2[0] == 'E' && alpha2[1] == 'U') | ||
| 185 | return &world_regdom; | ||
| 186 | /* Default, world roaming rules */ | ||
| 187 | return &world_regdom; | ||
| 188 | } | ||
| 189 | |||
| 190 | static bool is_old_static_regdom(const struct ieee80211_regdomain *rd) | ||
| 191 | { | ||
| 192 | if (rd == &us_regdom || rd == &jp_regdom || rd == &world_regdom) | ||
| 193 | return true; | ||
| 194 | return false; | ||
| 195 | } | ||
| 196 | #else | ||
| 197 | static inline bool is_old_static_regdom(const struct ieee80211_regdomain *rd) | ||
| 198 | { | ||
| 199 | return false; | ||
| 200 | } | ||
| 201 | #endif | ||
| 202 | |||
| 203 | static void reset_regdomains(void) | 135 | static void reset_regdomains(void) |
| 204 | { | 136 | { |
| 205 | /* avoid freeing static information or freeing something twice */ | 137 | /* avoid freeing static information or freeing something twice */ |
| @@ -209,8 +141,6 @@ static void reset_regdomains(void) | |||
| 209 | cfg80211_world_regdom = NULL; | 141 | cfg80211_world_regdom = NULL; |
| 210 | if (cfg80211_regdomain == &world_regdom) | 142 | if (cfg80211_regdomain == &world_regdom) |
| 211 | cfg80211_regdomain = NULL; | 143 | cfg80211_regdomain = NULL; |
| 212 | if (is_old_static_regdom(cfg80211_regdomain)) | ||
| 213 | cfg80211_regdomain = NULL; | ||
| 214 | 144 | ||
| 215 | kfree(cfg80211_regdomain); | 145 | kfree(cfg80211_regdomain); |
| 216 | kfree(cfg80211_world_regdom); | 146 | kfree(cfg80211_world_regdom); |
| @@ -316,24 +246,116 @@ static bool regdom_changes(const char *alpha2) | |||
| 316 | return true; | 246 | return true; |
| 317 | } | 247 | } |
| 318 | 248 | ||
| 319 | /** | 249 | /* |
| 320 | * country_ie_integrity_changes - tells us if the country IE has changed | 250 | * The NL80211_REGDOM_SET_BY_USER regdom alpha2 is cached, this lets |
| 321 | * @checksum: checksum of country IE of fields we are interested in | 251 | * you know if a valid regulatory hint with NL80211_REGDOM_SET_BY_USER |
| 322 | * | 252 | * has ever been issued. |
| 323 | * If the country IE has not changed you can ignore it safely. This is | ||
| 324 | * useful to determine if two devices are seeing two different country IEs | ||
| 325 | * even on the same alpha2. Note that this will return false if no IE has | ||
| 326 | * been set on the wireless core yet. | ||
| 327 | */ | 253 | */ |
| 328 | static bool country_ie_integrity_changes(u32 checksum) | 254 | static bool is_user_regdom_saved(void) |
| 329 | { | 255 | { |
| 330 | /* If no IE has been set then the checksum doesn't change */ | 256 | if (user_alpha2[0] == '9' && user_alpha2[1] == '7') |
| 331 | if (unlikely(!last_request->country_ie_checksum)) | ||
| 332 | return false; | 257 | return false; |
| 333 | if (unlikely(last_request->country_ie_checksum != checksum)) | 258 | |
| 334 | return true; | 259 | /* This would indicate a mistake on the design */ |
| 335 | return false; | 260 | if (WARN((!is_world_regdom(user_alpha2) && |
| 261 | !is_an_alpha2(user_alpha2)), | ||
| 262 | "Unexpected user alpha2: %c%c\n", | ||
| 263 | user_alpha2[0], | ||
| 264 | user_alpha2[1])) | ||
| 265 | return false; | ||
| 266 | |||
| 267 | return true; | ||
| 268 | } | ||
| 269 | |||
| 270 | static int reg_copy_regd(const struct ieee80211_regdomain **dst_regd, | ||
| 271 | const struct ieee80211_regdomain *src_regd) | ||
| 272 | { | ||
| 273 | struct ieee80211_regdomain *regd; | ||
| 274 | int size_of_regd = 0; | ||
| 275 | unsigned int i; | ||
| 276 | |||
| 277 | size_of_regd = sizeof(struct ieee80211_regdomain) + | ||
| 278 | ((src_regd->n_reg_rules + 1) * sizeof(struct ieee80211_reg_rule)); | ||
| 279 | |||
| 280 | regd = kzalloc(size_of_regd, GFP_KERNEL); | ||
| 281 | if (!regd) | ||
| 282 | return -ENOMEM; | ||
| 283 | |||
| 284 | memcpy(regd, src_regd, sizeof(struct ieee80211_regdomain)); | ||
| 285 | |||
| 286 | for (i = 0; i < src_regd->n_reg_rules; i++) | ||
| 287 | memcpy(®d->reg_rules[i], &src_regd->reg_rules[i], | ||
| 288 | sizeof(struct ieee80211_reg_rule)); | ||
| 289 | |||
| 290 | *dst_regd = regd; | ||
| 291 | return 0; | ||
| 292 | } | ||
| 293 | |||
| 294 | #ifdef CONFIG_CFG80211_INTERNAL_REGDB | ||
| 295 | struct reg_regdb_search_request { | ||
| 296 | char alpha2[2]; | ||
| 297 | struct list_head list; | ||
| 298 | }; | ||
| 299 | |||
| 300 | static LIST_HEAD(reg_regdb_search_list); | ||
| 301 | static DEFINE_MUTEX(reg_regdb_search_mutex); | ||
| 302 | |||
| 303 | static void reg_regdb_search(struct work_struct *work) | ||
| 304 | { | ||
| 305 | struct reg_regdb_search_request *request; | ||
| 306 | const struct ieee80211_regdomain *curdom, *regdom; | ||
| 307 | int i, r; | ||
| 308 | |||
| 309 | mutex_lock(®_regdb_search_mutex); | ||
| 310 | while (!list_empty(®_regdb_search_list)) { | ||
| 311 | request = list_first_entry(®_regdb_search_list, | ||
| 312 | struct reg_regdb_search_request, | ||
| 313 | list); | ||
| 314 | list_del(&request->list); | ||
| 315 | |||
| 316 | for (i=0; i<reg_regdb_size; i++) { | ||
| 317 | curdom = reg_regdb[i]; | ||
| 318 | |||
| 319 | if (!memcmp(request->alpha2, curdom->alpha2, 2)) { | ||
| 320 | r = reg_copy_regd(®dom, curdom); | ||
| 321 | if (r) | ||
| 322 | break; | ||
| 323 | mutex_lock(&cfg80211_mutex); | ||
| 324 | set_regdom(regdom); | ||
| 325 | mutex_unlock(&cfg80211_mutex); | ||
| 326 | break; | ||
| 327 | } | ||
| 328 | } | ||
| 329 | |||
| 330 | kfree(request); | ||
| 331 | } | ||
| 332 | mutex_unlock(®_regdb_search_mutex); | ||
| 333 | } | ||
| 334 | |||
| 335 | static DECLARE_WORK(reg_regdb_work, reg_regdb_search); | ||
| 336 | |||
| 337 | static void reg_regdb_query(const char *alpha2) | ||
| 338 | { | ||
| 339 | struct reg_regdb_search_request *request; | ||
| 340 | |||
| 341 | if (!alpha2) | ||
| 342 | return; | ||
| 343 | |||
| 344 | request = kzalloc(sizeof(struct reg_regdb_search_request), GFP_KERNEL); | ||
| 345 | if (!request) | ||
| 346 | return; | ||
| 347 | |||
| 348 | memcpy(request->alpha2, alpha2, 2); | ||
| 349 | |||
| 350 | mutex_lock(®_regdb_search_mutex); | ||
| 351 | list_add_tail(&request->list, ®_regdb_search_list); | ||
| 352 | mutex_unlock(®_regdb_search_mutex); | ||
| 353 | |||
| 354 | schedule_work(®_regdb_work); | ||
| 336 | } | 355 | } |
| 356 | #else | ||
| 357 | static inline void reg_regdb_query(const char *alpha2) {} | ||
| 358 | #endif /* CONFIG_CFG80211_INTERNAL_REGDB */ | ||
| 337 | 359 | ||
| 338 | /* | 360 | /* |
| 339 | * This lets us keep regulatory code which is updated on a regulatory | 361 | * This lets us keep regulatory code which is updated on a regulatory |
| @@ -354,6 +376,9 @@ static int call_crda(const char *alpha2) | |||
| 354 | printk(KERN_INFO "cfg80211: Calling CRDA to update world " | 376 | printk(KERN_INFO "cfg80211: Calling CRDA to update world " |
| 355 | "regulatory domain\n"); | 377 | "regulatory domain\n"); |
| 356 | 378 | ||
| 379 | /* query internal regulatory database (if it exists) */ | ||
| 380 | reg_regdb_query(alpha2); | ||
| 381 | |||
| 357 | country_env[8] = alpha2[0]; | 382 | country_env[8] = alpha2[0]; |
| 358 | country_env[9] = alpha2[1]; | 383 | country_env[9] = alpha2[1]; |
| 359 | 384 | ||
| @@ -454,215 +479,6 @@ static bool freq_in_rule_band(const struct ieee80211_freq_range *freq_range, | |||
| 454 | } | 479 | } |
| 455 | 480 | ||
| 456 | /* | 481 | /* |
| 457 | * Converts a country IE to a regulatory domain. A regulatory domain | ||
| 458 | * structure has a lot of information which the IE doesn't yet have, | ||
| 459 | * so for the other values we use upper max values as we will intersect | ||
| 460 | * with our userspace regulatory agent to get lower bounds. | ||
| 461 | */ | ||
| 462 | static struct ieee80211_regdomain *country_ie_2_rd( | ||
| 463 | u8 *country_ie, | ||
| 464 | u8 country_ie_len, | ||
| 465 | u32 *checksum) | ||
| 466 | { | ||
| 467 | struct ieee80211_regdomain *rd = NULL; | ||
| 468 | unsigned int i = 0; | ||
| 469 | char alpha2[2]; | ||
| 470 | u32 flags = 0; | ||
| 471 | u32 num_rules = 0, size_of_regd = 0; | ||
| 472 | u8 *triplets_start = NULL; | ||
| 473 | u8 len_at_triplet = 0; | ||
| 474 | /* the last channel we have registered in a subband (triplet) */ | ||
| 475 | int last_sub_max_channel = 0; | ||
| 476 | |||
| 477 | *checksum = 0xDEADBEEF; | ||
| 478 | |||
| 479 | /* Country IE requirements */ | ||
| 480 | BUG_ON(country_ie_len < IEEE80211_COUNTRY_IE_MIN_LEN || | ||
| 481 | country_ie_len & 0x01); | ||
| 482 | |||
| 483 | alpha2[0] = country_ie[0]; | ||
| 484 | alpha2[1] = country_ie[1]; | ||
| 485 | |||
| 486 | /* | ||
| 487 | * Third octet can be: | ||
| 488 | * 'I' - Indoor | ||
| 489 | * 'O' - Outdoor | ||
| 490 | * | ||
| 491 | * anything else we assume is no restrictions | ||
| 492 | */ | ||
| 493 | if (country_ie[2] == 'I') | ||
| 494 | flags = NL80211_RRF_NO_OUTDOOR; | ||
| 495 | else if (country_ie[2] == 'O') | ||
| 496 | flags = NL80211_RRF_NO_INDOOR; | ||
| 497 | |||
| 498 | country_ie += 3; | ||
| 499 | country_ie_len -= 3; | ||
| 500 | |||
| 501 | triplets_start = country_ie; | ||
| 502 | len_at_triplet = country_ie_len; | ||
| 503 | |||
| 504 | *checksum ^= ((flags ^ alpha2[0] ^ alpha2[1]) << 8); | ||
| 505 | |||
| 506 | /* | ||
| 507 | * We need to build a reg rule for each triplet, but first we must | ||
| 508 | * calculate the number of reg rules we will need. We will need one | ||
| 509 | * for each channel subband | ||
| 510 | */ | ||
| 511 | while (country_ie_len >= 3) { | ||
| 512 | int end_channel = 0; | ||
| 513 | struct ieee80211_country_ie_triplet *triplet = | ||
| 514 | (struct ieee80211_country_ie_triplet *) country_ie; | ||
| 515 | int cur_sub_max_channel = 0, cur_channel = 0; | ||
| 516 | |||
| 517 | if (triplet->ext.reg_extension_id >= | ||
| 518 | IEEE80211_COUNTRY_EXTENSION_ID) { | ||
| 519 | country_ie += 3; | ||
| 520 | country_ie_len -= 3; | ||
| 521 | continue; | ||
| 522 | } | ||
| 523 | |||
| 524 | /* 2 GHz */ | ||
| 525 | if (triplet->chans.first_channel <= 14) | ||
| 526 | end_channel = triplet->chans.first_channel + | ||
| 527 | triplet->chans.num_channels; | ||
| 528 | else | ||
| 529 | /* | ||
| 530 | * 5 GHz -- For example in country IEs if the first | ||
| 531 | * channel given is 36 and the number of channels is 4 | ||
| 532 | * then the individual channel numbers defined for the | ||
| 533 | * 5 GHz PHY by these parameters are: 36, 40, 44, and 48 | ||
| 534 | * and not 36, 37, 38, 39. | ||
| 535 | * | ||
| 536 | * See: http://tinyurl.com/11d-clarification | ||
| 537 | */ | ||
| 538 | end_channel = triplet->chans.first_channel + | ||
| 539 | (4 * (triplet->chans.num_channels - 1)); | ||
| 540 | |||
| 541 | cur_channel = triplet->chans.first_channel; | ||
| 542 | cur_sub_max_channel = end_channel; | ||
| 543 | |||
| 544 | /* Basic sanity check */ | ||
| 545 | if (cur_sub_max_channel < cur_channel) | ||
| 546 | return NULL; | ||
| 547 | |||
| 548 | /* | ||
| 549 | * Do not allow overlapping channels. Also channels | ||
| 550 | * passed in each subband must be monotonically | ||
| 551 | * increasing | ||
| 552 | */ | ||
| 553 | if (last_sub_max_channel) { | ||
| 554 | if (cur_channel <= last_sub_max_channel) | ||
| 555 | return NULL; | ||
| 556 | if (cur_sub_max_channel <= last_sub_max_channel) | ||
| 557 | return NULL; | ||
| 558 | } | ||
| 559 | |||
| 560 | /* | ||
| 561 | * When dot11RegulatoryClassesRequired is supported | ||
| 562 | * we can throw ext triplets as part of this soup, | ||
| 563 | * for now we don't care when those change as we | ||
| 564 | * don't support them | ||
| 565 | */ | ||
| 566 | *checksum ^= ((cur_channel ^ cur_sub_max_channel) << 8) | | ||
| 567 | ((cur_sub_max_channel ^ cur_sub_max_channel) << 16) | | ||
| 568 | ((triplet->chans.max_power ^ cur_sub_max_channel) << 24); | ||
| 569 | |||
| 570 | last_sub_max_channel = cur_sub_max_channel; | ||
| 571 | |||
| 572 | country_ie += 3; | ||
| 573 | country_ie_len -= 3; | ||
| 574 | num_rules++; | ||
| 575 | |||
| 576 | /* | ||
| 577 | * Note: this is not a IEEE requirement but | ||
| 578 | * simply a memory requirement | ||
| 579 | */ | ||
| 580 | if (num_rules > NL80211_MAX_SUPP_REG_RULES) | ||
| 581 | return NULL; | ||
| 582 | } | ||
| 583 | |||
| 584 | country_ie = triplets_start; | ||
| 585 | country_ie_len = len_at_triplet; | ||
| 586 | |||
| 587 | size_of_regd = sizeof(struct ieee80211_regdomain) + | ||
| 588 | (num_rules * sizeof(struct ieee80211_reg_rule)); | ||
| 589 | |||
| 590 | rd = kzalloc(size_of_regd, GFP_KERNEL); | ||
| 591 | if (!rd) | ||
| 592 | return NULL; | ||
| 593 | |||
| 594 | rd->n_reg_rules = num_rules; | ||
| 595 | rd->alpha2[0] = alpha2[0]; | ||
| 596 | rd->alpha2[1] = alpha2[1]; | ||
| 597 | |||
| 598 | /* This time around we fill in the rd */ | ||
| 599 | while (country_ie_len >= 3) { | ||
| 600 | int end_channel = 0; | ||
| 601 | struct ieee80211_country_ie_triplet *triplet = | ||
| 602 | (struct ieee80211_country_ie_triplet *) country_ie; | ||
| 603 | struct ieee80211_reg_rule *reg_rule = NULL; | ||
| 604 | struct ieee80211_freq_range *freq_range = NULL; | ||
| 605 | struct ieee80211_power_rule *power_rule = NULL; | ||
| 606 | |||
| 607 | /* | ||
| 608 | * Must parse if dot11RegulatoryClassesRequired is true, | ||
| 609 | * we don't support this yet | ||
| 610 | */ | ||
| 611 | if (triplet->ext.reg_extension_id >= | ||
| 612 | IEEE80211_COUNTRY_EXTENSION_ID) { | ||
| 613 | country_ie += 3; | ||
| 614 | country_ie_len -= 3; | ||
| 615 | continue; | ||
| 616 | } | ||
| 617 | |||
| 618 | reg_rule = &rd->reg_rules[i]; | ||
| 619 | freq_range = ®_rule->freq_range; | ||
| 620 | power_rule = ®_rule->power_rule; | ||
| 621 | |||
| 622 | reg_rule->flags = flags; | ||
| 623 | |||
| 624 | /* 2 GHz */ | ||
| 625 | if (triplet->chans.first_channel <= 14) | ||
| 626 | end_channel = triplet->chans.first_channel + | ||
| 627 | triplet->chans.num_channels; | ||
| 628 | else | ||
| 629 | end_channel = triplet->chans.first_channel + | ||
| 630 | (4 * (triplet->chans.num_channels - 1)); | ||
| 631 | |||
| 632 | /* | ||
| 633 | * The +10 is since the regulatory domain expects | ||
| 634 | * the actual band edge, not the center of freq for | ||
| 635 | * its start and end freqs, assuming 20 MHz bandwidth on | ||
| 636 | * the channels passed | ||
| 637 | */ | ||
| 638 | freq_range->start_freq_khz = | ||
| 639 | MHZ_TO_KHZ(ieee80211_channel_to_frequency( | ||
| 640 | triplet->chans.first_channel) - 10); | ||
| 641 | freq_range->end_freq_khz = | ||
| 642 | MHZ_TO_KHZ(ieee80211_channel_to_frequency( | ||
| 643 | end_channel) + 10); | ||
| 644 | |||
| 645 | /* | ||
| 646 | * These are large arbitrary values we use to intersect later. | ||
| 647 | * Increment this if we ever support >= 40 MHz channels | ||
| 648 | * in IEEE 802.11 | ||
| 649 | */ | ||
| 650 | freq_range->max_bandwidth_khz = MHZ_TO_KHZ(40); | ||
| 651 | power_rule->max_antenna_gain = DBI_TO_MBI(100); | ||
| 652 | power_rule->max_eirp = DBM_TO_MBM(100); | ||
| 653 | |||
| 654 | country_ie += 3; | ||
| 655 | country_ie_len -= 3; | ||
| 656 | i++; | ||
| 657 | |||
| 658 | BUG_ON(i > NL80211_MAX_SUPP_REG_RULES); | ||
| 659 | } | ||
| 660 | |||
| 661 | return rd; | ||
| 662 | } | ||
| 663 | |||
| 664 | |||
| 665 | /* | ||
| 666 | * Helper for regdom_intersect(), this does the real | 482 | * Helper for regdom_intersect(), this does the real |
| 667 | * mathematical intersection fun | 483 | * mathematical intersection fun |
| 668 | */ | 484 | */ |
| @@ -883,7 +699,6 @@ static int freq_reg_info_regd(struct wiphy *wiphy, | |||
| 883 | 699 | ||
| 884 | return -EINVAL; | 700 | return -EINVAL; |
| 885 | } | 701 | } |
| 886 | EXPORT_SYMBOL(freq_reg_info); | ||
| 887 | 702 | ||
| 888 | int freq_reg_info(struct wiphy *wiphy, | 703 | int freq_reg_info(struct wiphy *wiphy, |
| 889 | u32 center_freq, | 704 | u32 center_freq, |
| @@ -897,6 +712,7 @@ int freq_reg_info(struct wiphy *wiphy, | |||
| 897 | reg_rule, | 712 | reg_rule, |
| 898 | NULL); | 713 | NULL); |
| 899 | } | 714 | } |
| 715 | EXPORT_SYMBOL(freq_reg_info); | ||
| 900 | 716 | ||
| 901 | /* | 717 | /* |
| 902 | * Note that right now we assume the desired channel bandwidth | 718 | * Note that right now we assume the desired channel bandwidth |
| @@ -935,45 +751,8 @@ static void handle_channel(struct wiphy *wiphy, enum ieee80211_band band, | |||
| 935 | desired_bw_khz, | 751 | desired_bw_khz, |
| 936 | ®_rule); | 752 | ®_rule); |
| 937 | 753 | ||
| 938 | if (r) { | 754 | if (r) |
| 939 | /* | ||
| 940 | * This means no regulatory rule was found in the country IE | ||
| 941 | * with a frequency range on the center_freq's band, since | ||
| 942 | * IEEE-802.11 allows for a country IE to have a subset of the | ||
| 943 | * regulatory information provided in a country we ignore | ||
| 944 | * disabling the channel unless at least one reg rule was | ||
| 945 | * found on the center_freq's band. For details see this | ||
| 946 | * clarification: | ||
| 947 | * | ||
| 948 | * http://tinyurl.com/11d-clarification | ||
| 949 | */ | ||
| 950 | if (r == -ERANGE && | ||
| 951 | last_request->initiator == | ||
| 952 | NL80211_REGDOM_SET_BY_COUNTRY_IE) { | ||
| 953 | #ifdef CONFIG_CFG80211_REG_DEBUG | ||
| 954 | printk(KERN_DEBUG "cfg80211: Leaving channel %d MHz " | ||
| 955 | "intact on %s - no rule found in band on " | ||
| 956 | "Country IE\n", | ||
| 957 | chan->center_freq, wiphy_name(wiphy)); | ||
| 958 | #endif | ||
| 959 | } else { | ||
| 960 | /* | ||
| 961 | * In this case we know the country IE has at least one reg rule | ||
| 962 | * for the band so we respect its band definitions | ||
| 963 | */ | ||
| 964 | #ifdef CONFIG_CFG80211_REG_DEBUG | ||
| 965 | if (last_request->initiator == | ||
| 966 | NL80211_REGDOM_SET_BY_COUNTRY_IE) | ||
| 967 | printk(KERN_DEBUG "cfg80211: Disabling " | ||
| 968 | "channel %d MHz on %s due to " | ||
| 969 | "Country IE\n", | ||
| 970 | chan->center_freq, wiphy_name(wiphy)); | ||
| 971 | #endif | ||
| 972 | flags |= IEEE80211_CHAN_DISABLED; | ||
| 973 | chan->flags = flags; | ||
| 974 | } | ||
| 975 | return; | 755 | return; |
| 976 | } | ||
| 977 | 756 | ||
| 978 | power_rule = ®_rule->power_rule; | 757 | power_rule = ®_rule->power_rule; |
| 979 | freq_range = ®_rule->freq_range; | 758 | freq_range = ®_rule->freq_range; |
| @@ -1342,30 +1121,6 @@ void wiphy_apply_custom_regulatory(struct wiphy *wiphy, | |||
| 1342 | } | 1121 | } |
| 1343 | EXPORT_SYMBOL(wiphy_apply_custom_regulatory); | 1122 | EXPORT_SYMBOL(wiphy_apply_custom_regulatory); |
| 1344 | 1123 | ||
| 1345 | static int reg_copy_regd(const struct ieee80211_regdomain **dst_regd, | ||
| 1346 | const struct ieee80211_regdomain *src_regd) | ||
| 1347 | { | ||
| 1348 | struct ieee80211_regdomain *regd; | ||
| 1349 | int size_of_regd = 0; | ||
| 1350 | unsigned int i; | ||
| 1351 | |||
| 1352 | size_of_regd = sizeof(struct ieee80211_regdomain) + | ||
| 1353 | ((src_regd->n_reg_rules + 1) * sizeof(struct ieee80211_reg_rule)); | ||
| 1354 | |||
| 1355 | regd = kzalloc(size_of_regd, GFP_KERNEL); | ||
| 1356 | if (!regd) | ||
| 1357 | return -ENOMEM; | ||
| 1358 | |||
| 1359 | memcpy(regd, src_regd, sizeof(struct ieee80211_regdomain)); | ||
| 1360 | |||
| 1361 | for (i = 0; i < src_regd->n_reg_rules; i++) | ||
| 1362 | memcpy(®d->reg_rules[i], &src_regd->reg_rules[i], | ||
| 1363 | sizeof(struct ieee80211_reg_rule)); | ||
| 1364 | |||
| 1365 | *dst_regd = regd; | ||
| 1366 | return 0; | ||
| 1367 | } | ||
| 1368 | |||
| 1369 | /* | 1124 | /* |
| 1370 | * Return value which can be used by ignore_request() to indicate | 1125 | * Return value which can be used by ignore_request() to indicate |
| 1371 | * it has been determined we should intersect two regulatory domains | 1126 | * it has been determined we should intersect two regulatory domains |
| @@ -1387,7 +1142,7 @@ static int ignore_request(struct wiphy *wiphy, | |||
| 1387 | 1142 | ||
| 1388 | switch (pending_request->initiator) { | 1143 | switch (pending_request->initiator) { |
| 1389 | case NL80211_REGDOM_SET_BY_CORE: | 1144 | case NL80211_REGDOM_SET_BY_CORE: |
| 1390 | return -EINVAL; | 1145 | return 0; |
| 1391 | case NL80211_REGDOM_SET_BY_COUNTRY_IE: | 1146 | case NL80211_REGDOM_SET_BY_COUNTRY_IE: |
| 1392 | 1147 | ||
| 1393 | last_wiphy = wiphy_idx_to_wiphy(last_request->wiphy_idx); | 1148 | last_wiphy = wiphy_idx_to_wiphy(last_request->wiphy_idx); |
| @@ -1418,8 +1173,6 @@ static int ignore_request(struct wiphy *wiphy, | |||
| 1418 | return REG_INTERSECT; | 1173 | return REG_INTERSECT; |
| 1419 | case NL80211_REGDOM_SET_BY_DRIVER: | 1174 | case NL80211_REGDOM_SET_BY_DRIVER: |
| 1420 | if (last_request->initiator == NL80211_REGDOM_SET_BY_CORE) { | 1175 | if (last_request->initiator == NL80211_REGDOM_SET_BY_CORE) { |
| 1421 | if (is_old_static_regdom(cfg80211_regdomain)) | ||
| 1422 | return 0; | ||
| 1423 | if (regdom_changes(pending_request->alpha2)) | 1176 | if (regdom_changes(pending_request->alpha2)) |
| 1424 | return 0; | 1177 | return 0; |
| 1425 | return -EALREADY; | 1178 | return -EALREADY; |
| @@ -1456,8 +1209,7 @@ static int ignore_request(struct wiphy *wiphy, | |||
| 1456 | return -EAGAIN; | 1209 | return -EAGAIN; |
| 1457 | } | 1210 | } |
| 1458 | 1211 | ||
| 1459 | if (!is_old_static_regdom(cfg80211_regdomain) && | 1212 | if (!regdom_changes(pending_request->alpha2)) |
| 1460 | !regdom_changes(pending_request->alpha2)) | ||
| 1461 | return -EALREADY; | 1213 | return -EALREADY; |
| 1462 | 1214 | ||
| 1463 | return 0; | 1215 | return 0; |
| @@ -1529,6 +1281,11 @@ new_request: | |||
| 1529 | 1281 | ||
| 1530 | pending_request = NULL; | 1282 | pending_request = NULL; |
| 1531 | 1283 | ||
| 1284 | if (last_request->initiator == NL80211_REGDOM_SET_BY_USER) { | ||
| 1285 | user_alpha2[0] = last_request->alpha2[0]; | ||
| 1286 | user_alpha2[1] = last_request->alpha2[1]; | ||
| 1287 | } | ||
| 1288 | |||
| 1532 | /* When r == REG_INTERSECT we do need to call CRDA */ | 1289 | /* When r == REG_INTERSECT we do need to call CRDA */ |
| 1533 | if (r < 0) { | 1290 | if (r < 0) { |
| 1534 | /* | 1291 | /* |
| @@ -1549,6 +1306,7 @@ static void reg_process_hint(struct regulatory_request *reg_request) | |||
| 1549 | { | 1306 | { |
| 1550 | int r = 0; | 1307 | int r = 0; |
| 1551 | struct wiphy *wiphy = NULL; | 1308 | struct wiphy *wiphy = NULL; |
| 1309 | enum nl80211_reg_initiator initiator = reg_request->initiator; | ||
| 1552 | 1310 | ||
| 1553 | BUG_ON(!reg_request->alpha2); | 1311 | BUG_ON(!reg_request->alpha2); |
| 1554 | 1312 | ||
| @@ -1568,7 +1326,7 @@ static void reg_process_hint(struct regulatory_request *reg_request) | |||
| 1568 | /* This is required so that the orig_* parameters are saved */ | 1326 | /* This is required so that the orig_* parameters are saved */ |
| 1569 | if (r == -EALREADY && wiphy && | 1327 | if (r == -EALREADY && wiphy && |
| 1570 | wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY) | 1328 | wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY) |
| 1571 | wiphy_update_regulatory(wiphy, reg_request->initiator); | 1329 | wiphy_update_regulatory(wiphy, initiator); |
| 1572 | out: | 1330 | out: |
| 1573 | mutex_unlock(®_mutex); | 1331 | mutex_unlock(®_mutex); |
| 1574 | mutex_unlock(&cfg80211_mutex); | 1332 | mutex_unlock(&cfg80211_mutex); |
| @@ -1648,12 +1406,16 @@ static void queue_regulatory_request(struct regulatory_request *request) | |||
| 1648 | schedule_work(®_work); | 1406 | schedule_work(®_work); |
| 1649 | } | 1407 | } |
| 1650 | 1408 | ||
| 1651 | /* Core regulatory hint -- happens once during cfg80211_init() */ | 1409 | /* |
| 1410 | * Core regulatory hint -- happens during cfg80211_init() | ||
| 1411 | * and when we restore regulatory settings. | ||
| 1412 | */ | ||
| 1652 | static int regulatory_hint_core(const char *alpha2) | 1413 | static int regulatory_hint_core(const char *alpha2) |
| 1653 | { | 1414 | { |
| 1654 | struct regulatory_request *request; | 1415 | struct regulatory_request *request; |
| 1655 | 1416 | ||
| 1656 | BUG_ON(last_request); | 1417 | kfree(last_request); |
| 1418 | last_request = NULL; | ||
| 1657 | 1419 | ||
| 1658 | request = kzalloc(sizeof(struct regulatory_request), | 1420 | request = kzalloc(sizeof(struct regulatory_request), |
| 1659 | GFP_KERNEL); | 1421 | GFP_KERNEL); |
| @@ -1664,14 +1426,12 @@ static int regulatory_hint_core(const char *alpha2) | |||
| 1664 | request->alpha2[1] = alpha2[1]; | 1426 | request->alpha2[1] = alpha2[1]; |
| 1665 | request->initiator = NL80211_REGDOM_SET_BY_CORE; | 1427 | request->initiator = NL80211_REGDOM_SET_BY_CORE; |
| 1666 | 1428 | ||
| 1667 | queue_regulatory_request(request); | ||
| 1668 | |||
| 1669 | /* | 1429 | /* |
| 1670 | * This ensures last_request is populated once modules | 1430 | * This ensures last_request is populated once modules |
| 1671 | * come swinging in and calling regulatory hints and | 1431 | * come swinging in and calling regulatory hints and |
| 1672 | * wiphy_apply_custom_regulatory(). | 1432 | * wiphy_apply_custom_regulatory(). |
| 1673 | */ | 1433 | */ |
| 1674 | flush_scheduled_work(); | 1434 | reg_process_hint(request); |
| 1675 | 1435 | ||
| 1676 | return 0; | 1436 | return 0; |
| 1677 | } | 1437 | } |
| @@ -1724,46 +1484,16 @@ int regulatory_hint(struct wiphy *wiphy, const char *alpha2) | |||
| 1724 | } | 1484 | } |
| 1725 | EXPORT_SYMBOL(regulatory_hint); | 1485 | EXPORT_SYMBOL(regulatory_hint); |
| 1726 | 1486 | ||
| 1727 | /* Caller must hold reg_mutex */ | ||
| 1728 | static bool reg_same_country_ie_hint(struct wiphy *wiphy, | ||
| 1729 | u32 country_ie_checksum) | ||
| 1730 | { | ||
| 1731 | struct wiphy *request_wiphy; | ||
| 1732 | |||
| 1733 | assert_reg_lock(); | ||
| 1734 | |||
| 1735 | if (unlikely(last_request->initiator != | ||
| 1736 | NL80211_REGDOM_SET_BY_COUNTRY_IE)) | ||
| 1737 | return false; | ||
| 1738 | |||
| 1739 | request_wiphy = wiphy_idx_to_wiphy(last_request->wiphy_idx); | ||
| 1740 | |||
| 1741 | if (!request_wiphy) | ||
| 1742 | return false; | ||
| 1743 | |||
| 1744 | if (likely(request_wiphy != wiphy)) | ||
| 1745 | return !country_ie_integrity_changes(country_ie_checksum); | ||
| 1746 | /* | ||
| 1747 | * We should not have let these through at this point, they | ||
| 1748 | * should have been picked up earlier by the first alpha2 check | ||
| 1749 | * on the device | ||
| 1750 | */ | ||
| 1751 | if (WARN_ON(!country_ie_integrity_changes(country_ie_checksum))) | ||
| 1752 | return true; | ||
| 1753 | return false; | ||
| 1754 | } | ||
| 1755 | |||
| 1756 | /* | 1487 | /* |
| 1757 | * We hold wdev_lock() here so we cannot hold cfg80211_mutex() and | 1488 | * We hold wdev_lock() here so we cannot hold cfg80211_mutex() and |
| 1758 | * therefore cannot iterate over the rdev list here. | 1489 | * therefore cannot iterate over the rdev list here. |
| 1759 | */ | 1490 | */ |
| 1760 | void regulatory_hint_11d(struct wiphy *wiphy, | 1491 | void regulatory_hint_11d(struct wiphy *wiphy, |
| 1761 | u8 *country_ie, | 1492 | enum ieee80211_band band, |
| 1762 | u8 country_ie_len) | 1493 | u8 *country_ie, |
| 1494 | u8 country_ie_len) | ||
| 1763 | { | 1495 | { |
| 1764 | struct ieee80211_regdomain *rd = NULL; | ||
| 1765 | char alpha2[2]; | 1496 | char alpha2[2]; |
| 1766 | u32 checksum = 0; | ||
| 1767 | enum environment_cap env = ENVIRON_ANY; | 1497 | enum environment_cap env = ENVIRON_ANY; |
| 1768 | struct regulatory_request *request; | 1498 | struct regulatory_request *request; |
| 1769 | 1499 | ||
| @@ -1779,14 +1509,6 @@ void regulatory_hint_11d(struct wiphy *wiphy, | |||
| 1779 | if (country_ie_len < IEEE80211_COUNTRY_IE_MIN_LEN) | 1509 | if (country_ie_len < IEEE80211_COUNTRY_IE_MIN_LEN) |
| 1780 | goto out; | 1510 | goto out; |
| 1781 | 1511 | ||
| 1782 | /* | ||
| 1783 | * Pending country IE processing, this can happen after we | ||
| 1784 | * call CRDA and wait for a response if a beacon was received before | ||
| 1785 | * we were able to process the last regulatory_hint_11d() call | ||
| 1786 | */ | ||
| 1787 | if (country_ie_regdomain) | ||
| 1788 | goto out; | ||
| 1789 | |||
| 1790 | alpha2[0] = country_ie[0]; | 1512 | alpha2[0] = country_ie[0]; |
| 1791 | alpha2[1] = country_ie[1]; | 1513 | alpha2[1] = country_ie[1]; |
| 1792 | 1514 | ||
| @@ -1805,37 +1527,14 @@ void regulatory_hint_11d(struct wiphy *wiphy, | |||
| 1805 | wiphy_idx_valid(last_request->wiphy_idx))) | 1527 | wiphy_idx_valid(last_request->wiphy_idx))) |
| 1806 | goto out; | 1528 | goto out; |
| 1807 | 1529 | ||
| 1808 | rd = country_ie_2_rd(country_ie, country_ie_len, &checksum); | ||
| 1809 | if (!rd) | ||
| 1810 | goto out; | ||
| 1811 | |||
| 1812 | /* | ||
| 1813 | * This will not happen right now but we leave it here for the | ||
| 1814 | * the future when we want to add suspend/resume support and having | ||
| 1815 | * the user move to another country after doing so, or having the user | ||
| 1816 | * move to another AP. Right now we just trust the first AP. | ||
| 1817 | * | ||
| 1818 | * If we hit this before we add this support we want to be informed of | ||
| 1819 | * it as it would indicate a mistake in the current design | ||
| 1820 | */ | ||
| 1821 | if (WARN_ON(reg_same_country_ie_hint(wiphy, checksum))) | ||
| 1822 | goto free_rd_out; | ||
| 1823 | |||
| 1824 | request = kzalloc(sizeof(struct regulatory_request), GFP_KERNEL); | 1530 | request = kzalloc(sizeof(struct regulatory_request), GFP_KERNEL); |
| 1825 | if (!request) | 1531 | if (!request) |
| 1826 | goto free_rd_out; | 1532 | goto out; |
| 1827 | |||
| 1828 | /* | ||
| 1829 | * We keep this around for when CRDA comes back with a response so | ||
| 1830 | * we can intersect with that | ||
| 1831 | */ | ||
| 1832 | country_ie_regdomain = rd; | ||
| 1833 | 1533 | ||
| 1834 | request->wiphy_idx = get_wiphy_idx(wiphy); | 1534 | request->wiphy_idx = get_wiphy_idx(wiphy); |
| 1835 | request->alpha2[0] = rd->alpha2[0]; | 1535 | request->alpha2[0] = alpha2[0]; |
| 1836 | request->alpha2[1] = rd->alpha2[1]; | 1536 | request->alpha2[1] = alpha2[1]; |
| 1837 | request->initiator = NL80211_REGDOM_SET_BY_COUNTRY_IE; | 1537 | request->initiator = NL80211_REGDOM_SET_BY_COUNTRY_IE; |
| 1838 | request->country_ie_checksum = checksum; | ||
| 1839 | request->country_ie_env = env; | 1538 | request->country_ie_env = env; |
| 1840 | 1539 | ||
| 1841 | mutex_unlock(®_mutex); | 1540 | mutex_unlock(®_mutex); |
| @@ -1844,12 +1543,127 @@ void regulatory_hint_11d(struct wiphy *wiphy, | |||
| 1844 | 1543 | ||
| 1845 | return; | 1544 | return; |
| 1846 | 1545 | ||
| 1847 | free_rd_out: | ||
| 1848 | kfree(rd); | ||
| 1849 | out: | 1546 | out: |
| 1850 | mutex_unlock(®_mutex); | 1547 | mutex_unlock(®_mutex); |
| 1851 | } | 1548 | } |
| 1852 | 1549 | ||
| 1550 | static void restore_alpha2(char *alpha2, bool reset_user) | ||
| 1551 | { | ||
| 1552 | /* indicates there is no alpha2 to consider for restoration */ | ||
| 1553 | alpha2[0] = '9'; | ||
| 1554 | alpha2[1] = '7'; | ||
| 1555 | |||
| 1556 | /* The user setting has precedence over the module parameter */ | ||
| 1557 | if (is_user_regdom_saved()) { | ||
| 1558 | /* Unless we're asked to ignore it and reset it */ | ||
| 1559 | if (reset_user) { | ||
| 1560 | REG_DBG_PRINT("cfg80211: Restoring regulatory settings " | ||
| 1561 | "including user preference\n"); | ||
| 1562 | user_alpha2[0] = '9'; | ||
| 1563 | user_alpha2[1] = '7'; | ||
| 1564 | |||
| 1565 | /* | ||
| 1566 | * If we're ignoring user settings, we still need to | ||
| 1567 | * check the module parameter to ensure we put things | ||
| 1568 | * back as they were for a full restore. | ||
| 1569 | */ | ||
| 1570 | if (!is_world_regdom(ieee80211_regdom)) { | ||
| 1571 | REG_DBG_PRINT("cfg80211: Keeping preference on " | ||
| 1572 | "module parameter ieee80211_regdom: %c%c\n", | ||
| 1573 | ieee80211_regdom[0], | ||
| 1574 | ieee80211_regdom[1]); | ||
| 1575 | alpha2[0] = ieee80211_regdom[0]; | ||
| 1576 | alpha2[1] = ieee80211_regdom[1]; | ||
| 1577 | } | ||
| 1578 | } else { | ||
| 1579 | REG_DBG_PRINT("cfg80211: Restoring regulatory settings " | ||
| 1580 | "while preserving user preference for: %c%c\n", | ||
| 1581 | user_alpha2[0], | ||
| 1582 | user_alpha2[1]); | ||
| 1583 | alpha2[0] = user_alpha2[0]; | ||
| 1584 | alpha2[1] = user_alpha2[1]; | ||
| 1585 | } | ||
| 1586 | } else if (!is_world_regdom(ieee80211_regdom)) { | ||
| 1587 | REG_DBG_PRINT("cfg80211: Keeping preference on " | ||
| 1588 | "module parameter ieee80211_regdom: %c%c\n", | ||
| 1589 | ieee80211_regdom[0], | ||
| 1590 | ieee80211_regdom[1]); | ||
| 1591 | alpha2[0] = ieee80211_regdom[0]; | ||
| 1592 | alpha2[1] = ieee80211_regdom[1]; | ||
| 1593 | } else | ||
| 1594 | REG_DBG_PRINT("cfg80211: Restoring regulatory settings\n"); | ||
| 1595 | } | ||
| 1596 | |||
| 1597 | /* | ||
| 1598 | * Restoring regulatory settings involves ingoring any | ||
| 1599 | * possibly stale country IE information and user regulatory | ||
| 1600 | * settings if so desired, this includes any beacon hints | ||
| 1601 | * learned as we could have traveled outside to another country | ||
| 1602 | * after disconnection. To restore regulatory settings we do | ||
| 1603 | * exactly what we did at bootup: | ||
| 1604 | * | ||
| 1605 | * - send a core regulatory hint | ||
| 1606 | * - send a user regulatory hint if applicable | ||
| 1607 | * | ||
| 1608 | * Device drivers that send a regulatory hint for a specific country | ||
| 1609 | * keep their own regulatory domain on wiphy->regd so that does does | ||
| 1610 | * not need to be remembered. | ||
| 1611 | */ | ||
| 1612 | static void restore_regulatory_settings(bool reset_user) | ||
| 1613 | { | ||
| 1614 | char alpha2[2]; | ||
| 1615 | struct reg_beacon *reg_beacon, *btmp; | ||
| 1616 | |||
| 1617 | mutex_lock(&cfg80211_mutex); | ||
| 1618 | mutex_lock(®_mutex); | ||
| 1619 | |||
| 1620 | reset_regdomains(); | ||
| 1621 | restore_alpha2(alpha2, reset_user); | ||
| 1622 | |||
| 1623 | /* Clear beacon hints */ | ||
| 1624 | spin_lock_bh(®_pending_beacons_lock); | ||
| 1625 | if (!list_empty(®_pending_beacons)) { | ||
| 1626 | list_for_each_entry_safe(reg_beacon, btmp, | ||
| 1627 | ®_pending_beacons, list) { | ||
| 1628 | list_del(®_beacon->list); | ||
| 1629 | kfree(reg_beacon); | ||
| 1630 | } | ||
| 1631 | } | ||
| 1632 | spin_unlock_bh(®_pending_beacons_lock); | ||
| 1633 | |||
| 1634 | if (!list_empty(®_beacon_list)) { | ||
| 1635 | list_for_each_entry_safe(reg_beacon, btmp, | ||
| 1636 | ®_beacon_list, list) { | ||
| 1637 | list_del(®_beacon->list); | ||
| 1638 | kfree(reg_beacon); | ||
| 1639 | } | ||
| 1640 | } | ||
| 1641 | |||
| 1642 | /* First restore to the basic regulatory settings */ | ||
| 1643 | cfg80211_regdomain = cfg80211_world_regdom; | ||
| 1644 | |||
| 1645 | mutex_unlock(®_mutex); | ||
| 1646 | mutex_unlock(&cfg80211_mutex); | ||
| 1647 | |||
| 1648 | regulatory_hint_core(cfg80211_regdomain->alpha2); | ||
| 1649 | |||
| 1650 | /* | ||
| 1651 | * This restores the ieee80211_regdom module parameter | ||
| 1652 | * preference or the last user requested regulatory | ||
| 1653 | * settings, user regulatory settings takes precedence. | ||
| 1654 | */ | ||
| 1655 | if (is_an_alpha2(alpha2)) | ||
| 1656 | regulatory_hint_user(user_alpha2); | ||
| 1657 | } | ||
| 1658 | |||
| 1659 | |||
| 1660 | void regulatory_hint_disconnect(void) | ||
| 1661 | { | ||
| 1662 | REG_DBG_PRINT("cfg80211: All devices are disconnected, going to " | ||
| 1663 | "restore regulatory settings\n"); | ||
| 1664 | restore_regulatory_settings(false); | ||
| 1665 | } | ||
| 1666 | |||
| 1853 | static bool freq_is_chan_12_13_14(u16 freq) | 1667 | static bool freq_is_chan_12_13_14(u16 freq) |
| 1854 | { | 1668 | { |
| 1855 | if (freq == ieee80211_channel_to_frequency(12) || | 1669 | if (freq == ieee80211_channel_to_frequency(12) || |
| @@ -1875,13 +1689,12 @@ int regulatory_hint_found_beacon(struct wiphy *wiphy, | |||
| 1875 | if (!reg_beacon) | 1689 | if (!reg_beacon) |
| 1876 | return -ENOMEM; | 1690 | return -ENOMEM; |
| 1877 | 1691 | ||
| 1878 | #ifdef CONFIG_CFG80211_REG_DEBUG | 1692 | REG_DBG_PRINT("cfg80211: Found new beacon on " |
| 1879 | printk(KERN_DEBUG "cfg80211: Found new beacon on " | 1693 | "frequency: %d MHz (Ch %d) on %s\n", |
| 1880 | "frequency: %d MHz (Ch %d) on %s\n", | 1694 | beacon_chan->center_freq, |
| 1881 | beacon_chan->center_freq, | 1695 | ieee80211_frequency_to_channel(beacon_chan->center_freq), |
| 1882 | ieee80211_frequency_to_channel(beacon_chan->center_freq), | 1696 | wiphy_name(wiphy)); |
| 1883 | wiphy_name(wiphy)); | 1697 | |
| 1884 | #endif | ||
| 1885 | memcpy(®_beacon->chan, beacon_chan, | 1698 | memcpy(®_beacon->chan, beacon_chan, |
| 1886 | sizeof(struct ieee80211_channel)); | 1699 | sizeof(struct ieee80211_channel)); |
| 1887 | 1700 | ||
| @@ -1953,10 +1766,10 @@ static void print_regdomain(const struct ieee80211_regdomain *rd) | |||
| 1953 | rdev->country_ie_alpha2[1]); | 1766 | rdev->country_ie_alpha2[1]); |
| 1954 | } else | 1767 | } else |
| 1955 | printk(KERN_INFO "cfg80211: Current regulatory " | 1768 | printk(KERN_INFO "cfg80211: Current regulatory " |
| 1956 | "domain intersected: \n"); | 1769 | "domain intersected:\n"); |
| 1957 | } else | 1770 | } else |
| 1958 | printk(KERN_INFO "cfg80211: Current regulatory " | 1771 | printk(KERN_INFO "cfg80211: Current regulatory " |
| 1959 | "domain intersected: \n"); | 1772 | "domain intersected:\n"); |
| 1960 | } else if (is_world_regdom(rd->alpha2)) | 1773 | } else if (is_world_regdom(rd->alpha2)) |
| 1961 | printk(KERN_INFO "cfg80211: World regulatory " | 1774 | printk(KERN_INFO "cfg80211: World regulatory " |
| 1962 | "domain updated:\n"); | 1775 | "domain updated:\n"); |
| @@ -1980,33 +1793,6 @@ static void print_regdomain_info(const struct ieee80211_regdomain *rd) | |||
| 1980 | print_rd_rules(rd); | 1793 | print_rd_rules(rd); |
| 1981 | } | 1794 | } |
| 1982 | 1795 | ||
| 1983 | #ifdef CONFIG_CFG80211_REG_DEBUG | ||
| 1984 | static void reg_country_ie_process_debug( | ||
| 1985 | const struct ieee80211_regdomain *rd, | ||
| 1986 | const struct ieee80211_regdomain *country_ie_regdomain, | ||
| 1987 | const struct ieee80211_regdomain *intersected_rd) | ||
| 1988 | { | ||
| 1989 | printk(KERN_DEBUG "cfg80211: Received country IE:\n"); | ||
| 1990 | print_regdomain_info(country_ie_regdomain); | ||
| 1991 | printk(KERN_DEBUG "cfg80211: CRDA thinks this should applied:\n"); | ||
| 1992 | print_regdomain_info(rd); | ||
| 1993 | if (intersected_rd) { | ||
| 1994 | printk(KERN_DEBUG "cfg80211: We intersect both of these " | ||
| 1995 | "and get:\n"); | ||
| 1996 | print_regdomain_info(intersected_rd); | ||
| 1997 | return; | ||
| 1998 | } | ||
| 1999 | printk(KERN_DEBUG "cfg80211: Intersection between both failed\n"); | ||
| 2000 | } | ||
| 2001 | #else | ||
| 2002 | static inline void reg_country_ie_process_debug( | ||
| 2003 | const struct ieee80211_regdomain *rd, | ||
| 2004 | const struct ieee80211_regdomain *country_ie_regdomain, | ||
| 2005 | const struct ieee80211_regdomain *intersected_rd) | ||
| 2006 | { | ||
| 2007 | } | ||
| 2008 | #endif | ||
| 2009 | |||
| 2010 | /* Takes ownership of rd only if it doesn't fail */ | 1796 | /* Takes ownership of rd only if it doesn't fail */ |
| 2011 | static int __set_regdom(const struct ieee80211_regdomain *rd) | 1797 | static int __set_regdom(const struct ieee80211_regdomain *rd) |
| 2012 | { | 1798 | { |
| @@ -2039,8 +1825,7 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) | |||
| 2039 | * If someone else asked us to change the rd lets only bother | 1825 | * If someone else asked us to change the rd lets only bother |
| 2040 | * checking if the alpha2 changes if CRDA was already called | 1826 | * checking if the alpha2 changes if CRDA was already called |
| 2041 | */ | 1827 | */ |
| 2042 | if (!is_old_static_regdom(cfg80211_regdomain) && | 1828 | if (!regdom_changes(rd->alpha2)) |
| 2043 | !regdom_changes(rd->alpha2)) | ||
| 2044 | return -EINVAL; | 1829 | return -EINVAL; |
| 2045 | } | 1830 | } |
| 2046 | 1831 | ||
| @@ -2119,34 +1904,6 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) | |||
| 2119 | return 0; | 1904 | return 0; |
| 2120 | } | 1905 | } |
| 2121 | 1906 | ||
| 2122 | /* | ||
| 2123 | * Country IE requests are handled a bit differently, we intersect | ||
| 2124 | * the country IE rd with what CRDA believes that country should have | ||
| 2125 | */ | ||
| 2126 | |||
| 2127 | /* | ||
| 2128 | * Userspace could have sent two replies with only | ||
| 2129 | * one kernel request. By the second reply we would have | ||
| 2130 | * already processed and consumed the country_ie_regdomain. | ||
| 2131 | */ | ||
| 2132 | if (!country_ie_regdomain) | ||
| 2133 | return -EALREADY; | ||
| 2134 | BUG_ON(rd == country_ie_regdomain); | ||
| 2135 | |||
| 2136 | /* | ||
| 2137 | * Intersect what CRDA returned and our what we | ||
| 2138 | * had built from the Country IE received | ||
| 2139 | */ | ||
| 2140 | |||
| 2141 | intersected_rd = regdom_intersect(rd, country_ie_regdomain); | ||
| 2142 | |||
| 2143 | reg_country_ie_process_debug(rd, | ||
| 2144 | country_ie_regdomain, | ||
| 2145 | intersected_rd); | ||
| 2146 | |||
| 2147 | kfree(country_ie_regdomain); | ||
| 2148 | country_ie_regdomain = NULL; | ||
| 2149 | |||
| 2150 | if (!intersected_rd) | 1907 | if (!intersected_rd) |
| 2151 | return -EINVAL; | 1908 | return -EINVAL; |
| 2152 | 1909 | ||
| @@ -2228,7 +1985,7 @@ out: | |||
| 2228 | mutex_unlock(®_mutex); | 1985 | mutex_unlock(®_mutex); |
| 2229 | } | 1986 | } |
| 2230 | 1987 | ||
| 2231 | int regulatory_init(void) | 1988 | int __init regulatory_init(void) |
| 2232 | { | 1989 | { |
| 2233 | int err = 0; | 1990 | int err = 0; |
| 2234 | 1991 | ||
| @@ -2239,15 +1996,11 @@ int regulatory_init(void) | |||
| 2239 | spin_lock_init(®_requests_lock); | 1996 | spin_lock_init(®_requests_lock); |
| 2240 | spin_lock_init(®_pending_beacons_lock); | 1997 | spin_lock_init(®_pending_beacons_lock); |
| 2241 | 1998 | ||
| 2242 | #ifdef CONFIG_WIRELESS_OLD_REGULATORY | ||
| 2243 | cfg80211_regdomain = static_regdom(ieee80211_regdom); | ||
| 2244 | |||
| 2245 | printk(KERN_INFO "cfg80211: Using static regulatory domain info\n"); | ||
| 2246 | print_regdomain_info(cfg80211_regdomain); | ||
| 2247 | #else | ||
| 2248 | cfg80211_regdomain = cfg80211_world_regdom; | 1999 | cfg80211_regdomain = cfg80211_world_regdom; |
| 2249 | 2000 | ||
| 2250 | #endif | 2001 | user_alpha2[0] = '9'; |
| 2002 | user_alpha2[1] = '7'; | ||
| 2003 | |||
| 2251 | /* We always try to get an update for the static regdomain */ | 2004 | /* We always try to get an update for the static regdomain */ |
| 2252 | err = regulatory_hint_core(cfg80211_regdomain->alpha2); | 2005 | err = regulatory_hint_core(cfg80211_regdomain->alpha2); |
| 2253 | if (err) { | 2006 | if (err) { |
| @@ -2278,7 +2031,7 @@ int regulatory_init(void) | |||
| 2278 | return 0; | 2031 | return 0; |
| 2279 | } | 2032 | } |
| 2280 | 2033 | ||
| 2281 | void regulatory_exit(void) | 2034 | void /* __init_or_exit */ regulatory_exit(void) |
| 2282 | { | 2035 | { |
| 2283 | struct regulatory_request *reg_request, *tmp; | 2036 | struct regulatory_request *reg_request, *tmp; |
| 2284 | struct reg_beacon *reg_beacon, *btmp; | 2037 | struct reg_beacon *reg_beacon, *btmp; |
| @@ -2290,9 +2043,6 @@ void regulatory_exit(void) | |||
| 2290 | 2043 | ||
| 2291 | reset_regdomains(); | 2044 | reset_regdomains(); |
| 2292 | 2045 | ||
| 2293 | kfree(country_ie_regdomain); | ||
| 2294 | country_ie_regdomain = NULL; | ||
| 2295 | |||
| 2296 | kfree(last_request); | 2046 | kfree(last_request); |
| 2297 | 2047 | ||
| 2298 | platform_device_unregister(reg_pdev); | 2048 | platform_device_unregister(reg_pdev); |
