Blame view
net/wireless/reg.c
56.5 KB
8318d78a4 cfg80211 API for ... |
1 2 3 4 |
/* * Copyright 2002-2005, Instant802 Networks, Inc. * Copyright 2005-2006, Devicescape Software, Inc. * Copyright 2007 Johannes Berg <johannes@sipsolutions.net> |
b2e1b3029 cfg80211: Add new... |
5 |
* Copyright 2008 Luis R. Rodriguez <lrodriguz@atheros.com> |
8318d78a4 cfg80211 API for ... |
6 7 8 9 10 |
* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ |
b2e1b3029 cfg80211: Add new... |
11 12 |
/** * DOC: Wireless regulatory infrastructure |
8318d78a4 cfg80211 API for ... |
13 14 15 16 17 18 |
* * The usual implementation is for a driver to read a device EEPROM to * determine which regulatory domain it should be operating under, then * looking up the allowable channels in a driver-local table and finally * registering those channels in the wiphy structure. * |
b2e1b3029 cfg80211: Add new... |
19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
* Another set of compliance enforcement is for drivers to use their * own compliance limits which can be stored on the EEPROM. The host * driver or firmware may ensure these are used. * * In addition to all this we provide an extra layer of regulatory * conformance. For drivers which do not have any regulatory * information CRDA provides the complete regulatory solution. * For others it provides a community effort on further restrictions * to enhance compliance. * * Note: When number of rules --> infinity we will not be able to * index on alpha2 any more, instead we'll probably have to * rely on some SHA1 checksum of the regdomain for example. * |
8318d78a4 cfg80211 API for ... |
33 |
*/ |
e9c0268f0 net/wireless: Use... |
34 35 |
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
8318d78a4 cfg80211 API for ... |
36 |
#include <linux/kernel.h> |
5a0e3ad6a include cleanup: ... |
37 |
#include <linux/slab.h> |
b2e1b3029 cfg80211: Add new... |
38 39 |
#include <linux/list.h> #include <linux/random.h> |
c61029c77 wireless: upcase ... |
40 |
#include <linux/ctype.h> |
b2e1b3029 cfg80211: Add new... |
41 42 |
#include <linux/nl80211.h> #include <linux/platform_device.h> |
b2e1b3029 cfg80211: Add new... |
43 |
#include <net/cfg80211.h> |
8318d78a4 cfg80211 API for ... |
44 |
#include "core.h" |
b2e1b3029 cfg80211: Add new... |
45 |
#include "reg.h" |
3b377ea9d wireless: support... |
46 |
#include "regdb.h" |
73d54c9e7 cfg80211: add reg... |
47 |
#include "nl80211.h" |
8318d78a4 cfg80211 API for ... |
48 |
|
4113f7518 cfg80211: add a r... |
49 |
#ifdef CONFIG_CFG80211_REG_DEBUG |
8271195e3 wireless: fix bui... |
50 |
#define REG_DBG_PRINT(format, args...) \ |
4113f7518 cfg80211: add a r... |
51 |
do { \ |
e9c0268f0 net/wireless: Use... |
52 |
printk(KERN_DEBUG pr_fmt(format), ##args); \ |
4113f7518 cfg80211: add a r... |
53 54 |
} while (0) #else |
8271195e3 wireless: fix bui... |
55 |
#define REG_DBG_PRINT(args...) |
4113f7518 cfg80211: add a r... |
56 |
#endif |
5166ccd22 cfg80211: Add kdo... |
57 |
/* Receipt of information from last regulatory request */ |
f6037d09e wireless: get rid... |
58 |
static struct regulatory_request *last_request; |
734366dea cfg80211: clean u... |
59 |
|
b2e1b3029 cfg80211: Add new... |
60 61 |
/* To trigger userspace events */ static struct platform_device *reg_pdev; |
8318d78a4 cfg80211 API for ... |
62 |
|
fb1fc7add cfg80211: comment... |
63 64 |
/* * Central wireless core regulatory domains, we only need two, |
734366dea cfg80211: clean u... |
65 |
* the current one and a world regulatory domain in case we have no |
fb1fc7add cfg80211: comment... |
66 67 |
* information to give us an alpha2 */ |
f130347c2 cfg80211: add get... |
68 |
const struct ieee80211_regdomain *cfg80211_regdomain; |
734366dea cfg80211: clean u... |
69 |
|
fb1fc7add cfg80211: comment... |
70 |
/* |
abc7381bc cfg80211: decoupl... |
71 72 73 |
* Protects static reg.c components: * - cfg80211_world_regdom * - cfg80211_regdom |
abc7381bc cfg80211: decoupl... |
74 75 |
* - last_request */ |
670b7f11f wireless: mark re... |
76 |
static DEFINE_MUTEX(reg_mutex); |
46a5ebaf0 cfg80211/mac80211... |
77 78 79 80 81 |
static inline void assert_reg_lock(void) { lockdep_assert_held(®_mutex); } |
abc7381bc cfg80211: decoupl... |
82 |
|
e38f8a7a8 cfg80211: Add AP ... |
83 |
/* Used to queue up regulatory hints */ |
fe33eb390 cfg80211: move al... |
84 85 |
static LIST_HEAD(reg_requests_list); static spinlock_t reg_requests_lock; |
e38f8a7a8 cfg80211: Add AP ... |
86 87 88 89 90 91 92 93 94 95 96 |
/* Used to queue up beacon hints for review */ static LIST_HEAD(reg_pending_beacons); static spinlock_t reg_pending_beacons_lock; /* Used to keep track of processed beacon hints */ static LIST_HEAD(reg_beacon_list); struct reg_beacon { struct list_head list; struct ieee80211_channel chan; }; |
f333a7a2f cfg80211: move re... |
97 98 |
static void reg_todo(struct work_struct *work); static DECLARE_WORK(reg_work, reg_todo); |
734366dea cfg80211: clean u... |
99 100 |
/* We keep a static world regulatory domain in case of the absence of CRDA */ static const struct ieee80211_regdomain world_regdom = { |
611b6a82a cfg80211: Enable ... |
101 |
.n_reg_rules = 5, |
734366dea cfg80211: clean u... |
102 103 |
.alpha2 = "00", .reg_rules = { |
68798a626 cfg80211: enable ... |
104 105 |
/* IEEE 802.11b/g, channels 1..11 */ REG_RULE(2412-10, 2462+10, 40, 6, 20, 0), |
611b6a82a cfg80211: Enable ... |
106 107 108 |
/* IEEE 802.11b/g, channels 12..13. No HT40 * channel fits here. */ REG_RULE(2467-10, 2472+10, 20, 6, 20, |
3fc71f775 cfg80211: enable ... |
109 110 |
NL80211_RRF_PASSIVE_SCAN | NL80211_RRF_NO_IBSS), |
611b6a82a cfg80211: Enable ... |
111 112 113 114 115 116 117 |
/* IEEE 802.11 channel 14 - Only JP enables * this and for 802.11b only */ REG_RULE(2484-10, 2484+10, 20, 6, 20, NL80211_RRF_PASSIVE_SCAN | NL80211_RRF_NO_IBSS | NL80211_RRF_NO_OFDM), /* IEEE 802.11a, channel 36..48 */ |
ec329acef cfg80211: fix max... |
118 |
REG_RULE(5180-10, 5240+10, 40, 6, 20, |
611b6a82a cfg80211: Enable ... |
119 120 |
NL80211_RRF_PASSIVE_SCAN | NL80211_RRF_NO_IBSS), |
3fc71f775 cfg80211: enable ... |
121 122 123 124 |
/* NB: 5260 MHz - 5700 MHz requies DFS */ /* IEEE 802.11a, channel 149..165 */ |
ec329acef cfg80211: fix max... |
125 |
REG_RULE(5745-10, 5825+10, 40, 6, 20, |
3fc71f775 cfg80211: enable ... |
126 127 |
NL80211_RRF_PASSIVE_SCAN | NL80211_RRF_NO_IBSS), |
734366dea cfg80211: clean u... |
128 129 |
} }; |
a3d2eaf0d cfg80211: fix reg... |
130 131 |
static const struct ieee80211_regdomain *cfg80211_world_regdom = &world_regdom; |
734366dea cfg80211: clean u... |
132 |
|
6ee7d3305 cfg80211: make re... |
133 |
static char *ieee80211_regdom = "00"; |
09d989d17 cfg80211: add reg... |
134 |
static char user_alpha2[2]; |
6ee7d3305 cfg80211: make re... |
135 |
|
734366dea cfg80211: clean u... |
136 137 |
module_param(ieee80211_regdom, charp, 0444); MODULE_PARM_DESC(ieee80211_regdom, "IEEE 802.11 regulatory domain code"); |
734366dea cfg80211: clean u... |
138 139 |
static void reset_regdomains(void) { |
942b25cf9 cfg80211: clean u... |
140 141 142 143 144 145 146 |
/* avoid freeing static information or freeing something twice */ if (cfg80211_regdomain == cfg80211_world_regdom) cfg80211_regdomain = NULL; if (cfg80211_world_regdom == &world_regdom) cfg80211_world_regdom = NULL; if (cfg80211_regdomain == &world_regdom) cfg80211_regdomain = NULL; |
942b25cf9 cfg80211: clean u... |
147 148 149 |
kfree(cfg80211_regdomain); kfree(cfg80211_world_regdom); |
734366dea cfg80211: clean u... |
150 |
|
a3d2eaf0d cfg80211: fix reg... |
151 |
cfg80211_world_regdom = &world_regdom; |
734366dea cfg80211: clean u... |
152 153 |
cfg80211_regdomain = NULL; } |
fb1fc7add cfg80211: comment... |
154 155 156 157 |
/* * Dynamic world regulatory domain requested by the wireless * core upon initialization */ |
a3d2eaf0d cfg80211: fix reg... |
158 |
static void update_world_regdomain(const struct ieee80211_regdomain *rd) |
734366dea cfg80211: clean u... |
159 |
{ |
f6037d09e wireless: get rid... |
160 |
BUG_ON(!last_request); |
734366dea cfg80211: clean u... |
161 162 163 164 165 166 |
reset_regdomains(); cfg80211_world_regdom = rd; cfg80211_regdomain = rd; } |
734366dea cfg80211: clean u... |
167 |
|
a3d2eaf0d cfg80211: fix reg... |
168 |
bool is_world_regdom(const char *alpha2) |
b2e1b3029 cfg80211: Add new... |
169 170 171 172 173 174 175 |
{ if (!alpha2) return false; if (alpha2[0] == '0' && alpha2[1] == '0') return true; return false; } |
8318d78a4 cfg80211 API for ... |
176 |
|
a3d2eaf0d cfg80211: fix reg... |
177 |
static bool is_alpha2_set(const char *alpha2) |
b2e1b3029 cfg80211: Add new... |
178 179 180 181 182 183 184 |
{ if (!alpha2) return false; if (alpha2[0] != 0 && alpha2[1] != 0) return true; return false; } |
8318d78a4 cfg80211 API for ... |
185 |
|
a3d2eaf0d cfg80211: fix reg... |
186 |
static bool is_unknown_alpha2(const char *alpha2) |
b2e1b3029 cfg80211: Add new... |
187 188 189 |
{ if (!alpha2) return false; |
fb1fc7add cfg80211: comment... |
190 191 192 193 |
/* * Special case where regulatory domain was built by driver * but a specific alpha2 cannot be determined */ |
b2e1b3029 cfg80211: Add new... |
194 195 196 197 |
if (alpha2[0] == '9' && alpha2[1] == '9') return true; return false; } |
8318d78a4 cfg80211 API for ... |
198 |
|
3f2355cb9 cfg80211/mac80211... |
199 200 201 202 |
static bool is_intersected_alpha2(const char *alpha2) { if (!alpha2) return false; |
fb1fc7add cfg80211: comment... |
203 204 |
/* * Special case where regulatory domain is the |
3f2355cb9 cfg80211/mac80211... |
205 |
* result of an intersection between two regulatory domain |
fb1fc7add cfg80211: comment... |
206 207 |
* structures */ |
3f2355cb9 cfg80211/mac80211... |
208 209 210 211 |
if (alpha2[0] == '9' && alpha2[1] == '8') return true; return false; } |
a3d2eaf0d cfg80211: fix reg... |
212 |
static bool is_an_alpha2(const char *alpha2) |
b2e1b3029 cfg80211: Add new... |
213 214 215 |
{ if (!alpha2) return false; |
c61029c77 wireless: upcase ... |
216 |
if (isalpha(alpha2[0]) && isalpha(alpha2[1])) |
b2e1b3029 cfg80211: Add new... |
217 218 219 |
return true; return false; } |
8318d78a4 cfg80211 API for ... |
220 |
|
a3d2eaf0d cfg80211: fix reg... |
221 |
static bool alpha2_equal(const char *alpha2_x, const char *alpha2_y) |
b2e1b3029 cfg80211: Add new... |
222 223 224 225 226 227 228 229 |
{ if (!alpha2_x || !alpha2_y) return false; if (alpha2_x[0] == alpha2_y[0] && alpha2_x[1] == alpha2_y[1]) return true; return false; } |
69b1572bd cfg80211: rename ... |
230 |
static bool regdom_changes(const char *alpha2) |
b2e1b3029 cfg80211: Add new... |
231 |
{ |
761cf7ecf cfg80211: add ass... |
232 |
assert_cfg80211_lock(); |
b2e1b3029 cfg80211: Add new... |
233 234 235 236 237 238 |
if (!cfg80211_regdomain) return true; if (alpha2_equal(cfg80211_regdomain->alpha2, alpha2)) return false; return true; } |
09d989d17 cfg80211: add reg... |
239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 |
/* * The NL80211_REGDOM_SET_BY_USER regdom alpha2 is cached, this lets * you know if a valid regulatory hint with NL80211_REGDOM_SET_BY_USER * has ever been issued. */ static bool is_user_regdom_saved(void) { if (user_alpha2[0] == '9' && user_alpha2[1] == '7') return false; /* This would indicate a mistake on the design */ if (WARN((!is_world_regdom(user_alpha2) && !is_an_alpha2(user_alpha2)), "Unexpected user alpha2: %c%c ", user_alpha2[0], user_alpha2[1])) return false; return true; } |
3b377ea9d wireless: support... |
260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 |
static int reg_copy_regd(const struct ieee80211_regdomain **dst_regd, const struct ieee80211_regdomain *src_regd) { struct ieee80211_regdomain *regd; int size_of_regd = 0; unsigned int i; size_of_regd = sizeof(struct ieee80211_regdomain) + ((src_regd->n_reg_rules + 1) * sizeof(struct ieee80211_reg_rule)); regd = kzalloc(size_of_regd, GFP_KERNEL); if (!regd) return -ENOMEM; memcpy(regd, src_regd, sizeof(struct ieee80211_regdomain)); for (i = 0; i < src_regd->n_reg_rules; i++) memcpy(®d->reg_rules[i], &src_regd->reg_rules[i], sizeof(struct ieee80211_reg_rule)); *dst_regd = regd; return 0; } #ifdef CONFIG_CFG80211_INTERNAL_REGDB struct reg_regdb_search_request { char alpha2[2]; struct list_head list; }; static LIST_HEAD(reg_regdb_search_list); |
368d06f5b wireless: convert... |
291 |
static DEFINE_MUTEX(reg_regdb_search_mutex); |
3b377ea9d wireless: support... |
292 293 294 295 296 297 |
static void reg_regdb_search(struct work_struct *work) { struct reg_regdb_search_request *request; const struct ieee80211_regdomain *curdom, *regdom; int i, r; |
368d06f5b wireless: convert... |
298 |
mutex_lock(®_regdb_search_mutex); |
3b377ea9d wireless: support... |
299 300 301 302 303 304 305 306 307 308 309 310 311 |
while (!list_empty(®_regdb_search_list)) { request = list_first_entry(®_regdb_search_list, struct reg_regdb_search_request, list); list_del(&request->list); for (i=0; i<reg_regdb_size; i++) { curdom = reg_regdb[i]; if (!memcmp(request->alpha2, curdom->alpha2, 2)) { r = reg_copy_regd(®dom, curdom); if (r) break; |
3b377ea9d wireless: support... |
312 313 314 |
mutex_lock(&cfg80211_mutex); set_regdom(regdom); mutex_unlock(&cfg80211_mutex); |
3b377ea9d wireless: support... |
315 316 317 318 319 320 |
break; } } kfree(request); } |
368d06f5b wireless: convert... |
321 |
mutex_unlock(®_regdb_search_mutex); |
3b377ea9d wireless: support... |
322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 |
} static DECLARE_WORK(reg_regdb_work, reg_regdb_search); static void reg_regdb_query(const char *alpha2) { struct reg_regdb_search_request *request; if (!alpha2) return; request = kzalloc(sizeof(struct reg_regdb_search_request), GFP_KERNEL); if (!request) return; memcpy(request->alpha2, alpha2, 2); |
368d06f5b wireless: convert... |
338 |
mutex_lock(®_regdb_search_mutex); |
3b377ea9d wireless: support... |
339 |
list_add_tail(&request->list, ®_regdb_search_list); |
368d06f5b wireless: convert... |
340 |
mutex_unlock(®_regdb_search_mutex); |
3b377ea9d wireless: support... |
341 342 343 344 345 346 |
schedule_work(®_regdb_work); } #else static inline void reg_regdb_query(const char *alpha2) {} #endif /* CONFIG_CFG80211_INTERNAL_REGDB */ |
fb1fc7add cfg80211: comment... |
347 348 349 350 |
/* * This lets us keep regulatory code which is updated on a regulatory * basis in userspace. */ |
b2e1b3029 cfg80211: Add new... |
351 352 353 354 355 356 357 358 359 |
static int call_crda(const char *alpha2) { char country_env[9 + 2] = "COUNTRY="; char *envp[] = { country_env, NULL }; if (!is_world_regdom((char *) alpha2)) |
e9c0268f0 net/wireless: Use... |
360 361 |
pr_info("Calling CRDA for country: %c%c ", |
b2e1b3029 cfg80211: Add new... |
362 363 |
alpha2[0], alpha2[1]); else |
e9c0268f0 net/wireless: Use... |
364 365 |
pr_info("Calling CRDA to update world regulatory domain "); |
b2e1b3029 cfg80211: Add new... |
366 |
|
3b377ea9d wireless: support... |
367 368 |
/* query internal regulatory database (if it exists) */ reg_regdb_query(alpha2); |
b2e1b3029 cfg80211: Add new... |
369 370 371 372 373 |
country_env[8] = alpha2[0]; country_env[9] = alpha2[1]; return kobject_uevent_env(®_pdev->dev.kobj, KOBJ_CHANGE, envp); } |
b2e1b3029 cfg80211: Add new... |
374 |
/* Used by nl80211 before kmalloc'ing our regulatory domain */ |
a3d2eaf0d cfg80211: fix reg... |
375 |
bool reg_is_valid_request(const char *alpha2) |
b2e1b3029 cfg80211: Add new... |
376 |
{ |
61405e977 cfg80211: fix in ... |
377 |
assert_cfg80211_lock(); |
f6037d09e wireless: get rid... |
378 379 380 381 |
if (!last_request) return false; return alpha2_equal(last_request->alpha2, alpha2); |
b2e1b3029 cfg80211: Add new... |
382 |
} |
8318d78a4 cfg80211 API for ... |
383 |
|
b2e1b3029 cfg80211: Add new... |
384 |
/* Sanity check on a regulatory rule */ |
a3d2eaf0d cfg80211: fix reg... |
385 |
static bool is_valid_reg_rule(const struct ieee80211_reg_rule *rule) |
8318d78a4 cfg80211 API for ... |
386 |
{ |
a3d2eaf0d cfg80211: fix reg... |
387 |
const struct ieee80211_freq_range *freq_range = &rule->freq_range; |
b2e1b3029 cfg80211: Add new... |
388 |
u32 freq_diff; |
91e990041 cfg80211: mark ne... |
389 |
if (freq_range->start_freq_khz <= 0 || freq_range->end_freq_khz <= 0) |
b2e1b3029 cfg80211: Add new... |
390 391 392 393 394 395 |
return false; if (freq_range->start_freq_khz > freq_range->end_freq_khz) return false; freq_diff = freq_range->end_freq_khz - freq_range->start_freq_khz; |
bd05f28e1 cfg80211: test be... |
396 397 |
if (freq_range->end_freq_khz <= freq_range->start_freq_khz || freq_range->max_bandwidth_khz > freq_diff) |
b2e1b3029 cfg80211: Add new... |
398 399 400 401 |
return false; return true; } |
a3d2eaf0d cfg80211: fix reg... |
402 |
static bool is_valid_rd(const struct ieee80211_regdomain *rd) |
b2e1b3029 cfg80211: Add new... |
403 |
{ |
a3d2eaf0d cfg80211: fix reg... |
404 |
const struct ieee80211_reg_rule *reg_rule = NULL; |
b2e1b3029 cfg80211: Add new... |
405 |
unsigned int i; |
8318d78a4 cfg80211 API for ... |
406 |
|
b2e1b3029 cfg80211: Add new... |
407 408 |
if (!rd->n_reg_rules) return false; |
8318d78a4 cfg80211 API for ... |
409 |
|
88dc1c3f7 cfg80211: mark re... |
410 411 |
if (WARN_ON(rd->n_reg_rules > NL80211_MAX_SUPP_REG_RULES)) return false; |
b2e1b3029 cfg80211: Add new... |
412 413 414 415 416 417 418 |
for (i = 0; i < rd->n_reg_rules; i++) { reg_rule = &rd->reg_rules[i]; if (!is_valid_reg_rule(reg_rule)) return false; } return true; |
8318d78a4 cfg80211 API for ... |
419 |
} |
038659e7c cfg80211: Process... |
420 421 422 |
static bool reg_does_bw_fit(const struct ieee80211_freq_range *freq_range, u32 center_freq_khz, u32 bw_khz) |
b2e1b3029 cfg80211: Add new... |
423 |
{ |
038659e7c cfg80211: Process... |
424 425 426 427 428 429 430 431 432 433 |
u32 start_freq_khz, end_freq_khz; start_freq_khz = center_freq_khz - (bw_khz/2); end_freq_khz = center_freq_khz + (bw_khz/2); if (start_freq_khz >= freq_range->start_freq_khz && end_freq_khz <= freq_range->end_freq_khz) return true; return false; |
b2e1b3029 cfg80211: Add new... |
434 |
} |
8318d78a4 cfg80211 API for ... |
435 |
|
0c7dc45d2 cfg80211: Fix reg... |
436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 |
/** * freq_in_rule_band - tells us if a frequency is in a frequency band * @freq_range: frequency rule we want to query * @freq_khz: frequency we are inquiring about * * This lets us know if a specific frequency rule is or is not relevant to * a specific frequency's band. Bands are device specific and artificial * definitions (the "2.4 GHz band" and the "5 GHz band"), however it is * safe for now to assume that a frequency rule should not be part of a * frequency's band if the start freq or end freq are off by more than 2 GHz. * This resolution can be lowered and should be considered as we add * regulatory rule support for other "bands". **/ static bool freq_in_rule_band(const struct ieee80211_freq_range *freq_range, u32 freq_khz) { #define ONE_GHZ_IN_KHZ 1000000 if (abs(freq_khz - freq_range->start_freq_khz) <= (2 * ONE_GHZ_IN_KHZ)) return true; if (abs(freq_khz - freq_range->end_freq_khz) <= (2 * ONE_GHZ_IN_KHZ)) return true; return false; #undef ONE_GHZ_IN_KHZ } |
fb1fc7add cfg80211: comment... |
460 |
/* |
fb1fc7add cfg80211: comment... |
461 462 463 |
* Helper for regdom_intersect(), this does the real * mathematical intersection fun */ |
9c96477d1 cfg80211: Add reg... |
464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 |
static int reg_rules_intersect( const struct ieee80211_reg_rule *rule1, const struct ieee80211_reg_rule *rule2, struct ieee80211_reg_rule *intersected_rule) { const struct ieee80211_freq_range *freq_range1, *freq_range2; struct ieee80211_freq_range *freq_range; const struct ieee80211_power_rule *power_rule1, *power_rule2; struct ieee80211_power_rule *power_rule; u32 freq_diff; freq_range1 = &rule1->freq_range; freq_range2 = &rule2->freq_range; freq_range = &intersected_rule->freq_range; power_rule1 = &rule1->power_rule; power_rule2 = &rule2->power_rule; power_rule = &intersected_rule->power_rule; freq_range->start_freq_khz = max(freq_range1->start_freq_khz, freq_range2->start_freq_khz); freq_range->end_freq_khz = min(freq_range1->end_freq_khz, freq_range2->end_freq_khz); freq_range->max_bandwidth_khz = min(freq_range1->max_bandwidth_khz, freq_range2->max_bandwidth_khz); freq_diff = freq_range->end_freq_khz - freq_range->start_freq_khz; if (freq_range->max_bandwidth_khz > freq_diff) freq_range->max_bandwidth_khz = freq_diff; power_rule->max_eirp = min(power_rule1->max_eirp, power_rule2->max_eirp); power_rule->max_antenna_gain = min(power_rule1->max_antenna_gain, power_rule2->max_antenna_gain); intersected_rule->flags = (rule1->flags | rule2->flags); if (!is_valid_reg_rule(intersected_rule)) return -EINVAL; return 0; } /** * regdom_intersect - do the intersection between two regulatory domains * @rd1: first regulatory domain * @rd2: second regulatory domain * * Use this function to get the intersection between two regulatory domains. * Once completed we will mark the alpha2 for the rd as intersected, "98", * as no one single alpha2 can represent this regulatory domain. * * Returns a pointer to the regulatory domain structure which will hold the * resulting intersection of rules between rd1 and rd2. We will * kzalloc() this structure for you. */ static struct ieee80211_regdomain *regdom_intersect( const struct ieee80211_regdomain *rd1, const struct ieee80211_regdomain *rd2) { int r, size_of_regd; unsigned int x, y; unsigned int num_rules = 0, rule_idx = 0; const struct ieee80211_reg_rule *rule1, *rule2; struct ieee80211_reg_rule *intersected_rule; struct ieee80211_regdomain *rd; /* This is just a dummy holder to help us count */ struct ieee80211_reg_rule irule; /* Uses the stack temporarily for counter arithmetic */ intersected_rule = &irule; memset(intersected_rule, 0, sizeof(struct ieee80211_reg_rule)); if (!rd1 || !rd2) return NULL; |
fb1fc7add cfg80211: comment... |
540 541 |
/* * First we get a count of the rules we'll need, then we actually |
9c96477d1 cfg80211: Add reg... |
542 543 544 |
* build them. This is to so we can malloc() and free() a * regdomain once. The reason we use reg_rules_intersect() here * is it will return -EINVAL if the rule computed makes no sense. |
fb1fc7add cfg80211: comment... |
545 546 |
* All rules that do check out OK are valid. */ |
9c96477d1 cfg80211: Add reg... |
547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 |
for (x = 0; x < rd1->n_reg_rules; x++) { rule1 = &rd1->reg_rules[x]; for (y = 0; y < rd2->n_reg_rules; y++) { rule2 = &rd2->reg_rules[y]; if (!reg_rules_intersect(rule1, rule2, intersected_rule)) num_rules++; memset(intersected_rule, 0, sizeof(struct ieee80211_reg_rule)); } } if (!num_rules) return NULL; size_of_regd = sizeof(struct ieee80211_regdomain) + ((num_rules + 1) * sizeof(struct ieee80211_reg_rule)); rd = kzalloc(size_of_regd, GFP_KERNEL); if (!rd) return NULL; for (x = 0; x < rd1->n_reg_rules; x++) { rule1 = &rd1->reg_rules[x]; for (y = 0; y < rd2->n_reg_rules; y++) { rule2 = &rd2->reg_rules[y]; |
fb1fc7add cfg80211: comment... |
574 575 |
/* * This time around instead of using the stack lets |
9c96477d1 cfg80211: Add reg... |
576 |
* write to the target rule directly saving ourselves |
fb1fc7add cfg80211: comment... |
577 578 |
* a memcpy() */ |
9c96477d1 cfg80211: Add reg... |
579 580 581 |
intersected_rule = &rd->reg_rules[rule_idx]; r = reg_rules_intersect(rule1, rule2, intersected_rule); |
fb1fc7add cfg80211: comment... |
582 583 584 585 |
/* * No need to memset here the intersected rule here as * we're not using the stack anymore */ |
9c96477d1 cfg80211: Add reg... |
586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 |
if (r) continue; rule_idx++; } } if (rule_idx != num_rules) { kfree(rd); return NULL; } rd->n_reg_rules = num_rules; rd->alpha2[0] = '9'; rd->alpha2[1] = '8'; return rd; } |
fb1fc7add cfg80211: comment... |
603 604 605 606 |
/* * XXX: add support for the rest of enum nl80211_reg_rule_flags, we may * want to just have the channel structure use these */ |
b2e1b3029 cfg80211: Add new... |
607 608 609 610 611 612 613 614 615 616 617 |
static u32 map_regdom_flags(u32 rd_flags) { u32 channel_flags = 0; if (rd_flags & NL80211_RRF_PASSIVE_SCAN) channel_flags |= IEEE80211_CHAN_PASSIVE_SCAN; if (rd_flags & NL80211_RRF_NO_IBSS) channel_flags |= IEEE80211_CHAN_NO_IBSS; if (rd_flags & NL80211_RRF_DFS) channel_flags |= IEEE80211_CHAN_RADAR; return channel_flags; } |
1fa25e413 cfg80211: add wip... |
618 619 |
static int freq_reg_info_regd(struct wiphy *wiphy, u32 center_freq, |
038659e7c cfg80211: Process... |
620 |
u32 desired_bw_khz, |
1fa25e413 cfg80211: add wip... |
621 622 |
const struct ieee80211_reg_rule **reg_rule, const struct ieee80211_regdomain *custom_regd) |
8318d78a4 cfg80211 API for ... |
623 624 |
{ int i; |
0c7dc45d2 cfg80211: Fix reg... |
625 |
bool band_rule_found = false; |
3e0c3ff36 cfg80211: allow m... |
626 |
const struct ieee80211_regdomain *regd; |
038659e7c cfg80211: Process... |
627 628 629 630 |
bool bw_fits = false; if (!desired_bw_khz) desired_bw_khz = MHZ_TO_KHZ(20); |
8318d78a4 cfg80211 API for ... |
631 |
|
1fa25e413 cfg80211: add wip... |
632 |
regd = custom_regd ? custom_regd : cfg80211_regdomain; |
3e0c3ff36 cfg80211: allow m... |
633 |
|
fb1fc7add cfg80211: comment... |
634 635 636 637 |
/* * Follow the driver's regulatory domain, if present, unless a country * IE has been processed or a user wants to help complaince further */ |
2784fe915 cfg80211: fix nul... |
638 639 |
if (!custom_regd && last_request->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE && |
7db90f4a2 cfg80211: move en... |
640 |
last_request->initiator != NL80211_REGDOM_SET_BY_USER && |
3e0c3ff36 cfg80211: allow m... |
641 642 643 644 |
wiphy->regd) regd = wiphy->regd; if (!regd) |
b2e1b3029 cfg80211: Add new... |
645 |
return -EINVAL; |
3e0c3ff36 cfg80211: allow m... |
646 |
for (i = 0; i < regd->n_reg_rules; i++) { |
b2e1b3029 cfg80211: Add new... |
647 648 649 |
const struct ieee80211_reg_rule *rr; const struct ieee80211_freq_range *fr = NULL; const struct ieee80211_power_rule *pr = NULL; |
3e0c3ff36 cfg80211: allow m... |
650 |
rr = ®d->reg_rules[i]; |
b2e1b3029 cfg80211: Add new... |
651 652 |
fr = &rr->freq_range; pr = &rr->power_rule; |
0c7dc45d2 cfg80211: Fix reg... |
653 |
|
fb1fc7add cfg80211: comment... |
654 655 |
/* * We only need to know if one frequency rule was |
0c7dc45d2 cfg80211: Fix reg... |
656 |
* was in center_freq's band, that's enough, so lets |
fb1fc7add cfg80211: comment... |
657 658 |
* not overwrite it once found */ |
0c7dc45d2 cfg80211: Fix reg... |
659 660 |
if (!band_rule_found) band_rule_found = freq_in_rule_band(fr, center_freq); |
038659e7c cfg80211: Process... |
661 662 663 |
bw_fits = reg_does_bw_fit(fr, center_freq, desired_bw_khz); |
0c7dc45d2 cfg80211: Fix reg... |
664 |
|
038659e7c cfg80211: Process... |
665 |
if (band_rule_found && bw_fits) { |
b2e1b3029 cfg80211: Add new... |
666 |
*reg_rule = rr; |
038659e7c cfg80211: Process... |
667 |
return 0; |
8318d78a4 cfg80211 API for ... |
668 669 |
} } |
0c7dc45d2 cfg80211: Fix reg... |
670 671 |
if (!band_rule_found) return -ERANGE; |
038659e7c cfg80211: Process... |
672 |
return -EINVAL; |
b2e1b3029 cfg80211: Add new... |
673 |
} |
038659e7c cfg80211: Process... |
674 675 676 677 |
int freq_reg_info(struct wiphy *wiphy, u32 center_freq, u32 desired_bw_khz, const struct ieee80211_reg_rule **reg_rule) |
1fa25e413 cfg80211: add wip... |
678 |
{ |
ac46d48e0 cfg80211: fix rac... |
679 |
assert_cfg80211_lock(); |
038659e7c cfg80211: Process... |
680 681 682 683 684 |
return freq_reg_info_regd(wiphy, center_freq, desired_bw_khz, reg_rule, NULL); |
1fa25e413 cfg80211: add wip... |
685 |
} |
4f366c5da wireless: only us... |
686 |
EXPORT_SYMBOL(freq_reg_info); |
b2e1b3029 cfg80211: Add new... |
687 |
|
926a0a094 cfg80211: add deb... |
688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 |
#ifdef CONFIG_CFG80211_REG_DEBUG static const char *reg_initiator_name(enum nl80211_reg_initiator initiator) { switch (initiator) { case NL80211_REGDOM_SET_BY_CORE: return "Set by core"; case NL80211_REGDOM_SET_BY_USER: return "Set by user"; case NL80211_REGDOM_SET_BY_DRIVER: return "Set by driver"; case NL80211_REGDOM_SET_BY_COUNTRY_IE: return "Set by country IE"; default: WARN_ON(1); return "Set by bug"; } } |
e702d3cf2 cfg80211: add deb... |
705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 |
static void chan_reg_rule_print_dbg(struct ieee80211_channel *chan, u32 desired_bw_khz, const struct ieee80211_reg_rule *reg_rule) { const struct ieee80211_power_rule *power_rule; const struct ieee80211_freq_range *freq_range; char max_antenna_gain[32]; power_rule = ®_rule->power_rule; freq_range = ®_rule->freq_range; if (!power_rule->max_antenna_gain) snprintf(max_antenna_gain, 32, "N/A"); else snprintf(max_antenna_gain, 32, "%d", power_rule->max_antenna_gain); |
d91e41b69 cfg80211: prefix ... |
721 |
REG_DBG_PRINT("Updating information on frequency %d MHz " |
ff039c6fb cfg80211: fix tra... |
722 723 |
"for a %d MHz width channel with regulatory rule: ", |
e702d3cf2 cfg80211: add deb... |
724 725 |
chan->center_freq, KHZ_TO_MHZ(desired_bw_khz)); |
d91e41b69 cfg80211: prefix ... |
726 727 |
REG_DBG_PRINT("%d KHz - %d KHz @ KHz), (%s mBi, %d mBm) ", |
e702d3cf2 cfg80211: add deb... |
728 729 730 731 732 733 734 735 736 737 738 739 |
freq_range->start_freq_khz, freq_range->end_freq_khz, max_antenna_gain, power_rule->max_eirp); } #else static void chan_reg_rule_print_dbg(struct ieee80211_channel *chan, u32 desired_bw_khz, const struct ieee80211_reg_rule *reg_rule) { return; } |
926a0a094 cfg80211: add deb... |
740 |
#endif |
038659e7c cfg80211: Process... |
741 742 743 744 745 746 747 748 749 |
/* * Note that right now we assume the desired channel bandwidth * is always 20 MHz for each individual channel (HT40 uses 20 MHz * per channel, the primary and the extension channel). To support * smaller custom bandwidths such as 5 MHz or 10 MHz we'll need a * new ieee80211_channel.target_bw and re run the regulatory check * on the wiphy with the target_bw specified. Then we can simply use * that below for the desired_bw_khz below. */ |
7ca43d03b cfg80211: pass th... |
750 751 752 |
static void handle_channel(struct wiphy *wiphy, enum nl80211_reg_initiator initiator, enum ieee80211_band band, |
a92a3ce72 cfg80211: make ha... |
753 |
unsigned int chan_idx) |
b2e1b3029 cfg80211: Add new... |
754 755 |
{ int r; |
038659e7c cfg80211: Process... |
756 757 |
u32 flags, bw_flags = 0; u32 desired_bw_khz = MHZ_TO_KHZ(20); |
b2e1b3029 cfg80211: Add new... |
758 759 |
const struct ieee80211_reg_rule *reg_rule = NULL; const struct ieee80211_power_rule *power_rule = NULL; |
038659e7c cfg80211: Process... |
760 |
const struct ieee80211_freq_range *freq_range = NULL; |
a92a3ce72 cfg80211: make ha... |
761 762 |
struct ieee80211_supported_band *sband; struct ieee80211_channel *chan; |
fe33eb390 cfg80211: move al... |
763 |
struct wiphy *request_wiphy = NULL; |
a92a3ce72 cfg80211: make ha... |
764 |
|
761cf7ecf cfg80211: add ass... |
765 |
assert_cfg80211_lock(); |
806a9e396 cfg80211: make re... |
766 |
request_wiphy = wiphy_idx_to_wiphy(last_request->wiphy_idx); |
a92a3ce72 cfg80211: make ha... |
767 768 769 770 771 |
sband = wiphy->bands[band]; BUG_ON(chan_idx >= sband->n_channels); chan = &sband->channels[chan_idx]; flags = chan->orig_flags; |
b2e1b3029 cfg80211: Add new... |
772 |
|
038659e7c cfg80211: Process... |
773 774 775 776 |
r = freq_reg_info(wiphy, MHZ_TO_KHZ(chan->center_freq), desired_bw_khz, ®_rule); |
b2e1b3029 cfg80211: Add new... |
777 |
|
ca4ffe8f2 cfg80211: fix dis... |
778 779 780 781 782 783 784 785 786 787 788 789 790 791 |
if (r) { /* * We will disable all channels that do not match our * recieved regulatory rule unless the hint is coming * from a Country IE and the Country IE had no information * about a band. The IEEE 802.11 spec allows for an AP * to send only a subset of the regulatory rules allowed, * so an AP in the US that only supports 2.4 GHz may only send * a country IE with information for the 2.4 GHz band * while 5 GHz is still supported. */ if (initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE && r == -ERANGE) return; |
d91e41b69 cfg80211: prefix ... |
792 793 |
REG_DBG_PRINT("Disabling freq %d MHz ", chan->center_freq); |
ca4ffe8f2 cfg80211: fix dis... |
794 |
chan->flags = IEEE80211_CHAN_DISABLED; |
8318d78a4 cfg80211 API for ... |
795 |
return; |
ca4ffe8f2 cfg80211: fix dis... |
796 |
} |
8318d78a4 cfg80211 API for ... |
797 |
|
e702d3cf2 cfg80211: add deb... |
798 |
chan_reg_rule_print_dbg(chan, desired_bw_khz, reg_rule); |
b2e1b3029 cfg80211: Add new... |
799 |
power_rule = ®_rule->power_rule; |
038659e7c cfg80211: Process... |
800 801 802 803 |
freq_range = ®_rule->freq_range; if (freq_range->max_bandwidth_khz < MHZ_TO_KHZ(40)) bw_flags = IEEE80211_CHAN_NO_HT40; |
b2e1b3029 cfg80211: Add new... |
804 |
|
7db90f4a2 cfg80211: move en... |
805 |
if (last_request->initiator == NL80211_REGDOM_SET_BY_DRIVER && |
806a9e396 cfg80211: make re... |
806 |
request_wiphy && request_wiphy == wiphy && |
5be83de54 cfg80211: convert... |
807 |
request_wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY) { |
fb1fc7add cfg80211: comment... |
808 809 |
/* * This gaurantees the driver's requested regulatory domain |
f976376de cfg80211: Allow f... |
810 |
* will always be used as a base for further regulatory |
fb1fc7add cfg80211: comment... |
811 812 |
* settings */ |
f976376de cfg80211: Allow f... |
813 |
chan->flags = chan->orig_flags = |
038659e7c cfg80211: Process... |
814 |
map_regdom_flags(reg_rule->flags) | bw_flags; |
f976376de cfg80211: Allow f... |
815 816 |
chan->max_antenna_gain = chan->orig_mag = (int) MBI_TO_DBI(power_rule->max_antenna_gain); |
f976376de cfg80211: Allow f... |
817 818 819 820 |
chan->max_power = chan->orig_mpwr = (int) MBM_TO_DBM(power_rule->max_eirp); return; } |
038659e7c cfg80211: Process... |
821 |
chan->flags = flags | bw_flags | map_regdom_flags(reg_rule->flags); |
8318d78a4 cfg80211 API for ... |
822 |
chan->max_antenna_gain = min(chan->orig_mag, |
b2e1b3029 cfg80211: Add new... |
823 |
(int) MBI_TO_DBI(power_rule->max_antenna_gain)); |
253898c41 cfg80211: default... |
824 |
if (chan->orig_mpwr) |
b2e1b3029 cfg80211: Add new... |
825 826 |
chan->max_power = min(chan->orig_mpwr, (int) MBM_TO_DBM(power_rule->max_eirp)); |
253898c41 cfg80211: default... |
827 |
else |
b2e1b3029 cfg80211: Add new... |
828 |
chan->max_power = (int) MBM_TO_DBM(power_rule->max_eirp); |
8318d78a4 cfg80211 API for ... |
829 |
} |
7ca43d03b cfg80211: pass th... |
830 831 832 |
static void handle_band(struct wiphy *wiphy, enum ieee80211_band band, enum nl80211_reg_initiator initiator) |
8318d78a4 cfg80211 API for ... |
833 |
{ |
a92a3ce72 cfg80211: make ha... |
834 835 836 837 838 |
unsigned int i; struct ieee80211_supported_band *sband; BUG_ON(!wiphy->bands[band]); sband = wiphy->bands[band]; |
8318d78a4 cfg80211 API for ... |
839 840 |
for (i = 0; i < sband->n_channels; i++) |
7ca43d03b cfg80211: pass th... |
841 |
handle_channel(wiphy, initiator, band, i); |
8318d78a4 cfg80211 API for ... |
842 |
} |
7db90f4a2 cfg80211: move en... |
843 844 |
static bool ignore_reg_update(struct wiphy *wiphy, enum nl80211_reg_initiator initiator) |
14b9815af cfg80211: add sup... |
845 |
{ |
926a0a094 cfg80211: add deb... |
846 |
if (!last_request) { |
d91e41b69 cfg80211: prefix ... |
847 |
REG_DBG_PRINT("Ignoring regulatory request %s since " |
926a0a094 cfg80211: add deb... |
848 849 850 |
"last_request is not set ", reg_initiator_name(initiator)); |
14b9815af cfg80211: add sup... |
851 |
return true; |
926a0a094 cfg80211: add deb... |
852 |
} |
7db90f4a2 cfg80211: move en... |
853 |
if (initiator == NL80211_REGDOM_SET_BY_CORE && |
926a0a094 cfg80211: add deb... |
854 |
wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY) { |
d91e41b69 cfg80211: prefix ... |
855 |
REG_DBG_PRINT("Ignoring regulatory request %s " |
926a0a094 cfg80211: add deb... |
856 857 858 |
"since the driver uses its own custom " "regulatory domain ", reg_initiator_name(initiator)); |
14b9815af cfg80211: add sup... |
859 |
return true; |
926a0a094 cfg80211: add deb... |
860 |
} |
fb1fc7add cfg80211: comment... |
861 862 863 864 |
/* * wiphy->regd will be set once the device has its own * desired regulatory domain set */ |
5be83de54 cfg80211: convert... |
865 |
if (wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY && !wiphy->regd && |
749b527b2 cfg80211: fix all... |
866 |
initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE && |
926a0a094 cfg80211: add deb... |
867 |
!is_world_regdom(last_request->alpha2)) { |
d91e41b69 cfg80211: prefix ... |
868 |
REG_DBG_PRINT("Ignoring regulatory request %s " |
926a0a094 cfg80211: add deb... |
869 870 871 |
"since the driver requires its own regulaotry " "domain to be set first", reg_initiator_name(initiator)); |
14b9815af cfg80211: add sup... |
872 |
return true; |
926a0a094 cfg80211: add deb... |
873 |
} |
14b9815af cfg80211: add sup... |
874 875 |
return false; } |
7db90f4a2 cfg80211: move en... |
876 |
static void update_all_wiphy_regulatory(enum nl80211_reg_initiator initiator) |
8318d78a4 cfg80211 API for ... |
877 |
{ |
79c97e97a cfg80211: clean u... |
878 |
struct cfg80211_registered_device *rdev; |
8318d78a4 cfg80211 API for ... |
879 |
|
79c97e97a cfg80211: clean u... |
880 881 |
list_for_each_entry(rdev, &cfg80211_rdev_list, list) wiphy_update_regulatory(&rdev->wiphy, initiator); |
b2e1b3029 cfg80211: Add new... |
882 |
} |
e38f8a7a8 cfg80211: Add AP ... |
883 884 885 886 |
static void handle_reg_beacon(struct wiphy *wiphy, unsigned int chan_idx, struct reg_beacon *reg_beacon) { |
e38f8a7a8 cfg80211: Add AP ... |
887 888 |
struct ieee80211_supported_band *sband; struct ieee80211_channel *chan; |
6bad87666 cfg80211: send re... |
889 890 |
bool channel_changed = false; struct ieee80211_channel chan_before; |
e38f8a7a8 cfg80211: Add AP ... |
891 892 893 894 895 896 897 898 |
assert_cfg80211_lock(); sband = wiphy->bands[reg_beacon->chan.band]; chan = &sband->channels[chan_idx]; if (likely(chan->center_freq != reg_beacon->chan.center_freq)) return; |
6bad87666 cfg80211: send re... |
899 900 901 902 |
if (chan->beacon_found) return; chan->beacon_found = true; |
5be83de54 cfg80211: convert... |
903 |
if (wiphy->flags & WIPHY_FLAG_DISABLE_BEACON_HINTS) |
371842448 cfg80211: fix reg... |
904 |
return; |
6bad87666 cfg80211: send re... |
905 906 |
chan_before.center_freq = chan->center_freq; chan_before.flags = chan->flags; |
371842448 cfg80211: fix reg... |
907 |
if (chan->flags & IEEE80211_CHAN_PASSIVE_SCAN) { |
e38f8a7a8 cfg80211: Add AP ... |
908 |
chan->flags &= ~IEEE80211_CHAN_PASSIVE_SCAN; |
6bad87666 cfg80211: send re... |
909 |
channel_changed = true; |
e38f8a7a8 cfg80211: Add AP ... |
910 |
} |
371842448 cfg80211: fix reg... |
911 |
if (chan->flags & IEEE80211_CHAN_NO_IBSS) { |
e38f8a7a8 cfg80211: Add AP ... |
912 |
chan->flags &= ~IEEE80211_CHAN_NO_IBSS; |
6bad87666 cfg80211: send re... |
913 |
channel_changed = true; |
e38f8a7a8 cfg80211: Add AP ... |
914 |
} |
6bad87666 cfg80211: send re... |
915 916 |
if (channel_changed) nl80211_send_beacon_hint_event(wiphy, &chan_before, chan); |
e38f8a7a8 cfg80211: Add AP ... |
917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 |
} /* * Called when a scan on a wiphy finds a beacon on * new channel */ static void wiphy_update_new_beacon(struct wiphy *wiphy, struct reg_beacon *reg_beacon) { unsigned int i; struct ieee80211_supported_band *sband; assert_cfg80211_lock(); if (!wiphy->bands[reg_beacon->chan.band]) return; sband = wiphy->bands[reg_beacon->chan.band]; for (i = 0; i < sband->n_channels; i++) handle_reg_beacon(wiphy, i, reg_beacon); } /* * Called upon reg changes or a new wiphy is added */ static void wiphy_update_beacon_reg(struct wiphy *wiphy) { unsigned int i; struct ieee80211_supported_band *sband; struct reg_beacon *reg_beacon; assert_cfg80211_lock(); if (list_empty(®_beacon_list)) return; list_for_each_entry(reg_beacon, ®_beacon_list, list) { if (!wiphy->bands[reg_beacon->chan.band]) continue; sband = wiphy->bands[reg_beacon->chan.band]; for (i = 0; i < sband->n_channels; i++) handle_reg_beacon(wiphy, i, reg_beacon); } } static bool reg_is_world_roaming(struct wiphy *wiphy) { if (is_world_regdom(cfg80211_regdomain->alpha2) || (wiphy->regd && is_world_regdom(wiphy->regd->alpha2))) return true; |
b1ed8ddd2 cfg80211: fix bug... |
968 969 |
if (last_request && last_request->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE && |
5be83de54 cfg80211: convert... |
970 |
wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY) |
e38f8a7a8 cfg80211: Add AP ... |
971 972 973 974 975 976 977 |
return true; return false; } /* Reap the advantages of previously found beacons */ static void reg_process_beacons(struct wiphy *wiphy) { |
b1ed8ddd2 cfg80211: fix bug... |
978 979 980 981 982 983 |
/* * Means we are just firing up cfg80211, so no beacons would * have been processed yet. */ if (!last_request) return; |
e38f8a7a8 cfg80211: Add AP ... |
984 985 986 987 |
if (!reg_is_world_roaming(wiphy)) return; wiphy_update_beacon_reg(wiphy); } |
038659e7c cfg80211: Process... |
988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 |
static bool is_ht40_not_allowed(struct ieee80211_channel *chan) { if (!chan) return true; if (chan->flags & IEEE80211_CHAN_DISABLED) return true; /* This would happen when regulatory rules disallow HT40 completely */ if (IEEE80211_CHAN_NO_HT40 == (chan->flags & (IEEE80211_CHAN_NO_HT40))) return true; return false; } static void reg_process_ht_flags_channel(struct wiphy *wiphy, enum ieee80211_band band, unsigned int chan_idx) { struct ieee80211_supported_band *sband; struct ieee80211_channel *channel; struct ieee80211_channel *channel_before = NULL, *channel_after = NULL; unsigned int i; assert_cfg80211_lock(); sband = wiphy->bands[band]; BUG_ON(chan_idx >= sband->n_channels); channel = &sband->channels[chan_idx]; if (is_ht40_not_allowed(channel)) { channel->flags |= IEEE80211_CHAN_NO_HT40; return; } /* * We need to ensure the extension channels exist to * be able to use HT40- or HT40+, this finds them (or not) */ for (i = 0; i < sband->n_channels; i++) { struct ieee80211_channel *c = &sband->channels[i]; if (c->center_freq == (channel->center_freq - 20)) channel_before = c; if (c->center_freq == (channel->center_freq + 20)) channel_after = c; } /* * Please note that this assumes target bandwidth is 20 MHz, * if that ever changes we also need to change the below logic * to include that as well. */ if (is_ht40_not_allowed(channel_before)) |
689da1b3b wireless: rename ... |
1038 |
channel->flags |= IEEE80211_CHAN_NO_HT40MINUS; |
038659e7c cfg80211: Process... |
1039 |
else |
689da1b3b wireless: rename ... |
1040 |
channel->flags &= ~IEEE80211_CHAN_NO_HT40MINUS; |
038659e7c cfg80211: Process... |
1041 1042 |
if (is_ht40_not_allowed(channel_after)) |
689da1b3b wireless: rename ... |
1043 |
channel->flags |= IEEE80211_CHAN_NO_HT40PLUS; |
038659e7c cfg80211: Process... |
1044 |
else |
689da1b3b wireless: rename ... |
1045 |
channel->flags &= ~IEEE80211_CHAN_NO_HT40PLUS; |
038659e7c cfg80211: Process... |
1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 |
} static void reg_process_ht_flags_band(struct wiphy *wiphy, enum ieee80211_band band) { unsigned int i; struct ieee80211_supported_band *sband; BUG_ON(!wiphy->bands[band]); sband = wiphy->bands[band]; for (i = 0; i < sband->n_channels; i++) reg_process_ht_flags_channel(wiphy, band, i); } static void reg_process_ht_flags(struct wiphy *wiphy) { enum ieee80211_band band; if (!wiphy) return; for (band = 0; band < IEEE80211_NUM_BANDS; band++) { if (wiphy->bands[band]) reg_process_ht_flags_band(wiphy, band); } } |
7db90f4a2 cfg80211: move en... |
1074 1075 |
void wiphy_update_regulatory(struct wiphy *wiphy, enum nl80211_reg_initiator initiator) |
b2e1b3029 cfg80211: Add new... |
1076 1077 |
{ enum ieee80211_band band; |
d46e5b1d0 cfg80211: move ch... |
1078 |
|
7db90f4a2 cfg80211: move en... |
1079 |
if (ignore_reg_update(wiphy, initiator)) |
e38f8a7a8 cfg80211: Add AP ... |
1080 |
goto out; |
b2e1b3029 cfg80211: Add new... |
1081 |
for (band = 0; band < IEEE80211_NUM_BANDS; band++) { |
8318d78a4 cfg80211 API for ... |
1082 |
if (wiphy->bands[band]) |
7ca43d03b cfg80211: pass th... |
1083 |
handle_band(wiphy, band, initiator); |
b2e1b3029 cfg80211: Add new... |
1084 |
} |
e38f8a7a8 cfg80211: Add AP ... |
1085 1086 |
out: reg_process_beacons(wiphy); |
038659e7c cfg80211: Process... |
1087 |
reg_process_ht_flags(wiphy); |
560e28e14 cfg80211: call re... |
1088 |
if (wiphy->reg_notifier) |
716f9392e cfg80211: pass mo... |
1089 |
wiphy->reg_notifier(wiphy, last_request); |
b2e1b3029 cfg80211: Add new... |
1090 |
} |
1fa25e413 cfg80211: add wip... |
1091 1092 1093 1094 1095 1096 |
static void handle_channel_custom(struct wiphy *wiphy, enum ieee80211_band band, unsigned int chan_idx, const struct ieee80211_regdomain *regd) { int r; |
038659e7c cfg80211: Process... |
1097 1098 |
u32 desired_bw_khz = MHZ_TO_KHZ(20); u32 bw_flags = 0; |
1fa25e413 cfg80211: add wip... |
1099 1100 |
const struct ieee80211_reg_rule *reg_rule = NULL; const struct ieee80211_power_rule *power_rule = NULL; |
038659e7c cfg80211: Process... |
1101 |
const struct ieee80211_freq_range *freq_range = NULL; |
1fa25e413 cfg80211: add wip... |
1102 1103 |
struct ieee80211_supported_band *sband; struct ieee80211_channel *chan; |
abc7381bc cfg80211: decoupl... |
1104 |
assert_reg_lock(); |
ac46d48e0 cfg80211: fix rac... |
1105 |
|
1fa25e413 cfg80211: add wip... |
1106 1107 1108 |
sband = wiphy->bands[band]; BUG_ON(chan_idx >= sband->n_channels); chan = &sband->channels[chan_idx]; |
038659e7c cfg80211: Process... |
1109 1110 1111 1112 1113 |
r = freq_reg_info_regd(wiphy, MHZ_TO_KHZ(chan->center_freq), desired_bw_khz, ®_rule, regd); |
1fa25e413 cfg80211: add wip... |
1114 1115 |
if (r) { |
d91e41b69 cfg80211: prefix ... |
1116 |
REG_DBG_PRINT("Disabling freq %d MHz as custom " |
a65185367 cfg80211: add deb... |
1117 1118 1119 1120 1121 |
"regd has no rule that fits a %d MHz " "wide channel ", chan->center_freq, KHZ_TO_MHZ(desired_bw_khz)); |
1fa25e413 cfg80211: add wip... |
1122 1123 1124 |
chan->flags = IEEE80211_CHAN_DISABLED; return; } |
e702d3cf2 cfg80211: add deb... |
1125 |
chan_reg_rule_print_dbg(chan, desired_bw_khz, reg_rule); |
1fa25e413 cfg80211: add wip... |
1126 |
power_rule = ®_rule->power_rule; |
038659e7c cfg80211: Process... |
1127 1128 1129 1130 |
freq_range = ®_rule->freq_range; if (freq_range->max_bandwidth_khz < MHZ_TO_KHZ(40)) bw_flags = IEEE80211_CHAN_NO_HT40; |
1fa25e413 cfg80211: add wip... |
1131 |
|
038659e7c cfg80211: Process... |
1132 |
chan->flags |= map_regdom_flags(reg_rule->flags) | bw_flags; |
1fa25e413 cfg80211: add wip... |
1133 |
chan->max_antenna_gain = (int) MBI_TO_DBI(power_rule->max_antenna_gain); |
1fa25e413 cfg80211: add wip... |
1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 |
chan->max_power = (int) MBM_TO_DBM(power_rule->max_eirp); } static void handle_band_custom(struct wiphy *wiphy, enum ieee80211_band band, const struct ieee80211_regdomain *regd) { unsigned int i; struct ieee80211_supported_band *sband; BUG_ON(!wiphy->bands[band]); sband = wiphy->bands[band]; for (i = 0; i < sband->n_channels; i++) handle_channel_custom(wiphy, band, i, regd); } /* Used by drivers prior to wiphy registration */ void wiphy_apply_custom_regulatory(struct wiphy *wiphy, const struct ieee80211_regdomain *regd) { enum ieee80211_band band; |
bbcf3f027 cfg80211: warn wh... |
1155 |
unsigned int bands_set = 0; |
ac46d48e0 cfg80211: fix rac... |
1156 |
|
abc7381bc cfg80211: decoupl... |
1157 |
mutex_lock(®_mutex); |
1fa25e413 cfg80211: add wip... |
1158 |
for (band = 0; band < IEEE80211_NUM_BANDS; band++) { |
bbcf3f027 cfg80211: warn wh... |
1159 1160 1161 1162 |
if (!wiphy->bands[band]) continue; handle_band_custom(wiphy, band, regd); bands_set++; |
b2e1b3029 cfg80211: Add new... |
1163 |
} |
abc7381bc cfg80211: decoupl... |
1164 |
mutex_unlock(®_mutex); |
bbcf3f027 cfg80211: warn wh... |
1165 1166 1167 1168 1169 1170 |
/* * no point in calling this if it won't have any effect * on your device's supportd bands. */ WARN_ON(!bands_set); |
b2e1b3029 cfg80211: Add new... |
1171 |
} |
1fa25e413 cfg80211: add wip... |
1172 |
EXPORT_SYMBOL(wiphy_apply_custom_regulatory); |
fb1fc7add cfg80211: comment... |
1173 1174 1175 1176 |
/* * Return value which can be used by ignore_request() to indicate * it has been determined we should intersect two regulatory domains */ |
9c96477d1 cfg80211: Add reg... |
1177 |
#define REG_INTERSECT 1 |
84fa4f43c wireless regulato... |
1178 1179 |
/* This has the logic which determines when a new request * should be ignored. */ |
2f92cd2e5 cfg80211: pass th... |
1180 1181 |
static int ignore_request(struct wiphy *wiphy, struct regulatory_request *pending_request) |
84fa4f43c wireless regulato... |
1182 |
{ |
806a9e396 cfg80211: make re... |
1183 |
struct wiphy *last_wiphy = NULL; |
761cf7ecf cfg80211: add ass... |
1184 1185 |
assert_cfg80211_lock(); |
84fa4f43c wireless regulato... |
1186 1187 1188 |
/* All initial requests are respected */ if (!last_request) return 0; |
2f92cd2e5 cfg80211: pass th... |
1189 |
switch (pending_request->initiator) { |
7db90f4a2 cfg80211: move en... |
1190 |
case NL80211_REGDOM_SET_BY_CORE: |
09d989d17 cfg80211: add reg... |
1191 |
return 0; |
7db90f4a2 cfg80211: move en... |
1192 |
case NL80211_REGDOM_SET_BY_COUNTRY_IE: |
806a9e396 cfg80211: make re... |
1193 1194 |
last_wiphy = wiphy_idx_to_wiphy(last_request->wiphy_idx); |
2f92cd2e5 cfg80211: pass th... |
1195 |
if (unlikely(!is_an_alpha2(pending_request->alpha2))) |
84fa4f43c wireless regulato... |
1196 |
return -EINVAL; |
7db90f4a2 cfg80211: move en... |
1197 1198 |
if (last_request->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) { |
806a9e396 cfg80211: make re... |
1199 |
if (last_wiphy != wiphy) { |
84fa4f43c wireless regulato... |
1200 1201 |
/* * Two cards with two APs claiming different |
1fe90b033 trivial: remove d... |
1202 |
* Country IE alpha2s. We could |
84fa4f43c wireless regulato... |
1203 1204 1205 |
* intersect them, but that seems unlikely * to be correct. Reject second one for now. */ |
2f92cd2e5 cfg80211: pass th... |
1206 |
if (regdom_changes(pending_request->alpha2)) |
84fa4f43c wireless regulato... |
1207 1208 1209 |
return -EOPNOTSUPP; return -EALREADY; } |
fb1fc7add cfg80211: comment... |
1210 1211 1212 1213 |
/* * Two consecutive Country IE hints on the same wiphy. * This should be picked up early by the driver/stack */ |
2f92cd2e5 cfg80211: pass th... |
1214 |
if (WARN_ON(regdom_changes(pending_request->alpha2))) |
84fa4f43c wireless regulato... |
1215 1216 1217 |
return 0; return -EALREADY; } |
a171fba49 cfg80211: fix reg... |
1218 |
return 0; |
7db90f4a2 cfg80211: move en... |
1219 1220 |
case NL80211_REGDOM_SET_BY_DRIVER: if (last_request->initiator == NL80211_REGDOM_SET_BY_CORE) { |
2f92cd2e5 cfg80211: pass th... |
1221 |
if (regdom_changes(pending_request->alpha2)) |
e74b1e7fb cfg80211: ignore ... |
1222 |
return 0; |
84fa4f43c wireless regulato... |
1223 |
return -EALREADY; |
e74b1e7fb cfg80211: ignore ... |
1224 |
} |
fff32c04f cfg80211: allow d... |
1225 1226 1227 1228 1229 1230 |
/* * This would happen if you unplug and plug your card * back in or if you add a new device for which the previously * loaded card also agrees on the regulatory domain. */ |
7db90f4a2 cfg80211: move en... |
1231 |
if (last_request->initiator == NL80211_REGDOM_SET_BY_DRIVER && |
2f92cd2e5 cfg80211: pass th... |
1232 |
!regdom_changes(pending_request->alpha2)) |
fff32c04f cfg80211: allow d... |
1233 |
return -EALREADY; |
3e0c3ff36 cfg80211: allow m... |
1234 |
return REG_INTERSECT; |
7db90f4a2 cfg80211: move en... |
1235 1236 |
case NL80211_REGDOM_SET_BY_USER: if (last_request->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) |
9c96477d1 cfg80211: Add reg... |
1237 |
return REG_INTERSECT; |
fb1fc7add cfg80211: comment... |
1238 1239 1240 1241 |
/* * If the user knows better the user should set the regdom * to their country before the IE is picked up */ |
7db90f4a2 cfg80211: move en... |
1242 |
if (last_request->initiator == NL80211_REGDOM_SET_BY_USER && |
3f2355cb9 cfg80211/mac80211... |
1243 1244 |
last_request->intersect) return -EOPNOTSUPP; |
fb1fc7add cfg80211: comment... |
1245 1246 1247 1248 |
/* * Process user requests only after previous user/driver/core * requests have been processed */ |
7db90f4a2 cfg80211: move en... |
1249 1250 1251 |
if (last_request->initiator == NL80211_REGDOM_SET_BY_CORE || last_request->initiator == NL80211_REGDOM_SET_BY_DRIVER || last_request->initiator == NL80211_REGDOM_SET_BY_USER) { |
69b1572bd cfg80211: rename ... |
1252 |
if (regdom_changes(last_request->alpha2)) |
5eebade60 cfg80211: process... |
1253 1254 |
return -EAGAIN; } |
baeb66fe2 wireless: remove ... |
1255 |
if (!regdom_changes(pending_request->alpha2)) |
e74b1e7fb cfg80211: ignore ... |
1256 |
return -EALREADY; |
84fa4f43c wireless regulato... |
1257 1258 1259 1260 1261 |
return 0; } return -EINVAL; } |
b2e253cf3 cfg80211: Fix reg... |
1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 |
static void reg_set_request_processed(void) { bool need_more_processing = false; last_request->processed = true; spin_lock(®_requests_lock); if (!list_empty(®_requests_list)) need_more_processing = true; spin_unlock(®_requests_lock); if (need_more_processing) schedule_work(®_work); } |
d1c96a9a2 cfg80211: make __... |
1276 1277 1278 1279 |
/** * __regulatory_hint - hint to the wireless core a regulatory domain * @wiphy: if the hint comes from country information from an AP, this * is required to be set to the wiphy that received the information |
28da32d7c cfg80211: pass th... |
1280 |
* @pending_request: the regulatory request currently being processed |
d1c96a9a2 cfg80211: make __... |
1281 1282 |
* * The Wireless subsystem can use this function to hint to the wireless core |
28da32d7c cfg80211: pass th... |
1283 |
* what it believes should be the current regulatory domain. |
d1c96a9a2 cfg80211: make __... |
1284 1285 1286 1287 |
* * Returns zero if all went fine, %-EALREADY if a regulatory domain had * already been set or other standard error codes. * |
abc7381bc cfg80211: decoupl... |
1288 |
* Caller must hold &cfg80211_mutex and ®_mutex |
d1c96a9a2 cfg80211: make __... |
1289 |
*/ |
28da32d7c cfg80211: pass th... |
1290 1291 |
static int __regulatory_hint(struct wiphy *wiphy, struct regulatory_request *pending_request) |
b2e1b3029 cfg80211: Add new... |
1292 |
{ |
9c96477d1 cfg80211: Add reg... |
1293 |
bool intersect = false; |
b2e1b3029 cfg80211: Add new... |
1294 |
int r = 0; |
761cf7ecf cfg80211: add ass... |
1295 |
assert_cfg80211_lock(); |
2f92cd2e5 cfg80211: pass th... |
1296 |
r = ignore_request(wiphy, pending_request); |
9c96477d1 cfg80211: Add reg... |
1297 |
|
3e0c3ff36 cfg80211: allow m... |
1298 |
if (r == REG_INTERSECT) { |
7db90f4a2 cfg80211: move en... |
1299 1300 |
if (pending_request->initiator == NL80211_REGDOM_SET_BY_DRIVER) { |
3e0c3ff36 cfg80211: allow m... |
1301 |
r = reg_copy_regd(&wiphy->regd, cfg80211_regdomain); |
d951c1dde cfg80211: do not ... |
1302 1303 |
if (r) { kfree(pending_request); |
3e0c3ff36 cfg80211: allow m... |
1304 |
return r; |
d951c1dde cfg80211: do not ... |
1305 |
} |
3e0c3ff36 cfg80211: allow m... |
1306 |
} |
9c96477d1 cfg80211: Add reg... |
1307 |
intersect = true; |
3e0c3ff36 cfg80211: allow m... |
1308 |
} else if (r) { |
fb1fc7add cfg80211: comment... |
1309 1310 |
/* * If the regulatory domain being requested by the |
3e0c3ff36 cfg80211: allow m... |
1311 |
* driver has already been set just copy it to the |
fb1fc7add cfg80211: comment... |
1312 1313 |
* wiphy */ |
28da32d7c cfg80211: pass th... |
1314 |
if (r == -EALREADY && |
7db90f4a2 cfg80211: move en... |
1315 1316 |
pending_request->initiator == NL80211_REGDOM_SET_BY_DRIVER) { |
3e0c3ff36 cfg80211: allow m... |
1317 |
r = reg_copy_regd(&wiphy->regd, cfg80211_regdomain); |
d951c1dde cfg80211: do not ... |
1318 1319 |
if (r) { kfree(pending_request); |
3e0c3ff36 cfg80211: allow m... |
1320 |
return r; |
d951c1dde cfg80211: do not ... |
1321 |
} |
3e0c3ff36 cfg80211: allow m... |
1322 1323 1324 |
r = -EALREADY; goto new_request; } |
d951c1dde cfg80211: do not ... |
1325 |
kfree(pending_request); |
b2e1b3029 cfg80211: Add new... |
1326 |
return r; |
3e0c3ff36 cfg80211: allow m... |
1327 |
} |
b2e1b3029 cfg80211: Add new... |
1328 |
|
3e0c3ff36 cfg80211: allow m... |
1329 |
new_request: |
d951c1dde cfg80211: do not ... |
1330 |
kfree(last_request); |
5203cdb6a cfg80211: remove ... |
1331 |
|
d951c1dde cfg80211: do not ... |
1332 1333 |
last_request = pending_request; last_request->intersect = intersect; |
5203cdb6a cfg80211: remove ... |
1334 |
|
d951c1dde cfg80211: do not ... |
1335 |
pending_request = NULL; |
3e0c3ff36 cfg80211: allow m... |
1336 |
|
09d989d17 cfg80211: add reg... |
1337 1338 1339 1340 |
if (last_request->initiator == NL80211_REGDOM_SET_BY_USER) { user_alpha2[0] = last_request->alpha2[0]; user_alpha2[1] = last_request->alpha2[1]; } |
3e0c3ff36 cfg80211: allow m... |
1341 |
/* When r == REG_INTERSECT we do need to call CRDA */ |
73d54c9e7 cfg80211: add reg... |
1342 1343 1344 1345 1346 1347 |
if (r < 0) { /* * Since CRDA will not be called in this case as we already * have applied the requested regulatory domain before we just * inform userspace we have processed the request */ |
b2e253cf3 cfg80211: Fix reg... |
1348 |
if (r == -EALREADY) { |
73d54c9e7 cfg80211: add reg... |
1349 |
nl80211_send_reg_change_event(last_request); |
b2e253cf3 cfg80211: Fix reg... |
1350 1351 |
reg_set_request_processed(); } |
3e0c3ff36 cfg80211: allow m... |
1352 |
return r; |
73d54c9e7 cfg80211: add reg... |
1353 |
} |
3e0c3ff36 cfg80211: allow m... |
1354 |
|
d951c1dde cfg80211: do not ... |
1355 |
return call_crda(last_request->alpha2); |
b2e1b3029 cfg80211: Add new... |
1356 |
} |
30a548c72 cfg80211: fix com... |
1357 |
/* This processes *all* regulatory hints */ |
d951c1dde cfg80211: do not ... |
1358 |
static void reg_process_hint(struct regulatory_request *reg_request) |
fe33eb390 cfg80211: move al... |
1359 1360 1361 |
{ int r = 0; struct wiphy *wiphy = NULL; |
c4c322941 cfg80211: Update ... |
1362 |
enum nl80211_reg_initiator initiator = reg_request->initiator; |
fe33eb390 cfg80211: move al... |
1363 1364 |
BUG_ON(!reg_request->alpha2); |
fe33eb390 cfg80211: move al... |
1365 1366 |
if (wiphy_idx_valid(reg_request->wiphy_idx)) wiphy = wiphy_idx_to_wiphy(reg_request->wiphy_idx); |
7db90f4a2 cfg80211: move en... |
1367 |
if (reg_request->initiator == NL80211_REGDOM_SET_BY_DRIVER && |
fe33eb390 cfg80211: move al... |
1368 |
!wiphy) { |
d951c1dde cfg80211: do not ... |
1369 |
kfree(reg_request); |
b0e2880b0 cfg80211: move mu... |
1370 |
return; |
fe33eb390 cfg80211: move al... |
1371 |
} |
28da32d7c cfg80211: pass th... |
1372 |
r = __regulatory_hint(wiphy, reg_request); |
fe33eb390 cfg80211: move al... |
1373 |
/* This is required so that the orig_* parameters are saved */ |
5be83de54 cfg80211: convert... |
1374 1375 |
if (r == -EALREADY && wiphy && wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY) |
c4c322941 cfg80211: Update ... |
1376 |
wiphy_update_regulatory(wiphy, initiator); |
fe33eb390 cfg80211: move al... |
1377 |
} |
b2e253cf3 cfg80211: Fix reg... |
1378 1379 1380 1381 1382 |
/* * Processes regulatory hints, this is all the NL80211_REGDOM_SET_BY_* * Regulatory hints come on a first come first serve basis and we * must process each one atomically. */ |
fe33eb390 cfg80211: move al... |
1383 |
static void reg_process_pending_hints(void) |
b0e2880b0 cfg80211: move mu... |
1384 |
{ |
fe33eb390 cfg80211: move al... |
1385 |
struct regulatory_request *reg_request; |
fe33eb390 cfg80211: move al... |
1386 |
|
b0e2880b0 cfg80211: move mu... |
1387 1388 |
mutex_lock(&cfg80211_mutex); mutex_lock(®_mutex); |
b2e253cf3 cfg80211: Fix reg... |
1389 1390 1391 1392 1393 1394 |
/* When last_request->processed becomes true this will be rescheduled */ if (last_request && !last_request->processed) { REG_DBG_PRINT("Pending regulatory request, waiting " "for it to be processed..."); goto out; } |
fe33eb390 cfg80211: move al... |
1395 |
spin_lock(®_requests_lock); |
fe33eb390 cfg80211: move al... |
1396 |
|
b2e253cf3 cfg80211: Fix reg... |
1397 |
if (list_empty(®_requests_list)) { |
d951c1dde cfg80211: do not ... |
1398 |
spin_unlock(®_requests_lock); |
b2e253cf3 cfg80211: Fix reg... |
1399 |
goto out; |
fe33eb390 cfg80211: move al... |
1400 |
} |
b2e253cf3 cfg80211: Fix reg... |
1401 1402 1403 1404 1405 |
reg_request = list_first_entry(®_requests_list, struct regulatory_request, list); list_del_init(®_request->list); |
fe33eb390 cfg80211: move al... |
1406 |
spin_unlock(®_requests_lock); |
b0e2880b0 cfg80211: move mu... |
1407 |
|
b2e253cf3 cfg80211: Fix reg... |
1408 1409 1410 |
reg_process_hint(reg_request); out: |
b0e2880b0 cfg80211: move mu... |
1411 1412 |
mutex_unlock(®_mutex); mutex_unlock(&cfg80211_mutex); |
fe33eb390 cfg80211: move al... |
1413 |
} |
e38f8a7a8 cfg80211: Add AP ... |
1414 1415 1416 |
/* Processes beacon hints -- this has nothing to do with country IEs */ static void reg_process_pending_beacon_hints(void) { |
79c97e97a cfg80211: clean u... |
1417 |
struct cfg80211_registered_device *rdev; |
e38f8a7a8 cfg80211: Add AP ... |
1418 |
struct reg_beacon *pending_beacon, *tmp; |
abc7381bc cfg80211: decoupl... |
1419 1420 1421 1422 |
/* * No need to hold the reg_mutex here as we just touch wiphys * and do not read or access regulatory variables. */ |
e38f8a7a8 cfg80211: Add AP ... |
1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 |
mutex_lock(&cfg80211_mutex); /* This goes through the _pending_ beacon list */ spin_lock_bh(®_pending_beacons_lock); if (list_empty(®_pending_beacons)) { spin_unlock_bh(®_pending_beacons_lock); goto out; } list_for_each_entry_safe(pending_beacon, tmp, ®_pending_beacons, list) { list_del_init(&pending_beacon->list); /* Applies the beacon hint to current wiphys */ |
79c97e97a cfg80211: clean u... |
1439 1440 |
list_for_each_entry(rdev, &cfg80211_rdev_list, list) wiphy_update_new_beacon(&rdev->wiphy, pending_beacon); |
e38f8a7a8 cfg80211: Add AP ... |
1441 1442 1443 1444 1445 1446 1447 1448 1449 |
/* Remembers the beacon hint for new wiphys or reg changes */ list_add_tail(&pending_beacon->list, ®_beacon_list); } spin_unlock_bh(®_pending_beacons_lock); out: mutex_unlock(&cfg80211_mutex); } |
fe33eb390 cfg80211: move al... |
1450 1451 1452 |
static void reg_todo(struct work_struct *work) { reg_process_pending_hints(); |
e38f8a7a8 cfg80211: Add AP ... |
1453 |
reg_process_pending_beacon_hints(); |
fe33eb390 cfg80211: move al... |
1454 |
} |
fe33eb390 cfg80211: move al... |
1455 1456 |
static void queue_regulatory_request(struct regulatory_request *request) { |
c61029c77 wireless: upcase ... |
1457 1458 1459 1460 |
if (isalpha(request->alpha2[0])) request->alpha2[0] = toupper(request->alpha2[0]); if (isalpha(request->alpha2[1])) request->alpha2[1] = toupper(request->alpha2[1]); |
fe33eb390 cfg80211: move al... |
1461 1462 1463 1464 1465 1466 |
spin_lock(®_requests_lock); list_add_tail(&request->list, ®_requests_list); spin_unlock(®_requests_lock); schedule_work(®_work); } |
09d989d17 cfg80211: add reg... |
1467 1468 1469 1470 |
/* * Core regulatory hint -- happens during cfg80211_init() * and when we restore regulatory settings. */ |
ba25c1414 cfg80211: add reg... |
1471 1472 1473 |
static int regulatory_hint_core(const char *alpha2) { struct regulatory_request *request; |
09d989d17 cfg80211: add reg... |
1474 1475 |
kfree(last_request); last_request = NULL; |
ba25c1414 cfg80211: add reg... |
1476 1477 1478 1479 1480 1481 1482 1483 |
request = kzalloc(sizeof(struct regulatory_request), GFP_KERNEL); if (!request) return -ENOMEM; request->alpha2[0] = alpha2[0]; request->alpha2[1] = alpha2[1]; |
7db90f4a2 cfg80211: move en... |
1484 |
request->initiator = NL80211_REGDOM_SET_BY_CORE; |
ba25c1414 cfg80211: add reg... |
1485 |
|
31e99729a cfg80211: put cor... |
1486 |
queue_regulatory_request(request); |
5078b2e32 cfg80211: fix rac... |
1487 |
|
fe33eb390 cfg80211: move al... |
1488 |
return 0; |
ba25c1414 cfg80211: add reg... |
1489 |
} |
fe33eb390 cfg80211: move al... |
1490 1491 |
/* User hints */ int regulatory_hint_user(const char *alpha2) |
b2e1b3029 cfg80211: Add new... |
1492 |
{ |
fe33eb390 cfg80211: move al... |
1493 |
struct regulatory_request *request; |
be3d48106 wireless: remove ... |
1494 |
BUG_ON(!alpha2); |
b2e1b3029 cfg80211: Add new... |
1495 |
|
fe33eb390 cfg80211: move al... |
1496 1497 1498 1499 1500 1501 1502 |
request = kzalloc(sizeof(struct regulatory_request), GFP_KERNEL); if (!request) return -ENOMEM; request->wiphy_idx = WIPHY_IDX_STALE; request->alpha2[0] = alpha2[0]; request->alpha2[1] = alpha2[1]; |
e12822e1d cfg80211: fix syn... |
1503 |
request->initiator = NL80211_REGDOM_SET_BY_USER; |
fe33eb390 cfg80211: move al... |
1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 |
queue_regulatory_request(request); return 0; } /* Driver hints */ int regulatory_hint(struct wiphy *wiphy, const char *alpha2) { struct regulatory_request *request; BUG_ON(!alpha2); BUG_ON(!wiphy); request = kzalloc(sizeof(struct regulatory_request), GFP_KERNEL); if (!request) return -ENOMEM; request->wiphy_idx = get_wiphy_idx(wiphy); /* Must have registered wiphy first */ BUG_ON(!wiphy_idx_valid(request->wiphy_idx)); request->alpha2[0] = alpha2[0]; request->alpha2[1] = alpha2[1]; |
7db90f4a2 cfg80211: move en... |
1529 |
request->initiator = NL80211_REGDOM_SET_BY_DRIVER; |
fe33eb390 cfg80211: move al... |
1530 1531 1532 1533 |
queue_regulatory_request(request); return 0; |
b2e1b3029 cfg80211: Add new... |
1534 1535 |
} EXPORT_SYMBOL(regulatory_hint); |
4b44c8bc4 cfg80211: do not ... |
1536 1537 1538 1539 |
/* * We hold wdev_lock() here so we cannot hold cfg80211_mutex() and * therefore cannot iterate over the rdev list here. */ |
3f2355cb9 cfg80211/mac80211... |
1540 |
void regulatory_hint_11d(struct wiphy *wiphy, |
84920e3e4 cfg80211: make re... |
1541 1542 1543 |
enum ieee80211_band band, u8 *country_ie, u8 country_ie_len) |
3f2355cb9 cfg80211/mac80211... |
1544 |
{ |
3f2355cb9 cfg80211/mac80211... |
1545 |
char alpha2[2]; |
3f2355cb9 cfg80211/mac80211... |
1546 |
enum environment_cap env = ENVIRON_ANY; |
fe33eb390 cfg80211: move al... |
1547 |
struct regulatory_request *request; |
3f2355cb9 cfg80211/mac80211... |
1548 |
|
abc7381bc cfg80211: decoupl... |
1549 |
mutex_lock(®_mutex); |
3f2355cb9 cfg80211/mac80211... |
1550 |
|
9828b0170 cfg80211: use got... |
1551 1552 |
if (unlikely(!last_request)) goto out; |
d335fe639 cfg80211: protect... |
1553 |
|
3f2355cb9 cfg80211/mac80211... |
1554 1555 1556 1557 1558 1559 |
/* IE len must be evenly divisible by 2 */ if (country_ie_len & 0x01) goto out; if (country_ie_len < IEEE80211_COUNTRY_IE_MIN_LEN) goto out; |
3f2355cb9 cfg80211/mac80211... |
1560 1561 1562 1563 1564 1565 1566 |
alpha2[0] = country_ie[0]; alpha2[1] = country_ie[1]; if (country_ie[2] == 'I') env = ENVIRON_INDOOR; else if (country_ie[2] == 'O') env = ENVIRON_OUTDOOR; |
fb1fc7add cfg80211: comment... |
1567 |
/* |
8b19e6ca3 cfg80211: enable ... |
1568 |
* We will run this only upon a successful connection on cfg80211. |
4b44c8bc4 cfg80211: do not ... |
1569 1570 |
* We leave conflict resolution to the workqueue, where can hold * cfg80211_mutex. |
fb1fc7add cfg80211: comment... |
1571 |
*/ |
cc0b6fe88 cfg80211: fix inc... |
1572 1573 |
if (likely(last_request->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE && |
4b44c8bc4 cfg80211: do not ... |
1574 1575 |
wiphy_idx_valid(last_request->wiphy_idx))) goto out; |
3f2355cb9 cfg80211/mac80211... |
1576 |
|
fe33eb390 cfg80211: move al... |
1577 1578 |
request = kzalloc(sizeof(struct regulatory_request), GFP_KERNEL); if (!request) |
f9f9b6e3e wireless: remove ... |
1579 |
goto out; |
fe33eb390 cfg80211: move al... |
1580 |
|
fe33eb390 cfg80211: move al... |
1581 |
request->wiphy_idx = get_wiphy_idx(wiphy); |
4f366c5da wireless: only us... |
1582 1583 |
request->alpha2[0] = alpha2[0]; request->alpha2[1] = alpha2[1]; |
7db90f4a2 cfg80211: move en... |
1584 |
request->initiator = NL80211_REGDOM_SET_BY_COUNTRY_IE; |
fe33eb390 cfg80211: move al... |
1585 |
request->country_ie_env = env; |
abc7381bc cfg80211: decoupl... |
1586 |
mutex_unlock(®_mutex); |
3f2355cb9 cfg80211/mac80211... |
1587 |
|
fe33eb390 cfg80211: move al... |
1588 1589 1590 |
queue_regulatory_request(request); return; |
0441d6ffc cfg80211: free rd... |
1591 |
|
3f2355cb9 cfg80211/mac80211... |
1592 |
out: |
abc7381bc cfg80211: decoupl... |
1593 |
mutex_unlock(®_mutex); |
3f2355cb9 cfg80211/mac80211... |
1594 |
} |
b2e1b3029 cfg80211: Add new... |
1595 |
|
09d989d17 cfg80211: add reg... |
1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 |
static void restore_alpha2(char *alpha2, bool reset_user) { /* indicates there is no alpha2 to consider for restoration */ alpha2[0] = '9'; alpha2[1] = '7'; /* The user setting has precedence over the module parameter */ if (is_user_regdom_saved()) { /* Unless we're asked to ignore it and reset it */ if (reset_user) { |
d91e41b69 cfg80211: prefix ... |
1606 |
REG_DBG_PRINT("Restoring regulatory settings " |
09d989d17 cfg80211: add reg... |
1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 |
"including user preference "); user_alpha2[0] = '9'; user_alpha2[1] = '7'; /* * If we're ignoring user settings, we still need to * check the module parameter to ensure we put things * back as they were for a full restore. */ if (!is_world_regdom(ieee80211_regdom)) { |
d91e41b69 cfg80211: prefix ... |
1618 |
REG_DBG_PRINT("Keeping preference on " |
09d989d17 cfg80211: add reg... |
1619 1620 1621 1622 1623 1624 1625 1626 |
"module parameter ieee80211_regdom: %c%c ", ieee80211_regdom[0], ieee80211_regdom[1]); alpha2[0] = ieee80211_regdom[0]; alpha2[1] = ieee80211_regdom[1]; } } else { |
d91e41b69 cfg80211: prefix ... |
1627 |
REG_DBG_PRINT("Restoring regulatory settings " |
09d989d17 cfg80211: add reg... |
1628 1629 1630 1631 1632 1633 1634 1635 |
"while preserving user preference for: %c%c ", user_alpha2[0], user_alpha2[1]); alpha2[0] = user_alpha2[0]; alpha2[1] = user_alpha2[1]; } } else if (!is_world_regdom(ieee80211_regdom)) { |
d91e41b69 cfg80211: prefix ... |
1636 |
REG_DBG_PRINT("Keeping preference on " |
09d989d17 cfg80211: add reg... |
1637 1638 1639 1640 1641 1642 1643 |
"module parameter ieee80211_regdom: %c%c ", ieee80211_regdom[0], ieee80211_regdom[1]); alpha2[0] = ieee80211_regdom[0]; alpha2[1] = ieee80211_regdom[1]; } else |
d91e41b69 cfg80211: prefix ... |
1644 1645 |
REG_DBG_PRINT("Restoring regulatory settings "); |
09d989d17 cfg80211: add reg... |
1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 |
} /* * Restoring regulatory settings involves ingoring any * possibly stale country IE information and user regulatory * settings if so desired, this includes any beacon hints * learned as we could have traveled outside to another country * after disconnection. To restore regulatory settings we do * exactly what we did at bootup: * * - send a core regulatory hint * - send a user regulatory hint if applicable * * Device drivers that send a regulatory hint for a specific country * keep their own regulatory domain on wiphy->regd so that does does * not need to be remembered. */ static void restore_regulatory_settings(bool reset_user) { char alpha2[2]; struct reg_beacon *reg_beacon, *btmp; mutex_lock(&cfg80211_mutex); mutex_lock(®_mutex); reset_regdomains(); restore_alpha2(alpha2, reset_user); /* Clear beacon hints */ spin_lock_bh(®_pending_beacons_lock); if (!list_empty(®_pending_beacons)) { list_for_each_entry_safe(reg_beacon, btmp, ®_pending_beacons, list) { list_del(®_beacon->list); kfree(reg_beacon); } } spin_unlock_bh(®_pending_beacons_lock); if (!list_empty(®_beacon_list)) { list_for_each_entry_safe(reg_beacon, btmp, ®_beacon_list, list) { list_del(®_beacon->list); kfree(reg_beacon); } } /* First restore to the basic regulatory settings */ cfg80211_regdomain = cfg80211_world_regdom; mutex_unlock(®_mutex); mutex_unlock(&cfg80211_mutex); regulatory_hint_core(cfg80211_regdomain->alpha2); /* * This restores the ieee80211_regdom module parameter * preference or the last user requested regulatory * settings, user regulatory settings takes precedence. */ if (is_an_alpha2(alpha2)) regulatory_hint_user(user_alpha2); } void regulatory_hint_disconnect(void) { |
d91e41b69 cfg80211: prefix ... |
1713 |
REG_DBG_PRINT("All devices are disconnected, going to " |
09d989d17 cfg80211: add reg... |
1714 1715 1716 1717 |
"restore regulatory settings "); restore_regulatory_settings(false); } |
e38f8a7a8 cfg80211: Add AP ... |
1718 1719 |
static bool freq_is_chan_12_13_14(u16 freq) { |
59eb21a65 cfg80211: Extend ... |
1720 1721 1722 |
if (freq == ieee80211_channel_to_frequency(12, IEEE80211_BAND_2GHZ) || freq == ieee80211_channel_to_frequency(13, IEEE80211_BAND_2GHZ) || freq == ieee80211_channel_to_frequency(14, IEEE80211_BAND_2GHZ)) |
e38f8a7a8 cfg80211: Add AP ... |
1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 |
return true; return false; } int regulatory_hint_found_beacon(struct wiphy *wiphy, struct ieee80211_channel *beacon_chan, gfp_t gfp) { struct reg_beacon *reg_beacon; if (likely((beacon_chan->beacon_found || (beacon_chan->flags & IEEE80211_CHAN_RADAR) || (beacon_chan->band == IEEE80211_BAND_2GHZ && !freq_is_chan_12_13_14(beacon_chan->center_freq))))) return 0; reg_beacon = kzalloc(sizeof(struct reg_beacon), gfp); if (!reg_beacon) return -ENOMEM; |
d91e41b69 cfg80211: prefix ... |
1742 |
REG_DBG_PRINT("Found new beacon on " |
4113f7518 cfg80211: add a r... |
1743 1744 1745 1746 1747 |
"frequency: %d MHz (Ch %d) on %s ", beacon_chan->center_freq, ieee80211_frequency_to_channel(beacon_chan->center_freq), wiphy_name(wiphy)); |
e38f8a7a8 cfg80211: Add AP ... |
1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 |
memcpy(®_beacon->chan, beacon_chan, sizeof(struct ieee80211_channel)); /* * Since we can be called from BH or and non-BH context * we must use spin_lock_bh() */ spin_lock_bh(®_pending_beacons_lock); list_add_tail(®_beacon->list, ®_pending_beacons); spin_unlock_bh(®_pending_beacons_lock); schedule_work(®_work); return 0; } |
a3d2eaf0d cfg80211: fix reg... |
1764 |
static void print_rd_rules(const struct ieee80211_regdomain *rd) |
b2e1b3029 cfg80211: Add new... |
1765 1766 |
{ unsigned int i; |
a3d2eaf0d cfg80211: fix reg... |
1767 1768 1769 |
const struct ieee80211_reg_rule *reg_rule = NULL; const struct ieee80211_freq_range *freq_range = NULL; const struct ieee80211_power_rule *power_rule = NULL; |
b2e1b3029 cfg80211: Add new... |
1770 |
|
e9c0268f0 net/wireless: Use... |
1771 1772 |
pr_info(" (start_freq - end_freq @ bandwidth), (max_antenna_gain, max_eirp) "); |
b2e1b3029 cfg80211: Add new... |
1773 1774 1775 1776 1777 |
for (i = 0; i < rd->n_reg_rules; i++) { reg_rule = &rd->reg_rules[i]; freq_range = ®_rule->freq_range; power_rule = ®_rule->power_rule; |
fb1fc7add cfg80211: comment... |
1778 1779 1780 1781 |
/* * There may not be documentation for max antenna gain * in certain regions */ |
b2e1b3029 cfg80211: Add new... |
1782 |
if (power_rule->max_antenna_gain) |
e9c0268f0 net/wireless: Use... |
1783 1784 |
pr_info(" (%d KHz - %d KHz @ %d KHz), (%d mBi, %d mBm) ", |
b2e1b3029 cfg80211: Add new... |
1785 1786 1787 1788 1789 1790 |
freq_range->start_freq_khz, freq_range->end_freq_khz, freq_range->max_bandwidth_khz, power_rule->max_antenna_gain, power_rule->max_eirp); else |
e9c0268f0 net/wireless: Use... |
1791 1792 |
pr_info(" (%d KHz - %d KHz @ %d KHz), (N/A, %d mBm) ", |
b2e1b3029 cfg80211: Add new... |
1793 1794 1795 1796 1797 1798 |
freq_range->start_freq_khz, freq_range->end_freq_khz, freq_range->max_bandwidth_khz, power_rule->max_eirp); } } |
a3d2eaf0d cfg80211: fix reg... |
1799 |
static void print_regdomain(const struct ieee80211_regdomain *rd) |
b2e1b3029 cfg80211: Add new... |
1800 |
{ |
3f2355cb9 cfg80211/mac80211... |
1801 |
if (is_intersected_alpha2(rd->alpha2)) { |
3f2355cb9 cfg80211/mac80211... |
1802 |
|
7db90f4a2 cfg80211: move en... |
1803 1804 |
if (last_request->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) { |
79c97e97a cfg80211: clean u... |
1805 1806 |
struct cfg80211_registered_device *rdev; rdev = cfg80211_rdev_by_wiphy_idx( |
806a9e396 cfg80211: make re... |
1807 |
last_request->wiphy_idx); |
79c97e97a cfg80211: clean u... |
1808 |
if (rdev) { |
e9c0268f0 net/wireless: Use... |
1809 1810 |
pr_info("Current regulatory domain updated by AP to: %c%c ", |
79c97e97a cfg80211: clean u... |
1811 1812 |
rdev->country_ie_alpha2[0], rdev->country_ie_alpha2[1]); |
3f2355cb9 cfg80211/mac80211... |
1813 |
} else |
e9c0268f0 net/wireless: Use... |
1814 1815 |
pr_info("Current regulatory domain intersected: "); |
3f2355cb9 cfg80211/mac80211... |
1816 |
} else |
e9c0268f0 net/wireless: Use... |
1817 1818 |
pr_info("Current regulatory domain intersected: "); |
3f2355cb9 cfg80211/mac80211... |
1819 |
} else if (is_world_regdom(rd->alpha2)) |
e9c0268f0 net/wireless: Use... |
1820 1821 |
pr_info("World regulatory domain updated: "); |
b2e1b3029 cfg80211: Add new... |
1822 1823 |
else { if (is_unknown_alpha2(rd->alpha2)) |
e9c0268f0 net/wireless: Use... |
1824 1825 |
pr_info("Regulatory domain changed to driver built-in settings (unknown country) "); |
b2e1b3029 cfg80211: Add new... |
1826 |
else |
e9c0268f0 net/wireless: Use... |
1827 1828 |
pr_info("Regulatory domain changed to country: %c%c ", |
b2e1b3029 cfg80211: Add new... |
1829 1830 1831 1832 |
rd->alpha2[0], rd->alpha2[1]); } print_rd_rules(rd); } |
2df78167a wireless: fix a f... |
1833 |
static void print_regdomain_info(const struct ieee80211_regdomain *rd) |
b2e1b3029 cfg80211: Add new... |
1834 |
{ |
e9c0268f0 net/wireless: Use... |
1835 1836 |
pr_info("Regulatory domain: %c%c ", rd->alpha2[0], rd->alpha2[1]); |
b2e1b3029 cfg80211: Add new... |
1837 1838 |
print_rd_rules(rd); } |
d2372b315 wireless: make re... |
1839 |
/* Takes ownership of rd only if it doesn't fail */ |
a3d2eaf0d cfg80211: fix reg... |
1840 |
static int __set_regdom(const struct ieee80211_regdomain *rd) |
b2e1b3029 cfg80211: Add new... |
1841 |
{ |
9c96477d1 cfg80211: Add reg... |
1842 |
const struct ieee80211_regdomain *intersected_rd = NULL; |
79c97e97a cfg80211: clean u... |
1843 |
struct cfg80211_registered_device *rdev = NULL; |
806a9e396 cfg80211: make re... |
1844 |
struct wiphy *request_wiphy; |
b2e1b3029 cfg80211: Add new... |
1845 |
/* Some basic sanity checks first */ |
b2e1b3029 cfg80211: Add new... |
1846 |
if (is_world_regdom(rd->alpha2)) { |
f6037d09e wireless: get rid... |
1847 |
if (WARN_ON(!reg_is_valid_request(rd->alpha2))) |
b2e1b3029 cfg80211: Add new... |
1848 1849 1850 1851 |
return -EINVAL; update_world_regdomain(rd); return 0; } |
b2e1b3029 cfg80211: Add new... |
1852 1853 1854 1855 |
if (!is_alpha2_set(rd->alpha2) && !is_an_alpha2(rd->alpha2) && !is_unknown_alpha2(rd->alpha2)) return -EINVAL; |
f6037d09e wireless: get rid... |
1856 |
if (!last_request) |
b2e1b3029 cfg80211: Add new... |
1857 |
return -EINVAL; |
fb1fc7add cfg80211: comment... |
1858 1859 |
/* * Lets only bother proceeding on the same alpha2 if the current |
3f2355cb9 cfg80211/mac80211... |
1860 |
* rd is non static (it means CRDA was present and was used last) |
fb1fc7add cfg80211: comment... |
1861 1862 |
* and the pending request came in from a country IE */ |
7db90f4a2 cfg80211: move en... |
1863 |
if (last_request->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE) { |
fb1fc7add cfg80211: comment... |
1864 1865 1866 1867 |
/* * If someone else asked us to change the rd lets only bother * checking if the alpha2 changes if CRDA was already called */ |
baeb66fe2 wireless: remove ... |
1868 |
if (!regdom_changes(rd->alpha2)) |
3f2355cb9 cfg80211/mac80211... |
1869 1870 |
return -EINVAL; } |
fb1fc7add cfg80211: comment... |
1871 1872 |
/* * Now lets set the regulatory domain, update all driver channels |
b2e1b3029 cfg80211: Add new... |
1873 1874 |
* and finally inform them of what we have done, in case they want * to review or adjust their own settings based on their own |
fb1fc7add cfg80211: comment... |
1875 1876 |
* internal EEPROM data */ |
b2e1b3029 cfg80211: Add new... |
1877 |
|
f6037d09e wireless: get rid... |
1878 |
if (WARN_ON(!reg_is_valid_request(rd->alpha2))) |
b2e1b3029 cfg80211: Add new... |
1879 |
return -EINVAL; |
8375af3ba cfg80211: remove ... |
1880 |
if (!is_valid_rd(rd)) { |
e9c0268f0 net/wireless: Use... |
1881 1882 |
pr_err("Invalid regulatory domain detected: "); |
8375af3ba cfg80211: remove ... |
1883 1884 |
print_regdomain_info(rd); return -EINVAL; |
b2e1b3029 cfg80211: Add new... |
1885 |
} |
806a9e396 cfg80211: make re... |
1886 |
request_wiphy = wiphy_idx_to_wiphy(last_request->wiphy_idx); |
b8295acdc cfg80211: separat... |
1887 |
if (!last_request->intersect) { |
3e0c3ff36 cfg80211: allow m... |
1888 |
int r; |
7db90f4a2 cfg80211: move en... |
1889 |
if (last_request->initiator != NL80211_REGDOM_SET_BY_DRIVER) { |
3e0c3ff36 cfg80211: allow m... |
1890 1891 1892 1893 |
reset_regdomains(); cfg80211_regdomain = rd; return 0; } |
fb1fc7add cfg80211: comment... |
1894 1895 1896 1897 |
/* * For a driver hint, lets copy the regulatory domain the * driver wanted to the wiphy to deal with conflicts */ |
3e0c3ff36 cfg80211: allow m... |
1898 |
|
558f6d322 cfg80211: fix for... |
1899 1900 1901 1902 1903 1904 |
/* * Userspace could have sent two replies with only * one kernel request. */ if (request_wiphy->regd) return -EALREADY; |
3e0c3ff36 cfg80211: allow m... |
1905 |
|
806a9e396 cfg80211: make re... |
1906 |
r = reg_copy_regd(&request_wiphy->regd, rd); |
3e0c3ff36 cfg80211: allow m... |
1907 1908 |
if (r) return r; |
b8295acdc cfg80211: separat... |
1909 1910 1911 1912 1913 1914 |
reset_regdomains(); cfg80211_regdomain = rd; return 0; } /* Intersection requires a bit more work */ |
7db90f4a2 cfg80211: move en... |
1915 |
if (last_request->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE) { |
b8295acdc cfg80211: separat... |
1916 |
|
9c96477d1 cfg80211: Add reg... |
1917 1918 1919 |
intersected_rd = regdom_intersect(rd, cfg80211_regdomain); if (!intersected_rd) return -EINVAL; |
b8295acdc cfg80211: separat... |
1920 |
|
fb1fc7add cfg80211: comment... |
1921 1922 |
/* * We can trash what CRDA provided now. |
3e0c3ff36 cfg80211: allow m... |
1923 |
* However if a driver requested this specific regulatory |
fb1fc7add cfg80211: comment... |
1924 1925 |
* domain we keep it for its private use */ |
7db90f4a2 cfg80211: move en... |
1926 |
if (last_request->initiator == NL80211_REGDOM_SET_BY_DRIVER) |
806a9e396 cfg80211: make re... |
1927 |
request_wiphy->regd = rd; |
3e0c3ff36 cfg80211: allow m... |
1928 1929 |
else kfree(rd); |
b8295acdc cfg80211: separat... |
1930 1931 1932 1933 1934 1935 |
rd = NULL; reset_regdomains(); cfg80211_regdomain = intersected_rd; return 0; |
9c96477d1 cfg80211: Add reg... |
1936 |
} |
3f2355cb9 cfg80211/mac80211... |
1937 1938 |
if (!intersected_rd) return -EINVAL; |
79c97e97a cfg80211: clean u... |
1939 |
rdev = wiphy_to_dev(request_wiphy); |
3f2355cb9 cfg80211/mac80211... |
1940 |
|
79c97e97a cfg80211: clean u... |
1941 1942 1943 |
rdev->country_ie_alpha2[0] = rd->alpha2[0]; rdev->country_ie_alpha2[1] = rd->alpha2[1]; rdev->env = last_request->country_ie_env; |
3f2355cb9 cfg80211/mac80211... |
1944 1945 1946 1947 1948 |
BUG_ON(intersected_rd == rd); kfree(rd); rd = NULL; |
b8295acdc cfg80211: separat... |
1949 |
reset_regdomains(); |
3f2355cb9 cfg80211/mac80211... |
1950 |
cfg80211_regdomain = intersected_rd; |
b2e1b3029 cfg80211: Add new... |
1951 1952 1953 |
return 0; } |
fb1fc7add cfg80211: comment... |
1954 1955 |
/* * Use this call to set the current regulatory domain. Conflicts with |
b2e1b3029 cfg80211: Add new... |
1956 |
* multiple drivers can be ironed out later. Caller must've already |
fb1fc7add cfg80211: comment... |
1957 1958 |
* kmalloc'd the rd structure. Caller must hold cfg80211_mutex */ |
a3d2eaf0d cfg80211: fix reg... |
1959 |
int set_regdom(const struct ieee80211_regdomain *rd) |
b2e1b3029 cfg80211: Add new... |
1960 |
{ |
b2e1b3029 cfg80211: Add new... |
1961 |
int r; |
761cf7ecf cfg80211: add ass... |
1962 |
assert_cfg80211_lock(); |
abc7381bc cfg80211: decoupl... |
1963 |
mutex_lock(®_mutex); |
b2e1b3029 cfg80211: Add new... |
1964 1965 |
/* Note that this doesn't update the wiphys, this is done below */ r = __set_regdom(rd); |
d2372b315 wireless: make re... |
1966 1967 |
if (r) { kfree(rd); |
abc7381bc cfg80211: decoupl... |
1968 |
mutex_unlock(®_mutex); |
b2e1b3029 cfg80211: Add new... |
1969 |
return r; |
d2372b315 wireless: make re... |
1970 |
} |
b2e1b3029 cfg80211: Add new... |
1971 |
|
b2e1b3029 cfg80211: Add new... |
1972 |
/* This would make this whole thing pointless */ |
a01ddafd4 cfg80211: expect ... |
1973 1974 |
if (!last_request->intersect) BUG_ON(rd != cfg80211_regdomain); |
b2e1b3029 cfg80211: Add new... |
1975 1976 |
/* update all wiphys now with the new established regulatory domain */ |
f6037d09e wireless: get rid... |
1977 |
update_all_wiphy_regulatory(last_request->initiator); |
b2e1b3029 cfg80211: Add new... |
1978 |
|
a01ddafd4 cfg80211: expect ... |
1979 |
print_regdomain(cfg80211_regdomain); |
b2e1b3029 cfg80211: Add new... |
1980 |
|
73d54c9e7 cfg80211: add reg... |
1981 |
nl80211_send_reg_change_event(last_request); |
b2e253cf3 cfg80211: Fix reg... |
1982 |
reg_set_request_processed(); |
abc7381bc cfg80211: decoupl... |
1983 |
mutex_unlock(®_mutex); |
b2e1b3029 cfg80211: Add new... |
1984 1985 |
return r; } |
a1794390f cfg80211: rename ... |
1986 |
/* Caller must hold cfg80211_mutex */ |
3f2355cb9 cfg80211/mac80211... |
1987 1988 |
void reg_device_remove(struct wiphy *wiphy) { |
0ad8acaf4 cfg80211: fix NUL... |
1989 |
struct wiphy *request_wiphy = NULL; |
806a9e396 cfg80211: make re... |
1990 |
|
761cf7ecf cfg80211: add ass... |
1991 |
assert_cfg80211_lock(); |
abc7381bc cfg80211: decoupl... |
1992 |
mutex_lock(®_mutex); |
0ef9ccdd9 cfg80211: remove ... |
1993 |
kfree(wiphy->regd); |
0ad8acaf4 cfg80211: fix NUL... |
1994 1995 |
if (last_request) request_wiphy = wiphy_idx_to_wiphy(last_request->wiphy_idx); |
806a9e396 cfg80211: make re... |
1996 |
|
0ef9ccdd9 cfg80211: remove ... |
1997 |
if (!request_wiphy || request_wiphy != wiphy) |
abc7381bc cfg80211: decoupl... |
1998 |
goto out; |
0ef9ccdd9 cfg80211: remove ... |
1999 |
|
806a9e396 cfg80211: make re... |
2000 |
last_request->wiphy_idx = WIPHY_IDX_STALE; |
3f2355cb9 cfg80211/mac80211... |
2001 |
last_request->country_ie_env = ENVIRON_ANY; |
abc7381bc cfg80211: decoupl... |
2002 2003 |
out: mutex_unlock(®_mutex); |
3f2355cb9 cfg80211/mac80211... |
2004 |
} |
2fcc9f731 wireless: move re... |
2005 |
int __init regulatory_init(void) |
b2e1b3029 cfg80211: Add new... |
2006 |
{ |
bcf4f99b7 cfg80211: propaga... |
2007 |
int err = 0; |
734366dea cfg80211: clean u... |
2008 |
|
b2e1b3029 cfg80211: Add new... |
2009 2010 2011 |
reg_pdev = platform_device_register_simple("regulatory", 0, NULL, 0); if (IS_ERR(reg_pdev)) return PTR_ERR(reg_pdev); |
734366dea cfg80211: clean u... |
2012 |
|
fe33eb390 cfg80211: move al... |
2013 |
spin_lock_init(®_requests_lock); |
e38f8a7a8 cfg80211: Add AP ... |
2014 |
spin_lock_init(®_pending_beacons_lock); |
fe33eb390 cfg80211: move al... |
2015 |
|
a3d2eaf0d cfg80211: fix reg... |
2016 |
cfg80211_regdomain = cfg80211_world_regdom; |
734366dea cfg80211: clean u... |
2017 |
|
09d989d17 cfg80211: add reg... |
2018 2019 |
user_alpha2[0] = '9'; user_alpha2[1] = '7'; |
ae9e4b0d1 cfg80211: treat i... |
2020 2021 |
/* We always try to get an update for the static regdomain */ err = regulatory_hint_core(cfg80211_regdomain->alpha2); |
ba25c1414 cfg80211: add reg... |
2022 |
if (err) { |
bcf4f99b7 cfg80211: propaga... |
2023 2024 2025 2026 2027 2028 2029 2030 2031 |
if (err == -ENOMEM) return err; /* * N.B. kobject_uevent_env() can fail mainly for when we're out * memory which is handled and propagated appropriately above * but it can also fail during a netlink_broadcast() or during * early boot for call_usermodehelper(). For now treat these * errors as non-fatal. */ |
e9c0268f0 net/wireless: Use... |
2032 2033 |
pr_err("kobject_uevent_env() was unable to call CRDA during init "); |
bcf4f99b7 cfg80211: propaga... |
2034 2035 2036 |
#ifdef CONFIG_CFG80211_REG_DEBUG /* We want to find out exactly why when debugging */ WARN_ON(err); |
734366dea cfg80211: clean u... |
2037 |
#endif |
bcf4f99b7 cfg80211: propaga... |
2038 |
} |
734366dea cfg80211: clean u... |
2039 |
|
ae9e4b0d1 cfg80211: treat i... |
2040 2041 2042 2043 2044 2045 |
/* * Finally, if the user set the module parameter treat it * as a user hint. */ if (!is_world_regdom(ieee80211_regdom)) regulatory_hint_user(ieee80211_regdom); |
b2e1b3029 cfg80211: Add new... |
2046 2047 |
return 0; } |
2fcc9f731 wireless: move re... |
2048 |
void /* __init_or_exit */ regulatory_exit(void) |
b2e1b3029 cfg80211: Add new... |
2049 |
{ |
fe33eb390 cfg80211: move al... |
2050 |
struct regulatory_request *reg_request, *tmp; |
e38f8a7a8 cfg80211: Add AP ... |
2051 |
struct reg_beacon *reg_beacon, *btmp; |
fe33eb390 cfg80211: move al... |
2052 2053 |
cancel_work_sync(®_work); |
a1794390f cfg80211: rename ... |
2054 |
mutex_lock(&cfg80211_mutex); |
abc7381bc cfg80211: decoupl... |
2055 |
mutex_lock(®_mutex); |
734366dea cfg80211: clean u... |
2056 |
|
b2e1b3029 cfg80211: Add new... |
2057 |
reset_regdomains(); |
734366dea cfg80211: clean u... |
2058 |
|
f6037d09e wireless: get rid... |
2059 |
kfree(last_request); |
b2e1b3029 cfg80211: Add new... |
2060 |
platform_device_unregister(reg_pdev); |
734366dea cfg80211: clean u... |
2061 |
|
e38f8a7a8 cfg80211: Add AP ... |
2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 |
spin_lock_bh(®_pending_beacons_lock); if (!list_empty(®_pending_beacons)) { list_for_each_entry_safe(reg_beacon, btmp, ®_pending_beacons, list) { list_del(®_beacon->list); kfree(reg_beacon); } } spin_unlock_bh(®_pending_beacons_lock); if (!list_empty(®_beacon_list)) { list_for_each_entry_safe(reg_beacon, btmp, ®_beacon_list, list) { list_del(®_beacon->list); kfree(reg_beacon); } } |
fe33eb390 cfg80211: move al... |
2079 2080 2081 2082 2083 2084 2085 2086 2087 |
spin_lock(®_requests_lock); if (!list_empty(®_requests_list)) { list_for_each_entry_safe(reg_request, tmp, ®_requests_list, list) { list_del(®_request->list); kfree(reg_request); } } spin_unlock(®_requests_lock); |
abc7381bc cfg80211: decoupl... |
2088 |
mutex_unlock(®_mutex); |
a1794390f cfg80211: rename ... |
2089 |
mutex_unlock(&cfg80211_mutex); |
8318d78a4 cfg80211 API for ... |
2090 |
} |