diff options
author | John W. Linville <linville@tuxdriver.com> | 2013-01-28 14:43:00 -0500 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2013-01-28 14:43:00 -0500 |
commit | 4205e6ef4ee747aa81930537b6035086ba5f1e28 (patch) | |
tree | b2ebe2b4621f5f531f283cb9bf0005cd3c04ca7b /net/wireless/reg.c | |
parent | cef401de7be8c4e155c6746bfccf721a4fa5fab9 (diff) | |
parent | 9ebea3829fac7505e0cd2642fbd13cfa9c038831 (diff) |
Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-next into for-davem
Diffstat (limited to 'net/wireless/reg.c')
-rw-r--r-- | net/wireless/reg.c | 1124 |
1 files changed, 497 insertions, 627 deletions
diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 82c4fc7c994c..de02d633c212 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c | |||
@@ -48,7 +48,6 @@ | |||
48 | #include <linux/export.h> | 48 | #include <linux/export.h> |
49 | #include <linux/slab.h> | 49 | #include <linux/slab.h> |
50 | #include <linux/list.h> | 50 | #include <linux/list.h> |
51 | #include <linux/random.h> | ||
52 | #include <linux/ctype.h> | 51 | #include <linux/ctype.h> |
53 | #include <linux/nl80211.h> | 52 | #include <linux/nl80211.h> |
54 | #include <linux/platform_device.h> | 53 | #include <linux/platform_device.h> |
@@ -66,6 +65,13 @@ | |||
66 | #define REG_DBG_PRINT(args...) | 65 | #define REG_DBG_PRINT(args...) |
67 | #endif | 66 | #endif |
68 | 67 | ||
68 | enum reg_request_treatment { | ||
69 | REG_REQ_OK, | ||
70 | REG_REQ_IGNORE, | ||
71 | REG_REQ_INTERSECT, | ||
72 | REG_REQ_ALREADY_SET, | ||
73 | }; | ||
74 | |||
69 | static struct regulatory_request core_request_world = { | 75 | static struct regulatory_request core_request_world = { |
70 | .initiator = NL80211_REGDOM_SET_BY_CORE, | 76 | .initiator = NL80211_REGDOM_SET_BY_CORE, |
71 | .alpha2[0] = '0', | 77 | .alpha2[0] = '0', |
@@ -76,7 +82,8 @@ static struct regulatory_request core_request_world = { | |||
76 | }; | 82 | }; |
77 | 83 | ||
78 | /* Receipt of information from last regulatory request */ | 84 | /* Receipt of information from last regulatory request */ |
79 | static struct regulatory_request *last_request = &core_request_world; | 85 | static struct regulatory_request __rcu *last_request = |
86 | (void __rcu *)&core_request_world; | ||
80 | 87 | ||
81 | /* To trigger userspace events */ | 88 | /* To trigger userspace events */ |
82 | static struct platform_device *reg_pdev; | 89 | static struct platform_device *reg_pdev; |
@@ -88,16 +95,16 @@ static struct device_type reg_device_type = { | |||
88 | /* | 95 | /* |
89 | * Central wireless core regulatory domains, we only need two, | 96 | * Central wireless core regulatory domains, we only need two, |
90 | * the current one and a world regulatory domain in case we have no | 97 | * the current one and a world regulatory domain in case we have no |
91 | * information to give us an alpha2 | 98 | * information to give us an alpha2. |
92 | */ | 99 | */ |
93 | const struct ieee80211_regdomain *cfg80211_regdomain; | 100 | const struct ieee80211_regdomain __rcu *cfg80211_regdomain; |
94 | 101 | ||
95 | /* | 102 | /* |
96 | * Protects static reg.c components: | 103 | * Protects static reg.c components: |
97 | * - cfg80211_world_regdom | 104 | * - cfg80211_regdomain (if not used with RCU) |
98 | * - cfg80211_regdom | 105 | * - cfg80211_world_regdom |
99 | * - last_request | 106 | * - last_request (if not used with RCU) |
100 | * - reg_num_devs_support_basehint | 107 | * - reg_num_devs_support_basehint |
101 | */ | 108 | */ |
102 | static DEFINE_MUTEX(reg_mutex); | 109 | static DEFINE_MUTEX(reg_mutex); |
103 | 110 | ||
@@ -112,6 +119,31 @@ static inline void assert_reg_lock(void) | |||
112 | lockdep_assert_held(®_mutex); | 119 | lockdep_assert_held(®_mutex); |
113 | } | 120 | } |
114 | 121 | ||
122 | static const struct ieee80211_regdomain *get_cfg80211_regdom(void) | ||
123 | { | ||
124 | return rcu_dereference_protected(cfg80211_regdomain, | ||
125 | lockdep_is_held(®_mutex)); | ||
126 | } | ||
127 | |||
128 | static const struct ieee80211_regdomain *get_wiphy_regdom(struct wiphy *wiphy) | ||
129 | { | ||
130 | return rcu_dereference_protected(wiphy->regd, | ||
131 | lockdep_is_held(®_mutex)); | ||
132 | } | ||
133 | |||
134 | static void rcu_free_regdom(const struct ieee80211_regdomain *r) | ||
135 | { | ||
136 | if (!r) | ||
137 | return; | ||
138 | kfree_rcu((struct ieee80211_regdomain *)r, rcu_head); | ||
139 | } | ||
140 | |||
141 | static struct regulatory_request *get_last_request(void) | ||
142 | { | ||
143 | return rcu_dereference_check(last_request, | ||
144 | lockdep_is_held(®_mutex)); | ||
145 | } | ||
146 | |||
115 | /* Used to queue up regulatory hints */ | 147 | /* Used to queue up regulatory hints */ |
116 | static LIST_HEAD(reg_requests_list); | 148 | static LIST_HEAD(reg_requests_list); |
117 | static spinlock_t reg_requests_lock; | 149 | static spinlock_t reg_requests_lock; |
@@ -177,28 +209,37 @@ static char user_alpha2[2]; | |||
177 | module_param(ieee80211_regdom, charp, 0444); | 209 | module_param(ieee80211_regdom, charp, 0444); |
178 | MODULE_PARM_DESC(ieee80211_regdom, "IEEE 802.11 regulatory domain code"); | 210 | MODULE_PARM_DESC(ieee80211_regdom, "IEEE 802.11 regulatory domain code"); |
179 | 211 | ||
180 | static void reset_regdomains(bool full_reset) | 212 | static void reset_regdomains(bool full_reset, |
213 | const struct ieee80211_regdomain *new_regdom) | ||
181 | { | 214 | { |
215 | const struct ieee80211_regdomain *r; | ||
216 | struct regulatory_request *lr; | ||
217 | |||
218 | assert_reg_lock(); | ||
219 | |||
220 | r = get_cfg80211_regdom(); | ||
221 | |||
182 | /* avoid freeing static information or freeing something twice */ | 222 | /* avoid freeing static information or freeing something twice */ |
183 | if (cfg80211_regdomain == cfg80211_world_regdom) | 223 | if (r == cfg80211_world_regdom) |
184 | cfg80211_regdomain = NULL; | 224 | r = NULL; |
185 | if (cfg80211_world_regdom == &world_regdom) | 225 | if (cfg80211_world_regdom == &world_regdom) |
186 | cfg80211_world_regdom = NULL; | 226 | cfg80211_world_regdom = NULL; |
187 | if (cfg80211_regdomain == &world_regdom) | 227 | if (r == &world_regdom) |
188 | cfg80211_regdomain = NULL; | 228 | r = NULL; |
189 | 229 | ||
190 | kfree(cfg80211_regdomain); | 230 | rcu_free_regdom(r); |
191 | kfree(cfg80211_world_regdom); | 231 | rcu_free_regdom(cfg80211_world_regdom); |
192 | 232 | ||
193 | cfg80211_world_regdom = &world_regdom; | 233 | cfg80211_world_regdom = &world_regdom; |
194 | cfg80211_regdomain = NULL; | 234 | rcu_assign_pointer(cfg80211_regdomain, new_regdom); |
195 | 235 | ||
196 | if (!full_reset) | 236 | if (!full_reset) |
197 | return; | 237 | return; |
198 | 238 | ||
199 | if (last_request != &core_request_world) | 239 | lr = get_last_request(); |
200 | kfree(last_request); | 240 | if (lr != &core_request_world && lr) |
201 | last_request = &core_request_world; | 241 | kfree_rcu(lr, rcu_head); |
242 | rcu_assign_pointer(last_request, &core_request_world); | ||
202 | } | 243 | } |
203 | 244 | ||
204 | /* | 245 | /* |
@@ -207,30 +248,29 @@ static void reset_regdomains(bool full_reset) | |||
207 | */ | 248 | */ |
208 | static void update_world_regdomain(const struct ieee80211_regdomain *rd) | 249 | static void update_world_regdomain(const struct ieee80211_regdomain *rd) |
209 | { | 250 | { |
210 | BUG_ON(!last_request); | 251 | struct regulatory_request *lr; |
252 | |||
253 | lr = get_last_request(); | ||
254 | |||
255 | WARN_ON(!lr); | ||
211 | 256 | ||
212 | reset_regdomains(false); | 257 | reset_regdomains(false, rd); |
213 | 258 | ||
214 | cfg80211_world_regdom = rd; | 259 | cfg80211_world_regdom = rd; |
215 | cfg80211_regdomain = rd; | ||
216 | } | 260 | } |
217 | 261 | ||
218 | bool is_world_regdom(const char *alpha2) | 262 | bool is_world_regdom(const char *alpha2) |
219 | { | 263 | { |
220 | if (!alpha2) | 264 | if (!alpha2) |
221 | return false; | 265 | return false; |
222 | if (alpha2[0] == '0' && alpha2[1] == '0') | 266 | return alpha2[0] == '0' && alpha2[1] == '0'; |
223 | return true; | ||
224 | return false; | ||
225 | } | 267 | } |
226 | 268 | ||
227 | static bool is_alpha2_set(const char *alpha2) | 269 | static bool is_alpha2_set(const char *alpha2) |
228 | { | 270 | { |
229 | if (!alpha2) | 271 | if (!alpha2) |
230 | return false; | 272 | return false; |
231 | if (alpha2[0] != 0 && alpha2[1] != 0) | 273 | return alpha2[0] && alpha2[1]; |
232 | return true; | ||
233 | return false; | ||
234 | } | 274 | } |
235 | 275 | ||
236 | static bool is_unknown_alpha2(const char *alpha2) | 276 | static bool is_unknown_alpha2(const char *alpha2) |
@@ -241,9 +281,7 @@ static bool is_unknown_alpha2(const char *alpha2) | |||
241 | * Special case where regulatory domain was built by driver | 281 | * Special case where regulatory domain was built by driver |
242 | * but a specific alpha2 cannot be determined | 282 | * but a specific alpha2 cannot be determined |
243 | */ | 283 | */ |
244 | if (alpha2[0] == '9' && alpha2[1] == '9') | 284 | return alpha2[0] == '9' && alpha2[1] == '9'; |
245 | return true; | ||
246 | return false; | ||
247 | } | 285 | } |
248 | 286 | ||
249 | static bool is_intersected_alpha2(const char *alpha2) | 287 | static bool is_intersected_alpha2(const char *alpha2) |
@@ -255,39 +293,30 @@ static bool is_intersected_alpha2(const char *alpha2) | |||
255 | * result of an intersection between two regulatory domain | 293 | * result of an intersection between two regulatory domain |
256 | * structures | 294 | * structures |
257 | */ | 295 | */ |
258 | if (alpha2[0] == '9' && alpha2[1] == '8') | 296 | return alpha2[0] == '9' && alpha2[1] == '8'; |
259 | return true; | ||
260 | return false; | ||
261 | } | 297 | } |
262 | 298 | ||
263 | static bool is_an_alpha2(const char *alpha2) | 299 | static bool is_an_alpha2(const char *alpha2) |
264 | { | 300 | { |
265 | if (!alpha2) | 301 | if (!alpha2) |
266 | return false; | 302 | return false; |
267 | if (isalpha(alpha2[0]) && isalpha(alpha2[1])) | 303 | return isalpha(alpha2[0]) && isalpha(alpha2[1]); |
268 | return true; | ||
269 | return false; | ||
270 | } | 304 | } |
271 | 305 | ||
272 | static bool alpha2_equal(const char *alpha2_x, const char *alpha2_y) | 306 | static bool alpha2_equal(const char *alpha2_x, const char *alpha2_y) |
273 | { | 307 | { |
274 | if (!alpha2_x || !alpha2_y) | 308 | if (!alpha2_x || !alpha2_y) |
275 | return false; | 309 | return false; |
276 | if (alpha2_x[0] == alpha2_y[0] && | 310 | return alpha2_x[0] == alpha2_y[0] && alpha2_x[1] == alpha2_y[1]; |
277 | alpha2_x[1] == alpha2_y[1]) | ||
278 | return true; | ||
279 | return false; | ||
280 | } | 311 | } |
281 | 312 | ||
282 | static bool regdom_changes(const char *alpha2) | 313 | static bool regdom_changes(const char *alpha2) |
283 | { | 314 | { |
284 | assert_cfg80211_lock(); | 315 | const struct ieee80211_regdomain *r = get_cfg80211_regdom(); |
285 | 316 | ||
286 | if (!cfg80211_regdomain) | 317 | if (!r) |
287 | return true; | 318 | return true; |
288 | if (alpha2_equal(cfg80211_regdomain->alpha2, alpha2)) | 319 | return !alpha2_equal(r->alpha2, alpha2); |
289 | return false; | ||
290 | return true; | ||
291 | } | 320 | } |
292 | 321 | ||
293 | /* | 322 | /* |
@@ -301,38 +330,36 @@ static bool is_user_regdom_saved(void) | |||
301 | return false; | 330 | return false; |
302 | 331 | ||
303 | /* This would indicate a mistake on the design */ | 332 | /* This would indicate a mistake on the design */ |
304 | if (WARN((!is_world_regdom(user_alpha2) && | 333 | if (WARN(!is_world_regdom(user_alpha2) && !is_an_alpha2(user_alpha2), |
305 | !is_an_alpha2(user_alpha2)), | ||
306 | "Unexpected user alpha2: %c%c\n", | 334 | "Unexpected user alpha2: %c%c\n", |
307 | user_alpha2[0], | 335 | user_alpha2[0], user_alpha2[1])) |
308 | user_alpha2[1])) | ||
309 | return false; | 336 | return false; |
310 | 337 | ||
311 | return true; | 338 | return true; |
312 | } | 339 | } |
313 | 340 | ||
314 | static int reg_copy_regd(const struct ieee80211_regdomain **dst_regd, | 341 | static const struct ieee80211_regdomain * |
315 | const struct ieee80211_regdomain *src_regd) | 342 | reg_copy_regd(const struct ieee80211_regdomain *src_regd) |
316 | { | 343 | { |
317 | struct ieee80211_regdomain *regd; | 344 | struct ieee80211_regdomain *regd; |
318 | int size_of_regd = 0; | 345 | int size_of_regd; |
319 | unsigned int i; | 346 | unsigned int i; |
320 | 347 | ||
321 | size_of_regd = sizeof(struct ieee80211_regdomain) + | 348 | size_of_regd = |
322 | ((src_regd->n_reg_rules + 1) * sizeof(struct ieee80211_reg_rule)); | 349 | sizeof(struct ieee80211_regdomain) + |
350 | src_regd->n_reg_rules * sizeof(struct ieee80211_reg_rule); | ||
323 | 351 | ||
324 | regd = kzalloc(size_of_regd, GFP_KERNEL); | 352 | regd = kzalloc(size_of_regd, GFP_KERNEL); |
325 | if (!regd) | 353 | if (!regd) |
326 | return -ENOMEM; | 354 | return ERR_PTR(-ENOMEM); |
327 | 355 | ||
328 | memcpy(regd, src_regd, sizeof(struct ieee80211_regdomain)); | 356 | memcpy(regd, src_regd, sizeof(struct ieee80211_regdomain)); |
329 | 357 | ||
330 | for (i = 0; i < src_regd->n_reg_rules; i++) | 358 | for (i = 0; i < src_regd->n_reg_rules; i++) |
331 | memcpy(®d->reg_rules[i], &src_regd->reg_rules[i], | 359 | memcpy(®d->reg_rules[i], &src_regd->reg_rules[i], |
332 | sizeof(struct ieee80211_reg_rule)); | 360 | sizeof(struct ieee80211_reg_rule)); |
333 | 361 | ||
334 | *dst_regd = regd; | 362 | return regd; |
335 | return 0; | ||
336 | } | 363 | } |
337 | 364 | ||
338 | #ifdef CONFIG_CFG80211_INTERNAL_REGDB | 365 | #ifdef CONFIG_CFG80211_INTERNAL_REGDB |
@@ -347,9 +374,8 @@ static DEFINE_MUTEX(reg_regdb_search_mutex); | |||
347 | static void reg_regdb_search(struct work_struct *work) | 374 | static void reg_regdb_search(struct work_struct *work) |
348 | { | 375 | { |
349 | struct reg_regdb_search_request *request; | 376 | struct reg_regdb_search_request *request; |
350 | const struct ieee80211_regdomain *curdom, *regdom; | 377 | const struct ieee80211_regdomain *curdom, *regdom = NULL; |
351 | int i, r; | 378 | int i; |
352 | bool set_reg = false; | ||
353 | 379 | ||
354 | mutex_lock(&cfg80211_mutex); | 380 | mutex_lock(&cfg80211_mutex); |
355 | 381 | ||
@@ -360,14 +386,11 @@ static void reg_regdb_search(struct work_struct *work) | |||
360 | list); | 386 | list); |
361 | list_del(&request->list); | 387 | list_del(&request->list); |
362 | 388 | ||
363 | for (i=0; i<reg_regdb_size; i++) { | 389 | for (i = 0; i < reg_regdb_size; i++) { |
364 | curdom = reg_regdb[i]; | 390 | curdom = reg_regdb[i]; |
365 | 391 | ||
366 | if (!memcmp(request->alpha2, curdom->alpha2, 2)) { | 392 | if (alpha2_equal(request->alpha2, curdom->alpha2)) { |
367 | r = reg_copy_regd(®dom, curdom); | 393 | regdom = reg_copy_regd(curdom); |
368 | if (r) | ||
369 | break; | ||
370 | set_reg = true; | ||
371 | break; | 394 | break; |
372 | } | 395 | } |
373 | } | 396 | } |
@@ -376,7 +399,7 @@ static void reg_regdb_search(struct work_struct *work) | |||
376 | } | 399 | } |
377 | mutex_unlock(®_regdb_search_mutex); | 400 | mutex_unlock(®_regdb_search_mutex); |
378 | 401 | ||
379 | if (set_reg) | 402 | if (!IS_ERR_OR_NULL(regdom)) |
380 | set_regdom(regdom); | 403 | set_regdom(regdom); |
381 | 404 | ||
382 | mutex_unlock(&cfg80211_mutex); | 405 | mutex_unlock(&cfg80211_mutex); |
@@ -434,15 +457,14 @@ static int call_crda(const char *alpha2) | |||
434 | return kobject_uevent(®_pdev->dev.kobj, KOBJ_CHANGE); | 457 | return kobject_uevent(®_pdev->dev.kobj, KOBJ_CHANGE); |
435 | } | 458 | } |
436 | 459 | ||
437 | /* Used by nl80211 before kmalloc'ing our regulatory domain */ | 460 | static bool reg_is_valid_request(const char *alpha2) |
438 | bool reg_is_valid_request(const char *alpha2) | ||
439 | { | 461 | { |
440 | assert_cfg80211_lock(); | 462 | struct regulatory_request *lr = get_last_request(); |
441 | 463 | ||
442 | if (!last_request) | 464 | if (!lr || lr->processed) |
443 | return false; | 465 | return false; |
444 | 466 | ||
445 | return alpha2_equal(last_request->alpha2, alpha2); | 467 | return alpha2_equal(lr->alpha2, alpha2); |
446 | } | 468 | } |
447 | 469 | ||
448 | /* Sanity check on a regulatory rule */ | 470 | /* Sanity check on a regulatory rule */ |
@@ -460,7 +482,7 @@ static bool is_valid_reg_rule(const struct ieee80211_reg_rule *rule) | |||
460 | freq_diff = freq_range->end_freq_khz - freq_range->start_freq_khz; | 482 | freq_diff = freq_range->end_freq_khz - freq_range->start_freq_khz; |
461 | 483 | ||
462 | if (freq_range->end_freq_khz <= freq_range->start_freq_khz || | 484 | if (freq_range->end_freq_khz <= freq_range->start_freq_khz || |
463 | freq_range->max_bandwidth_khz > freq_diff) | 485 | freq_range->max_bandwidth_khz > freq_diff) |
464 | return false; | 486 | return false; |
465 | 487 | ||
466 | return true; | 488 | return true; |
@@ -487,8 +509,7 @@ static bool is_valid_rd(const struct ieee80211_regdomain *rd) | |||
487 | } | 509 | } |
488 | 510 | ||
489 | static bool reg_does_bw_fit(const struct ieee80211_freq_range *freq_range, | 511 | static bool reg_does_bw_fit(const struct ieee80211_freq_range *freq_range, |
490 | u32 center_freq_khz, | 512 | u32 center_freq_khz, u32 bw_khz) |
491 | u32 bw_khz) | ||
492 | { | 513 | { |
493 | u32 start_freq_khz, end_freq_khz; | 514 | u32 start_freq_khz, end_freq_khz; |
494 | 515 | ||
@@ -518,7 +539,7 @@ static bool reg_does_bw_fit(const struct ieee80211_freq_range *freq_range, | |||
518 | * regulatory rule support for other "bands". | 539 | * regulatory rule support for other "bands". |
519 | **/ | 540 | **/ |
520 | static bool freq_in_rule_band(const struct ieee80211_freq_range *freq_range, | 541 | static bool freq_in_rule_band(const struct ieee80211_freq_range *freq_range, |
521 | u32 freq_khz) | 542 | u32 freq_khz) |
522 | { | 543 | { |
523 | #define ONE_GHZ_IN_KHZ 1000000 | 544 | #define ONE_GHZ_IN_KHZ 1000000 |
524 | /* | 545 | /* |
@@ -540,10 +561,9 @@ static bool freq_in_rule_band(const struct ieee80211_freq_range *freq_range, | |||
540 | * Helper for regdom_intersect(), this does the real | 561 | * Helper for regdom_intersect(), this does the real |
541 | * mathematical intersection fun | 562 | * mathematical intersection fun |
542 | */ | 563 | */ |
543 | static int reg_rules_intersect( | 564 | static int reg_rules_intersect(const struct ieee80211_reg_rule *rule1, |
544 | const struct ieee80211_reg_rule *rule1, | 565 | const struct ieee80211_reg_rule *rule2, |
545 | const struct ieee80211_reg_rule *rule2, | 566 | struct ieee80211_reg_rule *intersected_rule) |
546 | struct ieee80211_reg_rule *intersected_rule) | ||
547 | { | 567 | { |
548 | const struct ieee80211_freq_range *freq_range1, *freq_range2; | 568 | const struct ieee80211_freq_range *freq_range1, *freq_range2; |
549 | struct ieee80211_freq_range *freq_range; | 569 | struct ieee80211_freq_range *freq_range; |
@@ -560,11 +580,11 @@ static int reg_rules_intersect( | |||
560 | power_rule = &intersected_rule->power_rule; | 580 | power_rule = &intersected_rule->power_rule; |
561 | 581 | ||
562 | freq_range->start_freq_khz = max(freq_range1->start_freq_khz, | 582 | freq_range->start_freq_khz = max(freq_range1->start_freq_khz, |
563 | freq_range2->start_freq_khz); | 583 | freq_range2->start_freq_khz); |
564 | freq_range->end_freq_khz = min(freq_range1->end_freq_khz, | 584 | freq_range->end_freq_khz = min(freq_range1->end_freq_khz, |
565 | freq_range2->end_freq_khz); | 585 | freq_range2->end_freq_khz); |
566 | freq_range->max_bandwidth_khz = min(freq_range1->max_bandwidth_khz, | 586 | freq_range->max_bandwidth_khz = min(freq_range1->max_bandwidth_khz, |
567 | freq_range2->max_bandwidth_khz); | 587 | freq_range2->max_bandwidth_khz); |
568 | 588 | ||
569 | freq_diff = freq_range->end_freq_khz - freq_range->start_freq_khz; | 589 | freq_diff = freq_range->end_freq_khz - freq_range->start_freq_khz; |
570 | if (freq_range->max_bandwidth_khz > freq_diff) | 590 | if (freq_range->max_bandwidth_khz > freq_diff) |
@@ -575,7 +595,7 @@ static int reg_rules_intersect( | |||
575 | power_rule->max_antenna_gain = min(power_rule1->max_antenna_gain, | 595 | power_rule->max_antenna_gain = min(power_rule1->max_antenna_gain, |
576 | power_rule2->max_antenna_gain); | 596 | power_rule2->max_antenna_gain); |
577 | 597 | ||
578 | intersected_rule->flags = (rule1->flags | rule2->flags); | 598 | intersected_rule->flags = rule1->flags | rule2->flags; |
579 | 599 | ||
580 | if (!is_valid_reg_rule(intersected_rule)) | 600 | if (!is_valid_reg_rule(intersected_rule)) |
581 | return -EINVAL; | 601 | return -EINVAL; |
@@ -596,9 +616,9 @@ static int reg_rules_intersect( | |||
596 | * resulting intersection of rules between rd1 and rd2. We will | 616 | * resulting intersection of rules between rd1 and rd2. We will |
597 | * kzalloc() this structure for you. | 617 | * kzalloc() this structure for you. |
598 | */ | 618 | */ |
599 | static struct ieee80211_regdomain *regdom_intersect( | 619 | static struct ieee80211_regdomain * |
600 | const struct ieee80211_regdomain *rd1, | 620 | regdom_intersect(const struct ieee80211_regdomain *rd1, |
601 | const struct ieee80211_regdomain *rd2) | 621 | const struct ieee80211_regdomain *rd2) |
602 | { | 622 | { |
603 | int r, size_of_regd; | 623 | int r, size_of_regd; |
604 | unsigned int x, y; | 624 | unsigned int x, y; |
@@ -607,12 +627,7 @@ static struct ieee80211_regdomain *regdom_intersect( | |||
607 | struct ieee80211_reg_rule *intersected_rule; | 627 | struct ieee80211_reg_rule *intersected_rule; |
608 | struct ieee80211_regdomain *rd; | 628 | struct ieee80211_regdomain *rd; |
609 | /* This is just a dummy holder to help us count */ | 629 | /* This is just a dummy holder to help us count */ |
610 | struct ieee80211_reg_rule irule; | 630 | struct ieee80211_reg_rule dummy_rule; |
611 | |||
612 | /* Uses the stack temporarily for counter arithmetic */ | ||
613 | intersected_rule = &irule; | ||
614 | |||
615 | memset(intersected_rule, 0, sizeof(struct ieee80211_reg_rule)); | ||
616 | 631 | ||
617 | if (!rd1 || !rd2) | 632 | if (!rd1 || !rd2) |
618 | return NULL; | 633 | return NULL; |
@@ -629,11 +644,8 @@ static struct ieee80211_regdomain *regdom_intersect( | |||
629 | rule1 = &rd1->reg_rules[x]; | 644 | rule1 = &rd1->reg_rules[x]; |
630 | for (y = 0; y < rd2->n_reg_rules; y++) { | 645 | for (y = 0; y < rd2->n_reg_rules; y++) { |
631 | rule2 = &rd2->reg_rules[y]; | 646 | rule2 = &rd2->reg_rules[y]; |
632 | if (!reg_rules_intersect(rule1, rule2, | 647 | if (!reg_rules_intersect(rule1, rule2, &dummy_rule)) |
633 | intersected_rule)) | ||
634 | num_rules++; | 648 | num_rules++; |
635 | memset(intersected_rule, 0, | ||
636 | sizeof(struct ieee80211_reg_rule)); | ||
637 | } | 649 | } |
638 | } | 650 | } |
639 | 651 | ||
@@ -641,15 +653,15 @@ static struct ieee80211_regdomain *regdom_intersect( | |||
641 | return NULL; | 653 | return NULL; |
642 | 654 | ||
643 | size_of_regd = sizeof(struct ieee80211_regdomain) + | 655 | size_of_regd = sizeof(struct ieee80211_regdomain) + |
644 | ((num_rules + 1) * sizeof(struct ieee80211_reg_rule)); | 656 | num_rules * sizeof(struct ieee80211_reg_rule); |
645 | 657 | ||
646 | rd = kzalloc(size_of_regd, GFP_KERNEL); | 658 | rd = kzalloc(size_of_regd, GFP_KERNEL); |
647 | if (!rd) | 659 | if (!rd) |
648 | return NULL; | 660 | return NULL; |
649 | 661 | ||
650 | for (x = 0; x < rd1->n_reg_rules; x++) { | 662 | for (x = 0; x < rd1->n_reg_rules && rule_idx < num_rules; x++) { |
651 | rule1 = &rd1->reg_rules[x]; | 663 | rule1 = &rd1->reg_rules[x]; |
652 | for (y = 0; y < rd2->n_reg_rules; y++) { | 664 | for (y = 0; y < rd2->n_reg_rules && rule_idx < num_rules; y++) { |
653 | rule2 = &rd2->reg_rules[y]; | 665 | rule2 = &rd2->reg_rules[y]; |
654 | /* | 666 | /* |
655 | * This time around instead of using the stack lets | 667 | * This time around instead of using the stack lets |
@@ -657,8 +669,7 @@ static struct ieee80211_regdomain *regdom_intersect( | |||
657 | * a memcpy() | 669 | * a memcpy() |
658 | */ | 670 | */ |
659 | intersected_rule = &rd->reg_rules[rule_idx]; | 671 | intersected_rule = &rd->reg_rules[rule_idx]; |
660 | r = reg_rules_intersect(rule1, rule2, | 672 | r = reg_rules_intersect(rule1, rule2, intersected_rule); |
661 | intersected_rule); | ||
662 | /* | 673 | /* |
663 | * No need to memset here the intersected rule here as | 674 | * No need to memset here the intersected rule here as |
664 | * we're not using the stack anymore | 675 | * we're not using the stack anymore |
@@ -699,34 +710,16 @@ static u32 map_regdom_flags(u32 rd_flags) | |||
699 | return channel_flags; | 710 | return channel_flags; |
700 | } | 711 | } |
701 | 712 | ||
702 | static int freq_reg_info_regd(struct wiphy *wiphy, | 713 | static const struct ieee80211_reg_rule * |
703 | u32 center_freq, | 714 | freq_reg_info_regd(struct wiphy *wiphy, u32 center_freq, |
704 | u32 desired_bw_khz, | 715 | const struct ieee80211_regdomain *regd) |
705 | const struct ieee80211_reg_rule **reg_rule, | ||
706 | const struct ieee80211_regdomain *custom_regd) | ||
707 | { | 716 | { |
708 | int i; | 717 | int i; |
709 | bool band_rule_found = false; | 718 | bool band_rule_found = false; |
710 | const struct ieee80211_regdomain *regd; | ||
711 | bool bw_fits = false; | 719 | bool bw_fits = false; |
712 | 720 | ||
713 | if (!desired_bw_khz) | ||
714 | desired_bw_khz = MHZ_TO_KHZ(20); | ||
715 | |||
716 | regd = custom_regd ? custom_regd : cfg80211_regdomain; | ||
717 | |||
718 | /* | ||
719 | * Follow the driver's regulatory domain, if present, unless a country | ||
720 | * IE has been processed or a user wants to help complaince further | ||
721 | */ | ||
722 | if (!custom_regd && | ||
723 | last_request->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE && | ||
724 | last_request->initiator != NL80211_REGDOM_SET_BY_USER && | ||
725 | wiphy->regd) | ||
726 | regd = wiphy->regd; | ||
727 | |||
728 | if (!regd) | 721 | if (!regd) |
729 | return -EINVAL; | 722 | return ERR_PTR(-EINVAL); |
730 | 723 | ||
731 | for (i = 0; i < regd->n_reg_rules; i++) { | 724 | for (i = 0; i < regd->n_reg_rules; i++) { |
732 | const struct ieee80211_reg_rule *rr; | 725 | const struct ieee80211_reg_rule *rr; |
@@ -743,33 +736,36 @@ static int freq_reg_info_regd(struct wiphy *wiphy, | |||
743 | if (!band_rule_found) | 736 | if (!band_rule_found) |
744 | band_rule_found = freq_in_rule_band(fr, center_freq); | 737 | band_rule_found = freq_in_rule_band(fr, center_freq); |
745 | 738 | ||
746 | bw_fits = reg_does_bw_fit(fr, | 739 | bw_fits = reg_does_bw_fit(fr, center_freq, MHZ_TO_KHZ(20)); |
747 | center_freq, | ||
748 | desired_bw_khz); | ||
749 | 740 | ||
750 | if (band_rule_found && bw_fits) { | 741 | if (band_rule_found && bw_fits) |
751 | *reg_rule = rr; | 742 | return rr; |
752 | return 0; | ||
753 | } | ||
754 | } | 743 | } |
755 | 744 | ||
756 | if (!band_rule_found) | 745 | if (!band_rule_found) |
757 | return -ERANGE; | 746 | return ERR_PTR(-ERANGE); |
758 | 747 | ||
759 | return -EINVAL; | 748 | return ERR_PTR(-EINVAL); |
760 | } | 749 | } |
761 | 750 | ||
762 | int freq_reg_info(struct wiphy *wiphy, | 751 | const struct ieee80211_reg_rule *freq_reg_info(struct wiphy *wiphy, |
763 | u32 center_freq, | 752 | u32 center_freq) |
764 | u32 desired_bw_khz, | ||
765 | const struct ieee80211_reg_rule **reg_rule) | ||
766 | { | 753 | { |
767 | assert_cfg80211_lock(); | 754 | const struct ieee80211_regdomain *regd; |
768 | return freq_reg_info_regd(wiphy, | 755 | struct regulatory_request *lr = get_last_request(); |
769 | center_freq, | 756 | |
770 | desired_bw_khz, | 757 | /* |
771 | reg_rule, | 758 | * Follow the driver's regulatory domain, if present, unless a country |
772 | NULL); | 759 | * IE has been processed or a user wants to help complaince further |
760 | */ | ||
761 | if (lr->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE && | ||
762 | lr->initiator != NL80211_REGDOM_SET_BY_USER && | ||
763 | wiphy->regd) | ||
764 | regd = get_wiphy_regdom(wiphy); | ||
765 | else | ||
766 | regd = get_cfg80211_regdom(); | ||
767 | |||
768 | return freq_reg_info_regd(wiphy, center_freq, regd); | ||
773 | } | 769 | } |
774 | EXPORT_SYMBOL(freq_reg_info); | 770 | EXPORT_SYMBOL(freq_reg_info); |
775 | 771 | ||
@@ -792,7 +788,6 @@ static const char *reg_initiator_name(enum nl80211_reg_initiator initiator) | |||
792 | } | 788 | } |
793 | 789 | ||
794 | static void chan_reg_rule_print_dbg(struct ieee80211_channel *chan, | 790 | static void chan_reg_rule_print_dbg(struct ieee80211_channel *chan, |
795 | u32 desired_bw_khz, | ||
796 | const struct ieee80211_reg_rule *reg_rule) | 791 | const struct ieee80211_reg_rule *reg_rule) |
797 | { | 792 | { |
798 | const struct ieee80211_power_rule *power_rule; | 793 | const struct ieee80211_power_rule *power_rule; |
@@ -807,21 +802,16 @@ static void chan_reg_rule_print_dbg(struct ieee80211_channel *chan, | |||
807 | else | 802 | else |
808 | snprintf(max_antenna_gain, 32, "%d", power_rule->max_antenna_gain); | 803 | snprintf(max_antenna_gain, 32, "%d", power_rule->max_antenna_gain); |
809 | 804 | ||
810 | REG_DBG_PRINT("Updating information on frequency %d MHz " | 805 | REG_DBG_PRINT("Updating information on frequency %d MHz with regulatory rule:\n", |
811 | "for a %d MHz width channel with regulatory rule:\n", | 806 | chan->center_freq); |
812 | chan->center_freq, | ||
813 | KHZ_TO_MHZ(desired_bw_khz)); | ||
814 | 807 | ||
815 | REG_DBG_PRINT("%d KHz - %d KHz @ %d KHz), (%s mBi, %d mBm)\n", | 808 | REG_DBG_PRINT("%d KHz - %d KHz @ %d KHz), (%s mBi, %d mBm)\n", |
816 | freq_range->start_freq_khz, | 809 | freq_range->start_freq_khz, freq_range->end_freq_khz, |
817 | freq_range->end_freq_khz, | 810 | freq_range->max_bandwidth_khz, max_antenna_gain, |
818 | freq_range->max_bandwidth_khz, | ||
819 | max_antenna_gain, | ||
820 | power_rule->max_eirp); | 811 | power_rule->max_eirp); |
821 | } | 812 | } |
822 | #else | 813 | #else |
823 | static void chan_reg_rule_print_dbg(struct ieee80211_channel *chan, | 814 | static void chan_reg_rule_print_dbg(struct ieee80211_channel *chan, |
824 | u32 desired_bw_khz, | ||
825 | const struct ieee80211_reg_rule *reg_rule) | 815 | const struct ieee80211_reg_rule *reg_rule) |
826 | { | 816 | { |
827 | return; | 817 | return; |
@@ -831,43 +821,25 @@ static void chan_reg_rule_print_dbg(struct ieee80211_channel *chan, | |||
831 | /* | 821 | /* |
832 | * Note that right now we assume the desired channel bandwidth | 822 | * Note that right now we assume the desired channel bandwidth |
833 | * is always 20 MHz for each individual channel (HT40 uses 20 MHz | 823 | * is always 20 MHz for each individual channel (HT40 uses 20 MHz |
834 | * per channel, the primary and the extension channel). To support | 824 | * per channel, the primary and the extension channel). |
835 | * smaller custom bandwidths such as 5 MHz or 10 MHz we'll need a | ||
836 | * new ieee80211_channel.target_bw and re run the regulatory check | ||
837 | * on the wiphy with the target_bw specified. Then we can simply use | ||
838 | * that below for the desired_bw_khz below. | ||
839 | */ | 825 | */ |
840 | static void handle_channel(struct wiphy *wiphy, | 826 | static void handle_channel(struct wiphy *wiphy, |
841 | enum nl80211_reg_initiator initiator, | 827 | enum nl80211_reg_initiator initiator, |
842 | enum ieee80211_band band, | 828 | struct ieee80211_channel *chan) |
843 | unsigned int chan_idx) | ||
844 | { | 829 | { |
845 | int r; | ||
846 | u32 flags, bw_flags = 0; | 830 | u32 flags, bw_flags = 0; |
847 | u32 desired_bw_khz = MHZ_TO_KHZ(20); | ||
848 | const struct ieee80211_reg_rule *reg_rule = NULL; | 831 | const struct ieee80211_reg_rule *reg_rule = NULL; |
849 | const struct ieee80211_power_rule *power_rule = NULL; | 832 | const struct ieee80211_power_rule *power_rule = NULL; |
850 | const struct ieee80211_freq_range *freq_range = NULL; | 833 | const struct ieee80211_freq_range *freq_range = NULL; |
851 | struct ieee80211_supported_band *sband; | ||
852 | struct ieee80211_channel *chan; | ||
853 | struct wiphy *request_wiphy = NULL; | 834 | struct wiphy *request_wiphy = NULL; |
835 | struct regulatory_request *lr = get_last_request(); | ||
854 | 836 | ||
855 | assert_cfg80211_lock(); | 837 | request_wiphy = wiphy_idx_to_wiphy(lr->wiphy_idx); |
856 | |||
857 | request_wiphy = wiphy_idx_to_wiphy(last_request->wiphy_idx); | ||
858 | |||
859 | sband = wiphy->bands[band]; | ||
860 | BUG_ON(chan_idx >= sband->n_channels); | ||
861 | chan = &sband->channels[chan_idx]; | ||
862 | 838 | ||
863 | flags = chan->orig_flags; | 839 | flags = chan->orig_flags; |
864 | 840 | ||
865 | r = freq_reg_info(wiphy, | 841 | reg_rule = freq_reg_info(wiphy, MHZ_TO_KHZ(chan->center_freq)); |
866 | MHZ_TO_KHZ(chan->center_freq), | 842 | if (IS_ERR(reg_rule)) { |
867 | desired_bw_khz, | ||
868 | ®_rule); | ||
869 | |||
870 | if (r) { | ||
871 | /* | 843 | /* |
872 | * We will disable all channels that do not match our | 844 | * We will disable all channels that do not match our |
873 | * received regulatory rule unless the hint is coming | 845 | * received regulatory rule unless the hint is coming |
@@ -879,7 +851,7 @@ static void handle_channel(struct wiphy *wiphy, | |||
879 | * while 5 GHz is still supported. | 851 | * while 5 GHz is still supported. |
880 | */ | 852 | */ |
881 | if (initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE && | 853 | if (initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE && |
882 | r == -ERANGE) | 854 | PTR_ERR(reg_rule) == -ERANGE) |
883 | return; | 855 | return; |
884 | 856 | ||
885 | REG_DBG_PRINT("Disabling freq %d MHz\n", chan->center_freq); | 857 | REG_DBG_PRINT("Disabling freq %d MHz\n", chan->center_freq); |
@@ -887,7 +859,7 @@ static void handle_channel(struct wiphy *wiphy, | |||
887 | return; | 859 | return; |
888 | } | 860 | } |
889 | 861 | ||
890 | chan_reg_rule_print_dbg(chan, desired_bw_khz, reg_rule); | 862 | chan_reg_rule_print_dbg(chan, reg_rule); |
891 | 863 | ||
892 | power_rule = ®_rule->power_rule; | 864 | power_rule = ®_rule->power_rule; |
893 | freq_range = ®_rule->freq_range; | 865 | freq_range = ®_rule->freq_range; |
@@ -895,7 +867,7 @@ static void handle_channel(struct wiphy *wiphy, | |||
895 | if (freq_range->max_bandwidth_khz < MHZ_TO_KHZ(40)) | 867 | if (freq_range->max_bandwidth_khz < MHZ_TO_KHZ(40)) |
896 | bw_flags = IEEE80211_CHAN_NO_HT40; | 868 | bw_flags = IEEE80211_CHAN_NO_HT40; |
897 | 869 | ||
898 | if (last_request->initiator == NL80211_REGDOM_SET_BY_DRIVER && | 870 | if (lr->initiator == NL80211_REGDOM_SET_BY_DRIVER && |
899 | request_wiphy && request_wiphy == wiphy && | 871 | request_wiphy && request_wiphy == wiphy && |
900 | request_wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY) { | 872 | request_wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY) { |
901 | /* | 873 | /* |
@@ -914,8 +886,9 @@ static void handle_channel(struct wiphy *wiphy, | |||
914 | 886 | ||
915 | chan->beacon_found = false; | 887 | chan->beacon_found = false; |
916 | chan->flags = flags | bw_flags | map_regdom_flags(reg_rule->flags); | 888 | chan->flags = flags | bw_flags | map_regdom_flags(reg_rule->flags); |
917 | chan->max_antenna_gain = min(chan->orig_mag, | 889 | chan->max_antenna_gain = |
918 | (int) MBI_TO_DBI(power_rule->max_antenna_gain)); | 890 | min_t(int, chan->orig_mag, |
891 | MBI_TO_DBI(power_rule->max_antenna_gain)); | ||
919 | chan->max_reg_power = (int) MBM_TO_DBM(power_rule->max_eirp); | 892 | chan->max_reg_power = (int) MBM_TO_DBM(power_rule->max_eirp); |
920 | if (chan->orig_mpwr) { | 893 | if (chan->orig_mpwr) { |
921 | /* | 894 | /* |
@@ -935,68 +908,65 @@ static void handle_channel(struct wiphy *wiphy, | |||
935 | } | 908 | } |
936 | 909 | ||
937 | static void handle_band(struct wiphy *wiphy, | 910 | static void handle_band(struct wiphy *wiphy, |
938 | enum ieee80211_band band, | 911 | enum nl80211_reg_initiator initiator, |
939 | enum nl80211_reg_initiator initiator) | 912 | struct ieee80211_supported_band *sband) |
940 | { | 913 | { |
941 | unsigned int i; | 914 | unsigned int i; |
942 | struct ieee80211_supported_band *sband; | ||
943 | 915 | ||
944 | BUG_ON(!wiphy->bands[band]); | 916 | if (!sband) |
945 | sband = wiphy->bands[band]; | 917 | return; |
946 | 918 | ||
947 | for (i = 0; i < sband->n_channels; i++) | 919 | for (i = 0; i < sband->n_channels; i++) |
948 | handle_channel(wiphy, initiator, band, i); | 920 | handle_channel(wiphy, initiator, &sband->channels[i]); |
949 | } | 921 | } |
950 | 922 | ||
951 | static bool reg_request_cell_base(struct regulatory_request *request) | 923 | static bool reg_request_cell_base(struct regulatory_request *request) |
952 | { | 924 | { |
953 | if (request->initiator != NL80211_REGDOM_SET_BY_USER) | 925 | if (request->initiator != NL80211_REGDOM_SET_BY_USER) |
954 | return false; | 926 | return false; |
955 | if (request->user_reg_hint_type != NL80211_USER_REG_HINT_CELL_BASE) | 927 | return request->user_reg_hint_type == NL80211_USER_REG_HINT_CELL_BASE; |
956 | return false; | ||
957 | return true; | ||
958 | } | 928 | } |
959 | 929 | ||
960 | bool reg_last_request_cell_base(void) | 930 | bool reg_last_request_cell_base(void) |
961 | { | 931 | { |
962 | bool val; | 932 | bool val; |
963 | assert_cfg80211_lock(); | ||
964 | 933 | ||
965 | mutex_lock(®_mutex); | 934 | mutex_lock(®_mutex); |
966 | val = reg_request_cell_base(last_request); | 935 | val = reg_request_cell_base(get_last_request()); |
967 | mutex_unlock(®_mutex); | 936 | mutex_unlock(®_mutex); |
937 | |||
968 | return val; | 938 | return val; |
969 | } | 939 | } |
970 | 940 | ||
971 | #ifdef CONFIG_CFG80211_CERTIFICATION_ONUS | 941 | #ifdef CONFIG_CFG80211_CERTIFICATION_ONUS |
972 | |||
973 | /* Core specific check */ | 942 | /* Core specific check */ |
974 | static int reg_ignore_cell_hint(struct regulatory_request *pending_request) | 943 | static enum reg_request_treatment |
944 | reg_ignore_cell_hint(struct regulatory_request *pending_request) | ||
975 | { | 945 | { |
946 | struct regulatory_request *lr = get_last_request(); | ||
947 | |||
976 | if (!reg_num_devs_support_basehint) | 948 | if (!reg_num_devs_support_basehint) |
977 | return -EOPNOTSUPP; | 949 | return REG_REQ_IGNORE; |
978 | 950 | ||
979 | if (reg_request_cell_base(last_request)) { | 951 | if (reg_request_cell_base(lr) && |
980 | if (!regdom_changes(pending_request->alpha2)) | 952 | !regdom_changes(pending_request->alpha2)) |
981 | return -EALREADY; | 953 | return REG_REQ_ALREADY_SET; |
982 | return 0; | 954 | |
983 | } | 955 | return REG_REQ_OK; |
984 | return 0; | ||
985 | } | 956 | } |
986 | 957 | ||
987 | /* Device specific check */ | 958 | /* Device specific check */ |
988 | static bool reg_dev_ignore_cell_hint(struct wiphy *wiphy) | 959 | static bool reg_dev_ignore_cell_hint(struct wiphy *wiphy) |
989 | { | 960 | { |
990 | if (!(wiphy->features & NL80211_FEATURE_CELL_BASE_REG_HINTS)) | 961 | return !(wiphy->features & NL80211_FEATURE_CELL_BASE_REG_HINTS); |
991 | return true; | ||
992 | return false; | ||
993 | } | 962 | } |
994 | #else | 963 | #else |
995 | static int reg_ignore_cell_hint(struct regulatory_request *pending_request) | 964 | static int reg_ignore_cell_hint(struct regulatory_request *pending_request) |
996 | { | 965 | { |
997 | return -EOPNOTSUPP; | 966 | return REG_REQ_IGNORE; |
998 | } | 967 | } |
999 | static int reg_dev_ignore_cell_hint(struct wiphy *wiphy) | 968 | |
969 | static bool reg_dev_ignore_cell_hint(struct wiphy *wiphy) | ||
1000 | { | 970 | { |
1001 | return true; | 971 | return true; |
1002 | } | 972 | } |
@@ -1006,18 +976,17 @@ static int reg_dev_ignore_cell_hint(struct wiphy *wiphy) | |||
1006 | static bool ignore_reg_update(struct wiphy *wiphy, | 976 | static bool ignore_reg_update(struct wiphy *wiphy, |
1007 | enum nl80211_reg_initiator initiator) | 977 | enum nl80211_reg_initiator initiator) |
1008 | { | 978 | { |
1009 | if (!last_request) { | 979 | struct regulatory_request *lr = get_last_request(); |
1010 | REG_DBG_PRINT("Ignoring regulatory request %s since " | 980 | |
1011 | "last_request is not set\n", | 981 | if (!lr) { |
982 | REG_DBG_PRINT("Ignoring regulatory request %s since last_request is not set\n", | ||
1012 | reg_initiator_name(initiator)); | 983 | reg_initiator_name(initiator)); |
1013 | return true; | 984 | return true; |
1014 | } | 985 | } |
1015 | 986 | ||
1016 | if (initiator == NL80211_REGDOM_SET_BY_CORE && | 987 | if (initiator == NL80211_REGDOM_SET_BY_CORE && |
1017 | wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY) { | 988 | wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY) { |
1018 | REG_DBG_PRINT("Ignoring regulatory request %s " | 989 | REG_DBG_PRINT("Ignoring regulatory request %s since the driver uses its own custom regulatory domain\n", |
1019 | "since the driver uses its own custom " | ||
1020 | "regulatory domain\n", | ||
1021 | reg_initiator_name(initiator)); | 990 | reg_initiator_name(initiator)); |
1022 | return true; | 991 | return true; |
1023 | } | 992 | } |
@@ -1028,22 +997,35 @@ static bool ignore_reg_update(struct wiphy *wiphy, | |||
1028 | */ | 997 | */ |
1029 | if (wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY && !wiphy->regd && | 998 | if (wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY && !wiphy->regd && |
1030 | initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE && | 999 | initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE && |
1031 | !is_world_regdom(last_request->alpha2)) { | 1000 | !is_world_regdom(lr->alpha2)) { |
1032 | REG_DBG_PRINT("Ignoring regulatory request %s " | 1001 | REG_DBG_PRINT("Ignoring regulatory request %s since the driver requires its own regulatory domain to be set first\n", |
1033 | "since the driver requires its own regulatory " | ||
1034 | "domain to be set first\n", | ||
1035 | reg_initiator_name(initiator)); | 1002 | reg_initiator_name(initiator)); |
1036 | return true; | 1003 | return true; |
1037 | } | 1004 | } |
1038 | 1005 | ||
1039 | if (reg_request_cell_base(last_request)) | 1006 | if (reg_request_cell_base(lr)) |
1040 | return reg_dev_ignore_cell_hint(wiphy); | 1007 | return reg_dev_ignore_cell_hint(wiphy); |
1041 | 1008 | ||
1042 | return false; | 1009 | return false; |
1043 | } | 1010 | } |
1044 | 1011 | ||
1045 | static void handle_reg_beacon(struct wiphy *wiphy, | 1012 | static bool reg_is_world_roaming(struct wiphy *wiphy) |
1046 | unsigned int chan_idx, | 1013 | { |
1014 | const struct ieee80211_regdomain *cr = get_cfg80211_regdom(); | ||
1015 | const struct ieee80211_regdomain *wr = get_wiphy_regdom(wiphy); | ||
1016 | struct regulatory_request *lr = get_last_request(); | ||
1017 | |||
1018 | if (is_world_regdom(cr->alpha2) || (wr && is_world_regdom(wr->alpha2))) | ||
1019 | return true; | ||
1020 | |||
1021 | if (lr && lr->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE && | ||
1022 | wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY) | ||
1023 | return true; | ||
1024 | |||
1025 | return false; | ||
1026 | } | ||
1027 | |||
1028 | static void handle_reg_beacon(struct wiphy *wiphy, unsigned int chan_idx, | ||
1047 | struct reg_beacon *reg_beacon) | 1029 | struct reg_beacon *reg_beacon) |
1048 | { | 1030 | { |
1049 | struct ieee80211_supported_band *sband; | 1031 | struct ieee80211_supported_band *sband; |
@@ -1051,8 +1033,6 @@ static void handle_reg_beacon(struct wiphy *wiphy, | |||
1051 | bool channel_changed = false; | 1033 | bool channel_changed = false; |
1052 | struct ieee80211_channel chan_before; | 1034 | struct ieee80211_channel chan_before; |
1053 | 1035 | ||
1054 | assert_cfg80211_lock(); | ||
1055 | |||
1056 | sband = wiphy->bands[reg_beacon->chan.band]; | 1036 | sband = wiphy->bands[reg_beacon->chan.band]; |
1057 | chan = &sband->channels[chan_idx]; | 1037 | chan = &sband->channels[chan_idx]; |
1058 | 1038 | ||
@@ -1064,6 +1044,9 @@ static void handle_reg_beacon(struct wiphy *wiphy, | |||
1064 | 1044 | ||
1065 | chan->beacon_found = true; | 1045 | chan->beacon_found = true; |
1066 | 1046 | ||
1047 | if (!reg_is_world_roaming(wiphy)) | ||
1048 | return; | ||
1049 | |||
1067 | if (wiphy->flags & WIPHY_FLAG_DISABLE_BEACON_HINTS) | 1050 | if (wiphy->flags & WIPHY_FLAG_DISABLE_BEACON_HINTS) |
1068 | return; | 1051 | return; |
1069 | 1052 | ||
@@ -1094,8 +1077,6 @@ static void wiphy_update_new_beacon(struct wiphy *wiphy, | |||
1094 | unsigned int i; | 1077 | unsigned int i; |
1095 | struct ieee80211_supported_band *sband; | 1078 | struct ieee80211_supported_band *sband; |
1096 | 1079 | ||
1097 | assert_cfg80211_lock(); | ||
1098 | |||
1099 | if (!wiphy->bands[reg_beacon->chan.band]) | 1080 | if (!wiphy->bands[reg_beacon->chan.band]) |
1100 | return; | 1081 | return; |
1101 | 1082 | ||
@@ -1114,11 +1095,6 @@ static void wiphy_update_beacon_reg(struct wiphy *wiphy) | |||
1114 | struct ieee80211_supported_band *sband; | 1095 | struct ieee80211_supported_band *sband; |
1115 | struct reg_beacon *reg_beacon; | 1096 | struct reg_beacon *reg_beacon; |
1116 | 1097 | ||
1117 | assert_cfg80211_lock(); | ||
1118 | |||
1119 | if (list_empty(®_beacon_list)) | ||
1120 | return; | ||
1121 | |||
1122 | list_for_each_entry(reg_beacon, ®_beacon_list, list) { | 1098 | list_for_each_entry(reg_beacon, ®_beacon_list, list) { |
1123 | if (!wiphy->bands[reg_beacon->chan.band]) | 1099 | if (!wiphy->bands[reg_beacon->chan.band]) |
1124 | continue; | 1100 | continue; |
@@ -1128,18 +1104,6 @@ static void wiphy_update_beacon_reg(struct wiphy *wiphy) | |||
1128 | } | 1104 | } |
1129 | } | 1105 | } |
1130 | 1106 | ||
1131 | static bool reg_is_world_roaming(struct wiphy *wiphy) | ||
1132 | { | ||
1133 | if (is_world_regdom(cfg80211_regdomain->alpha2) || | ||
1134 | (wiphy->regd && is_world_regdom(wiphy->regd->alpha2))) | ||
1135 | return true; | ||
1136 | if (last_request && | ||
1137 | last_request->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE && | ||
1138 | wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY) | ||
1139 | return true; | ||
1140 | return false; | ||
1141 | } | ||
1142 | |||
1143 | /* Reap the advantages of previously found beacons */ | 1107 | /* Reap the advantages of previously found beacons */ |
1144 | static void reg_process_beacons(struct wiphy *wiphy) | 1108 | static void reg_process_beacons(struct wiphy *wiphy) |
1145 | { | 1109 | { |
@@ -1149,39 +1113,29 @@ static void reg_process_beacons(struct wiphy *wiphy) | |||
1149 | */ | 1113 | */ |
1150 | if (!last_request) | 1114 | if (!last_request) |
1151 | return; | 1115 | return; |
1152 | if (!reg_is_world_roaming(wiphy)) | ||
1153 | return; | ||
1154 | wiphy_update_beacon_reg(wiphy); | 1116 | wiphy_update_beacon_reg(wiphy); |
1155 | } | 1117 | } |
1156 | 1118 | ||
1157 | static bool is_ht40_not_allowed(struct ieee80211_channel *chan) | 1119 | static bool is_ht40_allowed(struct ieee80211_channel *chan) |
1158 | { | 1120 | { |
1159 | if (!chan) | 1121 | if (!chan) |
1160 | return true; | 1122 | return false; |
1161 | if (chan->flags & IEEE80211_CHAN_DISABLED) | 1123 | if (chan->flags & IEEE80211_CHAN_DISABLED) |
1162 | return true; | 1124 | return false; |
1163 | /* This would happen when regulatory rules disallow HT40 completely */ | 1125 | /* This would happen when regulatory rules disallow HT40 completely */ |
1164 | if (IEEE80211_CHAN_NO_HT40 == (chan->flags & (IEEE80211_CHAN_NO_HT40))) | 1126 | if ((chan->flags & IEEE80211_CHAN_NO_HT40) == IEEE80211_CHAN_NO_HT40) |
1165 | return true; | 1127 | return false; |
1166 | return false; | 1128 | return true; |
1167 | } | 1129 | } |
1168 | 1130 | ||
1169 | static void reg_process_ht_flags_channel(struct wiphy *wiphy, | 1131 | static void reg_process_ht_flags_channel(struct wiphy *wiphy, |
1170 | enum ieee80211_band band, | 1132 | struct ieee80211_channel *channel) |
1171 | unsigned int chan_idx) | ||
1172 | { | 1133 | { |
1173 | struct ieee80211_supported_band *sband; | 1134 | struct ieee80211_supported_band *sband = wiphy->bands[channel->band]; |
1174 | struct ieee80211_channel *channel; | ||
1175 | struct ieee80211_channel *channel_before = NULL, *channel_after = NULL; | 1135 | struct ieee80211_channel *channel_before = NULL, *channel_after = NULL; |
1176 | unsigned int i; | 1136 | unsigned int i; |
1177 | 1137 | ||
1178 | assert_cfg80211_lock(); | 1138 | if (!is_ht40_allowed(channel)) { |
1179 | |||
1180 | sband = wiphy->bands[band]; | ||
1181 | BUG_ON(chan_idx >= sband->n_channels); | ||
1182 | channel = &sband->channels[chan_idx]; | ||
1183 | |||
1184 | if (is_ht40_not_allowed(channel)) { | ||
1185 | channel->flags |= IEEE80211_CHAN_NO_HT40; | 1139 | channel->flags |= IEEE80211_CHAN_NO_HT40; |
1186 | return; | 1140 | return; |
1187 | } | 1141 | } |
@@ -1192,6 +1146,7 @@ static void reg_process_ht_flags_channel(struct wiphy *wiphy, | |||
1192 | */ | 1146 | */ |
1193 | for (i = 0; i < sband->n_channels; i++) { | 1147 | for (i = 0; i < sband->n_channels; i++) { |
1194 | struct ieee80211_channel *c = &sband->channels[i]; | 1148 | struct ieee80211_channel *c = &sband->channels[i]; |
1149 | |||
1195 | if (c->center_freq == (channel->center_freq - 20)) | 1150 | if (c->center_freq == (channel->center_freq - 20)) |
1196 | channel_before = c; | 1151 | channel_before = c; |
1197 | if (c->center_freq == (channel->center_freq + 20)) | 1152 | if (c->center_freq == (channel->center_freq + 20)) |
@@ -1203,28 +1158,27 @@ static void reg_process_ht_flags_channel(struct wiphy *wiphy, | |||
1203 | * if that ever changes we also need to change the below logic | 1158 | * if that ever changes we also need to change the below logic |
1204 | * to include that as well. | 1159 | * to include that as well. |
1205 | */ | 1160 | */ |
1206 | if (is_ht40_not_allowed(channel_before)) | 1161 | if (!is_ht40_allowed(channel_before)) |
1207 | channel->flags |= IEEE80211_CHAN_NO_HT40MINUS; | 1162 | channel->flags |= IEEE80211_CHAN_NO_HT40MINUS; |
1208 | else | 1163 | else |
1209 | channel->flags &= ~IEEE80211_CHAN_NO_HT40MINUS; | 1164 | channel->flags &= ~IEEE80211_CHAN_NO_HT40MINUS; |
1210 | 1165 | ||
1211 | if (is_ht40_not_allowed(channel_after)) | 1166 | if (!is_ht40_allowed(channel_after)) |
1212 | channel->flags |= IEEE80211_CHAN_NO_HT40PLUS; | 1167 | channel->flags |= IEEE80211_CHAN_NO_HT40PLUS; |
1213 | else | 1168 | else |
1214 | channel->flags &= ~IEEE80211_CHAN_NO_HT40PLUS; | 1169 | channel->flags &= ~IEEE80211_CHAN_NO_HT40PLUS; |
1215 | } | 1170 | } |
1216 | 1171 | ||
1217 | static void reg_process_ht_flags_band(struct wiphy *wiphy, | 1172 | static void reg_process_ht_flags_band(struct wiphy *wiphy, |
1218 | enum ieee80211_band band) | 1173 | struct ieee80211_supported_band *sband) |
1219 | { | 1174 | { |
1220 | unsigned int i; | 1175 | unsigned int i; |
1221 | struct ieee80211_supported_band *sband; | ||
1222 | 1176 | ||
1223 | BUG_ON(!wiphy->bands[band]); | 1177 | if (!sband) |
1224 | sband = wiphy->bands[band]; | 1178 | return; |
1225 | 1179 | ||
1226 | for (i = 0; i < sband->n_channels; i++) | 1180 | for (i = 0; i < sband->n_channels; i++) |
1227 | reg_process_ht_flags_channel(wiphy, band, i); | 1181 | reg_process_ht_flags_channel(wiphy, &sband->channels[i]); |
1228 | } | 1182 | } |
1229 | 1183 | ||
1230 | static void reg_process_ht_flags(struct wiphy *wiphy) | 1184 | static void reg_process_ht_flags(struct wiphy *wiphy) |
@@ -1234,34 +1188,29 @@ static void reg_process_ht_flags(struct wiphy *wiphy) | |||
1234 | if (!wiphy) | 1188 | if (!wiphy) |
1235 | return; | 1189 | return; |
1236 | 1190 | ||
1237 | for (band = 0; band < IEEE80211_NUM_BANDS; band++) { | 1191 | for (band = 0; band < IEEE80211_NUM_BANDS; band++) |
1238 | if (wiphy->bands[band]) | 1192 | reg_process_ht_flags_band(wiphy, wiphy->bands[band]); |
1239 | reg_process_ht_flags_band(wiphy, band); | ||
1240 | } | ||
1241 | |||
1242 | } | 1193 | } |
1243 | 1194 | ||
1244 | static void wiphy_update_regulatory(struct wiphy *wiphy, | 1195 | static void wiphy_update_regulatory(struct wiphy *wiphy, |
1245 | enum nl80211_reg_initiator initiator) | 1196 | enum nl80211_reg_initiator initiator) |
1246 | { | 1197 | { |
1247 | enum ieee80211_band band; | 1198 | enum ieee80211_band band; |
1248 | 1199 | struct regulatory_request *lr = get_last_request(); | |
1249 | assert_reg_lock(); | ||
1250 | 1200 | ||
1251 | if (ignore_reg_update(wiphy, initiator)) | 1201 | if (ignore_reg_update(wiphy, initiator)) |
1252 | return; | 1202 | return; |
1253 | 1203 | ||
1254 | last_request->dfs_region = cfg80211_regdomain->dfs_region; | 1204 | lr->dfs_region = get_cfg80211_regdom()->dfs_region; |
1255 | 1205 | ||
1256 | for (band = 0; band < IEEE80211_NUM_BANDS; band++) { | 1206 | for (band = 0; band < IEEE80211_NUM_BANDS; band++) |
1257 | if (wiphy->bands[band]) | 1207 | handle_band(wiphy, initiator, wiphy->bands[band]); |
1258 | handle_band(wiphy, band, initiator); | ||
1259 | } | ||
1260 | 1208 | ||
1261 | reg_process_beacons(wiphy); | 1209 | reg_process_beacons(wiphy); |
1262 | reg_process_ht_flags(wiphy); | 1210 | reg_process_ht_flags(wiphy); |
1211 | |||
1263 | if (wiphy->reg_notifier) | 1212 | if (wiphy->reg_notifier) |
1264 | wiphy->reg_notifier(wiphy, last_request); | 1213 | wiphy->reg_notifier(wiphy, lr); |
1265 | } | 1214 | } |
1266 | 1215 | ||
1267 | static void update_all_wiphy_regulatory(enum nl80211_reg_initiator initiator) | 1216 | static void update_all_wiphy_regulatory(enum nl80211_reg_initiator initiator) |
@@ -1269,6 +1218,8 @@ static void update_all_wiphy_regulatory(enum nl80211_reg_initiator initiator) | |||
1269 | struct cfg80211_registered_device *rdev; | 1218 | struct cfg80211_registered_device *rdev; |
1270 | struct wiphy *wiphy; | 1219 | struct wiphy *wiphy; |
1271 | 1220 | ||
1221 | assert_cfg80211_lock(); | ||
1222 | |||
1272 | list_for_each_entry(rdev, &cfg80211_rdev_list, list) { | 1223 | list_for_each_entry(rdev, &cfg80211_rdev_list, list) { |
1273 | wiphy = &rdev->wiphy; | 1224 | wiphy = &rdev->wiphy; |
1274 | wiphy_update_regulatory(wiphy, initiator); | 1225 | wiphy_update_regulatory(wiphy, initiator); |
@@ -1280,47 +1231,30 @@ static void update_all_wiphy_regulatory(enum nl80211_reg_initiator initiator) | |||
1280 | if (initiator == NL80211_REGDOM_SET_BY_CORE && | 1231 | if (initiator == NL80211_REGDOM_SET_BY_CORE && |
1281 | wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY && | 1232 | wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY && |
1282 | wiphy->reg_notifier) | 1233 | wiphy->reg_notifier) |
1283 | wiphy->reg_notifier(wiphy, last_request); | 1234 | wiphy->reg_notifier(wiphy, get_last_request()); |
1284 | } | 1235 | } |
1285 | } | 1236 | } |
1286 | 1237 | ||
1287 | static void handle_channel_custom(struct wiphy *wiphy, | 1238 | static void handle_channel_custom(struct wiphy *wiphy, |
1288 | enum ieee80211_band band, | 1239 | struct ieee80211_channel *chan, |
1289 | unsigned int chan_idx, | ||
1290 | const struct ieee80211_regdomain *regd) | 1240 | const struct ieee80211_regdomain *regd) |
1291 | { | 1241 | { |
1292 | int r; | ||
1293 | u32 desired_bw_khz = MHZ_TO_KHZ(20); | ||
1294 | u32 bw_flags = 0; | 1242 | u32 bw_flags = 0; |
1295 | const struct ieee80211_reg_rule *reg_rule = NULL; | 1243 | const struct ieee80211_reg_rule *reg_rule = NULL; |
1296 | const struct ieee80211_power_rule *power_rule = NULL; | 1244 | const struct ieee80211_power_rule *power_rule = NULL; |
1297 | const struct ieee80211_freq_range *freq_range = NULL; | 1245 | const struct ieee80211_freq_range *freq_range = NULL; |
1298 | struct ieee80211_supported_band *sband; | ||
1299 | struct ieee80211_channel *chan; | ||
1300 | |||
1301 | assert_reg_lock(); | ||
1302 | 1246 | ||
1303 | sband = wiphy->bands[band]; | 1247 | reg_rule = freq_reg_info_regd(wiphy, MHZ_TO_KHZ(chan->center_freq), |
1304 | BUG_ON(chan_idx >= sband->n_channels); | 1248 | regd); |
1305 | chan = &sband->channels[chan_idx]; | ||
1306 | |||
1307 | r = freq_reg_info_regd(wiphy, | ||
1308 | MHZ_TO_KHZ(chan->center_freq), | ||
1309 | desired_bw_khz, | ||
1310 | ®_rule, | ||
1311 | regd); | ||
1312 | 1249 | ||
1313 | if (r) { | 1250 | if (IS_ERR(reg_rule)) { |
1314 | REG_DBG_PRINT("Disabling freq %d MHz as custom " | 1251 | REG_DBG_PRINT("Disabling freq %d MHz as custom regd has no rule that fits it\n", |
1315 | "regd has no rule that fits a %d MHz " | 1252 | chan->center_freq); |
1316 | "wide channel\n", | ||
1317 | chan->center_freq, | ||
1318 | KHZ_TO_MHZ(desired_bw_khz)); | ||
1319 | chan->flags = IEEE80211_CHAN_DISABLED; | 1253 | chan->flags = IEEE80211_CHAN_DISABLED; |
1320 | return; | 1254 | return; |
1321 | } | 1255 | } |
1322 | 1256 | ||
1323 | chan_reg_rule_print_dbg(chan, desired_bw_khz, reg_rule); | 1257 | chan_reg_rule_print_dbg(chan, reg_rule); |
1324 | 1258 | ||
1325 | power_rule = ®_rule->power_rule; | 1259 | power_rule = ®_rule->power_rule; |
1326 | freq_range = ®_rule->freq_range; | 1260 | freq_range = ®_rule->freq_range; |
@@ -1334,17 +1268,17 @@ static void handle_channel_custom(struct wiphy *wiphy, | |||
1334 | (int) MBM_TO_DBM(power_rule->max_eirp); | 1268 | (int) MBM_TO_DBM(power_rule->max_eirp); |
1335 | } | 1269 | } |
1336 | 1270 | ||
1337 | static void handle_band_custom(struct wiphy *wiphy, enum ieee80211_band band, | 1271 | static void handle_band_custom(struct wiphy *wiphy, |
1272 | struct ieee80211_supported_band *sband, | ||
1338 | const struct ieee80211_regdomain *regd) | 1273 | const struct ieee80211_regdomain *regd) |
1339 | { | 1274 | { |
1340 | unsigned int i; | 1275 | unsigned int i; |
1341 | struct ieee80211_supported_band *sband; | ||
1342 | 1276 | ||
1343 | BUG_ON(!wiphy->bands[band]); | 1277 | if (!sband) |
1344 | sband = wiphy->bands[band]; | 1278 | return; |
1345 | 1279 | ||
1346 | for (i = 0; i < sband->n_channels; i++) | 1280 | for (i = 0; i < sband->n_channels; i++) |
1347 | handle_channel_custom(wiphy, band, i, regd); | 1281 | handle_channel_custom(wiphy, &sband->channels[i], regd); |
1348 | } | 1282 | } |
1349 | 1283 | ||
1350 | /* Used by drivers prior to wiphy registration */ | 1284 | /* Used by drivers prior to wiphy registration */ |
@@ -1354,60 +1288,50 @@ void wiphy_apply_custom_regulatory(struct wiphy *wiphy, | |||
1354 | enum ieee80211_band band; | 1288 | enum ieee80211_band band; |
1355 | unsigned int bands_set = 0; | 1289 | unsigned int bands_set = 0; |
1356 | 1290 | ||
1357 | mutex_lock(®_mutex); | ||
1358 | for (band = 0; band < IEEE80211_NUM_BANDS; band++) { | 1291 | for (band = 0; band < IEEE80211_NUM_BANDS; band++) { |
1359 | if (!wiphy->bands[band]) | 1292 | if (!wiphy->bands[band]) |
1360 | continue; | 1293 | continue; |
1361 | handle_band_custom(wiphy, band, regd); | 1294 | handle_band_custom(wiphy, wiphy->bands[band], regd); |
1362 | bands_set++; | 1295 | bands_set++; |
1363 | } | 1296 | } |
1364 | mutex_unlock(®_mutex); | ||
1365 | 1297 | ||
1366 | /* | 1298 | /* |
1367 | * no point in calling this if it won't have any effect | 1299 | * no point in calling this if it won't have any effect |
1368 | * on your device's supportd bands. | 1300 | * on your device's supported bands. |
1369 | */ | 1301 | */ |
1370 | WARN_ON(!bands_set); | 1302 | WARN_ON(!bands_set); |
1371 | } | 1303 | } |
1372 | EXPORT_SYMBOL(wiphy_apply_custom_regulatory); | 1304 | EXPORT_SYMBOL(wiphy_apply_custom_regulatory); |
1373 | 1305 | ||
1374 | /* | ||
1375 | * Return value which can be used by ignore_request() to indicate | ||
1376 | * it has been determined we should intersect two regulatory domains | ||
1377 | */ | ||
1378 | #define REG_INTERSECT 1 | ||
1379 | |||
1380 | /* This has the logic which determines when a new request | 1306 | /* This has the logic which determines when a new request |
1381 | * should be ignored. */ | 1307 | * should be ignored. */ |
1382 | static int ignore_request(struct wiphy *wiphy, | 1308 | static enum reg_request_treatment |
1309 | get_reg_request_treatment(struct wiphy *wiphy, | ||
1383 | struct regulatory_request *pending_request) | 1310 | struct regulatory_request *pending_request) |
1384 | { | 1311 | { |
1385 | struct wiphy *last_wiphy = NULL; | 1312 | struct wiphy *last_wiphy = NULL; |
1386 | 1313 | struct regulatory_request *lr = get_last_request(); | |
1387 | assert_cfg80211_lock(); | ||
1388 | 1314 | ||
1389 | /* All initial requests are respected */ | 1315 | /* All initial requests are respected */ |
1390 | if (!last_request) | 1316 | if (!lr) |
1391 | return 0; | 1317 | return REG_REQ_OK; |
1392 | 1318 | ||
1393 | switch (pending_request->initiator) { | 1319 | switch (pending_request->initiator) { |
1394 | case NL80211_REGDOM_SET_BY_CORE: | 1320 | case NL80211_REGDOM_SET_BY_CORE: |
1395 | return 0; | 1321 | return REG_REQ_OK; |
1396 | case NL80211_REGDOM_SET_BY_COUNTRY_IE: | 1322 | case NL80211_REGDOM_SET_BY_COUNTRY_IE: |
1397 | 1323 | if (reg_request_cell_base(lr)) { | |
1398 | if (reg_request_cell_base(last_request)) { | ||
1399 | /* Trust a Cell base station over the AP's country IE */ | 1324 | /* Trust a Cell base station over the AP's country IE */ |
1400 | if (regdom_changes(pending_request->alpha2)) | 1325 | if (regdom_changes(pending_request->alpha2)) |
1401 | return -EOPNOTSUPP; | 1326 | return REG_REQ_IGNORE; |
1402 | return -EALREADY; | 1327 | return REG_REQ_ALREADY_SET; |
1403 | } | 1328 | } |
1404 | 1329 | ||
1405 | last_wiphy = wiphy_idx_to_wiphy(last_request->wiphy_idx); | 1330 | last_wiphy = wiphy_idx_to_wiphy(lr->wiphy_idx); |
1406 | 1331 | ||
1407 | if (unlikely(!is_an_alpha2(pending_request->alpha2))) | 1332 | if (unlikely(!is_an_alpha2(pending_request->alpha2))) |
1408 | return -EINVAL; | 1333 | return -EINVAL; |
1409 | if (last_request->initiator == | 1334 | if (lr->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) { |
1410 | NL80211_REGDOM_SET_BY_COUNTRY_IE) { | ||
1411 | if (last_wiphy != wiphy) { | 1335 | if (last_wiphy != wiphy) { |
1412 | /* | 1336 | /* |
1413 | * Two cards with two APs claiming different | 1337 | * Two cards with two APs claiming different |
@@ -1416,23 +1340,23 @@ static int ignore_request(struct wiphy *wiphy, | |||
1416 | * to be correct. Reject second one for now. | 1340 | * to be correct. Reject second one for now. |
1417 | */ | 1341 | */ |
1418 | if (regdom_changes(pending_request->alpha2)) | 1342 | if (regdom_changes(pending_request->alpha2)) |
1419 | return -EOPNOTSUPP; | 1343 | return REG_REQ_IGNORE; |
1420 | return -EALREADY; | 1344 | return REG_REQ_ALREADY_SET; |
1421 | } | 1345 | } |
1422 | /* | 1346 | /* |
1423 | * Two consecutive Country IE hints on the same wiphy. | 1347 | * Two consecutive Country IE hints on the same wiphy. |
1424 | * This should be picked up early by the driver/stack | 1348 | * This should be picked up early by the driver/stack |
1425 | */ | 1349 | */ |
1426 | if (WARN_ON(regdom_changes(pending_request->alpha2))) | 1350 | if (WARN_ON(regdom_changes(pending_request->alpha2))) |
1427 | return 0; | 1351 | return REG_REQ_OK; |
1428 | return -EALREADY; | 1352 | return REG_REQ_ALREADY_SET; |
1429 | } | 1353 | } |
1430 | return 0; | 1354 | return 0; |
1431 | case NL80211_REGDOM_SET_BY_DRIVER: | 1355 | case NL80211_REGDOM_SET_BY_DRIVER: |
1432 | if (last_request->initiator == NL80211_REGDOM_SET_BY_CORE) { | 1356 | if (lr->initiator == NL80211_REGDOM_SET_BY_CORE) { |
1433 | if (regdom_changes(pending_request->alpha2)) | 1357 | if (regdom_changes(pending_request->alpha2)) |
1434 | return 0; | 1358 | return REG_REQ_OK; |
1435 | return -EALREADY; | 1359 | return REG_REQ_ALREADY_SET; |
1436 | } | 1360 | } |
1437 | 1361 | ||
1438 | /* | 1362 | /* |
@@ -1440,59 +1364,59 @@ static int ignore_request(struct wiphy *wiphy, | |||
1440 | * back in or if you add a new device for which the previously | 1364 | * back in or if you add a new device for which the previously |
1441 | * loaded card also agrees on the regulatory domain. | 1365 | * loaded card also agrees on the regulatory domain. |
1442 | */ | 1366 | */ |
1443 | if (last_request->initiator == NL80211_REGDOM_SET_BY_DRIVER && | 1367 | if (lr->initiator == NL80211_REGDOM_SET_BY_DRIVER && |
1444 | !regdom_changes(pending_request->alpha2)) | 1368 | !regdom_changes(pending_request->alpha2)) |
1445 | return -EALREADY; | 1369 | return REG_REQ_ALREADY_SET; |
1446 | 1370 | ||
1447 | return REG_INTERSECT; | 1371 | return REG_REQ_INTERSECT; |
1448 | case NL80211_REGDOM_SET_BY_USER: | 1372 | case NL80211_REGDOM_SET_BY_USER: |
1449 | if (reg_request_cell_base(pending_request)) | 1373 | if (reg_request_cell_base(pending_request)) |
1450 | return reg_ignore_cell_hint(pending_request); | 1374 | return reg_ignore_cell_hint(pending_request); |
1451 | 1375 | ||
1452 | if (reg_request_cell_base(last_request)) | 1376 | if (reg_request_cell_base(lr)) |
1453 | return -EOPNOTSUPP; | 1377 | return REG_REQ_IGNORE; |
1454 | 1378 | ||
1455 | if (last_request->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) | 1379 | if (lr->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) |
1456 | return REG_INTERSECT; | 1380 | return REG_REQ_INTERSECT; |
1457 | /* | 1381 | /* |
1458 | * If the user knows better the user should set the regdom | 1382 | * If the user knows better the user should set the regdom |
1459 | * to their country before the IE is picked up | 1383 | * to their country before the IE is picked up |
1460 | */ | 1384 | */ |
1461 | if (last_request->initiator == NL80211_REGDOM_SET_BY_USER && | 1385 | if (lr->initiator == NL80211_REGDOM_SET_BY_USER && |
1462 | last_request->intersect) | 1386 | lr->intersect) |
1463 | return -EOPNOTSUPP; | 1387 | return REG_REQ_IGNORE; |
1464 | /* | 1388 | /* |
1465 | * Process user requests only after previous user/driver/core | 1389 | * Process user requests only after previous user/driver/core |
1466 | * requests have been processed | 1390 | * requests have been processed |
1467 | */ | 1391 | */ |
1468 | if (last_request->initiator == NL80211_REGDOM_SET_BY_CORE || | 1392 | if ((lr->initiator == NL80211_REGDOM_SET_BY_CORE || |
1469 | last_request->initiator == NL80211_REGDOM_SET_BY_DRIVER || | 1393 | lr->initiator == NL80211_REGDOM_SET_BY_DRIVER || |
1470 | last_request->initiator == NL80211_REGDOM_SET_BY_USER) { | 1394 | lr->initiator == NL80211_REGDOM_SET_BY_USER) && |
1471 | if (regdom_changes(last_request->alpha2)) | 1395 | regdom_changes(lr->alpha2)) |
1472 | return -EAGAIN; | 1396 | return REG_REQ_IGNORE; |
1473 | } | ||
1474 | 1397 | ||
1475 | if (!regdom_changes(pending_request->alpha2)) | 1398 | if (!regdom_changes(pending_request->alpha2)) |
1476 | return -EALREADY; | 1399 | return REG_REQ_ALREADY_SET; |
1477 | 1400 | ||
1478 | return 0; | 1401 | return REG_REQ_OK; |
1479 | } | 1402 | } |
1480 | 1403 | ||
1481 | return -EINVAL; | 1404 | return REG_REQ_IGNORE; |
1482 | } | 1405 | } |
1483 | 1406 | ||
1484 | static void reg_set_request_processed(void) | 1407 | static void reg_set_request_processed(void) |
1485 | { | 1408 | { |
1486 | bool need_more_processing = false; | 1409 | bool need_more_processing = false; |
1410 | struct regulatory_request *lr = get_last_request(); | ||
1487 | 1411 | ||
1488 | last_request->processed = true; | 1412 | lr->processed = true; |
1489 | 1413 | ||
1490 | spin_lock(®_requests_lock); | 1414 | spin_lock(®_requests_lock); |
1491 | if (!list_empty(®_requests_list)) | 1415 | if (!list_empty(®_requests_list)) |
1492 | need_more_processing = true; | 1416 | need_more_processing = true; |
1493 | spin_unlock(®_requests_lock); | 1417 | spin_unlock(®_requests_lock); |
1494 | 1418 | ||
1495 | if (last_request->initiator == NL80211_REGDOM_SET_BY_USER) | 1419 | if (lr->initiator == NL80211_REGDOM_SET_BY_USER) |
1496 | cancel_delayed_work(®_timeout); | 1420 | cancel_delayed_work(®_timeout); |
1497 | 1421 | ||
1498 | if (need_more_processing) | 1422 | if (need_more_processing) |
@@ -1508,116 +1432,122 @@ static void reg_set_request_processed(void) | |||
1508 | * The Wireless subsystem can use this function to hint to the wireless core | 1432 | * The Wireless subsystem can use this function to hint to the wireless core |
1509 | * what it believes should be the current regulatory domain. | 1433 | * what it believes should be the current regulatory domain. |
1510 | * | 1434 | * |
1511 | * Returns zero if all went fine, %-EALREADY if a regulatory domain had | 1435 | * Returns one of the different reg request treatment values. |
1512 | * already been set or other standard error codes. | ||
1513 | * | 1436 | * |
1514 | * Caller must hold &cfg80211_mutex and ®_mutex | 1437 | * Caller must hold ®_mutex |
1515 | */ | 1438 | */ |
1516 | static int __regulatory_hint(struct wiphy *wiphy, | 1439 | static enum reg_request_treatment |
1517 | struct regulatory_request *pending_request) | 1440 | __regulatory_hint(struct wiphy *wiphy, |
1441 | struct regulatory_request *pending_request) | ||
1518 | { | 1442 | { |
1443 | const struct ieee80211_regdomain *regd; | ||
1519 | bool intersect = false; | 1444 | bool intersect = false; |
1520 | int r = 0; | 1445 | enum reg_request_treatment treatment; |
1521 | 1446 | struct regulatory_request *lr; | |
1522 | assert_cfg80211_lock(); | ||
1523 | 1447 | ||
1524 | r = ignore_request(wiphy, pending_request); | 1448 | treatment = get_reg_request_treatment(wiphy, pending_request); |
1525 | 1449 | ||
1526 | if (r == REG_INTERSECT) { | 1450 | switch (treatment) { |
1451 | case REG_REQ_INTERSECT: | ||
1527 | if (pending_request->initiator == | 1452 | if (pending_request->initiator == |
1528 | NL80211_REGDOM_SET_BY_DRIVER) { | 1453 | NL80211_REGDOM_SET_BY_DRIVER) { |
1529 | r = reg_copy_regd(&wiphy->regd, cfg80211_regdomain); | 1454 | regd = reg_copy_regd(get_cfg80211_regdom()); |
1530 | if (r) { | 1455 | if (IS_ERR(regd)) { |
1531 | kfree(pending_request); | 1456 | kfree(pending_request); |
1532 | return r; | 1457 | return PTR_ERR(regd); |
1533 | } | 1458 | } |
1459 | rcu_assign_pointer(wiphy->regd, regd); | ||
1534 | } | 1460 | } |
1535 | intersect = true; | 1461 | intersect = true; |
1536 | } else if (r) { | 1462 | break; |
1463 | case REG_REQ_OK: | ||
1464 | break; | ||
1465 | default: | ||
1537 | /* | 1466 | /* |
1538 | * If the regulatory domain being requested by the | 1467 | * If the regulatory domain being requested by the |
1539 | * driver has already been set just copy it to the | 1468 | * driver has already been set just copy it to the |
1540 | * wiphy | 1469 | * wiphy |
1541 | */ | 1470 | */ |
1542 | if (r == -EALREADY && | 1471 | if (treatment == REG_REQ_ALREADY_SET && |
1543 | pending_request->initiator == | 1472 | pending_request->initiator == NL80211_REGDOM_SET_BY_DRIVER) { |
1544 | NL80211_REGDOM_SET_BY_DRIVER) { | 1473 | regd = reg_copy_regd(get_cfg80211_regdom()); |
1545 | r = reg_copy_regd(&wiphy->regd, cfg80211_regdomain); | 1474 | if (IS_ERR(regd)) { |
1546 | if (r) { | ||
1547 | kfree(pending_request); | 1475 | kfree(pending_request); |
1548 | return r; | 1476 | return REG_REQ_IGNORE; |
1549 | } | 1477 | } |
1550 | r = -EALREADY; | 1478 | treatment = REG_REQ_ALREADY_SET; |
1479 | rcu_assign_pointer(wiphy->regd, regd); | ||
1551 | goto new_request; | 1480 | goto new_request; |
1552 | } | 1481 | } |
1553 | kfree(pending_request); | 1482 | kfree(pending_request); |
1554 | return r; | 1483 | return treatment; |
1555 | } | 1484 | } |
1556 | 1485 | ||
1557 | new_request: | 1486 | new_request: |
1558 | if (last_request != &core_request_world) | 1487 | lr = get_last_request(); |
1559 | kfree(last_request); | 1488 | if (lr != &core_request_world && lr) |
1489 | kfree_rcu(lr, rcu_head); | ||
1560 | 1490 | ||
1561 | last_request = pending_request; | 1491 | pending_request->intersect = intersect; |
1562 | last_request->intersect = intersect; | 1492 | pending_request->processed = false; |
1493 | rcu_assign_pointer(last_request, pending_request); | ||
1494 | lr = pending_request; | ||
1563 | 1495 | ||
1564 | pending_request = NULL; | 1496 | pending_request = NULL; |
1565 | 1497 | ||
1566 | if (last_request->initiator == NL80211_REGDOM_SET_BY_USER) { | 1498 | if (lr->initiator == NL80211_REGDOM_SET_BY_USER) { |
1567 | user_alpha2[0] = last_request->alpha2[0]; | 1499 | user_alpha2[0] = lr->alpha2[0]; |
1568 | user_alpha2[1] = last_request->alpha2[1]; | 1500 | user_alpha2[1] = lr->alpha2[1]; |
1569 | } | 1501 | } |
1570 | 1502 | ||
1571 | /* When r == REG_INTERSECT we do need to call CRDA */ | 1503 | /* When r == REG_REQ_INTERSECT we do need to call CRDA */ |
1572 | if (r < 0) { | 1504 | if (treatment != REG_REQ_OK && treatment != REG_REQ_INTERSECT) { |
1573 | /* | 1505 | /* |
1574 | * Since CRDA will not be called in this case as we already | 1506 | * Since CRDA will not be called in this case as we already |
1575 | * have applied the requested regulatory domain before we just | 1507 | * have applied the requested regulatory domain before we just |
1576 | * inform userspace we have processed the request | 1508 | * inform userspace we have processed the request |
1577 | */ | 1509 | */ |
1578 | if (r == -EALREADY) { | 1510 | if (treatment == REG_REQ_ALREADY_SET) { |
1579 | nl80211_send_reg_change_event(last_request); | 1511 | nl80211_send_reg_change_event(lr); |
1580 | reg_set_request_processed(); | 1512 | reg_set_request_processed(); |
1581 | } | 1513 | } |
1582 | return r; | 1514 | return treatment; |
1583 | } | 1515 | } |
1584 | 1516 | ||
1585 | return call_crda(last_request->alpha2); | 1517 | if (call_crda(lr->alpha2)) |
1518 | return REG_REQ_IGNORE; | ||
1519 | return REG_REQ_OK; | ||
1586 | } | 1520 | } |
1587 | 1521 | ||
1588 | /* This processes *all* regulatory hints */ | 1522 | /* This processes *all* regulatory hints */ |
1589 | static void reg_process_hint(struct regulatory_request *reg_request, | 1523 | static void reg_process_hint(struct regulatory_request *reg_request, |
1590 | enum nl80211_reg_initiator reg_initiator) | 1524 | enum nl80211_reg_initiator reg_initiator) |
1591 | { | 1525 | { |
1592 | int r = 0; | ||
1593 | struct wiphy *wiphy = NULL; | 1526 | struct wiphy *wiphy = NULL; |
1594 | 1527 | ||
1595 | BUG_ON(!reg_request->alpha2); | 1528 | if (WARN_ON(!reg_request->alpha2)) |
1529 | return; | ||
1596 | 1530 | ||
1597 | if (wiphy_idx_valid(reg_request->wiphy_idx)) | 1531 | if (reg_request->wiphy_idx != WIPHY_IDX_INVALID) |
1598 | wiphy = wiphy_idx_to_wiphy(reg_request->wiphy_idx); | 1532 | wiphy = wiphy_idx_to_wiphy(reg_request->wiphy_idx); |
1599 | 1533 | ||
1600 | if (reg_initiator == NL80211_REGDOM_SET_BY_DRIVER && | 1534 | if (reg_initiator == NL80211_REGDOM_SET_BY_DRIVER && !wiphy) { |
1601 | !wiphy) { | ||
1602 | kfree(reg_request); | 1535 | kfree(reg_request); |
1603 | return; | 1536 | return; |
1604 | } | 1537 | } |
1605 | 1538 | ||
1606 | r = __regulatory_hint(wiphy, reg_request); | 1539 | switch (__regulatory_hint(wiphy, reg_request)) { |
1607 | /* This is required so that the orig_* parameters are saved */ | 1540 | case REG_REQ_ALREADY_SET: |
1608 | if (r == -EALREADY && wiphy && | 1541 | /* This is required so that the orig_* parameters are saved */ |
1609 | wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY) { | 1542 | if (wiphy && wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY) |
1610 | wiphy_update_regulatory(wiphy, reg_initiator); | 1543 | wiphy_update_regulatory(wiphy, reg_initiator); |
1611 | return; | 1544 | break; |
1545 | default: | ||
1546 | if (reg_initiator == NL80211_REGDOM_SET_BY_USER) | ||
1547 | schedule_delayed_work(®_timeout, | ||
1548 | msecs_to_jiffies(3142)); | ||
1549 | break; | ||
1612 | } | 1550 | } |
1613 | |||
1614 | /* | ||
1615 | * We only time out user hints, given that they should be the only | ||
1616 | * source of bogus requests. | ||
1617 | */ | ||
1618 | if (r != -EALREADY && | ||
1619 | reg_initiator == NL80211_REGDOM_SET_BY_USER) | ||
1620 | schedule_delayed_work(®_timeout, msecs_to_jiffies(3142)); | ||
1621 | } | 1551 | } |
1622 | 1552 | ||
1623 | /* | 1553 | /* |
@@ -1627,15 +1557,15 @@ static void reg_process_hint(struct regulatory_request *reg_request, | |||
1627 | */ | 1557 | */ |
1628 | static void reg_process_pending_hints(void) | 1558 | static void reg_process_pending_hints(void) |
1629 | { | 1559 | { |
1630 | struct regulatory_request *reg_request; | 1560 | struct regulatory_request *reg_request, *lr; |
1631 | 1561 | ||
1632 | mutex_lock(&cfg80211_mutex); | 1562 | mutex_lock(&cfg80211_mutex); |
1633 | mutex_lock(®_mutex); | 1563 | mutex_lock(®_mutex); |
1564 | lr = get_last_request(); | ||
1634 | 1565 | ||
1635 | /* When last_request->processed becomes true this will be rescheduled */ | 1566 | /* When last_request->processed becomes true this will be rescheduled */ |
1636 | if (last_request && !last_request->processed) { | 1567 | if (lr && !lr->processed) { |
1637 | REG_DBG_PRINT("Pending regulatory request, waiting " | 1568 | REG_DBG_PRINT("Pending regulatory request, waiting for it to be processed...\n"); |
1638 | "for it to be processed...\n"); | ||
1639 | goto out; | 1569 | goto out; |
1640 | } | 1570 | } |
1641 | 1571 | ||
@@ -1666,23 +1596,14 @@ static void reg_process_pending_beacon_hints(void) | |||
1666 | struct cfg80211_registered_device *rdev; | 1596 | struct cfg80211_registered_device *rdev; |
1667 | struct reg_beacon *pending_beacon, *tmp; | 1597 | struct reg_beacon *pending_beacon, *tmp; |
1668 | 1598 | ||
1669 | /* | ||
1670 | * No need to hold the reg_mutex here as we just touch wiphys | ||
1671 | * and do not read or access regulatory variables. | ||
1672 | */ | ||
1673 | mutex_lock(&cfg80211_mutex); | 1599 | mutex_lock(&cfg80211_mutex); |
1600 | mutex_lock(®_mutex); | ||
1674 | 1601 | ||
1675 | /* This goes through the _pending_ beacon list */ | 1602 | /* This goes through the _pending_ beacon list */ |
1676 | spin_lock_bh(®_pending_beacons_lock); | 1603 | spin_lock_bh(®_pending_beacons_lock); |
1677 | 1604 | ||
1678 | if (list_empty(®_pending_beacons)) { | ||
1679 | spin_unlock_bh(®_pending_beacons_lock); | ||
1680 | goto out; | ||
1681 | } | ||
1682 | |||
1683 | list_for_each_entry_safe(pending_beacon, tmp, | 1605 | list_for_each_entry_safe(pending_beacon, tmp, |
1684 | ®_pending_beacons, list) { | 1606 | ®_pending_beacons, list) { |
1685 | |||
1686 | list_del_init(&pending_beacon->list); | 1607 | list_del_init(&pending_beacon->list); |
1687 | 1608 | ||
1688 | /* Applies the beacon hint to current wiphys */ | 1609 | /* Applies the beacon hint to current wiphys */ |
@@ -1694,7 +1615,7 @@ static void reg_process_pending_beacon_hints(void) | |||
1694 | } | 1615 | } |
1695 | 1616 | ||
1696 | spin_unlock_bh(®_pending_beacons_lock); | 1617 | spin_unlock_bh(®_pending_beacons_lock); |
1697 | out: | 1618 | mutex_unlock(®_mutex); |
1698 | mutex_unlock(&cfg80211_mutex); | 1619 | mutex_unlock(&cfg80211_mutex); |
1699 | } | 1620 | } |
1700 | 1621 | ||
@@ -1706,10 +1627,8 @@ static void reg_todo(struct work_struct *work) | |||
1706 | 1627 | ||
1707 | static void queue_regulatory_request(struct regulatory_request *request) | 1628 | static void queue_regulatory_request(struct regulatory_request *request) |
1708 | { | 1629 | { |
1709 | if (isalpha(request->alpha2[0])) | 1630 | request->alpha2[0] = toupper(request->alpha2[0]); |
1710 | request->alpha2[0] = toupper(request->alpha2[0]); | 1631 | request->alpha2[1] = toupper(request->alpha2[1]); |
1711 | if (isalpha(request->alpha2[1])) | ||
1712 | request->alpha2[1] = toupper(request->alpha2[1]); | ||
1713 | 1632 | ||
1714 | spin_lock(®_requests_lock); | 1633 | spin_lock(®_requests_lock); |
1715 | list_add_tail(&request->list, ®_requests_list); | 1634 | list_add_tail(&request->list, ®_requests_list); |
@@ -1726,8 +1645,7 @@ static int regulatory_hint_core(const char *alpha2) | |||
1726 | { | 1645 | { |
1727 | struct regulatory_request *request; | 1646 | struct regulatory_request *request; |
1728 | 1647 | ||
1729 | request = kzalloc(sizeof(struct regulatory_request), | 1648 | request = kzalloc(sizeof(struct regulatory_request), GFP_KERNEL); |
1730 | GFP_KERNEL); | ||
1731 | if (!request) | 1649 | if (!request) |
1732 | return -ENOMEM; | 1650 | return -ENOMEM; |
1733 | 1651 | ||
@@ -1746,13 +1664,14 @@ int regulatory_hint_user(const char *alpha2, | |||
1746 | { | 1664 | { |
1747 | struct regulatory_request *request; | 1665 | struct regulatory_request *request; |
1748 | 1666 | ||
1749 | BUG_ON(!alpha2); | 1667 | if (WARN_ON(!alpha2)) |
1668 | return -EINVAL; | ||
1750 | 1669 | ||
1751 | request = kzalloc(sizeof(struct regulatory_request), GFP_KERNEL); | 1670 | request = kzalloc(sizeof(struct regulatory_request), GFP_KERNEL); |
1752 | if (!request) | 1671 | if (!request) |
1753 | return -ENOMEM; | 1672 | return -ENOMEM; |
1754 | 1673 | ||
1755 | request->wiphy_idx = WIPHY_IDX_STALE; | 1674 | request->wiphy_idx = WIPHY_IDX_INVALID; |
1756 | request->alpha2[0] = alpha2[0]; | 1675 | request->alpha2[0] = alpha2[0]; |
1757 | request->alpha2[1] = alpha2[1]; | 1676 | request->alpha2[1] = alpha2[1]; |
1758 | request->initiator = NL80211_REGDOM_SET_BY_USER; | 1677 | request->initiator = NL80211_REGDOM_SET_BY_USER; |
@@ -1768,8 +1687,8 @@ int regulatory_hint(struct wiphy *wiphy, const char *alpha2) | |||
1768 | { | 1687 | { |
1769 | struct regulatory_request *request; | 1688 | struct regulatory_request *request; |
1770 | 1689 | ||
1771 | BUG_ON(!alpha2); | 1690 | if (WARN_ON(!alpha2 || !wiphy)) |
1772 | BUG_ON(!wiphy); | 1691 | return -EINVAL; |
1773 | 1692 | ||
1774 | request = kzalloc(sizeof(struct regulatory_request), GFP_KERNEL); | 1693 | request = kzalloc(sizeof(struct regulatory_request), GFP_KERNEL); |
1775 | if (!request) | 1694 | if (!request) |
@@ -1777,9 +1696,6 @@ int regulatory_hint(struct wiphy *wiphy, const char *alpha2) | |||
1777 | 1696 | ||
1778 | request->wiphy_idx = get_wiphy_idx(wiphy); | 1697 | request->wiphy_idx = get_wiphy_idx(wiphy); |
1779 | 1698 | ||
1780 | /* Must have registered wiphy first */ | ||
1781 | BUG_ON(!wiphy_idx_valid(request->wiphy_idx)); | ||
1782 | |||
1783 | request->alpha2[0] = alpha2[0]; | 1699 | request->alpha2[0] = alpha2[0]; |
1784 | request->alpha2[1] = alpha2[1]; | 1700 | request->alpha2[1] = alpha2[1]; |
1785 | request->initiator = NL80211_REGDOM_SET_BY_DRIVER; | 1701 | request->initiator = NL80211_REGDOM_SET_BY_DRIVER; |
@@ -1794,18 +1710,17 @@ EXPORT_SYMBOL(regulatory_hint); | |||
1794 | * We hold wdev_lock() here so we cannot hold cfg80211_mutex() and | 1710 | * We hold wdev_lock() here so we cannot hold cfg80211_mutex() and |
1795 | * therefore cannot iterate over the rdev list here. | 1711 | * therefore cannot iterate over the rdev list here. |
1796 | */ | 1712 | */ |
1797 | void regulatory_hint_11d(struct wiphy *wiphy, | 1713 | void regulatory_hint_11d(struct wiphy *wiphy, enum ieee80211_band band, |
1798 | enum ieee80211_band band, | 1714 | const u8 *country_ie, u8 country_ie_len) |
1799 | const u8 *country_ie, | ||
1800 | u8 country_ie_len) | ||
1801 | { | 1715 | { |
1802 | char alpha2[2]; | 1716 | char alpha2[2]; |
1803 | enum environment_cap env = ENVIRON_ANY; | 1717 | enum environment_cap env = ENVIRON_ANY; |
1804 | struct regulatory_request *request; | 1718 | struct regulatory_request *request, *lr; |
1805 | 1719 | ||
1806 | mutex_lock(®_mutex); | 1720 | mutex_lock(®_mutex); |
1721 | lr = get_last_request(); | ||
1807 | 1722 | ||
1808 | if (unlikely(!last_request)) | 1723 | if (unlikely(!lr)) |
1809 | goto out; | 1724 | goto out; |
1810 | 1725 | ||
1811 | /* IE len must be evenly divisible by 2 */ | 1726 | /* IE len must be evenly divisible by 2 */ |
@@ -1828,9 +1743,8 @@ void regulatory_hint_11d(struct wiphy *wiphy, | |||
1828 | * We leave conflict resolution to the workqueue, where can hold | 1743 | * We leave conflict resolution to the workqueue, where can hold |
1829 | * cfg80211_mutex. | 1744 | * cfg80211_mutex. |
1830 | */ | 1745 | */ |
1831 | if (likely(last_request->initiator == | 1746 | if (lr->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE && |
1832 | NL80211_REGDOM_SET_BY_COUNTRY_IE && | 1747 | lr->wiphy_idx != WIPHY_IDX_INVALID) |
1833 | wiphy_idx_valid(last_request->wiphy_idx))) | ||
1834 | goto out; | 1748 | goto out; |
1835 | 1749 | ||
1836 | request = kzalloc(sizeof(struct regulatory_request), GFP_KERNEL); | 1750 | request = kzalloc(sizeof(struct regulatory_request), GFP_KERNEL); |
@@ -1843,12 +1757,7 @@ void regulatory_hint_11d(struct wiphy *wiphy, | |||
1843 | request->initiator = NL80211_REGDOM_SET_BY_COUNTRY_IE; | 1757 | request->initiator = NL80211_REGDOM_SET_BY_COUNTRY_IE; |
1844 | request->country_ie_env = env; | 1758 | request->country_ie_env = env; |
1845 | 1759 | ||
1846 | mutex_unlock(®_mutex); | ||
1847 | |||
1848 | queue_regulatory_request(request); | 1760 | queue_regulatory_request(request); |
1849 | |||
1850 | return; | ||
1851 | |||
1852 | out: | 1761 | out: |
1853 | mutex_unlock(®_mutex); | 1762 | mutex_unlock(®_mutex); |
1854 | } | 1763 | } |
@@ -1863,8 +1772,7 @@ static void restore_alpha2(char *alpha2, bool reset_user) | |||
1863 | if (is_user_regdom_saved()) { | 1772 | if (is_user_regdom_saved()) { |
1864 | /* Unless we're asked to ignore it and reset it */ | 1773 | /* Unless we're asked to ignore it and reset it */ |
1865 | if (reset_user) { | 1774 | if (reset_user) { |
1866 | REG_DBG_PRINT("Restoring regulatory settings " | 1775 | REG_DBG_PRINT("Restoring regulatory settings including user preference\n"); |
1867 | "including user preference\n"); | ||
1868 | user_alpha2[0] = '9'; | 1776 | user_alpha2[0] = '9'; |
1869 | user_alpha2[1] = '7'; | 1777 | user_alpha2[1] = '7'; |
1870 | 1778 | ||
@@ -1874,26 +1782,20 @@ static void restore_alpha2(char *alpha2, bool reset_user) | |||
1874 | * back as they were for a full restore. | 1782 | * back as they were for a full restore. |
1875 | */ | 1783 | */ |
1876 | if (!is_world_regdom(ieee80211_regdom)) { | 1784 | if (!is_world_regdom(ieee80211_regdom)) { |
1877 | REG_DBG_PRINT("Keeping preference on " | 1785 | REG_DBG_PRINT("Keeping preference on module parameter ieee80211_regdom: %c%c\n", |
1878 | "module parameter ieee80211_regdom: %c%c\n", | 1786 | ieee80211_regdom[0], ieee80211_regdom[1]); |
1879 | ieee80211_regdom[0], | ||
1880 | ieee80211_regdom[1]); | ||
1881 | alpha2[0] = ieee80211_regdom[0]; | 1787 | alpha2[0] = ieee80211_regdom[0]; |
1882 | alpha2[1] = ieee80211_regdom[1]; | 1788 | alpha2[1] = ieee80211_regdom[1]; |
1883 | } | 1789 | } |
1884 | } else { | 1790 | } else { |
1885 | REG_DBG_PRINT("Restoring regulatory settings " | 1791 | REG_DBG_PRINT("Restoring regulatory settings while preserving user preference for: %c%c\n", |
1886 | "while preserving user preference for: %c%c\n", | 1792 | user_alpha2[0], user_alpha2[1]); |
1887 | user_alpha2[0], | ||
1888 | user_alpha2[1]); | ||
1889 | alpha2[0] = user_alpha2[0]; | 1793 | alpha2[0] = user_alpha2[0]; |
1890 | alpha2[1] = user_alpha2[1]; | 1794 | alpha2[1] = user_alpha2[1]; |
1891 | } | 1795 | } |
1892 | } else if (!is_world_regdom(ieee80211_regdom)) { | 1796 | } else if (!is_world_regdom(ieee80211_regdom)) { |
1893 | REG_DBG_PRINT("Keeping preference on " | 1797 | REG_DBG_PRINT("Keeping preference on module parameter ieee80211_regdom: %c%c\n", |
1894 | "module parameter ieee80211_regdom: %c%c\n", | 1798 | ieee80211_regdom[0], ieee80211_regdom[1]); |
1895 | ieee80211_regdom[0], | ||
1896 | ieee80211_regdom[1]); | ||
1897 | alpha2[0] = ieee80211_regdom[0]; | 1799 | alpha2[0] = ieee80211_regdom[0]; |
1898 | alpha2[1] = ieee80211_regdom[1]; | 1800 | alpha2[1] = ieee80211_regdom[1]; |
1899 | } else | 1801 | } else |
@@ -1948,7 +1850,7 @@ static void restore_regulatory_settings(bool reset_user) | |||
1948 | mutex_lock(&cfg80211_mutex); | 1850 | mutex_lock(&cfg80211_mutex); |
1949 | mutex_lock(®_mutex); | 1851 | mutex_lock(®_mutex); |
1950 | 1852 | ||
1951 | reset_regdomains(true); | 1853 | reset_regdomains(true, &world_regdom); |
1952 | restore_alpha2(alpha2, reset_user); | 1854 | restore_alpha2(alpha2, reset_user); |
1953 | 1855 | ||
1954 | /* | 1856 | /* |
@@ -1958,49 +1860,35 @@ static void restore_regulatory_settings(bool reset_user) | |||
1958 | * settings. | 1860 | * settings. |
1959 | */ | 1861 | */ |
1960 | spin_lock(®_requests_lock); | 1862 | spin_lock(®_requests_lock); |
1961 | if (!list_empty(®_requests_list)) { | 1863 | list_for_each_entry_safe(reg_request, tmp, ®_requests_list, list) { |
1962 | list_for_each_entry_safe(reg_request, tmp, | 1864 | if (reg_request->initiator != NL80211_REGDOM_SET_BY_USER) |
1963 | ®_requests_list, list) { | 1865 | continue; |
1964 | if (reg_request->initiator != | 1866 | list_move_tail(®_request->list, &tmp_reg_req_list); |
1965 | NL80211_REGDOM_SET_BY_USER) | ||
1966 | continue; | ||
1967 | list_move_tail(®_request->list, &tmp_reg_req_list); | ||
1968 | } | ||
1969 | } | 1867 | } |
1970 | spin_unlock(®_requests_lock); | 1868 | spin_unlock(®_requests_lock); |
1971 | 1869 | ||
1972 | /* Clear beacon hints */ | 1870 | /* Clear beacon hints */ |
1973 | spin_lock_bh(®_pending_beacons_lock); | 1871 | spin_lock_bh(®_pending_beacons_lock); |
1974 | if (!list_empty(®_pending_beacons)) { | 1872 | list_for_each_entry_safe(reg_beacon, btmp, ®_pending_beacons, list) { |
1975 | list_for_each_entry_safe(reg_beacon, btmp, | 1873 | list_del(®_beacon->list); |
1976 | ®_pending_beacons, list) { | 1874 | kfree(reg_beacon); |
1977 | list_del(®_beacon->list); | ||
1978 | kfree(reg_beacon); | ||
1979 | } | ||
1980 | } | 1875 | } |
1981 | spin_unlock_bh(®_pending_beacons_lock); | 1876 | spin_unlock_bh(®_pending_beacons_lock); |
1982 | 1877 | ||
1983 | if (!list_empty(®_beacon_list)) { | 1878 | list_for_each_entry_safe(reg_beacon, btmp, ®_beacon_list, list) { |
1984 | list_for_each_entry_safe(reg_beacon, btmp, | 1879 | list_del(®_beacon->list); |
1985 | ®_beacon_list, list) { | 1880 | kfree(reg_beacon); |
1986 | list_del(®_beacon->list); | ||
1987 | kfree(reg_beacon); | ||
1988 | } | ||
1989 | } | 1881 | } |
1990 | 1882 | ||
1991 | /* First restore to the basic regulatory settings */ | 1883 | /* First restore to the basic regulatory settings */ |
1992 | cfg80211_regdomain = cfg80211_world_regdom; | 1884 | world_alpha2[0] = cfg80211_world_regdom->alpha2[0]; |
1993 | world_alpha2[0] = cfg80211_regdomain->alpha2[0]; | 1885 | world_alpha2[1] = cfg80211_world_regdom->alpha2[1]; |
1994 | world_alpha2[1] = cfg80211_regdomain->alpha2[1]; | ||
1995 | 1886 | ||
1996 | list_for_each_entry(rdev, &cfg80211_rdev_list, list) { | 1887 | list_for_each_entry(rdev, &cfg80211_rdev_list, list) { |
1997 | if (rdev->wiphy.flags & WIPHY_FLAG_CUSTOM_REGULATORY) | 1888 | if (rdev->wiphy.flags & WIPHY_FLAG_CUSTOM_REGULATORY) |
1998 | restore_custom_reg_settings(&rdev->wiphy); | 1889 | restore_custom_reg_settings(&rdev->wiphy); |
1999 | } | 1890 | } |
2000 | 1891 | ||
2001 | mutex_unlock(®_mutex); | ||
2002 | mutex_unlock(&cfg80211_mutex); | ||
2003 | |||
2004 | regulatory_hint_core(world_alpha2); | 1892 | regulatory_hint_core(world_alpha2); |
2005 | 1893 | ||
2006 | /* | 1894 | /* |
@@ -2011,20 +1899,8 @@ static void restore_regulatory_settings(bool reset_user) | |||
2011 | if (is_an_alpha2(alpha2)) | 1899 | if (is_an_alpha2(alpha2)) |
2012 | regulatory_hint_user(user_alpha2, NL80211_USER_REG_HINT_USER); | 1900 | regulatory_hint_user(user_alpha2, NL80211_USER_REG_HINT_USER); |
2013 | 1901 | ||
2014 | if (list_empty(&tmp_reg_req_list)) | ||
2015 | return; | ||
2016 | |||
2017 | mutex_lock(&cfg80211_mutex); | ||
2018 | mutex_lock(®_mutex); | ||
2019 | |||
2020 | spin_lock(®_requests_lock); | 1902 | spin_lock(®_requests_lock); |
2021 | list_for_each_entry_safe(reg_request, tmp, &tmp_reg_req_list, list) { | 1903 | list_splice_tail_init(&tmp_reg_req_list, ®_requests_list); |
2022 | REG_DBG_PRINT("Adding request for country %c%c back " | ||
2023 | "into the queue\n", | ||
2024 | reg_request->alpha2[0], | ||
2025 | reg_request->alpha2[1]); | ||
2026 | list_move_tail(®_request->list, ®_requests_list); | ||
2027 | } | ||
2028 | spin_unlock(®_requests_lock); | 1904 | spin_unlock(®_requests_lock); |
2029 | 1905 | ||
2030 | mutex_unlock(®_mutex); | 1906 | mutex_unlock(®_mutex); |
@@ -2037,8 +1913,7 @@ static void restore_regulatory_settings(bool reset_user) | |||
2037 | 1913 | ||
2038 | void regulatory_hint_disconnect(void) | 1914 | void regulatory_hint_disconnect(void) |
2039 | { | 1915 | { |
2040 | REG_DBG_PRINT("All devices are disconnected, going to " | 1916 | REG_DBG_PRINT("All devices are disconnected, going to restore regulatory settings\n"); |
2041 | "restore regulatory settings\n"); | ||
2042 | restore_regulatory_settings(false); | 1917 | restore_regulatory_settings(false); |
2043 | } | 1918 | } |
2044 | 1919 | ||
@@ -2051,31 +1926,48 @@ static bool freq_is_chan_12_13_14(u16 freq) | |||
2051 | return false; | 1926 | return false; |
2052 | } | 1927 | } |
2053 | 1928 | ||
1929 | static bool pending_reg_beacon(struct ieee80211_channel *beacon_chan) | ||
1930 | { | ||
1931 | struct reg_beacon *pending_beacon; | ||
1932 | |||
1933 | list_for_each_entry(pending_beacon, ®_pending_beacons, list) | ||
1934 | if (beacon_chan->center_freq == | ||
1935 | pending_beacon->chan.center_freq) | ||
1936 | return true; | ||
1937 | return false; | ||
1938 | } | ||
1939 | |||
2054 | int regulatory_hint_found_beacon(struct wiphy *wiphy, | 1940 | int regulatory_hint_found_beacon(struct wiphy *wiphy, |
2055 | struct ieee80211_channel *beacon_chan, | 1941 | struct ieee80211_channel *beacon_chan, |
2056 | gfp_t gfp) | 1942 | gfp_t gfp) |
2057 | { | 1943 | { |
2058 | struct reg_beacon *reg_beacon; | 1944 | struct reg_beacon *reg_beacon; |
1945 | bool processing; | ||
2059 | 1946 | ||
2060 | if (likely((beacon_chan->beacon_found || | 1947 | if (beacon_chan->beacon_found || |
2061 | (beacon_chan->flags & IEEE80211_CHAN_RADAR) || | 1948 | beacon_chan->flags & IEEE80211_CHAN_RADAR || |
2062 | (beacon_chan->band == IEEE80211_BAND_2GHZ && | 1949 | (beacon_chan->band == IEEE80211_BAND_2GHZ && |
2063 | !freq_is_chan_12_13_14(beacon_chan->center_freq))))) | 1950 | !freq_is_chan_12_13_14(beacon_chan->center_freq))) |
1951 | return 0; | ||
1952 | |||
1953 | spin_lock_bh(®_pending_beacons_lock); | ||
1954 | processing = pending_reg_beacon(beacon_chan); | ||
1955 | spin_unlock_bh(®_pending_beacons_lock); | ||
1956 | |||
1957 | if (processing) | ||
2064 | return 0; | 1958 | return 0; |
2065 | 1959 | ||
2066 | reg_beacon = kzalloc(sizeof(struct reg_beacon), gfp); | 1960 | reg_beacon = kzalloc(sizeof(struct reg_beacon), gfp); |
2067 | if (!reg_beacon) | 1961 | if (!reg_beacon) |
2068 | return -ENOMEM; | 1962 | return -ENOMEM; |
2069 | 1963 | ||
2070 | REG_DBG_PRINT("Found new beacon on " | 1964 | REG_DBG_PRINT("Found new beacon on frequency: %d MHz (Ch %d) on %s\n", |
2071 | "frequency: %d MHz (Ch %d) on %s\n", | ||
2072 | beacon_chan->center_freq, | 1965 | beacon_chan->center_freq, |
2073 | ieee80211_frequency_to_channel(beacon_chan->center_freq), | 1966 | ieee80211_frequency_to_channel(beacon_chan->center_freq), |
2074 | wiphy_name(wiphy)); | 1967 | wiphy_name(wiphy)); |
2075 | 1968 | ||
2076 | memcpy(®_beacon->chan, beacon_chan, | 1969 | memcpy(®_beacon->chan, beacon_chan, |
2077 | sizeof(struct ieee80211_channel)); | 1970 | sizeof(struct ieee80211_channel)); |
2078 | |||
2079 | 1971 | ||
2080 | /* | 1972 | /* |
2081 | * Since we can be called from BH or and non-BH context | 1973 | * Since we can be called from BH or and non-BH context |
@@ -2155,21 +2047,19 @@ static void print_dfs_region(u8 dfs_region) | |||
2155 | pr_info(" DFS Master region JP"); | 2047 | pr_info(" DFS Master region JP"); |
2156 | break; | 2048 | break; |
2157 | default: | 2049 | default: |
2158 | pr_info(" DFS Master region Uknown"); | 2050 | pr_info(" DFS Master region Unknown"); |
2159 | break; | 2051 | break; |
2160 | } | 2052 | } |
2161 | } | 2053 | } |
2162 | 2054 | ||
2163 | static void print_regdomain(const struct ieee80211_regdomain *rd) | 2055 | static void print_regdomain(const struct ieee80211_regdomain *rd) |
2164 | { | 2056 | { |
2057 | struct regulatory_request *lr = get_last_request(); | ||
2165 | 2058 | ||
2166 | if (is_intersected_alpha2(rd->alpha2)) { | 2059 | if (is_intersected_alpha2(rd->alpha2)) { |
2167 | 2060 | if (lr->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) { | |
2168 | if (last_request->initiator == | ||
2169 | NL80211_REGDOM_SET_BY_COUNTRY_IE) { | ||
2170 | struct cfg80211_registered_device *rdev; | 2061 | struct cfg80211_registered_device *rdev; |
2171 | rdev = cfg80211_rdev_by_wiphy_idx( | 2062 | rdev = cfg80211_rdev_by_wiphy_idx(lr->wiphy_idx); |
2172 | last_request->wiphy_idx); | ||
2173 | if (rdev) { | 2063 | if (rdev) { |
2174 | pr_info("Current regulatory domain updated by AP to: %c%c\n", | 2064 | pr_info("Current regulatory domain updated by AP to: %c%c\n", |
2175 | rdev->country_ie_alpha2[0], | 2065 | rdev->country_ie_alpha2[0], |
@@ -2178,22 +2068,21 @@ static void print_regdomain(const struct ieee80211_regdomain *rd) | |||
2178 | pr_info("Current regulatory domain intersected:\n"); | 2068 | pr_info("Current regulatory domain intersected:\n"); |
2179 | } else | 2069 | } else |
2180 | pr_info("Current regulatory domain intersected:\n"); | 2070 | pr_info("Current regulatory domain intersected:\n"); |
2181 | } else if (is_world_regdom(rd->alpha2)) | 2071 | } else if (is_world_regdom(rd->alpha2)) { |
2182 | pr_info("World regulatory domain updated:\n"); | 2072 | pr_info("World regulatory domain updated:\n"); |
2183 | else { | 2073 | } else { |
2184 | if (is_unknown_alpha2(rd->alpha2)) | 2074 | if (is_unknown_alpha2(rd->alpha2)) |
2185 | pr_info("Regulatory domain changed to driver built-in settings (unknown country)\n"); | 2075 | pr_info("Regulatory domain changed to driver built-in settings (unknown country)\n"); |
2186 | else { | 2076 | else { |
2187 | if (reg_request_cell_base(last_request)) | 2077 | if (reg_request_cell_base(lr)) |
2188 | pr_info("Regulatory domain changed " | 2078 | pr_info("Regulatory domain changed to country: %c%c by Cell Station\n", |
2189 | "to country: %c%c by Cell Station\n", | ||
2190 | rd->alpha2[0], rd->alpha2[1]); | 2079 | rd->alpha2[0], rd->alpha2[1]); |
2191 | else | 2080 | else |
2192 | pr_info("Regulatory domain changed " | 2081 | pr_info("Regulatory domain changed to country: %c%c\n", |
2193 | "to country: %c%c\n", | ||
2194 | rd->alpha2[0], rd->alpha2[1]); | 2082 | rd->alpha2[0], rd->alpha2[1]); |
2195 | } | 2083 | } |
2196 | } | 2084 | } |
2085 | |||
2197 | print_dfs_region(rd->dfs_region); | 2086 | print_dfs_region(rd->dfs_region); |
2198 | print_rd_rules(rd); | 2087 | print_rd_rules(rd); |
2199 | } | 2088 | } |
@@ -2207,22 +2096,23 @@ static void print_regdomain_info(const struct ieee80211_regdomain *rd) | |||
2207 | /* Takes ownership of rd only if it doesn't fail */ | 2096 | /* Takes ownership of rd only if it doesn't fail */ |
2208 | static int __set_regdom(const struct ieee80211_regdomain *rd) | 2097 | static int __set_regdom(const struct ieee80211_regdomain *rd) |
2209 | { | 2098 | { |
2099 | const struct ieee80211_regdomain *regd; | ||
2210 | const struct ieee80211_regdomain *intersected_rd = NULL; | 2100 | const struct ieee80211_regdomain *intersected_rd = NULL; |
2211 | struct wiphy *request_wiphy; | 2101 | struct wiphy *request_wiphy; |
2102 | struct regulatory_request *lr = get_last_request(); | ||
2103 | |||
2212 | /* Some basic sanity checks first */ | 2104 | /* Some basic sanity checks first */ |
2213 | 2105 | ||
2106 | if (!reg_is_valid_request(rd->alpha2)) | ||
2107 | return -EINVAL; | ||
2108 | |||
2214 | if (is_world_regdom(rd->alpha2)) { | 2109 | if (is_world_regdom(rd->alpha2)) { |
2215 | if (WARN_ON(!reg_is_valid_request(rd->alpha2))) | ||
2216 | return -EINVAL; | ||
2217 | update_world_regdomain(rd); | 2110 | update_world_regdomain(rd); |
2218 | return 0; | 2111 | return 0; |
2219 | } | 2112 | } |
2220 | 2113 | ||
2221 | if (!is_alpha2_set(rd->alpha2) && !is_an_alpha2(rd->alpha2) && | 2114 | if (!is_alpha2_set(rd->alpha2) && !is_an_alpha2(rd->alpha2) && |
2222 | !is_unknown_alpha2(rd->alpha2)) | 2115 | !is_unknown_alpha2(rd->alpha2)) |
2223 | return -EINVAL; | ||
2224 | |||
2225 | if (!last_request) | ||
2226 | return -EINVAL; | 2116 | return -EINVAL; |
2227 | 2117 | ||
2228 | /* | 2118 | /* |
@@ -2230,7 +2120,7 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) | |||
2230 | * rd is non static (it means CRDA was present and was used last) | 2120 | * rd is non static (it means CRDA was present and was used last) |
2231 | * and the pending request came in from a country IE | 2121 | * and the pending request came in from a country IE |
2232 | */ | 2122 | */ |
2233 | if (last_request->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE) { | 2123 | if (lr->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE) { |
2234 | /* | 2124 | /* |
2235 | * If someone else asked us to change the rd lets only bother | 2125 | * If someone else asked us to change the rd lets only bother |
2236 | * checking if the alpha2 changes if CRDA was already called | 2126 | * checking if the alpha2 changes if CRDA was already called |
@@ -2246,29 +2136,23 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) | |||
2246 | * internal EEPROM data | 2136 | * internal EEPROM data |
2247 | */ | 2137 | */ |
2248 | 2138 | ||
2249 | if (WARN_ON(!reg_is_valid_request(rd->alpha2))) | ||
2250 | return -EINVAL; | ||
2251 | |||
2252 | if (!is_valid_rd(rd)) { | 2139 | if (!is_valid_rd(rd)) { |
2253 | pr_err("Invalid regulatory domain detected:\n"); | 2140 | pr_err("Invalid regulatory domain detected:\n"); |
2254 | print_regdomain_info(rd); | 2141 | print_regdomain_info(rd); |
2255 | return -EINVAL; | 2142 | return -EINVAL; |
2256 | } | 2143 | } |
2257 | 2144 | ||
2258 | request_wiphy = wiphy_idx_to_wiphy(last_request->wiphy_idx); | 2145 | request_wiphy = wiphy_idx_to_wiphy(lr->wiphy_idx); |
2259 | if (!request_wiphy && | 2146 | if (!request_wiphy && |
2260 | (last_request->initiator == NL80211_REGDOM_SET_BY_DRIVER || | 2147 | (lr->initiator == NL80211_REGDOM_SET_BY_DRIVER || |
2261 | last_request->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE)) { | 2148 | lr->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE)) { |
2262 | schedule_delayed_work(®_timeout, 0); | 2149 | schedule_delayed_work(®_timeout, 0); |
2263 | return -ENODEV; | 2150 | return -ENODEV; |
2264 | } | 2151 | } |
2265 | 2152 | ||
2266 | if (!last_request->intersect) { | 2153 | if (!lr->intersect) { |
2267 | int r; | 2154 | if (lr->initiator != NL80211_REGDOM_SET_BY_DRIVER) { |
2268 | 2155 | reset_regdomains(false, rd); | |
2269 | if (last_request->initiator != NL80211_REGDOM_SET_BY_DRIVER) { | ||
2270 | reset_regdomains(false); | ||
2271 | cfg80211_regdomain = rd; | ||
2272 | return 0; | 2156 | return 0; |
2273 | } | 2157 | } |
2274 | 2158 | ||
@@ -2284,20 +2168,19 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) | |||
2284 | if (request_wiphy->regd) | 2168 | if (request_wiphy->regd) |
2285 | return -EALREADY; | 2169 | return -EALREADY; |
2286 | 2170 | ||
2287 | r = reg_copy_regd(&request_wiphy->regd, rd); | 2171 | regd = reg_copy_regd(rd); |
2288 | if (r) | 2172 | if (IS_ERR(regd)) |
2289 | return r; | 2173 | return PTR_ERR(regd); |
2290 | 2174 | ||
2291 | reset_regdomains(false); | 2175 | rcu_assign_pointer(request_wiphy->regd, regd); |
2292 | cfg80211_regdomain = rd; | 2176 | reset_regdomains(false, rd); |
2293 | return 0; | 2177 | return 0; |
2294 | } | 2178 | } |
2295 | 2179 | ||
2296 | /* Intersection requires a bit more work */ | 2180 | /* Intersection requires a bit more work */ |
2297 | 2181 | ||
2298 | if (last_request->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE) { | 2182 | if (lr->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE) { |
2299 | 2183 | intersected_rd = regdom_intersect(rd, get_cfg80211_regdom()); | |
2300 | intersected_rd = regdom_intersect(rd, cfg80211_regdomain); | ||
2301 | if (!intersected_rd) | 2184 | if (!intersected_rd) |
2302 | return -EINVAL; | 2185 | return -EINVAL; |
2303 | 2186 | ||
@@ -2306,15 +2189,14 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) | |||
2306 | * However if a driver requested this specific regulatory | 2189 | * However if a driver requested this specific regulatory |
2307 | * domain we keep it for its private use | 2190 | * domain we keep it for its private use |
2308 | */ | 2191 | */ |
2309 | if (last_request->initiator == NL80211_REGDOM_SET_BY_DRIVER) | 2192 | if (lr->initiator == NL80211_REGDOM_SET_BY_DRIVER) |
2310 | request_wiphy->regd = rd; | 2193 | rcu_assign_pointer(request_wiphy->regd, rd); |
2311 | else | 2194 | else |
2312 | kfree(rd); | 2195 | kfree(rd); |
2313 | 2196 | ||
2314 | rd = NULL; | 2197 | rd = NULL; |
2315 | 2198 | ||
2316 | reset_regdomains(false); | 2199 | reset_regdomains(false, intersected_rd); |
2317 | cfg80211_regdomain = intersected_rd; | ||
2318 | 2200 | ||
2319 | return 0; | 2201 | return 0; |
2320 | } | 2202 | } |
@@ -2326,15 +2208,15 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) | |||
2326 | /* | 2208 | /* |
2327 | * Use this call to set the current regulatory domain. Conflicts with | 2209 | * Use this call to set the current regulatory domain. Conflicts with |
2328 | * multiple drivers can be ironed out later. Caller must've already | 2210 | * multiple drivers can be ironed out later. Caller must've already |
2329 | * kmalloc'd the rd structure. Caller must hold cfg80211_mutex | 2211 | * kmalloc'd the rd structure. |
2330 | */ | 2212 | */ |
2331 | int set_regdom(const struct ieee80211_regdomain *rd) | 2213 | int set_regdom(const struct ieee80211_regdomain *rd) |
2332 | { | 2214 | { |
2215 | struct regulatory_request *lr; | ||
2333 | int r; | 2216 | int r; |
2334 | 2217 | ||
2335 | assert_cfg80211_lock(); | ||
2336 | |||
2337 | mutex_lock(®_mutex); | 2218 | mutex_lock(®_mutex); |
2219 | lr = get_last_request(); | ||
2338 | 2220 | ||
2339 | /* Note that this doesn't update the wiphys, this is done below */ | 2221 | /* Note that this doesn't update the wiphys, this is done below */ |
2340 | r = __set_regdom(rd); | 2222 | r = __set_regdom(rd); |
@@ -2343,23 +2225,25 @@ int set_regdom(const struct ieee80211_regdomain *rd) | |||
2343 | reg_set_request_processed(); | 2225 | reg_set_request_processed(); |
2344 | 2226 | ||
2345 | kfree(rd); | 2227 | kfree(rd); |
2346 | mutex_unlock(®_mutex); | 2228 | goto out; |
2347 | return r; | ||
2348 | } | 2229 | } |
2349 | 2230 | ||
2350 | /* This would make this whole thing pointless */ | 2231 | /* This would make this whole thing pointless */ |
2351 | if (!last_request->intersect) | 2232 | if (WARN_ON(!lr->intersect && rd != get_cfg80211_regdom())) { |
2352 | BUG_ON(rd != cfg80211_regdomain); | 2233 | r = -EINVAL; |
2234 | goto out; | ||
2235 | } | ||
2353 | 2236 | ||
2354 | /* update all wiphys now with the new established regulatory domain */ | 2237 | /* update all wiphys now with the new established regulatory domain */ |
2355 | update_all_wiphy_regulatory(last_request->initiator); | 2238 | update_all_wiphy_regulatory(lr->initiator); |
2356 | 2239 | ||
2357 | print_regdomain(cfg80211_regdomain); | 2240 | print_regdomain(get_cfg80211_regdom()); |
2358 | 2241 | ||
2359 | nl80211_send_reg_change_event(last_request); | 2242 | nl80211_send_reg_change_event(lr); |
2360 | 2243 | ||
2361 | reg_set_request_processed(); | 2244 | reg_set_request_processed(); |
2362 | 2245 | ||
2246 | out: | ||
2363 | mutex_unlock(®_mutex); | 2247 | mutex_unlock(®_mutex); |
2364 | 2248 | ||
2365 | return r; | 2249 | return r; |
@@ -2367,20 +2251,26 @@ int set_regdom(const struct ieee80211_regdomain *rd) | |||
2367 | 2251 | ||
2368 | int reg_device_uevent(struct device *dev, struct kobj_uevent_env *env) | 2252 | int reg_device_uevent(struct device *dev, struct kobj_uevent_env *env) |
2369 | { | 2253 | { |
2370 | if (last_request && !last_request->processed) { | 2254 | struct regulatory_request *lr; |
2371 | if (add_uevent_var(env, "COUNTRY=%c%c", | 2255 | u8 alpha2[2]; |
2372 | last_request->alpha2[0], | 2256 | bool add = false; |
2373 | last_request->alpha2[1])) | 2257 | |
2374 | return -ENOMEM; | 2258 | rcu_read_lock(); |
2259 | lr = get_last_request(); | ||
2260 | if (lr && !lr->processed) { | ||
2261 | memcpy(alpha2, lr->alpha2, 2); | ||
2262 | add = true; | ||
2375 | } | 2263 | } |
2264 | rcu_read_unlock(); | ||
2376 | 2265 | ||
2266 | if (add) | ||
2267 | return add_uevent_var(env, "COUNTRY=%c%c", | ||
2268 | alpha2[0], alpha2[1]); | ||
2377 | return 0; | 2269 | return 0; |
2378 | } | 2270 | } |
2379 | 2271 | ||
2380 | void wiphy_regulatory_register(struct wiphy *wiphy) | 2272 | void wiphy_regulatory_register(struct wiphy *wiphy) |
2381 | { | 2273 | { |
2382 | assert_cfg80211_lock(); | ||
2383 | |||
2384 | mutex_lock(®_mutex); | 2274 | mutex_lock(®_mutex); |
2385 | 2275 | ||
2386 | if (!reg_dev_ignore_cell_hint(wiphy)) | 2276 | if (!reg_dev_ignore_cell_hint(wiphy)) |
@@ -2395,32 +2285,32 @@ void wiphy_regulatory_register(struct wiphy *wiphy) | |||
2395 | void wiphy_regulatory_deregister(struct wiphy *wiphy) | 2285 | void wiphy_regulatory_deregister(struct wiphy *wiphy) |
2396 | { | 2286 | { |
2397 | struct wiphy *request_wiphy = NULL; | 2287 | struct wiphy *request_wiphy = NULL; |
2398 | 2288 | struct regulatory_request *lr; | |
2399 | assert_cfg80211_lock(); | ||
2400 | 2289 | ||
2401 | mutex_lock(®_mutex); | 2290 | mutex_lock(®_mutex); |
2291 | lr = get_last_request(); | ||
2402 | 2292 | ||
2403 | if (!reg_dev_ignore_cell_hint(wiphy)) | 2293 | if (!reg_dev_ignore_cell_hint(wiphy)) |
2404 | reg_num_devs_support_basehint--; | 2294 | reg_num_devs_support_basehint--; |
2405 | 2295 | ||
2406 | kfree(wiphy->regd); | 2296 | rcu_free_regdom(get_wiphy_regdom(wiphy)); |
2297 | rcu_assign_pointer(wiphy->regd, NULL); | ||
2407 | 2298 | ||
2408 | if (last_request) | 2299 | if (lr) |
2409 | request_wiphy = wiphy_idx_to_wiphy(last_request->wiphy_idx); | 2300 | request_wiphy = wiphy_idx_to_wiphy(lr->wiphy_idx); |
2410 | 2301 | ||
2411 | if (!request_wiphy || request_wiphy != wiphy) | 2302 | if (!request_wiphy || request_wiphy != wiphy) |
2412 | goto out; | 2303 | goto out; |
2413 | 2304 | ||
2414 | last_request->wiphy_idx = WIPHY_IDX_STALE; | 2305 | lr->wiphy_idx = WIPHY_IDX_INVALID; |
2415 | last_request->country_ie_env = ENVIRON_ANY; | 2306 | lr->country_ie_env = ENVIRON_ANY; |
2416 | out: | 2307 | out: |
2417 | mutex_unlock(®_mutex); | 2308 | mutex_unlock(®_mutex); |
2418 | } | 2309 | } |
2419 | 2310 | ||
2420 | static void reg_timeout_work(struct work_struct *work) | 2311 | static void reg_timeout_work(struct work_struct *work) |
2421 | { | 2312 | { |
2422 | REG_DBG_PRINT("Timeout while waiting for CRDA to reply, " | 2313 | REG_DBG_PRINT("Timeout while waiting for CRDA to reply, restoring regulatory settings\n"); |
2423 | "restoring regulatory settings\n"); | ||
2424 | restore_regulatory_settings(true); | 2314 | restore_regulatory_settings(true); |
2425 | } | 2315 | } |
2426 | 2316 | ||
@@ -2439,13 +2329,13 @@ int __init regulatory_init(void) | |||
2439 | 2329 | ||
2440 | reg_regdb_size_check(); | 2330 | reg_regdb_size_check(); |
2441 | 2331 | ||
2442 | cfg80211_regdomain = cfg80211_world_regdom; | 2332 | rcu_assign_pointer(cfg80211_regdomain, cfg80211_world_regdom); |
2443 | 2333 | ||
2444 | user_alpha2[0] = '9'; | 2334 | user_alpha2[0] = '9'; |
2445 | user_alpha2[1] = '7'; | 2335 | user_alpha2[1] = '7'; |
2446 | 2336 | ||
2447 | /* We always try to get an update for the static regdomain */ | 2337 | /* We always try to get an update for the static regdomain */ |
2448 | err = regulatory_hint_core(cfg80211_regdomain->alpha2); | 2338 | err = regulatory_hint_core(cfg80211_world_regdom->alpha2); |
2449 | if (err) { | 2339 | if (err) { |
2450 | if (err == -ENOMEM) | 2340 | if (err == -ENOMEM) |
2451 | return err; | 2341 | return err; |
@@ -2457,10 +2347,6 @@ int __init regulatory_init(void) | |||
2457 | * errors as non-fatal. | 2347 | * errors as non-fatal. |
2458 | */ | 2348 | */ |
2459 | pr_err("kobject_uevent_env() was unable to call CRDA during init\n"); | 2349 | pr_err("kobject_uevent_env() was unable to call CRDA during init\n"); |
2460 | #ifdef CONFIG_CFG80211_REG_DEBUG | ||
2461 | /* We want to find out exactly why when debugging */ | ||
2462 | WARN_ON(err); | ||
2463 | #endif | ||
2464 | } | 2350 | } |
2465 | 2351 | ||
2466 | /* | 2352 | /* |
@@ -2474,7 +2360,7 @@ int __init regulatory_init(void) | |||
2474 | return 0; | 2360 | return 0; |
2475 | } | 2361 | } |
2476 | 2362 | ||
2477 | void /* __init_or_exit */ regulatory_exit(void) | 2363 | void regulatory_exit(void) |
2478 | { | 2364 | { |
2479 | struct regulatory_request *reg_request, *tmp; | 2365 | struct regulatory_request *reg_request, *tmp; |
2480 | struct reg_beacon *reg_beacon, *btmp; | 2366 | struct reg_beacon *reg_beacon, *btmp; |
@@ -2482,43 +2368,27 @@ void /* __init_or_exit */ regulatory_exit(void) | |||
2482 | cancel_work_sync(®_work); | 2368 | cancel_work_sync(®_work); |
2483 | cancel_delayed_work_sync(®_timeout); | 2369 | cancel_delayed_work_sync(®_timeout); |
2484 | 2370 | ||
2485 | mutex_lock(&cfg80211_mutex); | 2371 | /* Lock to suppress warnings */ |
2486 | mutex_lock(®_mutex); | 2372 | mutex_lock(®_mutex); |
2487 | 2373 | reset_regdomains(true, NULL); | |
2488 | reset_regdomains(true); | 2374 | mutex_unlock(®_mutex); |
2489 | 2375 | ||
2490 | dev_set_uevent_suppress(®_pdev->dev, true); | 2376 | dev_set_uevent_suppress(®_pdev->dev, true); |
2491 | 2377 | ||
2492 | platform_device_unregister(reg_pdev); | 2378 | platform_device_unregister(reg_pdev); |
2493 | 2379 | ||
2494 | spin_lock_bh(®_pending_beacons_lock); | 2380 | list_for_each_entry_safe(reg_beacon, btmp, ®_pending_beacons, list) { |
2495 | if (!list_empty(®_pending_beacons)) { | 2381 | list_del(®_beacon->list); |
2496 | list_for_each_entry_safe(reg_beacon, btmp, | 2382 | kfree(reg_beacon); |
2497 | ®_pending_beacons, list) { | ||
2498 | list_del(®_beacon->list); | ||
2499 | kfree(reg_beacon); | ||
2500 | } | ||
2501 | } | 2383 | } |
2502 | spin_unlock_bh(®_pending_beacons_lock); | ||
2503 | 2384 | ||
2504 | if (!list_empty(®_beacon_list)) { | 2385 | list_for_each_entry_safe(reg_beacon, btmp, ®_beacon_list, list) { |
2505 | list_for_each_entry_safe(reg_beacon, btmp, | 2386 | list_del(®_beacon->list); |
2506 | ®_beacon_list, list) { | 2387 | kfree(reg_beacon); |
2507 | list_del(®_beacon->list); | ||
2508 | kfree(reg_beacon); | ||
2509 | } | ||
2510 | } | 2388 | } |
2511 | 2389 | ||
2512 | spin_lock(®_requests_lock); | 2390 | list_for_each_entry_safe(reg_request, tmp, ®_requests_list, list) { |
2513 | if (!list_empty(®_requests_list)) { | 2391 | list_del(®_request->list); |
2514 | list_for_each_entry_safe(reg_request, tmp, | 2392 | kfree(reg_request); |
2515 | ®_requests_list, list) { | ||
2516 | list_del(®_request->list); | ||
2517 | kfree(reg_request); | ||
2518 | } | ||
2519 | } | 2393 | } |
2520 | spin_unlock(®_requests_lock); | ||
2521 | |||
2522 | mutex_unlock(®_mutex); | ||
2523 | mutex_unlock(&cfg80211_mutex); | ||
2524 | } | 2394 | } |