Blame view
net/wireless/reg.c
87 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> |
3b77d5ec0 cfg80211: relicen... |
5 |
* Copyright 2008-2011 Luis R. Rodriguez <mcgrof@qca.qualcomm.com> |
2740f0cf8 cfg80211: add Int... |
6 |
* Copyright 2013-2014 Intel Mobile Communications GmbH |
4e0854a74 cfg80211: honor N... |
7 |
* Copyright 2017 Intel Deutschland GmbH |
8318d78a4 cfg80211 API for ... |
8 |
* |
3b77d5ec0 cfg80211: relicen... |
9 10 11 12 13 14 15 16 17 18 19 |
* Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
8318d78a4 cfg80211 API for ... |
20 |
*/ |
3b77d5ec0 cfg80211: relicen... |
21 |
|
b2e1b3029 cfg80211: Add new... |
22 23 |
/** * DOC: Wireless regulatory infrastructure |
8318d78a4 cfg80211 API for ... |
24 25 26 27 28 29 |
* * 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... |
30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
* 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 ... |
44 |
*/ |
e9c0268f0 net/wireless: Use... |
45 46 |
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
8318d78a4 cfg80211 API for ... |
47 |
#include <linux/kernel.h> |
bc3b2d7fb net: Add export.h... |
48 |
#include <linux/export.h> |
5a0e3ad6a include cleanup: ... |
49 |
#include <linux/slab.h> |
b2e1b3029 cfg80211: Add new... |
50 |
#include <linux/list.h> |
c61029c77 wireless: upcase ... |
51 |
#include <linux/ctype.h> |
b2e1b3029 cfg80211: Add new... |
52 53 |
#include <linux/nl80211.h> #include <linux/platform_device.h> |
d9b938421 net: add modulepa... |
54 |
#include <linux/moduleparam.h> |
b2e1b3029 cfg80211: Add new... |
55 |
#include <net/cfg80211.h> |
8318d78a4 cfg80211 API for ... |
56 |
#include "core.h" |
b2e1b3029 cfg80211: Add new... |
57 |
#include "reg.h" |
ad932f046 cfg80211: leave i... |
58 |
#include "rdev-ops.h" |
3b377ea9d wireless: support... |
59 |
#include "regdb.h" |
73d54c9e7 cfg80211: add reg... |
60 |
#include "nl80211.h" |
8318d78a4 cfg80211 API for ... |
61 |
|
ad932f046 cfg80211: leave i... |
62 63 64 65 66 |
/* * Grace period we give before making sure all current interfaces reside on * channels allowed by the current regulatory domain. */ #define REG_ENFORCE_GRACE_MS 60000 |
52616f2b4 cfg80211: Add an ... |
67 68 69 70 71 72 73 74 75 |
/** * enum reg_request_treatment - regulatory request treatment * * @REG_REQ_OK: continue processing the regulatory request * @REG_REQ_IGNORE: ignore the regulatory request * @REG_REQ_INTERSECT: the regulatory domain resulting from this request should * be intersected with the current one. * @REG_REQ_ALREADY_SET: the regulatory request will not change the current * regulatory settings, and no further processing is required. |
52616f2b4 cfg80211: Add an ... |
76 |
*/ |
2f92212b7 regulatory: use p... |
77 78 79 80 81 82 |
enum reg_request_treatment { REG_REQ_OK, REG_REQ_IGNORE, REG_REQ_INTERSECT, REG_REQ_ALREADY_SET, }; |
a042994dd cfg80211: fix rac... |
83 84 85 86 87 88 89 90 |
static struct regulatory_request core_request_world = { .initiator = NL80211_REGDOM_SET_BY_CORE, .alpha2[0] = '0', .alpha2[1] = '0', .intersect = false, .processed = true, .country_ie_env = ENVIRON_ANY, }; |
38fd2143f regulatory: remov... |
91 92 93 94 |
/* * Receipt of information from last regulatory request, * protected by RTNL (and can be accessed with RCU protection) */ |
c492db370 regulatory: use R... |
95 |
static struct regulatory_request __rcu *last_request = |
cec3f0ed7 cfg80211: use __f... |
96 |
(void __force __rcu *)&core_request_world; |
734366dea cfg80211: clean u... |
97 |
|
b2e1b3029 cfg80211: Add new... |
98 99 |
/* To trigger userspace events */ static struct platform_device *reg_pdev; |
8318d78a4 cfg80211 API for ... |
100 |
|
fb1fc7add cfg80211: comment... |
101 102 |
/* * Central wireless core regulatory domains, we only need two, |
734366dea cfg80211: clean u... |
103 |
* the current one and a world regulatory domain in case we have no |
e8da2bb4f regulatory: clari... |
104 |
* information to give us an alpha2. |
38fd2143f regulatory: remov... |
105 |
* (protected by RTNL, can be read under RCU) |
fb1fc7add cfg80211: comment... |
106 |
*/ |
458f4f9e9 regulatory: use R... |
107 |
const struct ieee80211_regdomain __rcu *cfg80211_regdomain; |
734366dea cfg80211: clean u... |
108 |
|
fb1fc7add cfg80211: comment... |
109 |
/* |
57b5ce072 cfg80211: add cel... |
110 111 |
* Number of devices that registered to the core * that support cellular base station regulatory hints |
38fd2143f regulatory: remov... |
112 |
* (protected by RTNL) |
57b5ce072 cfg80211: add cel... |
113 114 |
*/ static int reg_num_devs_support_basehint; |
52616f2b4 cfg80211: Add an ... |
115 116 117 118 |
/* * State variable indicating if the platform on which the devices * are attached is operating in an indoor environment. The state variable * is relevant for all registered devices. |
52616f2b4 cfg80211: Add an ... |
119 120 |
*/ static bool reg_is_indoor; |
050507536 cfg80211: Add API... |
121 122 123 124 |
static spinlock_t reg_indoor_lock; /* Used to track the userspace process controlling the indoor setting */ static u32 reg_is_indoor_portid; |
52616f2b4 cfg80211: Add an ... |
125 |
|
b68630369 cfg80211: reg: ma... |
126 |
static void restore_regulatory_settings(bool reset_user); |
c37722bd1 cfg80211: Stop ca... |
127 |
|
458f4f9e9 regulatory: use R... |
128 129 |
static const struct ieee80211_regdomain *get_cfg80211_regdom(void) { |
38fd2143f regulatory: remov... |
130 |
return rtnl_dereference(cfg80211_regdomain); |
458f4f9e9 regulatory: use R... |
131 |
} |
ad30ca2c0 cfg80211: allow u... |
132 |
const struct ieee80211_regdomain *get_wiphy_regdom(struct wiphy *wiphy) |
458f4f9e9 regulatory: use R... |
133 |
{ |
38fd2143f regulatory: remov... |
134 |
return rtnl_dereference(wiphy->regd); |
458f4f9e9 regulatory: use R... |
135 |
} |
3ef121b51 cfg80211: replace... |
136 137 138 139 140 141 142 143 144 145 146 147 148 149 |
static const char *reg_dfs_region_str(enum nl80211_dfs_regions dfs_region) { switch (dfs_region) { case NL80211_DFS_UNSET: return "unset"; case NL80211_DFS_FCC: return "FCC"; case NL80211_DFS_ETSI: return "ETSI"; case NL80211_DFS_JP: return "JP"; } return "Unknown"; } |
6c474799d cfg80211: add reg... |
150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 |
enum nl80211_dfs_regions reg_get_dfs_region(struct wiphy *wiphy) { const struct ieee80211_regdomain *regd = NULL; const struct ieee80211_regdomain *wiphy_regd = NULL; regd = get_cfg80211_regdom(); if (!wiphy) goto out; wiphy_regd = get_wiphy_regdom(wiphy); if (!wiphy_regd) goto out; if (wiphy_regd->dfs_region == regd->dfs_region) goto out; |
c799ba6ea cfg80211: remove ... |
165 166 167 168 169 |
pr_debug("%s: device specific dfs_region (%s) disagrees with cfg80211's central dfs_region (%s) ", dev_name(&wiphy->dev), reg_dfs_region_str(wiphy_regd->dfs_region), reg_dfs_region_str(regd->dfs_region)); |
6c474799d cfg80211: add reg... |
170 171 172 173 |
out: return regd->dfs_region; } |
458f4f9e9 regulatory: use R... |
174 175 176 177 178 179 |
static void rcu_free_regdom(const struct ieee80211_regdomain *r) { if (!r) return; kfree_rcu((struct ieee80211_regdomain *)r, rcu_head); } |
c492db370 regulatory: use R... |
180 181 |
static struct regulatory_request *get_last_request(void) { |
38fd2143f regulatory: remov... |
182 |
return rcu_dereference_rtnl(last_request); |
c492db370 regulatory: use R... |
183 |
} |
e38f8a7a8 cfg80211: Add AP ... |
184 |
/* Used to queue up regulatory hints */ |
fe33eb390 cfg80211: move al... |
185 186 |
static LIST_HEAD(reg_requests_list); static spinlock_t reg_requests_lock; |
e38f8a7a8 cfg80211: Add AP ... |
187 188 189 190 191 192 193 194 195 196 197 |
/* 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; }; |
ad932f046 cfg80211: leave i... |
198 199 |
static void reg_check_chans_work(struct work_struct *work); static DECLARE_DELAYED_WORK(reg_check_chans, reg_check_chans_work); |
f333a7a2f cfg80211: move re... |
200 201 |
static void reg_todo(struct work_struct *work); static DECLARE_WORK(reg_work, reg_todo); |
734366dea cfg80211: clean u... |
202 203 |
/* We keep a static world regulatory domain in case of the absence of CRDA */ static const struct ieee80211_regdomain world_regdom = { |
28981e5eb cfg80211: fix n_r... |
204 |
.n_reg_rules = 8, |
734366dea cfg80211: clean u... |
205 206 |
.alpha2 = "00", .reg_rules = { |
68798a626 cfg80211: enable ... |
207 208 |
/* IEEE 802.11b/g, channels 1..11 */ REG_RULE(2412-10, 2462+10, 40, 6, 20, 0), |
43c771a19 wireless: allow 4... |
209 |
/* IEEE 802.11b/g, channels 12..13. */ |
c3826807b regulatory: fix w... |
210 211 |
REG_RULE(2467-10, 2472+10, 20, 6, 20, NL80211_RRF_NO_IR | NL80211_RRF_AUTO_BW), |
611b6a82a cfg80211: Enable ... |
212 213 214 |
/* IEEE 802.11 channel 14 - Only JP enables * this and for 802.11b only */ REG_RULE(2484-10, 2484+10, 20, 6, 20, |
8fe02e167 cfg80211: consoli... |
215 |
NL80211_RRF_NO_IR | |
611b6a82a cfg80211: Enable ... |
216 217 |
NL80211_RRF_NO_OFDM), /* IEEE 802.11a, channel 36..48 */ |
c3826807b regulatory: fix w... |
218 219 220 |
REG_RULE(5180-10, 5240+10, 80, 6, 20, NL80211_RRF_NO_IR | NL80211_RRF_AUTO_BW), |
3fc71f775 cfg80211: enable ... |
221 |
|
131a19bc9 regulatory: enabl... |
222 |
/* IEEE 802.11a, channel 52..64 - DFS required */ |
c3826807b regulatory: fix w... |
223 |
REG_RULE(5260-10, 5320+10, 80, 6, 20, |
8fe02e167 cfg80211: consoli... |
224 |
NL80211_RRF_NO_IR | |
c3826807b regulatory: fix w... |
225 |
NL80211_RRF_AUTO_BW | |
131a19bc9 regulatory: enabl... |
226 227 228 229 |
NL80211_RRF_DFS), /* IEEE 802.11a, channel 100..144 - DFS required */ REG_RULE(5500-10, 5720+10, 160, 6, 20, |
8fe02e167 cfg80211: consoli... |
230 |
NL80211_RRF_NO_IR | |
131a19bc9 regulatory: enabl... |
231 |
NL80211_RRF_DFS), |
3fc71f775 cfg80211: enable ... |
232 233 |
/* IEEE 802.11a, channel 149..165 */ |
8ab9d85c6 regulatory: allow... |
234 |
REG_RULE(5745-10, 5825+10, 80, 6, 20, |
8fe02e167 cfg80211: consoli... |
235 |
NL80211_RRF_NO_IR), |
90cdc6df7 wireless: regulat... |
236 |
|
8047d2616 cfg80211: fix gHz... |
237 |
/* IEEE 802.11ad (60GHz), channels 1..3 */ |
90cdc6df7 wireless: regulat... |
238 |
REG_RULE(56160+2160*1-1080, 56160+2160*3+1080, 2160, 0, 0, 0), |
734366dea cfg80211: clean u... |
239 240 |
} }; |
38fd2143f regulatory: remov... |
241 |
/* protected by RTNL */ |
a3d2eaf0d cfg80211: fix reg... |
242 243 |
static const struct ieee80211_regdomain *cfg80211_world_regdom = &world_regdom; |
734366dea cfg80211: clean u... |
244 |
|
6ee7d3305 cfg80211: make re... |
245 |
static char *ieee80211_regdom = "00"; |
09d989d17 cfg80211: add reg... |
246 |
static char user_alpha2[2]; |
6ee7d3305 cfg80211: make re... |
247 |
|
734366dea cfg80211: clean u... |
248 249 |
module_param(ieee80211_regdom, charp, 0444); MODULE_PARM_DESC(ieee80211_regdom, "IEEE 802.11 regulatory domain code"); |
c888393b7 cfg80211: avoid f... |
250 |
static void reg_free_request(struct regulatory_request *request) |
5ad6ef5e0 cfg80211: add hel... |
251 |
{ |
d34265a3e cfg80211: reg: ce... |
252 253 |
if (request == &core_request_world) return; |
c888393b7 cfg80211: avoid f... |
254 255 256 257 258 259 260 |
if (request != get_last_request()) kfree(request); } static void reg_free_last_request(void) { struct regulatory_request *lr = get_last_request(); |
5ad6ef5e0 cfg80211: add hel... |
261 262 263 |
if (lr != &core_request_world && lr) kfree_rcu(lr, rcu_head); } |
05f1a3ea2 cfg80211: add hel... |
264 265 |
static void reg_update_last_request(struct regulatory_request *request) { |
255e25b0e cfg80211: allow r... |
266 267 268 269 270 |
struct regulatory_request *lr; lr = get_last_request(); if (lr == request) return; |
c888393b7 cfg80211: avoid f... |
271 |
reg_free_last_request(); |
05f1a3ea2 cfg80211: add hel... |
272 273 |
rcu_assign_pointer(last_request, request); } |
379b82f4c regulatory: pass ... |
274 275 |
static void reset_regdomains(bool full_reset, const struct ieee80211_regdomain *new_regdom) |
734366dea cfg80211: clean u... |
276 |
{ |
458f4f9e9 regulatory: use R... |
277 |
const struct ieee80211_regdomain *r; |
38fd2143f regulatory: remov... |
278 |
ASSERT_RTNL(); |
e8da2bb4f regulatory: clari... |
279 |
|
458f4f9e9 regulatory: use R... |
280 |
r = get_cfg80211_regdom(); |
942b25cf9 cfg80211: clean u... |
281 |
/* avoid freeing static information or freeing something twice */ |
458f4f9e9 regulatory: use R... |
282 283 |
if (r == cfg80211_world_regdom) r = NULL; |
942b25cf9 cfg80211: clean u... |
284 285 |
if (cfg80211_world_regdom == &world_regdom) cfg80211_world_regdom = NULL; |
458f4f9e9 regulatory: use R... |
286 287 |
if (r == &world_regdom) r = NULL; |
942b25cf9 cfg80211: clean u... |
288 |
|
458f4f9e9 regulatory: use R... |
289 290 |
rcu_free_regdom(r); rcu_free_regdom(cfg80211_world_regdom); |
734366dea cfg80211: clean u... |
291 |
|
a3d2eaf0d cfg80211: fix reg... |
292 |
cfg80211_world_regdom = &world_regdom; |
458f4f9e9 regulatory: use R... |
293 |
rcu_assign_pointer(cfg80211_regdomain, new_regdom); |
a042994dd cfg80211: fix rac... |
294 295 296 |
if (!full_reset) return; |
05f1a3ea2 cfg80211: add hel... |
297 |
reg_update_last_request(&core_request_world); |
734366dea cfg80211: clean u... |
298 |
} |
fb1fc7add cfg80211: comment... |
299 300 301 302 |
/* * Dynamic world regulatory domain requested by the wireless * core upon initialization */ |
a3d2eaf0d cfg80211: fix reg... |
303 |
static void update_world_regdomain(const struct ieee80211_regdomain *rd) |
734366dea cfg80211: clean u... |
304 |
{ |
c492db370 regulatory: use R... |
305 |
struct regulatory_request *lr; |
734366dea cfg80211: clean u... |
306 |
|
c492db370 regulatory: use R... |
307 308 309 |
lr = get_last_request(); WARN_ON(!lr); |
734366dea cfg80211: clean u... |
310 |
|
379b82f4c regulatory: pass ... |
311 |
reset_regdomains(false, rd); |
734366dea cfg80211: clean u... |
312 313 |
cfg80211_world_regdom = rd; |
734366dea cfg80211: clean u... |
314 |
} |
734366dea cfg80211: clean u... |
315 |
|
a3d2eaf0d cfg80211: fix reg... |
316 |
bool is_world_regdom(const char *alpha2) |
b2e1b3029 cfg80211: Add new... |
317 318 319 |
{ if (!alpha2) return false; |
1a9193185 regulatory: code ... |
320 |
return alpha2[0] == '0' && alpha2[1] == '0'; |
b2e1b3029 cfg80211: Add new... |
321 |
} |
8318d78a4 cfg80211 API for ... |
322 |
|
a3d2eaf0d cfg80211: fix reg... |
323 |
static bool is_alpha2_set(const char *alpha2) |
b2e1b3029 cfg80211: Add new... |
324 325 326 |
{ if (!alpha2) return false; |
1a9193185 regulatory: code ... |
327 |
return alpha2[0] && alpha2[1]; |
b2e1b3029 cfg80211: Add new... |
328 |
} |
8318d78a4 cfg80211 API for ... |
329 |
|
a3d2eaf0d cfg80211: fix reg... |
330 |
static bool is_unknown_alpha2(const char *alpha2) |
b2e1b3029 cfg80211: Add new... |
331 332 333 |
{ if (!alpha2) return false; |
fb1fc7add cfg80211: comment... |
334 335 336 337 |
/* * Special case where regulatory domain was built by driver * but a specific alpha2 cannot be determined */ |
1a9193185 regulatory: code ... |
338 |
return alpha2[0] == '9' && alpha2[1] == '9'; |
b2e1b3029 cfg80211: Add new... |
339 |
} |
8318d78a4 cfg80211 API for ... |
340 |
|
3f2355cb9 cfg80211/mac80211... |
341 342 343 344 |
static bool is_intersected_alpha2(const char *alpha2) { if (!alpha2) return false; |
fb1fc7add cfg80211: comment... |
345 346 |
/* * Special case where regulatory domain is the |
3f2355cb9 cfg80211/mac80211... |
347 |
* result of an intersection between two regulatory domain |
fb1fc7add cfg80211: comment... |
348 349 |
* structures */ |
1a9193185 regulatory: code ... |
350 |
return alpha2[0] == '9' && alpha2[1] == '8'; |
3f2355cb9 cfg80211/mac80211... |
351 |
} |
a3d2eaf0d cfg80211: fix reg... |
352 |
static bool is_an_alpha2(const char *alpha2) |
b2e1b3029 cfg80211: Add new... |
353 354 355 |
{ if (!alpha2) return false; |
1a9193185 regulatory: code ... |
356 |
return isalpha(alpha2[0]) && isalpha(alpha2[1]); |
b2e1b3029 cfg80211: Add new... |
357 |
} |
8318d78a4 cfg80211 API for ... |
358 |
|
a3d2eaf0d cfg80211: fix reg... |
359 |
static bool alpha2_equal(const char *alpha2_x, const char *alpha2_y) |
b2e1b3029 cfg80211: Add new... |
360 361 362 |
{ if (!alpha2_x || !alpha2_y) return false; |
1a9193185 regulatory: code ... |
363 |
return alpha2_x[0] == alpha2_y[0] && alpha2_x[1] == alpha2_y[1]; |
b2e1b3029 cfg80211: Add new... |
364 |
} |
69b1572bd cfg80211: rename ... |
365 |
static bool regdom_changes(const char *alpha2) |
b2e1b3029 cfg80211: Add new... |
366 |
{ |
458f4f9e9 regulatory: use R... |
367 |
const struct ieee80211_regdomain *r = get_cfg80211_regdom(); |
761cf7ecf cfg80211: add ass... |
368 |
|
458f4f9e9 regulatory: use R... |
369 |
if (!r) |
b2e1b3029 cfg80211: Add new... |
370 |
return true; |
458f4f9e9 regulatory: use R... |
371 |
return !alpha2_equal(r->alpha2, alpha2); |
b2e1b3029 cfg80211: Add new... |
372 |
} |
09d989d17 cfg80211: add reg... |
373 374 375 376 377 378 379 380 381 382 383 |
/* * 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 */ |
1a9193185 regulatory: code ... |
384 |
if (WARN(!is_world_regdom(user_alpha2) && !is_an_alpha2(user_alpha2), |
09d989d17 cfg80211: add reg... |
385 386 |
"Unexpected user alpha2: %c%c ", |
1a9193185 regulatory: code ... |
387 |
user_alpha2[0], user_alpha2[1])) |
09d989d17 cfg80211: add reg... |
388 389 390 391 |
return false; return true; } |
e9763c3c2 regulatory: clean... |
392 393 |
static const struct ieee80211_regdomain * reg_copy_regd(const struct ieee80211_regdomain *src_regd) |
3b377ea9d wireless: support... |
394 395 |
{ struct ieee80211_regdomain *regd; |
e9763c3c2 regulatory: clean... |
396 |
int size_of_regd; |
3b377ea9d wireless: support... |
397 |
unsigned int i; |
82f208563 regulatory: don't... |
398 399 400 |
size_of_regd = sizeof(struct ieee80211_regdomain) + src_regd->n_reg_rules * sizeof(struct ieee80211_reg_rule); |
3b377ea9d wireless: support... |
401 402 403 |
regd = kzalloc(size_of_regd, GFP_KERNEL); if (!regd) |
e9763c3c2 regulatory: clean... |
404 |
return ERR_PTR(-ENOMEM); |
3b377ea9d wireless: support... |
405 406 407 408 409 |
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], |
e9763c3c2 regulatory: clean... |
410 |
sizeof(struct ieee80211_reg_rule)); |
3b377ea9d wireless: support... |
411 |
|
e9763c3c2 regulatory: clean... |
412 |
return regd; |
3b377ea9d wireless: support... |
413 414 415 |
} #ifdef CONFIG_CFG80211_INTERNAL_REGDB |
c7d319e54 cfg80211: reg: se... |
416 |
struct reg_regdb_apply_request { |
3b377ea9d wireless: support... |
417 |
struct list_head list; |
c7d319e54 cfg80211: reg: se... |
418 |
const struct ieee80211_regdomain *regdom; |
3b377ea9d wireless: support... |
419 |
}; |
c7d319e54 cfg80211: reg: se... |
420 421 |
static LIST_HEAD(reg_regdb_apply_list); static DEFINE_MUTEX(reg_regdb_apply_mutex); |
3b377ea9d wireless: support... |
422 |
|
c7d319e54 cfg80211: reg: se... |
423 |
static void reg_regdb_apply(struct work_struct *work) |
3b377ea9d wireless: support... |
424 |
{ |
c7d319e54 cfg80211: reg: se... |
425 |
struct reg_regdb_apply_request *request; |
a85d0d7f3 cfg80211: fix pos... |
426 |
|
5fe231e87 cfg80211: vastly ... |
427 |
rtnl_lock(); |
3b377ea9d wireless: support... |
428 |
|
c7d319e54 cfg80211: reg: se... |
429 430 431 432 |
mutex_lock(®_regdb_apply_mutex); while (!list_empty(®_regdb_apply_list)) { request = list_first_entry(®_regdb_apply_list, struct reg_regdb_apply_request, |
3b377ea9d wireless: support... |
433 434 |
list); list_del(&request->list); |
c7d319e54 cfg80211: reg: se... |
435 |
set_regdom(request->regdom, REGD_SOURCE_INTERNAL_DB); |
3b377ea9d wireless: support... |
436 437 |
kfree(request); } |
c7d319e54 cfg80211: reg: se... |
438 |
mutex_unlock(®_regdb_apply_mutex); |
a85d0d7f3 cfg80211: fix pos... |
439 |
|
5fe231e87 cfg80211: vastly ... |
440 |
rtnl_unlock(); |
3b377ea9d wireless: support... |
441 |
} |
c7d319e54 cfg80211: reg: se... |
442 |
static DECLARE_WORK(reg_regdb_work, reg_regdb_apply); |
3b377ea9d wireless: support... |
443 |
|
fd453d3c5 cfg80211: reg: re... |
444 |
static int reg_query_builtin(const char *alpha2) |
3b377ea9d wireless: support... |
445 |
{ |
c7d319e54 cfg80211: reg: se... |
446 447 448 449 450 451 452 453 454 455 456 457 458 |
const struct ieee80211_regdomain *regdom = NULL; struct reg_regdb_apply_request *request; unsigned int i; for (i = 0; i < reg_regdb_size; i++) { if (alpha2_equal(alpha2, reg_regdb[i]->alpha2)) { regdom = reg_regdb[i]; break; } } if (!regdom) return -ENODATA; |
3b377ea9d wireless: support... |
459 |
|
c7d319e54 cfg80211: reg: se... |
460 |
request = kzalloc(sizeof(struct reg_regdb_apply_request), GFP_KERNEL); |
3b377ea9d wireless: support... |
461 |
if (!request) |
c7d319e54 cfg80211: reg: se... |
462 |
return -ENOMEM; |
3b377ea9d wireless: support... |
463 |
|
c7d319e54 cfg80211: reg: se... |
464 465 466 467 468 |
request->regdom = reg_copy_regd(regdom); if (IS_ERR_OR_NULL(request->regdom)) { kfree(request); return -ENOMEM; } |
3b377ea9d wireless: support... |
469 |
|
c7d319e54 cfg80211: reg: se... |
470 471 472 |
mutex_lock(®_regdb_apply_mutex); list_add_tail(&request->list, ®_regdb_apply_list); mutex_unlock(®_regdb_apply_mutex); |
3b377ea9d wireless: support... |
473 474 |
schedule_work(®_regdb_work); |
c7d319e54 cfg80211: reg: se... |
475 476 |
return 0; |
3b377ea9d wireless: support... |
477 |
} |
80007efef cfg80211: warn if... |
478 479 480 481 482 483 484 |
/* Feel free to add any other sanity checks here */ static void reg_regdb_size_check(void) { /* We should ideally BUILD_BUG_ON() but then random builds would fail */ WARN_ONCE(!reg_regdb_size, "db.txt is empty, you should update it..."); } |
3b377ea9d wireless: support... |
485 |
#else |
80007efef cfg80211: warn if... |
486 |
static inline void reg_regdb_size_check(void) {} |
fd453d3c5 cfg80211: reg: re... |
487 |
static inline int reg_query_builtin(const char *alpha2) |
c7d319e54 cfg80211: reg: se... |
488 489 490 |
{ return -ENODATA; } |
3b377ea9d wireless: support... |
491 |
#endif /* CONFIG_CFG80211_INTERNAL_REGDB */ |
b68630369 cfg80211: reg: ma... |
492 493 494 495 496 497 498 499 500 501 502 |
#ifdef CONFIG_CFG80211_CRDA_SUPPORT /* Max number of consecutive attempts to communicate with CRDA */ #define REG_MAX_CRDA_TIMEOUTS 10 static u32 reg_crda_timeouts; static void crda_timeout_work(struct work_struct *work); static DECLARE_DELAYED_WORK(crda_timeout, crda_timeout_work); static void crda_timeout_work(struct work_struct *work) { |
c799ba6ea cfg80211: remove ... |
503 504 |
pr_debug("Timeout while waiting for CRDA to reply, restoring regulatory settings "); |
b68630369 cfg80211: reg: ma... |
505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 |
rtnl_lock(); reg_crda_timeouts++; restore_regulatory_settings(true); rtnl_unlock(); } static void cancel_crda_timeout(void) { cancel_delayed_work(&crda_timeout); } static void cancel_crda_timeout_sync(void) { cancel_delayed_work_sync(&crda_timeout); } static void reset_crda_timeouts(void) { reg_crda_timeouts = 0; } |
fb1fc7add cfg80211: comment... |
525 526 |
/* * This lets us keep regulatory code which is updated on a regulatory |
1226d2587 cfg80211: regulat... |
527 |
* basis in userspace. |
fb1fc7add cfg80211: comment... |
528 |
*/ |
b2e1b3029 cfg80211: Add new... |
529 530 |
static int call_crda(const char *alpha2) { |
1226d2587 cfg80211: regulat... |
531 532 |
char country[12]; char *env[] = { country, NULL }; |
c7d319e54 cfg80211: reg: se... |
533 |
int ret; |
1226d2587 cfg80211: regulat... |
534 535 536 |
snprintf(country, sizeof(country), "COUNTRY=%c%c", alpha2[0], alpha2[1]); |
c37722bd1 cfg80211: Stop ca... |
537 |
if (reg_crda_timeouts > REG_MAX_CRDA_TIMEOUTS) { |
042ab5fc7 wireless: regulat... |
538 539 |
pr_debug("Exceeded CRDA call max attempts. Not calling CRDA "); |
c37722bd1 cfg80211: Stop ca... |
540 541 |
return -EINVAL; } |
b2e1b3029 cfg80211: Add new... |
542 |
if (!is_world_regdom((char *) alpha2)) |
042ab5fc7 wireless: regulat... |
543 544 |
pr_debug("Calling CRDA for country: %c%c ", |
c799ba6ea cfg80211: remove ... |
545 |
alpha2[0], alpha2[1]); |
b2e1b3029 cfg80211: Add new... |
546 |
else |
042ab5fc7 wireless: regulat... |
547 548 |
pr_debug("Calling CRDA to update world regulatory domain "); |
b2e1b3029 cfg80211: Add new... |
549 |
|
c7d319e54 cfg80211: reg: se... |
550 551 552 553 554 |
ret = kobject_uevent_env(®_pdev->dev.kobj, KOBJ_CHANGE, env); if (ret) return ret; queue_delayed_work(system_power_efficient_wq, |
b68630369 cfg80211: reg: ma... |
555 |
&crda_timeout, msecs_to_jiffies(3142)); |
c7d319e54 cfg80211: reg: se... |
556 |
return 0; |
b2e1b3029 cfg80211: Add new... |
557 |
} |
b68630369 cfg80211: reg: ma... |
558 559 560 561 562 563 564 565 566 |
#else static inline void cancel_crda_timeout(void) {} static inline void cancel_crda_timeout_sync(void) {} static inline void reset_crda_timeouts(void) {} static inline int call_crda(const char *alpha2) { return -ENODATA; } #endif /* CONFIG_CFG80211_CRDA_SUPPORT */ |
b2e1b3029 cfg80211: Add new... |
567 |
|
cecbb069c cfg80211: reg: re... |
568 |
static bool reg_query_database(struct regulatory_request *request) |
fe6631ff0 cfg80211: add hel... |
569 |
{ |
c7d319e54 cfg80211: reg: se... |
570 |
/* query internal regulatory database (if it exists) */ |
fd453d3c5 cfg80211: reg: re... |
571 |
if (reg_query_builtin(request->alpha2) == 0) |
c7d319e54 cfg80211: reg: se... |
572 |
return true; |
eeca9fce1 cfg80211: Schedul... |
573 |
|
c7d319e54 cfg80211: reg: se... |
574 575 576 577 |
if (call_crda(request->alpha2) == 0) return true; return false; |
fe6631ff0 cfg80211: add hel... |
578 |
} |
e438768ff cfg80211: check r... |
579 |
bool reg_is_valid_request(const char *alpha2) |
b2e1b3029 cfg80211: Add new... |
580 |
{ |
c492db370 regulatory: use R... |
581 |
struct regulatory_request *lr = get_last_request(); |
61405e977 cfg80211: fix in ... |
582 |
|
c492db370 regulatory: use R... |
583 |
if (!lr || lr->processed) |
f6037d09e wireless: get rid... |
584 |
return false; |
c492db370 regulatory: use R... |
585 |
return alpha2_equal(lr->alpha2, alpha2); |
b2e1b3029 cfg80211: Add new... |
586 |
} |
8318d78a4 cfg80211 API for ... |
587 |
|
e3961af1e cfg80211: add hel... |
588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 |
static const struct ieee80211_regdomain *reg_get_regdomain(struct wiphy *wiphy) { struct regulatory_request *lr = get_last_request(); /* * Follow the driver's regulatory domain, if present, unless a country * IE has been processed or a user wants to help complaince further */ if (lr->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE && lr->initiator != NL80211_REGDOM_SET_BY_USER && wiphy->regd) return get_wiphy_regdom(wiphy); return get_cfg80211_regdom(); } |
a6d4a534e cfg80211: introdu... |
603 604 605 |
static unsigned int reg_get_max_bandwidth_from_range(const struct ieee80211_regdomain *rd, const struct ieee80211_reg_rule *rule) |
975248208 cfg80211: regulat... |
606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 |
{ const struct ieee80211_freq_range *freq_range = &rule->freq_range; const struct ieee80211_freq_range *freq_range_tmp; const struct ieee80211_reg_rule *tmp; u32 start_freq, end_freq, idx, no; for (idx = 0; idx < rd->n_reg_rules; idx++) if (rule == &rd->reg_rules[idx]) break; if (idx == rd->n_reg_rules) return 0; /* get start_freq */ no = idx; while (no) { tmp = &rd->reg_rules[--no]; freq_range_tmp = &tmp->freq_range; if (freq_range_tmp->end_freq_khz < freq_range->start_freq_khz) break; |
975248208 cfg80211: regulat... |
628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 |
freq_range = freq_range_tmp; } start_freq = freq_range->start_freq_khz; /* get end_freq */ freq_range = &rule->freq_range; no = idx; while (no < rd->n_reg_rules - 1) { tmp = &rd->reg_rules[++no]; freq_range_tmp = &tmp->freq_range; if (freq_range_tmp->start_freq_khz > freq_range->end_freq_khz) break; |
975248208 cfg80211: regulat... |
643 644 645 646 647 648 649 |
freq_range = freq_range_tmp; } end_freq = freq_range->end_freq_khz; return end_freq - start_freq; } |
a6d4a534e cfg80211: introdu... |
650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 |
unsigned int reg_get_max_bandwidth(const struct ieee80211_regdomain *rd, const struct ieee80211_reg_rule *rule) { unsigned int bw = reg_get_max_bandwidth_from_range(rd, rule); if (rule->flags & NL80211_RRF_NO_160MHZ) bw = min_t(unsigned int, bw, MHZ_TO_KHZ(80)); if (rule->flags & NL80211_RRF_NO_80MHZ) bw = min_t(unsigned int, bw, MHZ_TO_KHZ(40)); /* * HT40+/HT40- limits are handled per-channel. Only limit BW if both * are not allowed. */ if (rule->flags & NL80211_RRF_NO_HT40MINUS && rule->flags & NL80211_RRF_NO_HT40PLUS) bw = min_t(unsigned int, bw, MHZ_TO_KHZ(20)); return bw; } |
b2e1b3029 cfg80211: Add new... |
670 |
/* Sanity check on a regulatory rule */ |
a3d2eaf0d cfg80211: fix reg... |
671 |
static bool is_valid_reg_rule(const struct ieee80211_reg_rule *rule) |
8318d78a4 cfg80211 API for ... |
672 |
{ |
a3d2eaf0d cfg80211: fix reg... |
673 |
const struct ieee80211_freq_range *freq_range = &rule->freq_range; |
b2e1b3029 cfg80211: Add new... |
674 |
u32 freq_diff; |
91e990041 cfg80211: mark ne... |
675 |
if (freq_range->start_freq_khz <= 0 || freq_range->end_freq_khz <= 0) |
b2e1b3029 cfg80211: Add new... |
676 677 678 679 680 681 |
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... |
682 |
if (freq_range->end_freq_khz <= freq_range->start_freq_khz || |
1a9193185 regulatory: code ... |
683 |
freq_range->max_bandwidth_khz > freq_diff) |
b2e1b3029 cfg80211: Add new... |
684 685 686 687 |
return false; return true; } |
a3d2eaf0d cfg80211: fix reg... |
688 |
static bool is_valid_rd(const struct ieee80211_regdomain *rd) |
b2e1b3029 cfg80211: Add new... |
689 |
{ |
a3d2eaf0d cfg80211: fix reg... |
690 |
const struct ieee80211_reg_rule *reg_rule = NULL; |
b2e1b3029 cfg80211: Add new... |
691 |
unsigned int i; |
8318d78a4 cfg80211 API for ... |
692 |
|
b2e1b3029 cfg80211: Add new... |
693 694 |
if (!rd->n_reg_rules) return false; |
8318d78a4 cfg80211 API for ... |
695 |
|
88dc1c3f7 cfg80211: mark re... |
696 697 |
if (WARN_ON(rd->n_reg_rules > NL80211_MAX_SUPP_REG_RULES)) return false; |
b2e1b3029 cfg80211: Add new... |
698 699 700 701 702 703 704 |
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 ... |
705 |
} |
0c7dc45d2 cfg80211: Fix reg... |
706 707 708 709 710 711 712 |
/** * 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 |
64629b9d4 cfg80211: Fix reg... |
713 714 715 716 717 |
* definitions (the "2.4 GHz band", the "5 GHz band" and the "60GHz 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 for the 2.4 and 5 GHz bands, and by more than 10 GHz for the * 60 GHz band. |
0c7dc45d2 cfg80211: Fix reg... |
718 719 720 721 |
* 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, |
1a9193185 regulatory: code ... |
722 |
u32 freq_khz) |
0c7dc45d2 cfg80211: Fix reg... |
723 724 |
{ #define ONE_GHZ_IN_KHZ 1000000 |
64629b9d4 cfg80211: Fix reg... |
725 726 727 728 729 730 731 732 |
/* * From 802.11ad: directional multi-gigabit (DMG): * Pertaining to operation in a frequency band containing a channel * with the Channel starting frequency above 45 GHz. */ u32 limit = freq_khz > 45 * ONE_GHZ_IN_KHZ ? 10 * ONE_GHZ_IN_KHZ : 2 * ONE_GHZ_IN_KHZ; if (abs(freq_khz - freq_range->start_freq_khz) <= limit) |
0c7dc45d2 cfg80211: Fix reg... |
733 |
return true; |
64629b9d4 cfg80211: Fix reg... |
734 |
if (abs(freq_khz - freq_range->end_freq_khz) <= limit) |
0c7dc45d2 cfg80211: Fix reg... |
735 736 737 738 |
return true; return false; #undef ONE_GHZ_IN_KHZ } |
fb1fc7add cfg80211: comment... |
739 |
/* |
adbfb0581 cfg80211: interse... |
740 741 742 743 744 745 746 747 748 749 750 751 752 753 |
* Later on we can perhaps use the more restrictive DFS * region but we don't have information for that yet so * for now simply disallow conflicts. */ static enum nl80211_dfs_regions reg_intersect_dfs_region(const enum nl80211_dfs_regions dfs_region1, const enum nl80211_dfs_regions dfs_region2) { if (dfs_region1 != dfs_region2) return NL80211_DFS_UNSET; return dfs_region1; } /* |
fb1fc7add cfg80211: comment... |
754 755 756 |
* Helper for regdom_intersect(), this does the real * mathematical intersection fun */ |
975248208 cfg80211: regulat... |
757 758 759 |
static int reg_rules_intersect(const struct ieee80211_regdomain *rd1, const struct ieee80211_regdomain *rd2, const struct ieee80211_reg_rule *rule1, |
1a9193185 regulatory: code ... |
760 761 |
const struct ieee80211_reg_rule *rule2, struct ieee80211_reg_rule *intersected_rule) |
9c96477d1 cfg80211: Add reg... |
762 763 764 765 766 |
{ 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; |
975248208 cfg80211: regulat... |
767 |
u32 freq_diff, max_bandwidth1, max_bandwidth2; |
9c96477d1 cfg80211: Add reg... |
768 769 770 771 772 773 774 775 776 777 |
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, |
1a9193185 regulatory: code ... |
778 |
freq_range2->start_freq_khz); |
9c96477d1 cfg80211: Add reg... |
779 |
freq_range->end_freq_khz = min(freq_range1->end_freq_khz, |
1a9193185 regulatory: code ... |
780 |
freq_range2->end_freq_khz); |
975248208 cfg80211: regulat... |
781 782 783 |
max_bandwidth1 = freq_range1->max_bandwidth_khz; max_bandwidth2 = freq_range2->max_bandwidth_khz; |
b0dfd2ea1 cfg80211: regulat... |
784 785 786 787 |
if (rule1->flags & NL80211_RRF_AUTO_BW) max_bandwidth1 = reg_get_max_bandwidth(rd1, rule1); if (rule2->flags & NL80211_RRF_AUTO_BW) max_bandwidth2 = reg_get_max_bandwidth(rd2, rule2); |
975248208 cfg80211: regulat... |
788 789 |
freq_range->max_bandwidth_khz = min(max_bandwidth1, max_bandwidth2); |
9c96477d1 cfg80211: Add reg... |
790 |
|
b0dfd2ea1 cfg80211: regulat... |
791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 |
intersected_rule->flags = rule1->flags | rule2->flags; /* * In case NL80211_RRF_AUTO_BW requested for both rules * set AUTO_BW in intersected rule also. Next we will * calculate BW correctly in handle_channel function. * In other case remove AUTO_BW flag while we calculate * maximum bandwidth correctly and auto calculation is * not required. */ if ((rule1->flags & NL80211_RRF_AUTO_BW) && (rule2->flags & NL80211_RRF_AUTO_BW)) intersected_rule->flags |= NL80211_RRF_AUTO_BW; else intersected_rule->flags &= ~NL80211_RRF_AUTO_BW; |
9c96477d1 cfg80211: Add reg... |
806 807 808 809 810 811 812 813 |
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); |
089027e57 cfg80211: regulat... |
814 815 |
intersected_rule->dfs_cac_ms = max(rule1->dfs_cac_ms, rule2->dfs_cac_ms); |
9c96477d1 cfg80211: Add reg... |
816 817 818 819 820 |
if (!is_valid_reg_rule(intersected_rule)) return -EINVAL; return 0; } |
a62a1aed3 cfg80211: avoid d... |
821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 |
/* check whether old rule contains new rule */ static bool rule_contains(struct ieee80211_reg_rule *r1, struct ieee80211_reg_rule *r2) { /* for simplicity, currently consider only same flags */ if (r1->flags != r2->flags) return false; /* verify r1 is more restrictive */ if ((r1->power_rule.max_antenna_gain > r2->power_rule.max_antenna_gain) || r1->power_rule.max_eirp > r2->power_rule.max_eirp) return false; /* make sure r2's range is contained within r1 */ if (r1->freq_range.start_freq_khz > r2->freq_range.start_freq_khz || r1->freq_range.end_freq_khz < r2->freq_range.end_freq_khz) return false; /* and finally verify that r1.max_bw >= r2.max_bw */ if (r1->freq_range.max_bandwidth_khz < r2->freq_range.max_bandwidth_khz) return false; return true; } /* add or extend current rules. do nothing if rule is already contained */ static void add_rule(struct ieee80211_reg_rule *rule, struct ieee80211_reg_rule *reg_rules, u32 *n_rules) { struct ieee80211_reg_rule *tmp_rule; int i; for (i = 0; i < *n_rules; i++) { tmp_rule = ®_rules[i]; /* rule is already contained - do nothing */ if (rule_contains(tmp_rule, rule)) return; /* extend rule if possible */ if (rule_contains(rule, tmp_rule)) { memcpy(tmp_rule, rule, sizeof(*rule)); return; } } memcpy(®_rules[*n_rules], rule, sizeof(*rule)); (*n_rules)++; } |
9c96477d1 cfg80211: Add reg... |
871 872 873 874 875 876 877 878 879 880 881 882 883 |
/** * 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. */ |
1a9193185 regulatory: code ... |
884 885 886 |
static struct ieee80211_regdomain * regdom_intersect(const struct ieee80211_regdomain *rd1, const struct ieee80211_regdomain *rd2) |
9c96477d1 cfg80211: Add reg... |
887 888 889 |
{ int r, size_of_regd; unsigned int x, y; |
a62a1aed3 cfg80211: avoid d... |
890 |
unsigned int num_rules = 0; |
9c96477d1 cfg80211: Add reg... |
891 |
const struct ieee80211_reg_rule *rule1, *rule2; |
a62a1aed3 cfg80211: avoid d... |
892 |
struct ieee80211_reg_rule intersected_rule; |
9c96477d1 cfg80211: Add reg... |
893 |
struct ieee80211_regdomain *rd; |
9c96477d1 cfg80211: Add reg... |
894 895 896 |
if (!rd1 || !rd2) return NULL; |
fb1fc7add cfg80211: comment... |
897 898 |
/* * First we get a count of the rules we'll need, then we actually |
9c96477d1 cfg80211: Add reg... |
899 900 901 |
* 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... |
902 903 |
* All rules that do check out OK are valid. */ |
9c96477d1 cfg80211: Add reg... |
904 905 906 907 908 |
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]; |
975248208 cfg80211: regulat... |
909 |
if (!reg_rules_intersect(rd1, rd2, rule1, rule2, |
a62a1aed3 cfg80211: avoid d... |
910 |
&intersected_rule)) |
9c96477d1 cfg80211: Add reg... |
911 |
num_rules++; |
9c96477d1 cfg80211: Add reg... |
912 913 914 915 916 917 918 |
} } if (!num_rules) return NULL; size_of_regd = sizeof(struct ieee80211_regdomain) + |
82f208563 regulatory: don't... |
919 |
num_rules * sizeof(struct ieee80211_reg_rule); |
9c96477d1 cfg80211: Add reg... |
920 921 922 923 |
rd = kzalloc(size_of_regd, GFP_KERNEL); if (!rd) return NULL; |
a62a1aed3 cfg80211: avoid d... |
924 |
for (x = 0; x < rd1->n_reg_rules; x++) { |
9c96477d1 cfg80211: Add reg... |
925 |
rule1 = &rd1->reg_rules[x]; |
a62a1aed3 cfg80211: avoid d... |
926 |
for (y = 0; y < rd2->n_reg_rules; y++) { |
9c96477d1 cfg80211: Add reg... |
927 |
rule2 = &rd2->reg_rules[y]; |
975248208 cfg80211: regulat... |
928 |
r = reg_rules_intersect(rd1, rd2, rule1, rule2, |
a62a1aed3 cfg80211: avoid d... |
929 |
&intersected_rule); |
fb1fc7add cfg80211: comment... |
930 931 932 933 |
/* * No need to memset here the intersected rule here as * we're not using the stack anymore */ |
9c96477d1 cfg80211: Add reg... |
934 935 |
if (r) continue; |
9c96477d1 cfg80211: Add reg... |
936 |
|
a62a1aed3 cfg80211: avoid d... |
937 938 939 |
add_rule(&intersected_rule, rd->reg_rules, &rd->n_reg_rules); } |
9c96477d1 cfg80211: Add reg... |
940 |
} |
9c96477d1 cfg80211: Add reg... |
941 942 |
rd->alpha2[0] = '9'; rd->alpha2[1] = '8'; |
adbfb0581 cfg80211: interse... |
943 944 |
rd->dfs_region = reg_intersect_dfs_region(rd1->dfs_region, rd2->dfs_region); |
9c96477d1 cfg80211: Add reg... |
945 946 947 |
return rd; } |
fb1fc7add cfg80211: comment... |
948 949 950 951 |
/* * 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... |
952 953 954 |
static u32 map_regdom_flags(u32 rd_flags) { u32 channel_flags = 0; |
8fe02e167 cfg80211: consoli... |
955 956 |
if (rd_flags & NL80211_RRF_NO_IR_ALL) channel_flags |= IEEE80211_CHAN_NO_IR; |
b2e1b3029 cfg80211: Add new... |
957 958 |
if (rd_flags & NL80211_RRF_DFS) channel_flags |= IEEE80211_CHAN_RADAR; |
03f6b0843 cfg80211: add cha... |
959 960 |
if (rd_flags & NL80211_RRF_NO_OFDM) channel_flags |= IEEE80211_CHAN_NO_OFDM; |
570dbde13 cfg80211: Add ind... |
961 962 |
if (rd_flags & NL80211_RRF_NO_OUTDOOR) channel_flags |= IEEE80211_CHAN_INDOOR_ONLY; |
06f207fc5 cfg80211: change ... |
963 964 |
if (rd_flags & NL80211_RRF_IR_CONCURRENT) channel_flags |= IEEE80211_CHAN_IR_CONCURRENT; |
a6d4a534e cfg80211: introdu... |
965 966 967 968 969 970 971 972 |
if (rd_flags & NL80211_RRF_NO_HT40MINUS) channel_flags |= IEEE80211_CHAN_NO_HT40MINUS; if (rd_flags & NL80211_RRF_NO_HT40PLUS) channel_flags |= IEEE80211_CHAN_NO_HT40PLUS; if (rd_flags & NL80211_RRF_NO_80MHZ) channel_flags |= IEEE80211_CHAN_NO_80MHZ; if (rd_flags & NL80211_RRF_NO_160MHZ) channel_flags |= IEEE80211_CHAN_NO_160MHZ; |
b2e1b3029 cfg80211: Add new... |
973 974 |
return channel_flags; } |
361c9c8b0 regulatory: use I... |
975 |
static const struct ieee80211_reg_rule * |
491728746 cfg80211: reg: Re... |
976 |
freq_reg_info_regd(u32 center_freq, |
4edd56981 cfg80211: regulat... |
977 |
const struct ieee80211_regdomain *regd, u32 bw) |
8318d78a4 cfg80211 API for ... |
978 979 |
{ int i; |
0c7dc45d2 cfg80211: Fix reg... |
980 |
bool band_rule_found = false; |
038659e7c cfg80211: Process... |
981 |
bool bw_fits = false; |
3e0c3ff36 cfg80211: allow m... |
982 |
if (!regd) |
361c9c8b0 regulatory: use I... |
983 |
return ERR_PTR(-EINVAL); |
b2e1b3029 cfg80211: Add new... |
984 |
|
3e0c3ff36 cfg80211: allow m... |
985 |
for (i = 0; i < regd->n_reg_rules; i++) { |
b2e1b3029 cfg80211: Add new... |
986 987 |
const struct ieee80211_reg_rule *rr; const struct ieee80211_freq_range *fr = NULL; |
b2e1b3029 cfg80211: Add new... |
988 |
|
3e0c3ff36 cfg80211: allow m... |
989 |
rr = ®d->reg_rules[i]; |
b2e1b3029 cfg80211: Add new... |
990 |
fr = &rr->freq_range; |
0c7dc45d2 cfg80211: Fix reg... |
991 |
|
fb1fc7add cfg80211: comment... |
992 993 |
/* * We only need to know if one frequency rule was |
0c7dc45d2 cfg80211: Fix reg... |
994 |
* was in center_freq's band, that's enough, so lets |
fb1fc7add cfg80211: comment... |
995 996 |
* not overwrite it once found */ |
0c7dc45d2 cfg80211: Fix reg... |
997 998 |
if (!band_rule_found) band_rule_found = freq_in_rule_band(fr, center_freq); |
4787cfa08 cfg80211: move fu... |
999 |
bw_fits = cfg80211_does_bw_fit_range(fr, center_freq, bw); |
0c7dc45d2 cfg80211: Fix reg... |
1000 |
|
361c9c8b0 regulatory: use I... |
1001 1002 |
if (band_rule_found && bw_fits) return rr; |
8318d78a4 cfg80211 API for ... |
1003 |
} |
0c7dc45d2 cfg80211: Fix reg... |
1004 |
if (!band_rule_found) |
361c9c8b0 regulatory: use I... |
1005 |
return ERR_PTR(-ERANGE); |
0c7dc45d2 cfg80211: Fix reg... |
1006 |
|
361c9c8b0 regulatory: use I... |
1007 |
return ERR_PTR(-EINVAL); |
b2e1b3029 cfg80211: Add new... |
1008 |
} |
8de1c63ba wireless: make __... |
1009 1010 |
static const struct ieee80211_reg_rule * __freq_reg_info(struct wiphy *wiphy, u32 center_freq, u32 min_bw) |
1fa25e413 cfg80211: add wip... |
1011 |
{ |
4edd56981 cfg80211: regulat... |
1012 1013 1014 |
const struct ieee80211_regdomain *regd = reg_get_regdomain(wiphy); const struct ieee80211_reg_rule *reg_rule = NULL; u32 bw; |
1a9193185 regulatory: code ... |
1015 |
|
4edd56981 cfg80211: regulat... |
1016 |
for (bw = MHZ_TO_KHZ(20); bw >= min_bw; bw = bw / 2) { |
491728746 cfg80211: reg: Re... |
1017 |
reg_rule = freq_reg_info_regd(center_freq, regd, bw); |
4edd56981 cfg80211: regulat... |
1018 1019 1020 |
if (!IS_ERR(reg_rule)) return reg_rule; } |
5d885b999 regulatory: simpl... |
1021 |
|
4edd56981 cfg80211: regulat... |
1022 1023 1024 1025 1026 1027 1028 |
return reg_rule; } const struct ieee80211_reg_rule *freq_reg_info(struct wiphy *wiphy, u32 center_freq) { return __freq_reg_info(wiphy, center_freq, MHZ_TO_KHZ(20)); |
1fa25e413 cfg80211: add wip... |
1029 |
} |
4f366c5da wireless: only us... |
1030 |
EXPORT_SYMBOL(freq_reg_info); |
b2e1b3029 cfg80211: Add new... |
1031 |
|
034c6d6e6 cfg80211: export ... |
1032 |
const char *reg_initiator_name(enum nl80211_reg_initiator initiator) |
926a0a094 cfg80211: add deb... |
1033 1034 1035 |
{ switch (initiator) { case NL80211_REGDOM_SET_BY_CORE: |
034c6d6e6 cfg80211: export ... |
1036 |
return "core"; |
926a0a094 cfg80211: add deb... |
1037 |
case NL80211_REGDOM_SET_BY_USER: |
034c6d6e6 cfg80211: export ... |
1038 |
return "user"; |
926a0a094 cfg80211: add deb... |
1039 |
case NL80211_REGDOM_SET_BY_DRIVER: |
034c6d6e6 cfg80211: export ... |
1040 |
return "driver"; |
926a0a094 cfg80211: add deb... |
1041 |
case NL80211_REGDOM_SET_BY_COUNTRY_IE: |
034c6d6e6 cfg80211: export ... |
1042 |
return "country IE"; |
926a0a094 cfg80211: add deb... |
1043 1044 |
default: WARN_ON(1); |
034c6d6e6 cfg80211: export ... |
1045 |
return "bug"; |
926a0a094 cfg80211: add deb... |
1046 1047 |
} } |
034c6d6e6 cfg80211: export ... |
1048 |
EXPORT_SYMBOL(reg_initiator_name); |
e702d3cf2 cfg80211: add deb... |
1049 |
|
1aeb135f8 cfg80211: reg: Re... |
1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 |
static uint32_t reg_rule_to_chan_bw_flags(const struct ieee80211_regdomain *regd, const struct ieee80211_reg_rule *reg_rule, const struct ieee80211_channel *chan) { const struct ieee80211_freq_range *freq_range = NULL; u32 max_bandwidth_khz, bw_flags = 0; freq_range = ®_rule->freq_range; max_bandwidth_khz = freq_range->max_bandwidth_khz; /* Check if auto calculation requested */ if (reg_rule->flags & NL80211_RRF_AUTO_BW) max_bandwidth_khz = reg_get_max_bandwidth(regd, reg_rule); /* If we get a reg_rule we can assume that at least 5Mhz fit */ |
4787cfa08 cfg80211: move fu... |
1065 1066 1067 |
if (!cfg80211_does_bw_fit_range(freq_range, MHZ_TO_KHZ(chan->center_freq), MHZ_TO_KHZ(10))) |
1aeb135f8 cfg80211: reg: Re... |
1068 |
bw_flags |= IEEE80211_CHAN_NO_10MHZ; |
4787cfa08 cfg80211: move fu... |
1069 1070 1071 |
if (!cfg80211_does_bw_fit_range(freq_range, MHZ_TO_KHZ(chan->center_freq), MHZ_TO_KHZ(20))) |
1aeb135f8 cfg80211: reg: Re... |
1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 |
bw_flags |= IEEE80211_CHAN_NO_20MHZ; if (max_bandwidth_khz < MHZ_TO_KHZ(10)) bw_flags |= IEEE80211_CHAN_NO_10MHZ; if (max_bandwidth_khz < MHZ_TO_KHZ(20)) bw_flags |= IEEE80211_CHAN_NO_20MHZ; if (max_bandwidth_khz < MHZ_TO_KHZ(40)) bw_flags |= IEEE80211_CHAN_NO_HT40; if (max_bandwidth_khz < MHZ_TO_KHZ(80)) bw_flags |= IEEE80211_CHAN_NO_80MHZ; if (max_bandwidth_khz < MHZ_TO_KHZ(160)) bw_flags |= IEEE80211_CHAN_NO_160MHZ; return bw_flags; } |
e33e2241e Revert "cfg80211:... |
1086 1087 1088 1089 |
/* * 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). |
038659e7c cfg80211: Process... |
1090 |
*/ |
7ca43d03b cfg80211: pass th... |
1091 1092 |
static void handle_channel(struct wiphy *wiphy, enum nl80211_reg_initiator initiator, |
fdc9d7b28 regulatory: remov... |
1093 |
struct ieee80211_channel *chan) |
b2e1b3029 cfg80211: Add new... |
1094 |
{ |
038659e7c cfg80211: Process... |
1095 |
u32 flags, bw_flags = 0; |
b2e1b3029 cfg80211: Add new... |
1096 1097 |
const struct ieee80211_reg_rule *reg_rule = NULL; const struct ieee80211_power_rule *power_rule = NULL; |
fe33eb390 cfg80211: move al... |
1098 |
struct wiphy *request_wiphy = NULL; |
c492db370 regulatory: use R... |
1099 |
struct regulatory_request *lr = get_last_request(); |
975248208 cfg80211: regulat... |
1100 |
const struct ieee80211_regdomain *regd; |
a92a3ce72 cfg80211: make ha... |
1101 |
|
c492db370 regulatory: use R... |
1102 |
request_wiphy = wiphy_idx_to_wiphy(lr->wiphy_idx); |
a92a3ce72 cfg80211: make ha... |
1103 1104 |
flags = chan->orig_flags; |
b2e1b3029 cfg80211: Add new... |
1105 |
|
361c9c8b0 regulatory: use I... |
1106 1107 |
reg_rule = freq_reg_info(wiphy, MHZ_TO_KHZ(chan->center_freq)); if (IS_ERR(reg_rule)) { |
ca4ffe8f2 cfg80211: fix dis... |
1108 1109 |
/* * We will disable all channels that do not match our |
25985edce Fix common misspe... |
1110 |
* received regulatory rule unless the hint is coming |
ca4ffe8f2 cfg80211: fix dis... |
1111 1112 1113 1114 1115 1116 1117 1118 |
* 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 && |
361c9c8b0 regulatory: use I... |
1119 |
PTR_ERR(reg_rule) == -ERANGE) |
ca4ffe8f2 cfg80211: fix dis... |
1120 |
return; |
cc493e4f5 cfg80211: enforce... |
1121 1122 |
if (lr->initiator == NL80211_REGDOM_SET_BY_DRIVER && request_wiphy && request_wiphy == wiphy && |
a2f73b6c5 cfg80211: move re... |
1123 |
request_wiphy->regulatory_flags & REGULATORY_STRICT_REG) { |
c799ba6ea cfg80211: remove ... |
1124 1125 1126 |
pr_debug("Disabling freq %d MHz for good ", chan->center_freq); |
cc493e4f5 cfg80211: enforce... |
1127 1128 1129 |
chan->orig_flags |= IEEE80211_CHAN_DISABLED; chan->flags = chan->orig_flags; } else { |
c799ba6ea cfg80211: remove ... |
1130 1131 1132 |
pr_debug("Disabling freq %d MHz ", chan->center_freq); |
cc493e4f5 cfg80211: enforce... |
1133 1134 |
chan->flags |= IEEE80211_CHAN_DISABLED; } |
8318d78a4 cfg80211 API for ... |
1135 |
return; |
ca4ffe8f2 cfg80211: fix dis... |
1136 |
} |
8318d78a4 cfg80211 API for ... |
1137 |
|
b0dfd2ea1 cfg80211: regulat... |
1138 |
regd = reg_get_regdomain(wiphy); |
e702d3cf2 cfg80211: add deb... |
1139 |
|
b2e1b3029 cfg80211: Add new... |
1140 |
power_rule = ®_rule->power_rule; |
1aeb135f8 cfg80211: reg: Re... |
1141 |
bw_flags = reg_rule_to_chan_bw_flags(regd, reg_rule, chan); |
b2e1b3029 cfg80211: Add new... |
1142 |
|
c492db370 regulatory: use R... |
1143 |
if (lr->initiator == NL80211_REGDOM_SET_BY_DRIVER && |
806a9e396 cfg80211: make re... |
1144 |
request_wiphy && request_wiphy == wiphy && |
a2f73b6c5 cfg80211: move re... |
1145 |
request_wiphy->regulatory_flags & REGULATORY_STRICT_REG) { |
fb1fc7add cfg80211: comment... |
1146 |
/* |
25985edce Fix common misspe... |
1147 |
* This guarantees the driver's requested regulatory domain |
f976376de cfg80211: Allow f... |
1148 |
* will always be used as a base for further regulatory |
fb1fc7add cfg80211: comment... |
1149 1150 |
* settings */ |
f976376de cfg80211: Allow f... |
1151 |
chan->flags = chan->orig_flags = |
038659e7c cfg80211: Process... |
1152 |
map_regdom_flags(reg_rule->flags) | bw_flags; |
f976376de cfg80211: Allow f... |
1153 1154 |
chan->max_antenna_gain = chan->orig_mag = (int) MBI_TO_DBI(power_rule->max_antenna_gain); |
279f0f552 cfg80211: fix ini... |
1155 |
chan->max_reg_power = chan->max_power = chan->orig_mpwr = |
f976376de cfg80211: Allow f... |
1156 |
(int) MBM_TO_DBM(power_rule->max_eirp); |
4f267c119 cfg80211: reg: se... |
1157 1158 1159 1160 1161 1162 |
if (chan->flags & IEEE80211_CHAN_RADAR) { chan->dfs_cac_ms = IEEE80211_DFS_MIN_CAC_TIME_MS; if (reg_rule->dfs_cac_ms) chan->dfs_cac_ms = reg_rule->dfs_cac_ms; } |
f976376de cfg80211: Allow f... |
1163 1164 |
return; } |
04f39047a nl80211/cfg80211:... |
1165 1166 |
chan->dfs_state = NL80211_DFS_USABLE; chan->dfs_state_entered = jiffies; |
aa3d7eef3 wireless: Reset b... |
1167 |
chan->beacon_found = false; |
038659e7c cfg80211: Process... |
1168 |
chan->flags = flags | bw_flags | map_regdom_flags(reg_rule->flags); |
1a9193185 regulatory: code ... |
1169 1170 1171 |
chan->max_antenna_gain = min_t(int, chan->orig_mag, MBI_TO_DBI(power_rule->max_antenna_gain)); |
eccc068e8 wireless: Save or... |
1172 |
chan->max_reg_power = (int) MBM_TO_DBM(power_rule->max_eirp); |
089027e57 cfg80211: regulat... |
1173 1174 1175 1176 1177 1178 1179 |
if (chan->flags & IEEE80211_CHAN_RADAR) { if (reg_rule->dfs_cac_ms) chan->dfs_cac_ms = reg_rule->dfs_cac_ms; else chan->dfs_cac_ms = IEEE80211_DFS_MIN_CAC_TIME_MS; } |
5e31fc081 wireless: reg: re... |
1180 1181 |
if (chan->orig_mpwr) { /* |
a09a85a01 cfg80211: add fla... |
1182 1183 |
* Devices that use REGULATORY_COUNTRY_IE_FOLLOW_POWER * will always follow the passed country IE power settings. |
5e31fc081 wireless: reg: re... |
1184 1185 |
*/ if (initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE && |
a09a85a01 cfg80211: add fla... |
1186 |
wiphy->regulatory_flags & REGULATORY_COUNTRY_IE_FOLLOW_POWER) |
5e31fc081 wireless: reg: re... |
1187 1188 1189 1190 1191 1192 |
chan->max_power = chan->max_reg_power; else chan->max_power = min(chan->orig_mpwr, chan->max_reg_power); } else chan->max_power = chan->max_reg_power; |
8318d78a4 cfg80211 API for ... |
1193 |
} |
7ca43d03b cfg80211: pass th... |
1194 |
static void handle_band(struct wiphy *wiphy, |
fdc9d7b28 regulatory: remov... |
1195 1196 |
enum nl80211_reg_initiator initiator, struct ieee80211_supported_band *sband) |
8318d78a4 cfg80211 API for ... |
1197 |
{ |
a92a3ce72 cfg80211: make ha... |
1198 |
unsigned int i; |
a92a3ce72 cfg80211: make ha... |
1199 |
|
fdc9d7b28 regulatory: remov... |
1200 1201 |
if (!sband) return; |
8318d78a4 cfg80211 API for ... |
1202 1203 |
for (i = 0; i < sband->n_channels; i++) |
fdc9d7b28 regulatory: remov... |
1204 |
handle_channel(wiphy, initiator, &sband->channels[i]); |
8318d78a4 cfg80211 API for ... |
1205 |
} |
57b5ce072 cfg80211: add cel... |
1206 1207 1208 1209 |
static bool reg_request_cell_base(struct regulatory_request *request) { if (request->initiator != NL80211_REGDOM_SET_BY_USER) return false; |
1a9193185 regulatory: code ... |
1210 |
return request->user_reg_hint_type == NL80211_USER_REG_HINT_CELL_BASE; |
57b5ce072 cfg80211: add cel... |
1211 1212 1213 1214 |
} bool reg_last_request_cell_base(void) { |
38fd2143f regulatory: remov... |
1215 |
return reg_request_cell_base(get_last_request()); |
57b5ce072 cfg80211: add cel... |
1216 |
} |
94fc661f6 cfg80211: Add Kco... |
1217 |
#ifdef CONFIG_CFG80211_REG_CELLULAR_HINTS |
57b5ce072 cfg80211: add cel... |
1218 |
/* Core specific check */ |
2f92212b7 regulatory: use p... |
1219 1220 |
static enum reg_request_treatment reg_ignore_cell_hint(struct regulatory_request *pending_request) |
57b5ce072 cfg80211: add cel... |
1221 |
{ |
c492db370 regulatory: use R... |
1222 |
struct regulatory_request *lr = get_last_request(); |
57b5ce072 cfg80211: add cel... |
1223 |
if (!reg_num_devs_support_basehint) |
2f92212b7 regulatory: use p... |
1224 |
return REG_REQ_IGNORE; |
57b5ce072 cfg80211: add cel... |
1225 |
|
c492db370 regulatory: use R... |
1226 |
if (reg_request_cell_base(lr) && |
1a9193185 regulatory: code ... |
1227 |
!regdom_changes(pending_request->alpha2)) |
2f92212b7 regulatory: use p... |
1228 |
return REG_REQ_ALREADY_SET; |
1a9193185 regulatory: code ... |
1229 |
|
2f92212b7 regulatory: use p... |
1230 |
return REG_REQ_OK; |
57b5ce072 cfg80211: add cel... |
1231 1232 1233 1234 1235 |
} /* Device specific check */ static bool reg_dev_ignore_cell_hint(struct wiphy *wiphy) { |
1a9193185 regulatory: code ... |
1236 |
return !(wiphy->features & NL80211_FEATURE_CELL_BASE_REG_HINTS); |
57b5ce072 cfg80211: add cel... |
1237 1238 |
} #else |
a515de660 cfg80211: reg: fi... |
1239 1240 |
static enum reg_request_treatment reg_ignore_cell_hint(struct regulatory_request *pending_request) |
57b5ce072 cfg80211: add cel... |
1241 |
{ |
2f92212b7 regulatory: use p... |
1242 |
return REG_REQ_IGNORE; |
57b5ce072 cfg80211: add cel... |
1243 |
} |
1a9193185 regulatory: code ... |
1244 1245 |
static bool reg_dev_ignore_cell_hint(struct wiphy *wiphy) |
57b5ce072 cfg80211: add cel... |
1246 1247 1248 1249 |
{ return true; } #endif |
fa1fb9cb1 cfg80211: simplif... |
1250 1251 |
static bool wiphy_strict_alpha2_regd(struct wiphy *wiphy) { |
a2f73b6c5 cfg80211: move re... |
1252 1253 |
if (wiphy->regulatory_flags & REGULATORY_STRICT_REG && !(wiphy->regulatory_flags & REGULATORY_CUSTOM_REG)) |
fa1fb9cb1 cfg80211: simplif... |
1254 1255 1256 |
return true; return false; } |
57b5ce072 cfg80211: add cel... |
1257 |
|
7db90f4a2 cfg80211: move en... |
1258 1259 |
static bool ignore_reg_update(struct wiphy *wiphy, enum nl80211_reg_initiator initiator) |
14b9815af cfg80211: add sup... |
1260 |
{ |
c492db370 regulatory: use R... |
1261 |
struct regulatory_request *lr = get_last_request(); |
b0d7aa595 cfg80211: allow w... |
1262 1263 |
if (wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED) return true; |
c492db370 regulatory: use R... |
1264 |
if (!lr) { |
c799ba6ea cfg80211: remove ... |
1265 1266 1267 |
pr_debug("Ignoring regulatory request set by %s since last_request is not set ", reg_initiator_name(initiator)); |
14b9815af cfg80211: add sup... |
1268 |
return true; |
926a0a094 cfg80211: add deb... |
1269 |
} |
7db90f4a2 cfg80211: move en... |
1270 |
if (initiator == NL80211_REGDOM_SET_BY_CORE && |
a2f73b6c5 cfg80211: move re... |
1271 |
wiphy->regulatory_flags & REGULATORY_CUSTOM_REG) { |
c799ba6ea cfg80211: remove ... |
1272 1273 1274 |
pr_debug("Ignoring regulatory request set by %s since the driver uses its own custom regulatory domain ", reg_initiator_name(initiator)); |
14b9815af cfg80211: add sup... |
1275 |
return true; |
926a0a094 cfg80211: add deb... |
1276 |
} |
fb1fc7add cfg80211: comment... |
1277 1278 1279 1280 |
/* * wiphy->regd will be set once the device has its own * desired regulatory domain set */ |
fa1fb9cb1 cfg80211: simplif... |
1281 |
if (wiphy_strict_alpha2_regd(wiphy) && !wiphy->regd && |
749b527b2 cfg80211: fix all... |
1282 |
initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE && |
c492db370 regulatory: use R... |
1283 |
!is_world_regdom(lr->alpha2)) { |
c799ba6ea cfg80211: remove ... |
1284 1285 1286 |
pr_debug("Ignoring regulatory request set by %s since the driver requires its own regulatory domain to be set first ", reg_initiator_name(initiator)); |
14b9815af cfg80211: add sup... |
1287 |
return true; |
926a0a094 cfg80211: add deb... |
1288 |
} |
c492db370 regulatory: use R... |
1289 |
if (reg_request_cell_base(lr)) |
57b5ce072 cfg80211: add cel... |
1290 |
return reg_dev_ignore_cell_hint(wiphy); |
14b9815af cfg80211: add sup... |
1291 1292 |
return false; } |
3195e489a cfg80211: move re... |
1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 |
static bool reg_is_world_roaming(struct wiphy *wiphy) { const struct ieee80211_regdomain *cr = get_cfg80211_regdom(); const struct ieee80211_regdomain *wr = get_wiphy_regdom(wiphy); struct regulatory_request *lr = get_last_request(); if (is_world_regdom(cr->alpha2) || (wr && is_world_regdom(wr->alpha2))) return true; if (lr && lr->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE && |
a2f73b6c5 cfg80211: move re... |
1303 |
wiphy->regulatory_flags & REGULATORY_CUSTOM_REG) |
3195e489a cfg80211: move re... |
1304 1305 1306 1307 |
return true; return false; } |
1a9193185 regulatory: code ... |
1308 |
static void handle_reg_beacon(struct wiphy *wiphy, unsigned int chan_idx, |
e38f8a7a8 cfg80211: Add AP ... |
1309 1310 |
struct reg_beacon *reg_beacon) { |
e38f8a7a8 cfg80211: Add AP ... |
1311 1312 |
struct ieee80211_supported_band *sband; struct ieee80211_channel *chan; |
6bad87666 cfg80211: send re... |
1313 1314 |
bool channel_changed = false; struct ieee80211_channel chan_before; |
e38f8a7a8 cfg80211: Add AP ... |
1315 |
|
e38f8a7a8 cfg80211: Add AP ... |
1316 1317 1318 1319 1320 |
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... |
1321 1322 1323 1324 |
if (chan->beacon_found) return; chan->beacon_found = true; |
0f500a5f6 cfg80211: move wo... |
1325 1326 |
if (!reg_is_world_roaming(wiphy)) return; |
a2f73b6c5 cfg80211: move re... |
1327 |
if (wiphy->regulatory_flags & REGULATORY_DISABLE_BEACON_HINTS) |
371842448 cfg80211: fix reg... |
1328 |
return; |
6bad87666 cfg80211: send re... |
1329 1330 |
chan_before.center_freq = chan->center_freq; chan_before.flags = chan->flags; |
8fe02e167 cfg80211: consoli... |
1331 1332 |
if (chan->flags & IEEE80211_CHAN_NO_IR) { chan->flags &= ~IEEE80211_CHAN_NO_IR; |
6bad87666 cfg80211: send re... |
1333 |
channel_changed = true; |
e38f8a7a8 cfg80211: Add AP ... |
1334 |
} |
6bad87666 cfg80211: send re... |
1335 1336 |
if (channel_changed) nl80211_send_beacon_hint_event(wiphy, &chan_before, chan); |
e38f8a7a8 cfg80211: Add AP ... |
1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 |
} /* * 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; |
e38f8a7a8 cfg80211: Add AP ... |
1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 |
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; |
e38f8a7a8 cfg80211: Add AP ... |
1365 1366 1367 1368 1369 1370 1371 1372 |
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); } } |
e38f8a7a8 cfg80211: Add AP ... |
1373 1374 1375 |
/* Reap the advantages of previously found beacons */ static void reg_process_beacons(struct wiphy *wiphy) { |
b1ed8ddd2 cfg80211: fix bug... |
1376 1377 1378 1379 1380 1381 |
/* * Means we are just firing up cfg80211, so no beacons would * have been processed yet. */ if (!last_request) return; |
e38f8a7a8 cfg80211: Add AP ... |
1382 1383 |
wiphy_update_beacon_reg(wiphy); } |
1a9193185 regulatory: code ... |
1384 |
static bool is_ht40_allowed(struct ieee80211_channel *chan) |
038659e7c cfg80211: Process... |
1385 1386 |
{ if (!chan) |
1a9193185 regulatory: code ... |
1387 |
return false; |
038659e7c cfg80211: Process... |
1388 |
if (chan->flags & IEEE80211_CHAN_DISABLED) |
1a9193185 regulatory: code ... |
1389 |
return false; |
038659e7c cfg80211: Process... |
1390 |
/* This would happen when regulatory rules disallow HT40 completely */ |
55b183ad8 wireless: fix reg... |
1391 1392 1393 |
if ((chan->flags & IEEE80211_CHAN_NO_HT40) == IEEE80211_CHAN_NO_HT40) return false; return true; |
038659e7c cfg80211: Process... |
1394 1395 1396 |
} static void reg_process_ht_flags_channel(struct wiphy *wiphy, |
fdc9d7b28 regulatory: remov... |
1397 |
struct ieee80211_channel *channel) |
038659e7c cfg80211: Process... |
1398 |
{ |
fdc9d7b28 regulatory: remov... |
1399 |
struct ieee80211_supported_band *sband = wiphy->bands[channel->band]; |
038659e7c cfg80211: Process... |
1400 |
struct ieee80211_channel *channel_before = NULL, *channel_after = NULL; |
4e0854a74 cfg80211: honor N... |
1401 |
const struct ieee80211_regdomain *regd; |
038659e7c cfg80211: Process... |
1402 |
unsigned int i; |
4e0854a74 cfg80211: honor N... |
1403 |
u32 flags; |
038659e7c cfg80211: Process... |
1404 |
|
1a9193185 regulatory: code ... |
1405 |
if (!is_ht40_allowed(channel)) { |
038659e7c cfg80211: Process... |
1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 |
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]; |
1a9193185 regulatory: code ... |
1416 |
|
038659e7c cfg80211: Process... |
1417 1418 1419 1420 1421 |
if (c->center_freq == (channel->center_freq - 20)) channel_before = c; if (c->center_freq == (channel->center_freq + 20)) channel_after = c; } |
4e0854a74 cfg80211: honor N... |
1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 |
flags = 0; regd = get_wiphy_regdom(wiphy); if (regd) { const struct ieee80211_reg_rule *reg_rule = freq_reg_info_regd(MHZ_TO_KHZ(channel->center_freq), regd, MHZ_TO_KHZ(20)); if (!IS_ERR(reg_rule)) flags = reg_rule->flags; } |
038659e7c cfg80211: Process... |
1432 1433 1434 1435 1436 |
/* * 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. */ |
4e0854a74 cfg80211: honor N... |
1437 1438 |
if (!is_ht40_allowed(channel_before) || flags & NL80211_RRF_NO_HT40MINUS) |
689da1b3b wireless: rename ... |
1439 |
channel->flags |= IEEE80211_CHAN_NO_HT40MINUS; |
038659e7c cfg80211: Process... |
1440 |
else |
689da1b3b wireless: rename ... |
1441 |
channel->flags &= ~IEEE80211_CHAN_NO_HT40MINUS; |
038659e7c cfg80211: Process... |
1442 |
|
4e0854a74 cfg80211: honor N... |
1443 1444 |
if (!is_ht40_allowed(channel_after) || flags & NL80211_RRF_NO_HT40PLUS) |
689da1b3b wireless: rename ... |
1445 |
channel->flags |= IEEE80211_CHAN_NO_HT40PLUS; |
038659e7c cfg80211: Process... |
1446 |
else |
689da1b3b wireless: rename ... |
1447 |
channel->flags &= ~IEEE80211_CHAN_NO_HT40PLUS; |
038659e7c cfg80211: Process... |
1448 1449 1450 |
} static void reg_process_ht_flags_band(struct wiphy *wiphy, |
fdc9d7b28 regulatory: remov... |
1451 |
struct ieee80211_supported_band *sband) |
038659e7c cfg80211: Process... |
1452 1453 |
{ unsigned int i; |
038659e7c cfg80211: Process... |
1454 |
|
fdc9d7b28 regulatory: remov... |
1455 1456 |
if (!sband) return; |
038659e7c cfg80211: Process... |
1457 1458 |
for (i = 0; i < sband->n_channels; i++) |
fdc9d7b28 regulatory: remov... |
1459 |
reg_process_ht_flags_channel(wiphy, &sband->channels[i]); |
038659e7c cfg80211: Process... |
1460 1461 1462 1463 |
} static void reg_process_ht_flags(struct wiphy *wiphy) { |
57fbcce37 cfg80211: remove ... |
1464 |
enum nl80211_band band; |
038659e7c cfg80211: Process... |
1465 1466 1467 |
if (!wiphy) return; |
57fbcce37 cfg80211: remove ... |
1468 |
for (band = 0; band < NUM_NL80211_BANDS; band++) |
fdc9d7b28 regulatory: remov... |
1469 |
reg_process_ht_flags_band(wiphy, wiphy->bands[band]); |
038659e7c cfg80211: Process... |
1470 |
} |
0e3802dbf cfg80211: move co... |
1471 1472 1473 1474 1475 1476 |
static void reg_call_notifier(struct wiphy *wiphy, struct regulatory_request *request) { if (wiphy->reg_notifier) wiphy->reg_notifier(wiphy, request); } |
ad932f046 cfg80211: leave i... |
1477 1478 |
static bool reg_wdev_chan_valid(struct wiphy *wiphy, struct wireless_dev *wdev) { |
ad932f046 cfg80211: leave i... |
1479 1480 |
struct cfg80211_chan_def chandef; struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); |
20658702e cfg80211: fix dea... |
1481 |
enum nl80211_iftype iftype; |
ad932f046 cfg80211: leave i... |
1482 1483 |
wdev_lock(wdev); |
20658702e cfg80211: fix dea... |
1484 |
iftype = wdev->iftype; |
ad932f046 cfg80211: leave i... |
1485 |
|
20658702e cfg80211: fix dea... |
1486 |
/* make sure the interface is active */ |
ad932f046 cfg80211: leave i... |
1487 |
if (!wdev->netdev || !netif_running(wdev->netdev)) |
20658702e cfg80211: fix dea... |
1488 |
goto wdev_inactive_unlock; |
ad932f046 cfg80211: leave i... |
1489 |
|
20658702e cfg80211: fix dea... |
1490 |
switch (iftype) { |
ad932f046 cfg80211: leave i... |
1491 1492 1493 |
case NL80211_IFTYPE_AP: case NL80211_IFTYPE_P2P_GO: if (!wdev->beacon_interval) |
20658702e cfg80211: fix dea... |
1494 1495 |
goto wdev_inactive_unlock; chandef = wdev->chandef; |
ad932f046 cfg80211: leave i... |
1496 |
break; |
185076d6d cfg80211: correct... |
1497 1498 |
case NL80211_IFTYPE_ADHOC: if (!wdev->ssid_len) |
20658702e cfg80211: fix dea... |
1499 1500 |
goto wdev_inactive_unlock; chandef = wdev->chandef; |
185076d6d cfg80211: correct... |
1501 |
break; |
ad932f046 cfg80211: leave i... |
1502 1503 |
case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_P2P_CLIENT: |
ad932f046 cfg80211: leave i... |
1504 1505 |
if (!wdev->current_bss || !wdev->current_bss->pub.channel) |
20658702e cfg80211: fix dea... |
1506 |
goto wdev_inactive_unlock; |
ad932f046 cfg80211: leave i... |
1507 |
|
20658702e cfg80211: fix dea... |
1508 1509 1510 1511 1512 |
if (!rdev->ops->get_channel || rdev_get_channel(rdev, wdev, &chandef)) cfg80211_chandef_create(&chandef, wdev->current_bss->pub.channel, NL80211_CHAN_NO_HT); |
ad932f046 cfg80211: leave i... |
1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 |
break; case NL80211_IFTYPE_MONITOR: case NL80211_IFTYPE_AP_VLAN: case NL80211_IFTYPE_P2P_DEVICE: /* no enforcement required */ break; default: /* others not implemented for now */ WARN_ON(1); break; } |
ad932f046 cfg80211: leave i... |
1524 |
wdev_unlock(wdev); |
20658702e cfg80211: fix dea... |
1525 1526 1527 1528 1529 |
switch (iftype) { case NL80211_IFTYPE_AP: case NL80211_IFTYPE_P2P_GO: case NL80211_IFTYPE_ADHOC: |
923b352f1 cfg80211: use RTN... |
1530 |
return cfg80211_reg_can_beacon_relax(wiphy, &chandef, iftype); |
20658702e cfg80211: fix dea... |
1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 |
case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_P2P_CLIENT: return cfg80211_chandef_usable(wiphy, &chandef, IEEE80211_CHAN_DISABLED); default: break; } return true; wdev_inactive_unlock: wdev_unlock(wdev); return true; |
ad932f046 cfg80211: leave i... |
1544 1545 1546 1547 1548 1549 1550 1551 |
} static void reg_leave_invalid_chans(struct wiphy *wiphy) { struct wireless_dev *wdev; struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); ASSERT_RTNL(); |
53873f134 cfg80211: make wd... |
1552 |
list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) |
ad932f046 cfg80211: leave i... |
1553 1554 1555 1556 1557 1558 1559 |
if (!reg_wdev_chan_valid(wiphy, wdev)) cfg80211_leave(rdev, wdev); } static void reg_check_chans_work(struct work_struct *work) { struct cfg80211_registered_device *rdev; |
c799ba6ea cfg80211: remove ... |
1560 1561 |
pr_debug("Verifying active interfaces after reg change "); |
ad932f046 cfg80211: leave i... |
1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 |
rtnl_lock(); list_for_each_entry(rdev, &cfg80211_rdev_list, list) if (!(rdev->wiphy.regulatory_flags & REGULATORY_IGNORE_STALE_KICKOFF)) reg_leave_invalid_chans(&rdev->wiphy); rtnl_unlock(); } static void reg_check_channels(void) { /* * Give usermode a chance to do something nicer (move to another * channel, orderly disconnection), before forcing a disconnection. */ mod_delayed_work(system_power_efficient_wq, ®_check_chans, msecs_to_jiffies(REG_ENFORCE_GRACE_MS)); } |
eac03e381 cfg80211: hold re... |
1582 1583 |
static void wiphy_update_regulatory(struct wiphy *wiphy, enum nl80211_reg_initiator initiator) |
b2e1b3029 cfg80211: Add new... |
1584 |
{ |
57fbcce37 cfg80211: remove ... |
1585 |
enum nl80211_band band; |
c492db370 regulatory: use R... |
1586 |
struct regulatory_request *lr = get_last_request(); |
eac03e381 cfg80211: hold re... |
1587 |
|
0e3802dbf cfg80211: move co... |
1588 1589 1590 1591 1592 1593 1594 |
if (ignore_reg_update(wiphy, initiator)) { /* * Regulatory updates set by CORE are ignored for custom * regulatory cards. Let us notify the changes to the driver, * as some drivers used this to restore its orig_* reg domain. */ if (initiator == NL80211_REGDOM_SET_BY_CORE && |
a2f73b6c5 cfg80211: move re... |
1595 |
wiphy->regulatory_flags & REGULATORY_CUSTOM_REG) |
0e3802dbf cfg80211: move co... |
1596 |
reg_call_notifier(wiphy, lr); |
a203c2aa4 cfg80211: really ... |
1597 |
return; |
0e3802dbf cfg80211: move co... |
1598 |
} |
a203c2aa4 cfg80211: really ... |
1599 |
|
c492db370 regulatory: use R... |
1600 |
lr->dfs_region = get_cfg80211_regdom()->dfs_region; |
b68e6b3b3 cfg80211: pass DF... |
1601 |
|
57fbcce37 cfg80211: remove ... |
1602 |
for (band = 0; band < NUM_NL80211_BANDS; band++) |
fdc9d7b28 regulatory: remov... |
1603 |
handle_band(wiphy, initiator, wiphy->bands[band]); |
a203c2aa4 cfg80211: really ... |
1604 |
|
e38f8a7a8 cfg80211: Add AP ... |
1605 |
reg_process_beacons(wiphy); |
038659e7c cfg80211: Process... |
1606 |
reg_process_ht_flags(wiphy); |
0e3802dbf cfg80211: move co... |
1607 |
reg_call_notifier(wiphy, lr); |
b2e1b3029 cfg80211: Add new... |
1608 |
} |
d7549cbb9 cfg80211: reorder... |
1609 1610 1611 |
static void update_all_wiphy_regulatory(enum nl80211_reg_initiator initiator) { struct cfg80211_registered_device *rdev; |
4a38994f1 cfg80211: notify ... |
1612 |
struct wiphy *wiphy; |
d7549cbb9 cfg80211: reorder... |
1613 |
|
5fe231e87 cfg80211: vastly ... |
1614 |
ASSERT_RTNL(); |
458f4f9e9 regulatory: use R... |
1615 |
|
4a38994f1 cfg80211: notify ... |
1616 1617 1618 |
list_for_each_entry(rdev, &cfg80211_rdev_list, list) { wiphy = &rdev->wiphy; wiphy_update_regulatory(wiphy, initiator); |
4a38994f1 cfg80211: notify ... |
1619 |
} |
ad932f046 cfg80211: leave i... |
1620 1621 |
reg_check_channels(); |
d7549cbb9 cfg80211: reorder... |
1622 |
} |
1fa25e413 cfg80211: add wip... |
1623 |
static void handle_channel_custom(struct wiphy *wiphy, |
fdc9d7b28 regulatory: remov... |
1624 |
struct ieee80211_channel *chan, |
1fa25e413 cfg80211: add wip... |
1625 1626 |
const struct ieee80211_regdomain *regd) { |
038659e7c cfg80211: Process... |
1627 |
u32 bw_flags = 0; |
1fa25e413 cfg80211: add wip... |
1628 1629 |
const struct ieee80211_reg_rule *reg_rule = NULL; const struct ieee80211_power_rule *power_rule = NULL; |
4edd56981 cfg80211: regulat... |
1630 |
u32 bw; |
ac46d48e0 cfg80211: fix rac... |
1631 |
|
4edd56981 cfg80211: regulat... |
1632 |
for (bw = MHZ_TO_KHZ(20); bw >= MHZ_TO_KHZ(5); bw = bw / 2) { |
491728746 cfg80211: reg: Re... |
1633 |
reg_rule = freq_reg_info_regd(MHZ_TO_KHZ(chan->center_freq), |
4edd56981 cfg80211: regulat... |
1634 1635 1636 1637 |
regd, bw); if (!IS_ERR(reg_rule)) break; } |
1fa25e413 cfg80211: add wip... |
1638 |
|
361c9c8b0 regulatory: use I... |
1639 |
if (IS_ERR(reg_rule)) { |
c799ba6ea cfg80211: remove ... |
1640 1641 1642 |
pr_debug("Disabling freq %d MHz as custom regd has no rule that fits it ", chan->center_freq); |
db8dfee57 cfg80211: avoid i... |
1643 1644 1645 1646 1647 1648 |
if (wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED) { chan->flags |= IEEE80211_CHAN_DISABLED; } else { chan->orig_flags |= IEEE80211_CHAN_DISABLED; chan->flags = chan->orig_flags; } |
1fa25e413 cfg80211: add wip... |
1649 1650 1651 1652 |
return; } power_rule = ®_rule->power_rule; |
1aeb135f8 cfg80211: reg: Re... |
1653 |
bw_flags = reg_rule_to_chan_bw_flags(regd, reg_rule, chan); |
1fa25e413 cfg80211: add wip... |
1654 |
|
2e18b38fc cfg80211: update ... |
1655 |
chan->dfs_state_entered = jiffies; |
c7ab50819 cfg80211: explici... |
1656 1657 1658 |
chan->dfs_state = NL80211_DFS_USABLE; chan->beacon_found = false; |
db8dfee57 cfg80211: avoid i... |
1659 1660 1661 1662 1663 1664 |
if (wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED) chan->flags = chan->orig_flags | bw_flags | map_regdom_flags(reg_rule->flags); else chan->flags |= map_regdom_flags(reg_rule->flags) | bw_flags; |
1fa25e413 cfg80211: add wip... |
1665 |
chan->max_antenna_gain = (int) MBI_TO_DBI(power_rule->max_antenna_gain); |
279f0f552 cfg80211: fix ini... |
1666 1667 |
chan->max_reg_power = chan->max_power = (int) MBM_TO_DBM(power_rule->max_eirp); |
2e18b38fc cfg80211: update ... |
1668 1669 1670 1671 1672 1673 1674 1675 1676 |
if (chan->flags & IEEE80211_CHAN_RADAR) { if (reg_rule->dfs_cac_ms) chan->dfs_cac_ms = reg_rule->dfs_cac_ms; else chan->dfs_cac_ms = IEEE80211_DFS_MIN_CAC_TIME_MS; } chan->max_power = chan->max_reg_power; |
1fa25e413 cfg80211: add wip... |
1677 |
} |
fdc9d7b28 regulatory: remov... |
1678 1679 |
static void handle_band_custom(struct wiphy *wiphy, struct ieee80211_supported_band *sband, |
1fa25e413 cfg80211: add wip... |
1680 1681 1682 |
const struct ieee80211_regdomain *regd) { unsigned int i; |
1fa25e413 cfg80211: add wip... |
1683 |
|
fdc9d7b28 regulatory: remov... |
1684 1685 |
if (!sband) return; |
1fa25e413 cfg80211: add wip... |
1686 1687 |
for (i = 0; i < sband->n_channels; i++) |
fdc9d7b28 regulatory: remov... |
1688 |
handle_channel_custom(wiphy, &sband->channels[i], regd); |
1fa25e413 cfg80211: add wip... |
1689 1690 1691 1692 1693 1694 |
} /* Used by drivers prior to wiphy registration */ void wiphy_apply_custom_regulatory(struct wiphy *wiphy, const struct ieee80211_regdomain *regd) { |
57fbcce37 cfg80211: remove ... |
1695 |
enum nl80211_band band; |
bbcf3f027 cfg80211: warn wh... |
1696 |
unsigned int bands_set = 0; |
ac46d48e0 cfg80211: fix rac... |
1697 |
|
a2f73b6c5 cfg80211: move re... |
1698 1699 1700 1701 |
WARN(!(wiphy->regulatory_flags & REGULATORY_CUSTOM_REG), "wiphy should have REGULATORY_CUSTOM_REG "); wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG; |
222ea5819 cfg80211: force W... |
1702 |
|
57fbcce37 cfg80211: remove ... |
1703 |
for (band = 0; band < NUM_NL80211_BANDS; band++) { |
bbcf3f027 cfg80211: warn wh... |
1704 1705 |
if (!wiphy->bands[band]) continue; |
fdc9d7b28 regulatory: remov... |
1706 |
handle_band_custom(wiphy, wiphy->bands[band], regd); |
bbcf3f027 cfg80211: warn wh... |
1707 |
bands_set++; |
b2e1b3029 cfg80211: Add new... |
1708 |
} |
bbcf3f027 cfg80211: warn wh... |
1709 1710 1711 |
/* * no point in calling this if it won't have any effect |
1a9193185 regulatory: code ... |
1712 |
* on your device's supported bands. |
bbcf3f027 cfg80211: warn wh... |
1713 1714 |
*/ WARN_ON(!bands_set); |
b2e1b3029 cfg80211: Add new... |
1715 |
} |
1fa25e413 cfg80211: add wip... |
1716 |
EXPORT_SYMBOL(wiphy_apply_custom_regulatory); |
b2e253cf3 cfg80211: Fix reg... |
1717 1718 1719 |
static void reg_set_request_processed(void) { bool need_more_processing = false; |
c492db370 regulatory: use R... |
1720 |
struct regulatory_request *lr = get_last_request(); |
b2e253cf3 cfg80211: Fix reg... |
1721 |
|
c492db370 regulatory: use R... |
1722 |
lr->processed = true; |
b2e253cf3 cfg80211: Fix reg... |
1723 1724 1725 1726 1727 |
spin_lock(®_requests_lock); if (!list_empty(®_requests_list)) need_more_processing = true; spin_unlock(®_requests_lock); |
b68630369 cfg80211: reg: ma... |
1728 |
cancel_crda_timeout(); |
a90c7a313 cfg80211: add a t... |
1729 |
|
b2e253cf3 cfg80211: Fix reg... |
1730 1731 1732 |
if (need_more_processing) schedule_work(®_work); } |
d1c96a9a2 cfg80211: make __... |
1733 |
/** |
b3eb7f3f5 cfg80211: process... |
1734 1735 1736 1737 1738 |
* reg_process_hint_core - process core regulatory requests * @pending_request: a pending core regulatory request * * The wireless subsystem can use this function to process * a regulatory request issued by the regulatory core. |
b3eb7f3f5 cfg80211: process... |
1739 |
*/ |
d34265a3e cfg80211: reg: ce... |
1740 1741 |
static enum reg_request_treatment reg_process_hint_core(struct regulatory_request *core_request) |
b3eb7f3f5 cfg80211: process... |
1742 |
{ |
cecbb069c cfg80211: reg: re... |
1743 |
if (reg_query_database(core_request)) { |
25b20dbdc cfg80211: reg: fi... |
1744 1745 1746 |
core_request->intersect = false; core_request->processed = false; reg_update_last_request(core_request); |
d34265a3e cfg80211: reg: ce... |
1747 |
return REG_REQ_OK; |
25b20dbdc cfg80211: reg: fi... |
1748 |
} |
d34265a3e cfg80211: reg: ce... |
1749 1750 |
return REG_REQ_IGNORE; |
b3eb7f3f5 cfg80211: process... |
1751 |
} |
0d97a6191 cfg80211: process... |
1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 |
static enum reg_request_treatment __reg_process_hint_user(struct regulatory_request *user_request) { struct regulatory_request *lr = get_last_request(); if (reg_request_cell_base(user_request)) return reg_ignore_cell_hint(user_request); if (reg_request_cell_base(lr)) return REG_REQ_IGNORE; if (lr->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) return REG_REQ_INTERSECT; /* * If the user knows better the user should set the regdom * to their country before the IE is picked up */ if (lr->initiator == NL80211_REGDOM_SET_BY_USER && lr->intersect) return REG_REQ_IGNORE; /* * Process user requests only after previous user/driver/core * requests have been processed */ if ((lr->initiator == NL80211_REGDOM_SET_BY_CORE || lr->initiator == NL80211_REGDOM_SET_BY_DRIVER || lr->initiator == NL80211_REGDOM_SET_BY_USER) && regdom_changes(lr->alpha2)) return REG_REQ_IGNORE; if (!regdom_changes(user_request->alpha2)) return REG_REQ_ALREADY_SET; return REG_REQ_OK; } /** * reg_process_hint_user - process user regulatory requests * @user_request: a pending user regulatory request * * The wireless subsystem can use this function to process * a regulatory request initiated by userspace. |
0d97a6191 cfg80211: process... |
1794 |
*/ |
d34265a3e cfg80211: reg: ce... |
1795 1796 |
static enum reg_request_treatment reg_process_hint_user(struct regulatory_request *user_request) |
0d97a6191 cfg80211: process... |
1797 1798 |
{ enum reg_request_treatment treatment; |
0d97a6191 cfg80211: process... |
1799 1800 1801 |
treatment = __reg_process_hint_user(user_request); if (treatment == REG_REQ_IGNORE || |
d34265a3e cfg80211: reg: ce... |
1802 1803 |
treatment == REG_REQ_ALREADY_SET) return REG_REQ_IGNORE; |
0d97a6191 cfg80211: process... |
1804 |
|
0d97a6191 cfg80211: process... |
1805 1806 |
user_request->intersect = treatment == REG_REQ_INTERSECT; user_request->processed = false; |
5ad6ef5e0 cfg80211: add hel... |
1807 |
|
cecbb069c cfg80211: reg: re... |
1808 |
if (reg_query_database(user_request)) { |
25b20dbdc cfg80211: reg: fi... |
1809 1810 1811 |
reg_update_last_request(user_request); user_alpha2[0] = user_request->alpha2[0]; user_alpha2[1] = user_request->alpha2[1]; |
d34265a3e cfg80211: reg: ce... |
1812 |
return REG_REQ_OK; |
25b20dbdc cfg80211: reg: fi... |
1813 |
} |
d34265a3e cfg80211: reg: ce... |
1814 1815 |
return REG_REQ_IGNORE; |
0d97a6191 cfg80211: process... |
1816 |
} |
21636c7fa cfg80211: process... |
1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 |
static enum reg_request_treatment __reg_process_hint_driver(struct regulatory_request *driver_request) { struct regulatory_request *lr = get_last_request(); if (lr->initiator == NL80211_REGDOM_SET_BY_CORE) { if (regdom_changes(driver_request->alpha2)) return REG_REQ_OK; return REG_REQ_ALREADY_SET; } /* * 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. */ if (lr->initiator == NL80211_REGDOM_SET_BY_DRIVER && !regdom_changes(driver_request->alpha2)) return REG_REQ_ALREADY_SET; return REG_REQ_INTERSECT; } /** * reg_process_hint_driver - process driver regulatory requests * @driver_request: a pending driver regulatory request * * The wireless subsystem can use this function to process * a regulatory request issued by an 802.11 driver. * * Returns one of the different reg request treatment values. */ static enum reg_request_treatment reg_process_hint_driver(struct wiphy *wiphy, struct regulatory_request *driver_request) { |
34f05f543 cfg80211: avoid m... |
1853 |
const struct ieee80211_regdomain *regd, *tmp; |
21636c7fa cfg80211: process... |
1854 |
enum reg_request_treatment treatment; |
21636c7fa cfg80211: process... |
1855 1856 1857 1858 1859 1860 1861 |
treatment = __reg_process_hint_driver(driver_request); switch (treatment) { case REG_REQ_OK: break; case REG_REQ_IGNORE: |
d34265a3e cfg80211: reg: ce... |
1862 |
return REG_REQ_IGNORE; |
21636c7fa cfg80211: process... |
1863 |
case REG_REQ_INTERSECT: |
21636c7fa cfg80211: process... |
1864 1865 |
case REG_REQ_ALREADY_SET: regd = reg_copy_regd(get_cfg80211_regdom()); |
d34265a3e cfg80211: reg: ce... |
1866 1867 |
if (IS_ERR(regd)) return REG_REQ_IGNORE; |
34f05f543 cfg80211: avoid m... |
1868 1869 |
tmp = get_wiphy_regdom(wiphy); |
21636c7fa cfg80211: process... |
1870 |
rcu_assign_pointer(wiphy->regd, regd); |
34f05f543 cfg80211: avoid m... |
1871 |
rcu_free_regdom(tmp); |
21636c7fa cfg80211: process... |
1872 |
} |
21636c7fa cfg80211: process... |
1873 1874 1875 |
driver_request->intersect = treatment == REG_REQ_INTERSECT; driver_request->processed = false; |
5ad6ef5e0 cfg80211: add hel... |
1876 |
|
21636c7fa cfg80211: process... |
1877 1878 1879 1880 1881 1882 1883 |
/* * 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 */ if (treatment == REG_REQ_ALREADY_SET) { nl80211_send_reg_change_event(driver_request); |
25b20dbdc cfg80211: reg: fi... |
1884 |
reg_update_last_request(driver_request); |
21636c7fa cfg80211: process... |
1885 |
reg_set_request_processed(); |
480908a7e cfg80211: reg: cl... |
1886 |
return REG_REQ_ALREADY_SET; |
21636c7fa cfg80211: process... |
1887 |
} |
d34265a3e cfg80211: reg: ce... |
1888 |
if (reg_query_database(driver_request)) { |
25b20dbdc cfg80211: reg: fi... |
1889 |
reg_update_last_request(driver_request); |
d34265a3e cfg80211: reg: ce... |
1890 1891 |
return REG_REQ_OK; } |
25b20dbdc cfg80211: reg: fi... |
1892 |
|
d34265a3e cfg80211: reg: ce... |
1893 |
return REG_REQ_IGNORE; |
21636c7fa cfg80211: process... |
1894 |
} |
b23e7a9e6 cfg80211: process... |
1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 |
static enum reg_request_treatment __reg_process_hint_country_ie(struct wiphy *wiphy, struct regulatory_request *country_ie_request) { struct wiphy *last_wiphy = NULL; struct regulatory_request *lr = get_last_request(); if (reg_request_cell_base(lr)) { /* Trust a Cell base station over the AP's country IE */ if (regdom_changes(country_ie_request->alpha2)) return REG_REQ_IGNORE; return REG_REQ_ALREADY_SET; |
2a901468c cfg80211: add an ... |
1907 1908 1909 |
} else { if (wiphy->regulatory_flags & REGULATORY_COUNTRY_IE_IGNORE) return REG_REQ_IGNORE; |
b23e7a9e6 cfg80211: process... |
1910 |
} |
b23e7a9e6 cfg80211: process... |
1911 1912 |
if (unlikely(!is_an_alpha2(country_ie_request->alpha2))) return -EINVAL; |
2f1c6c572 cfg80211: process... |
1913 1914 1915 1916 1917 1918 1919 |
if (lr->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE) return REG_REQ_OK; last_wiphy = wiphy_idx_to_wiphy(lr->wiphy_idx); if (last_wiphy != wiphy) { |
b23e7a9e6 cfg80211: process... |
1920 |
/* |
2f1c6c572 cfg80211: process... |
1921 1922 1923 1924 |
* Two cards with two APs claiming different * Country IE alpha2s. We could * intersect them, but that seems unlikely * to be correct. Reject second one for now. |
b23e7a9e6 cfg80211: process... |
1925 |
*/ |
2f1c6c572 cfg80211: process... |
1926 1927 |
if (regdom_changes(country_ie_request->alpha2)) return REG_REQ_IGNORE; |
b23e7a9e6 cfg80211: process... |
1928 1929 |
return REG_REQ_ALREADY_SET; } |
70dcec5a4 cfg80211: don't W... |
1930 1931 |
if (regdom_changes(country_ie_request->alpha2)) |
2f1c6c572 cfg80211: process... |
1932 1933 |
return REG_REQ_OK; return REG_REQ_ALREADY_SET; |
b23e7a9e6 cfg80211: process... |
1934 |
} |
b3eb7f3f5 cfg80211: process... |
1935 |
/** |
b23e7a9e6 cfg80211: process... |
1936 1937 |
* reg_process_hint_country_ie - process regulatory requests from country IEs * @country_ie_request: a regulatory request from a country IE |
d1c96a9a2 cfg80211: make __... |
1938 |
* |
b23e7a9e6 cfg80211: process... |
1939 1940 |
* The wireless subsystem can use this function to process * a regulatory request issued by a country Information Element. |
d1c96a9a2 cfg80211: make __... |
1941 |
* |
2f92212b7 regulatory: use p... |
1942 |
* Returns one of the different reg request treatment values. |
d1c96a9a2 cfg80211: make __... |
1943 |
*/ |
2f92212b7 regulatory: use p... |
1944 |
static enum reg_request_treatment |
b23e7a9e6 cfg80211: process... |
1945 1946 |
reg_process_hint_country_ie(struct wiphy *wiphy, struct regulatory_request *country_ie_request) |
b2e1b3029 cfg80211: Add new... |
1947 |
{ |
2f92212b7 regulatory: use p... |
1948 |
enum reg_request_treatment treatment; |
761cf7ecf cfg80211: add ass... |
1949 |
|
b23e7a9e6 cfg80211: process... |
1950 |
treatment = __reg_process_hint_country_ie(wiphy, country_ie_request); |
9c96477d1 cfg80211: Add reg... |
1951 |
|
2f92212b7 regulatory: use p... |
1952 |
switch (treatment) { |
2f92212b7 regulatory: use p... |
1953 1954 |
case REG_REQ_OK: break; |
b23e7a9e6 cfg80211: process... |
1955 |
case REG_REQ_IGNORE: |
d34265a3e cfg80211: reg: ce... |
1956 |
return REG_REQ_IGNORE; |
b23e7a9e6 cfg80211: process... |
1957 |
case REG_REQ_ALREADY_SET: |
c888393b7 cfg80211: avoid f... |
1958 |
reg_free_request(country_ie_request); |
480908a7e cfg80211: reg: cl... |
1959 |
return REG_REQ_ALREADY_SET; |
b23e7a9e6 cfg80211: process... |
1960 |
case REG_REQ_INTERSECT: |
fb1fc7add cfg80211: comment... |
1961 |
/* |
b23e7a9e6 cfg80211: process... |
1962 1963 |
* This doesn't happen yet, not sure we * ever want to support it for this case. |
fb1fc7add cfg80211: comment... |
1964 |
*/ |
b23e7a9e6 cfg80211: process... |
1965 |
WARN_ONCE(1, "Unexpected intersection for country IEs"); |
d34265a3e cfg80211: reg: ce... |
1966 |
return REG_REQ_IGNORE; |
3e0c3ff36 cfg80211: allow m... |
1967 |
} |
b2e1b3029 cfg80211: Add new... |
1968 |
|
b23e7a9e6 cfg80211: process... |
1969 1970 |
country_ie_request->intersect = false; country_ie_request->processed = false; |
5ad6ef5e0 cfg80211: add hel... |
1971 |
|
d34265a3e cfg80211: reg: ce... |
1972 |
if (reg_query_database(country_ie_request)) { |
25b20dbdc cfg80211: reg: fi... |
1973 |
reg_update_last_request(country_ie_request); |
d34265a3e cfg80211: reg: ce... |
1974 1975 |
return REG_REQ_OK; } |
3e0c3ff36 cfg80211: allow m... |
1976 |
|
d34265a3e cfg80211: reg: ce... |
1977 |
return REG_REQ_IGNORE; |
b2e1b3029 cfg80211: Add new... |
1978 |
} |
897667273 cfg80211: Share C... |
1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 |
bool reg_dfs_domain_same(struct wiphy *wiphy1, struct wiphy *wiphy2) { const struct ieee80211_regdomain *wiphy1_regd = NULL; const struct ieee80211_regdomain *wiphy2_regd = NULL; const struct ieee80211_regdomain *cfg80211_regd = NULL; bool dfs_domain_same; rcu_read_lock(); cfg80211_regd = rcu_dereference(cfg80211_regdomain); wiphy1_regd = rcu_dereference(wiphy1->regd); if (!wiphy1_regd) wiphy1_regd = cfg80211_regd; wiphy2_regd = rcu_dereference(wiphy2->regd); if (!wiphy2_regd) wiphy2_regd = cfg80211_regd; dfs_domain_same = wiphy1_regd->dfs_region == wiphy2_regd->dfs_region; rcu_read_unlock(); return dfs_domain_same; } static void reg_copy_dfs_chan_state(struct ieee80211_channel *dst_chan, struct ieee80211_channel *src_chan) { if (!(dst_chan->flags & IEEE80211_CHAN_RADAR) || !(src_chan->flags & IEEE80211_CHAN_RADAR)) return; if (dst_chan->flags & IEEE80211_CHAN_DISABLED || src_chan->flags & IEEE80211_CHAN_DISABLED) return; if (src_chan->center_freq == dst_chan->center_freq && dst_chan->dfs_state == NL80211_DFS_USABLE) { dst_chan->dfs_state = src_chan->dfs_state; dst_chan->dfs_state_entered = src_chan->dfs_state_entered; } } static void wiphy_share_dfs_chan_state(struct wiphy *dst_wiphy, struct wiphy *src_wiphy) { struct ieee80211_supported_band *src_sband, *dst_sband; struct ieee80211_channel *src_chan, *dst_chan; int i, j, band; if (!reg_dfs_domain_same(dst_wiphy, src_wiphy)) return; for (band = 0; band < NUM_NL80211_BANDS; band++) { dst_sband = dst_wiphy->bands[band]; src_sband = src_wiphy->bands[band]; if (!dst_sband || !src_sband) continue; for (i = 0; i < dst_sband->n_channels; i++) { dst_chan = &dst_sband->channels[i]; for (j = 0; j < src_sband->n_channels; j++) { src_chan = &src_sband->channels[j]; reg_copy_dfs_chan_state(dst_chan, src_chan); } } } } static void wiphy_all_share_dfs_chan_state(struct wiphy *wiphy) { struct cfg80211_registered_device *rdev; ASSERT_RTNL(); list_for_each_entry(rdev, &cfg80211_rdev_list, list) { if (wiphy == &rdev->wiphy) continue; wiphy_share_dfs_chan_state(wiphy, &rdev->wiphy); } } |
30a548c72 cfg80211: fix com... |
2060 |
/* This processes *all* regulatory hints */ |
1daa37c7b cfg80211: remove ... |
2061 |
static void reg_process_hint(struct regulatory_request *reg_request) |
fe33eb390 cfg80211: move al... |
2062 |
{ |
fe33eb390 cfg80211: move al... |
2063 |
struct wiphy *wiphy = NULL; |
b3eb7f3f5 cfg80211: process... |
2064 |
enum reg_request_treatment treatment; |
492a81318 cfg80211: fix use... |
2065 |
enum nl80211_reg_initiator initiator = reg_request->initiator; |
fe33eb390 cfg80211: move al... |
2066 |
|
f41737669 cfg80211: remove ... |
2067 |
if (reg_request->wiphy_idx != WIPHY_IDX_INVALID) |
fe33eb390 cfg80211: move al... |
2068 |
wiphy = wiphy_idx_to_wiphy(reg_request->wiphy_idx); |
492a81318 cfg80211: fix use... |
2069 |
switch (initiator) { |
b3eb7f3f5 cfg80211: process... |
2070 |
case NL80211_REGDOM_SET_BY_CORE: |
d34265a3e cfg80211: reg: ce... |
2071 2072 |
treatment = reg_process_hint_core(reg_request); break; |
b3eb7f3f5 cfg80211: process... |
2073 |
case NL80211_REGDOM_SET_BY_USER: |
d34265a3e cfg80211: reg: ce... |
2074 2075 |
treatment = reg_process_hint_user(reg_request); break; |
b3eb7f3f5 cfg80211: process... |
2076 |
case NL80211_REGDOM_SET_BY_DRIVER: |
772f03893 cfg80211: fix few... |
2077 2078 |
if (!wiphy) goto out_free; |
21636c7fa cfg80211: process... |
2079 2080 |
treatment = reg_process_hint_driver(wiphy, reg_request); break; |
b3eb7f3f5 cfg80211: process... |
2081 |
case NL80211_REGDOM_SET_BY_COUNTRY_IE: |
772f03893 cfg80211: fix few... |
2082 2083 |
if (!wiphy) goto out_free; |
b23e7a9e6 cfg80211: process... |
2084 |
treatment = reg_process_hint_country_ie(wiphy, reg_request); |
b3eb7f3f5 cfg80211: process... |
2085 2086 |
break; default: |
492a81318 cfg80211: fix use... |
2087 2088 |
WARN(1, "invalid initiator %d ", initiator); |
772f03893 cfg80211: fix few... |
2089 |
goto out_free; |
b3eb7f3f5 cfg80211: process... |
2090 |
} |
d34265a3e cfg80211: reg: ce... |
2091 2092 |
if (treatment == REG_REQ_IGNORE) goto out_free; |
480908a7e cfg80211: reg: cl... |
2093 2094 2095 |
WARN(treatment != REG_REQ_OK && treatment != REG_REQ_ALREADY_SET, "unexpected treatment value %d ", treatment); |
841b351cf wireless: remove ... |
2096 2097 2098 |
/* This is required so that the orig_* parameters are saved. * NOTE: treatment must be set for any case that reaches here! */ |
b23e7a9e6 cfg80211: process... |
2099 |
if (treatment == REG_REQ_ALREADY_SET && wiphy && |
ad932f046 cfg80211: leave i... |
2100 |
wiphy->regulatory_flags & REGULATORY_STRICT_REG) { |
492a81318 cfg80211: fix use... |
2101 |
wiphy_update_regulatory(wiphy, initiator); |
897667273 cfg80211: Share C... |
2102 |
wiphy_all_share_dfs_chan_state(wiphy); |
ad932f046 cfg80211: leave i... |
2103 2104 |
reg_check_channels(); } |
772f03893 cfg80211: fix few... |
2105 2106 2107 2108 |
return; out_free: |
c888393b7 cfg80211: avoid f... |
2109 |
reg_free_request(reg_request); |
fe33eb390 cfg80211: move al... |
2110 |
} |
ef51fb1d1 cfg80211: avoid r... |
2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 |
static bool reg_only_self_managed_wiphys(void) { struct cfg80211_registered_device *rdev; struct wiphy *wiphy; bool self_managed_found = false; ASSERT_RTNL(); list_for_each_entry(rdev, &cfg80211_rdev_list, list) { wiphy = &rdev->wiphy; if (wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED) self_managed_found = true; else return false; } /* make sure at least one self-managed wiphy exists */ return self_managed_found; } |
b2e253cf3 cfg80211: Fix reg... |
2130 2131 2132 2133 2134 |
/* * 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... |
2135 |
static void reg_process_pending_hints(void) |
b0e2880b0 cfg80211: move mu... |
2136 |
{ |
c492db370 regulatory: use R... |
2137 |
struct regulatory_request *reg_request, *lr; |
fe33eb390 cfg80211: move al... |
2138 |
|
c492db370 regulatory: use R... |
2139 |
lr = get_last_request(); |
b0e2880b0 cfg80211: move mu... |
2140 |
|
b2e253cf3 cfg80211: Fix reg... |
2141 |
/* When last_request->processed becomes true this will be rescheduled */ |
c492db370 regulatory: use R... |
2142 |
if (lr && !lr->processed) { |
96cce12ff cfg80211: fix pro... |
2143 |
reg_process_hint(lr); |
5fe231e87 cfg80211: vastly ... |
2144 |
return; |
b2e253cf3 cfg80211: Fix reg... |
2145 |
} |
fe33eb390 cfg80211: move al... |
2146 |
spin_lock(®_requests_lock); |
fe33eb390 cfg80211: move al... |
2147 |
|
b2e253cf3 cfg80211: Fix reg... |
2148 |
if (list_empty(®_requests_list)) { |
d951c1dde cfg80211: do not ... |
2149 |
spin_unlock(®_requests_lock); |
5fe231e87 cfg80211: vastly ... |
2150 |
return; |
fe33eb390 cfg80211: move al... |
2151 |
} |
b2e253cf3 cfg80211: Fix reg... |
2152 2153 2154 2155 2156 |
reg_request = list_first_entry(®_requests_list, struct regulatory_request, list); list_del_init(®_request->list); |
fe33eb390 cfg80211: move al... |
2157 |
spin_unlock(®_requests_lock); |
b0e2880b0 cfg80211: move mu... |
2158 |
|
ef51fb1d1 cfg80211: avoid r... |
2159 2160 2161 2162 |
if (reg_only_self_managed_wiphys()) { reg_free_request(reg_request); return; } |
1daa37c7b cfg80211: remove ... |
2163 |
reg_process_hint(reg_request); |
2e54a6895 cfg80211: Process... |
2164 2165 2166 2167 2168 2169 2170 |
lr = get_last_request(); spin_lock(®_requests_lock); if (!list_empty(®_requests_list) && lr && lr->processed) schedule_work(®_work); spin_unlock(®_requests_lock); |
fe33eb390 cfg80211: move al... |
2171 |
} |
e38f8a7a8 cfg80211: Add AP ... |
2172 2173 2174 |
/* Processes beacon hints -- this has nothing to do with country IEs */ static void reg_process_pending_beacon_hints(void) { |
79c97e97a cfg80211: clean u... |
2175 |
struct cfg80211_registered_device *rdev; |
e38f8a7a8 cfg80211: Add AP ... |
2176 |
struct reg_beacon *pending_beacon, *tmp; |
e38f8a7a8 cfg80211: Add AP ... |
2177 2178 |
/* This goes through the _pending_ beacon list */ spin_lock_bh(®_pending_beacons_lock); |
e38f8a7a8 cfg80211: Add AP ... |
2179 2180 |
list_for_each_entry_safe(pending_beacon, tmp, ®_pending_beacons, list) { |
e38f8a7a8 cfg80211: Add AP ... |
2181 2182 2183 |
list_del_init(&pending_beacon->list); /* Applies the beacon hint to current wiphys */ |
79c97e97a cfg80211: clean u... |
2184 2185 |
list_for_each_entry(rdev, &cfg80211_rdev_list, list) wiphy_update_new_beacon(&rdev->wiphy, pending_beacon); |
e38f8a7a8 cfg80211: Add AP ... |
2186 2187 2188 2189 2190 2191 |
/* Remembers the beacon hint for new wiphys or reg changes */ list_add_tail(&pending_beacon->list, ®_beacon_list); } spin_unlock_bh(®_pending_beacons_lock); |
e38f8a7a8 cfg80211: Add AP ... |
2192 |
} |
b0d7aa595 cfg80211: allow w... |
2193 2194 2195 2196 2197 2198 |
static void reg_process_self_managed_hints(void) { struct cfg80211_registered_device *rdev; struct wiphy *wiphy; const struct ieee80211_regdomain *tmp; const struct ieee80211_regdomain *regd; |
57fbcce37 cfg80211: remove ... |
2199 |
enum nl80211_band band; |
b0d7aa595 cfg80211: allow w... |
2200 2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 |
struct regulatory_request request = {}; list_for_each_entry(rdev, &cfg80211_rdev_list, list) { wiphy = &rdev->wiphy; spin_lock(®_requests_lock); regd = rdev->requested_regd; rdev->requested_regd = NULL; spin_unlock(®_requests_lock); if (regd == NULL) continue; tmp = get_wiphy_regdom(wiphy); rcu_assign_pointer(wiphy->regd, regd); rcu_free_regdom(tmp); |
57fbcce37 cfg80211: remove ... |
2216 |
for (band = 0; band < NUM_NL80211_BANDS; band++) |
b0d7aa595 cfg80211: allow w... |
2217 2218 2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 |
handle_band_custom(wiphy, wiphy->bands[band], regd); reg_process_ht_flags(wiphy); request.wiphy_idx = get_wiphy_idx(wiphy); request.alpha2[0] = regd->alpha2[0]; request.alpha2[1] = regd->alpha2[1]; request.initiator = NL80211_REGDOM_SET_BY_DRIVER; nl80211_send_wiphy_reg_change_event(&request); } reg_check_channels(); } |
fe33eb390 cfg80211: move al... |
2231 2232 |
static void reg_todo(struct work_struct *work) { |
5fe231e87 cfg80211: vastly ... |
2233 |
rtnl_lock(); |
fe33eb390 cfg80211: move al... |
2234 |
reg_process_pending_hints(); |
e38f8a7a8 cfg80211: Add AP ... |
2235 |
reg_process_pending_beacon_hints(); |
b0d7aa595 cfg80211: allow w... |
2236 |
reg_process_self_managed_hints(); |
5fe231e87 cfg80211: vastly ... |
2237 |
rtnl_unlock(); |
fe33eb390 cfg80211: move al... |
2238 |
} |
fe33eb390 cfg80211: move al... |
2239 2240 |
static void queue_regulatory_request(struct regulatory_request *request) { |
d4f2c8819 regulatory: remov... |
2241 2242 |
request->alpha2[0] = toupper(request->alpha2[0]); request->alpha2[1] = toupper(request->alpha2[1]); |
c61029c77 wireless: upcase ... |
2243 |
|
fe33eb390 cfg80211: move al... |
2244 2245 2246 2247 2248 2249 |
spin_lock(®_requests_lock); list_add_tail(&request->list, ®_requests_list); spin_unlock(®_requests_lock); schedule_work(®_work); } |
09d989d17 cfg80211: add reg... |
2250 2251 2252 2253 |
/* * Core regulatory hint -- happens during cfg80211_init() * and when we restore regulatory settings. */ |
ba25c1414 cfg80211: add reg... |
2254 2255 2256 |
static int regulatory_hint_core(const char *alpha2) { struct regulatory_request *request; |
1a9193185 regulatory: code ... |
2257 |
request = kzalloc(sizeof(struct regulatory_request), GFP_KERNEL); |
ba25c1414 cfg80211: add reg... |
2258 2259 2260 2261 2262 |
if (!request) return -ENOMEM; request->alpha2[0] = alpha2[0]; request->alpha2[1] = alpha2[1]; |
7db90f4a2 cfg80211: move en... |
2263 |
request->initiator = NL80211_REGDOM_SET_BY_CORE; |
d46c334f0 cfg80211: reg: In... |
2264 |
request->wiphy_idx = WIPHY_IDX_INVALID; |
ba25c1414 cfg80211: add reg... |
2265 |
|
31e99729a cfg80211: put cor... |
2266 |
queue_regulatory_request(request); |
5078b2e32 cfg80211: fix rac... |
2267 |
|
fe33eb390 cfg80211: move al... |
2268 |
return 0; |
ba25c1414 cfg80211: add reg... |
2269 |
} |
fe33eb390 cfg80211: move al... |
2270 |
/* User hints */ |
57b5ce072 cfg80211: add cel... |
2271 2272 |
int regulatory_hint_user(const char *alpha2, enum nl80211_user_reg_hint_type user_reg_hint_type) |
b2e1b3029 cfg80211: Add new... |
2273 |
{ |
fe33eb390 cfg80211: move al... |
2274 |
struct regulatory_request *request; |
fdc9d7b28 regulatory: remov... |
2275 2276 |
if (WARN_ON(!alpha2)) return -EINVAL; |
b2e1b3029 cfg80211: Add new... |
2277 |
|
fe33eb390 cfg80211: move al... |
2278 2279 2280 |
request = kzalloc(sizeof(struct regulatory_request), GFP_KERNEL); if (!request) return -ENOMEM; |
f41737669 cfg80211: remove ... |
2281 |
request->wiphy_idx = WIPHY_IDX_INVALID; |
fe33eb390 cfg80211: move al... |
2282 2283 |
request->alpha2[0] = alpha2[0]; request->alpha2[1] = alpha2[1]; |
e12822e1d cfg80211: fix syn... |
2284 |
request->initiator = NL80211_REGDOM_SET_BY_USER; |
57b5ce072 cfg80211: add cel... |
2285 |
request->user_reg_hint_type = user_reg_hint_type; |
fe33eb390 cfg80211: move al... |
2286 |
|
c37722bd1 cfg80211: Stop ca... |
2287 |
/* Allow calling CRDA again */ |
b68630369 cfg80211: reg: ma... |
2288 |
reset_crda_timeouts(); |
c37722bd1 cfg80211: Stop ca... |
2289 |
|
fe33eb390 cfg80211: move al... |
2290 2291 2292 2293 |
queue_regulatory_request(request); return 0; } |
7101e4fa0 MLK-17362-01 Chan... |
2294 |
EXPORT_SYMBOL(regulatory_hint_user); |
fe33eb390 cfg80211: move al... |
2295 |
|
050507536 cfg80211: Add API... |
2296 |
int regulatory_hint_indoor(bool is_indoor, u32 portid) |
52616f2b4 cfg80211: Add an ... |
2297 |
{ |
050507536 cfg80211: Add API... |
2298 |
spin_lock(®_indoor_lock); |
52616f2b4 cfg80211: Add an ... |
2299 |
|
050507536 cfg80211: Add API... |
2300 2301 2302 2303 2304 2305 2306 2307 2308 2309 2310 2311 2312 2313 |
/* It is possible that more than one user space process is trying to * configure the indoor setting. To handle such cases, clear the indoor * setting in case that some process does not think that the device * is operating in an indoor environment. In addition, if a user space * process indicates that it is controlling the indoor setting, save its * portid, i.e., make it the owner. */ reg_is_indoor = is_indoor; if (reg_is_indoor) { if (!reg_is_indoor_portid) reg_is_indoor_portid = portid; } else { reg_is_indoor_portid = 0; } |
52616f2b4 cfg80211: Add an ... |
2314 |
|
050507536 cfg80211: Add API... |
2315 |
spin_unlock(®_indoor_lock); |
52616f2b4 cfg80211: Add an ... |
2316 |
|
050507536 cfg80211: Add API... |
2317 2318 |
if (!is_indoor) reg_check_channels(); |
52616f2b4 cfg80211: Add an ... |
2319 2320 2321 |
return 0; } |
050507536 cfg80211: Add API... |
2322 2323 2324 2325 2326 2327 2328 2329 2330 2331 2332 2333 2334 2335 2336 2337 |
void regulatory_netlink_notify(u32 portid) { spin_lock(®_indoor_lock); if (reg_is_indoor_portid != portid) { spin_unlock(®_indoor_lock); return; } reg_is_indoor = false; reg_is_indoor_portid = 0; spin_unlock(®_indoor_lock); reg_check_channels(); } |
fe33eb390 cfg80211: move al... |
2338 2339 2340 2341 |
/* Driver hints */ int regulatory_hint(struct wiphy *wiphy, const char *alpha2) { struct regulatory_request *request; |
fdc9d7b28 regulatory: remov... |
2342 2343 |
if (WARN_ON(!alpha2 || !wiphy)) return -EINVAL; |
fe33eb390 cfg80211: move al... |
2344 |
|
4f7b91404 cfg80211: make re... |
2345 |
wiphy->regulatory_flags &= ~REGULATORY_CUSTOM_REG; |
fe33eb390 cfg80211: move al... |
2346 2347 2348 2349 2350 |
request = kzalloc(sizeof(struct regulatory_request), GFP_KERNEL); if (!request) return -ENOMEM; request->wiphy_idx = get_wiphy_idx(wiphy); |
fe33eb390 cfg80211: move al... |
2351 2352 |
request->alpha2[0] = alpha2[0]; request->alpha2[1] = alpha2[1]; |
7db90f4a2 cfg80211: move en... |
2353 |
request->initiator = NL80211_REGDOM_SET_BY_DRIVER; |
fe33eb390 cfg80211: move al... |
2354 |
|
c37722bd1 cfg80211: Stop ca... |
2355 |
/* Allow calling CRDA again */ |
b68630369 cfg80211: reg: ma... |
2356 |
reset_crda_timeouts(); |
c37722bd1 cfg80211: Stop ca... |
2357 |
|
fe33eb390 cfg80211: move al... |
2358 2359 2360 |
queue_regulatory_request(request); return 0; |
b2e1b3029 cfg80211: Add new... |
2361 2362 |
} EXPORT_SYMBOL(regulatory_hint); |
57fbcce37 cfg80211: remove ... |
2363 |
void regulatory_hint_country_ie(struct wiphy *wiphy, enum nl80211_band band, |
789fd0333 cfg80211: rename ... |
2364 |
const u8 *country_ie, u8 country_ie_len) |
3f2355cb9 cfg80211/mac80211... |
2365 |
{ |
3f2355cb9 cfg80211/mac80211... |
2366 |
char alpha2[2]; |
3f2355cb9 cfg80211/mac80211... |
2367 |
enum environment_cap env = ENVIRON_ANY; |
db2424c58 regulatory: use R... |
2368 |
struct regulatory_request *request = NULL, *lr; |
d335fe639 cfg80211: protect... |
2369 |
|
3f2355cb9 cfg80211/mac80211... |
2370 2371 |
/* IE len must be evenly divisible by 2 */ if (country_ie_len & 0x01) |
db2424c58 regulatory: use R... |
2372 |
return; |
3f2355cb9 cfg80211/mac80211... |
2373 2374 |
if (country_ie_len < IEEE80211_COUNTRY_IE_MIN_LEN) |
db2424c58 regulatory: use R... |
2375 2376 2377 2378 2379 |
return; request = kzalloc(sizeof(*request), GFP_KERNEL); if (!request) return; |
3f2355cb9 cfg80211/mac80211... |
2380 |
|
3f2355cb9 cfg80211/mac80211... |
2381 2382 2383 2384 2385 2386 2387 |
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; |
db2424c58 regulatory: use R... |
2388 2389 2390 2391 2392 |
rcu_read_lock(); lr = get_last_request(); if (unlikely(!lr)) goto out; |
fb1fc7add cfg80211: comment... |
2393 |
/* |
8b19e6ca3 cfg80211: enable ... |
2394 |
* We will run this only upon a successful connection on cfg80211. |
4b44c8bc4 cfg80211: do not ... |
2395 |
* We leave conflict resolution to the workqueue, where can hold |
5fe231e87 cfg80211: vastly ... |
2396 |
* the RTNL. |
fb1fc7add cfg80211: comment... |
2397 |
*/ |
c492db370 regulatory: use R... |
2398 2399 |
if (lr->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE && lr->wiphy_idx != WIPHY_IDX_INVALID) |
4b44c8bc4 cfg80211: do not ... |
2400 |
goto out; |
3f2355cb9 cfg80211/mac80211... |
2401 |
|
fe33eb390 cfg80211: move al... |
2402 |
request->wiphy_idx = get_wiphy_idx(wiphy); |
4f366c5da wireless: only us... |
2403 2404 |
request->alpha2[0] = alpha2[0]; request->alpha2[1] = alpha2[1]; |
7db90f4a2 cfg80211: move en... |
2405 |
request->initiator = NL80211_REGDOM_SET_BY_COUNTRY_IE; |
fe33eb390 cfg80211: move al... |
2406 |
request->country_ie_env = env; |
c37722bd1 cfg80211: Stop ca... |
2407 |
/* Allow calling CRDA again */ |
b68630369 cfg80211: reg: ma... |
2408 |
reset_crda_timeouts(); |
c37722bd1 cfg80211: Stop ca... |
2409 |
|
fe33eb390 cfg80211: move al... |
2410 |
queue_regulatory_request(request); |
db2424c58 regulatory: use R... |
2411 |
request = NULL; |
3f2355cb9 cfg80211/mac80211... |
2412 |
out: |
db2424c58 regulatory: use R... |
2413 2414 |
kfree(request); rcu_read_unlock(); |
3f2355cb9 cfg80211/mac80211... |
2415 |
} |
b2e1b3029 cfg80211: Add new... |
2416 |
|
09d989d17 cfg80211: add reg... |
2417 2418 2419 2420 2421 2422 2423 2424 2425 2426 |
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) { |
c799ba6ea cfg80211: remove ... |
2427 2428 |
pr_debug("Restoring regulatory settings including user preference "); |
09d989d17 cfg80211: add reg... |
2429 2430 2431 2432 2433 2434 2435 2436 2437 |
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)) { |
c799ba6ea cfg80211: remove ... |
2438 2439 2440 |
pr_debug("Keeping preference on module parameter ieee80211_regdom: %c%c ", ieee80211_regdom[0], ieee80211_regdom[1]); |
09d989d17 cfg80211: add reg... |
2441 2442 2443 2444 |
alpha2[0] = ieee80211_regdom[0]; alpha2[1] = ieee80211_regdom[1]; } } else { |
c799ba6ea cfg80211: remove ... |
2445 2446 2447 |
pr_debug("Restoring regulatory settings while preserving user preference for: %c%c ", user_alpha2[0], user_alpha2[1]); |
09d989d17 cfg80211: add reg... |
2448 2449 2450 2451 |
alpha2[0] = user_alpha2[0]; alpha2[1] = user_alpha2[1]; } } else if (!is_world_regdom(ieee80211_regdom)) { |
c799ba6ea cfg80211: remove ... |
2452 2453 2454 |
pr_debug("Keeping preference on module parameter ieee80211_regdom: %c%c ", ieee80211_regdom[0], ieee80211_regdom[1]); |
09d989d17 cfg80211: add reg... |
2455 2456 2457 |
alpha2[0] = ieee80211_regdom[0]; alpha2[1] = ieee80211_regdom[1]; } else |
c799ba6ea cfg80211: remove ... |
2458 2459 |
pr_debug("Restoring regulatory settings "); |
09d989d17 cfg80211: add reg... |
2460 |
} |
5ce543d14 cfg80211: Restore... |
2461 2462 2463 |
static void restore_custom_reg_settings(struct wiphy *wiphy) { struct ieee80211_supported_band *sband; |
57fbcce37 cfg80211: remove ... |
2464 |
enum nl80211_band band; |
5ce543d14 cfg80211: Restore... |
2465 2466 |
struct ieee80211_channel *chan; int i; |
57fbcce37 cfg80211: remove ... |
2467 |
for (band = 0; band < NUM_NL80211_BANDS; band++) { |
5ce543d14 cfg80211: Restore... |
2468 2469 2470 2471 2472 2473 2474 2475 |
sband = wiphy->bands[band]; if (!sband) continue; for (i = 0; i < sband->n_channels; i++) { chan = &sband->channels[i]; chan->flags = chan->orig_flags; chan->max_antenna_gain = chan->orig_mag; chan->max_power = chan->orig_mpwr; |
899852af6 cfg80211: Clear "... |
2476 |
chan->beacon_found = false; |
5ce543d14 cfg80211: Restore... |
2477 2478 2479 |
} } } |
09d989d17 cfg80211: add reg... |
2480 2481 2482 2483 2484 2485 2486 2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 2497 |
/* * 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]; |
cee0bec58 wireless: Protect... |
2498 |
char world_alpha2[2]; |
09d989d17 cfg80211: add reg... |
2499 |
struct reg_beacon *reg_beacon, *btmp; |
146095557 cfg80211: fix reg... |
2500 |
LIST_HEAD(tmp_reg_req_list); |
5ce543d14 cfg80211: Restore... |
2501 |
struct cfg80211_registered_device *rdev; |
09d989d17 cfg80211: add reg... |
2502 |
|
5fe231e87 cfg80211: vastly ... |
2503 |
ASSERT_RTNL(); |
050507536 cfg80211: Add API... |
2504 2505 2506 2507 2508 2509 2510 2511 2512 2513 2514 |
/* * Clear the indoor setting in case that it is not controlled by user * space, as otherwise there is no guarantee that the device is still * operating in an indoor environment. */ spin_lock(®_indoor_lock); if (reg_is_indoor && !reg_is_indoor_portid) { reg_is_indoor = false; reg_check_channels(); } spin_unlock(®_indoor_lock); |
52616f2b4 cfg80211: Add an ... |
2515 |
|
2d3198676 regulatory: fix r... |
2516 |
reset_regdomains(true, &world_regdom); |
09d989d17 cfg80211: add reg... |
2517 |
restore_alpha2(alpha2, reset_user); |
146095557 cfg80211: fix reg... |
2518 2519 2520 2521 2522 2523 2524 |
/* * If there's any pending requests we simply * stash them to a temporary pending queue and * add then after we've restored regulatory * settings. */ spin_lock(®_requests_lock); |
eeca9fce1 cfg80211: Schedul... |
2525 |
list_splice_tail_init(®_requests_list, &tmp_reg_req_list); |
146095557 cfg80211: fix reg... |
2526 |
spin_unlock(®_requests_lock); |
09d989d17 cfg80211: add reg... |
2527 2528 |
/* Clear beacon hints */ spin_lock_bh(®_pending_beacons_lock); |
fea9bcedc regulatory: don't... |
2529 2530 2531 |
list_for_each_entry_safe(reg_beacon, btmp, ®_pending_beacons, list) { list_del(®_beacon->list); kfree(reg_beacon); |
09d989d17 cfg80211: add reg... |
2532 2533 |
} spin_unlock_bh(®_pending_beacons_lock); |
fea9bcedc regulatory: don't... |
2534 2535 2536 |
list_for_each_entry_safe(reg_beacon, btmp, ®_beacon_list, list) { list_del(®_beacon->list); kfree(reg_beacon); |
09d989d17 cfg80211: add reg... |
2537 2538 2539 |
} /* First restore to the basic regulatory settings */ |
379b82f4c regulatory: pass ... |
2540 2541 |
world_alpha2[0] = cfg80211_world_regdom->alpha2[0]; world_alpha2[1] = cfg80211_world_regdom->alpha2[1]; |
09d989d17 cfg80211: add reg... |
2542 |
|
5ce543d14 cfg80211: Restore... |
2543 |
list_for_each_entry(rdev, &cfg80211_rdev_list, list) { |
b0d7aa595 cfg80211: allow w... |
2544 2545 |
if (rdev->wiphy.regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED) continue; |
a2f73b6c5 cfg80211: move re... |
2546 |
if (rdev->wiphy.regulatory_flags & REGULATORY_CUSTOM_REG) |
5ce543d14 cfg80211: Restore... |
2547 2548 |
restore_custom_reg_settings(&rdev->wiphy); } |
cee0bec58 wireless: Protect... |
2549 |
regulatory_hint_core(world_alpha2); |
09d989d17 cfg80211: add reg... |
2550 2551 2552 2553 2554 2555 2556 |
/* * 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)) |
549cc1c56 cfg80211: regulat... |
2557 |
regulatory_hint_user(alpha2, NL80211_USER_REG_HINT_USER); |
09d989d17 cfg80211: add reg... |
2558 |
|
146095557 cfg80211: fix reg... |
2559 |
spin_lock(®_requests_lock); |
11cff96c0 regulatory: simpl... |
2560 |
list_splice_tail_init(&tmp_reg_req_list, ®_requests_list); |
146095557 cfg80211: fix reg... |
2561 |
spin_unlock(®_requests_lock); |
c799ba6ea cfg80211: remove ... |
2562 2563 |
pr_debug("Kicking the queue "); |
146095557 cfg80211: fix reg... |
2564 2565 2566 |
schedule_work(®_work); } |
09d989d17 cfg80211: add reg... |
2567 2568 2569 |
void regulatory_hint_disconnect(void) { |
c799ba6ea cfg80211: remove ... |
2570 2571 |
pr_debug("All devices are disconnected, going to restore regulatory settings "); |
09d989d17 cfg80211: add reg... |
2572 2573 |
restore_regulatory_settings(false); } |
e38f8a7a8 cfg80211: Add AP ... |
2574 2575 |
static bool freq_is_chan_12_13_14(u16 freq) { |
57fbcce37 cfg80211: remove ... |
2576 2577 2578 |
if (freq == ieee80211_channel_to_frequency(12, NL80211_BAND_2GHZ) || freq == ieee80211_channel_to_frequency(13, NL80211_BAND_2GHZ) || freq == ieee80211_channel_to_frequency(14, NL80211_BAND_2GHZ)) |
e38f8a7a8 cfg80211: Add AP ... |
2579 2580 2581 |
return true; return false; } |
3ebfa6e76 cfg80211: do not ... |
2582 2583 2584 2585 2586 2587 2588 2589 2590 2591 |
static bool pending_reg_beacon(struct ieee80211_channel *beacon_chan) { struct reg_beacon *pending_beacon; list_for_each_entry(pending_beacon, ®_pending_beacons, list) if (beacon_chan->center_freq == pending_beacon->chan.center_freq) return true; return false; } |
e38f8a7a8 cfg80211: Add AP ... |
2592 2593 2594 2595 2596 |
int regulatory_hint_found_beacon(struct wiphy *wiphy, struct ieee80211_channel *beacon_chan, gfp_t gfp) { struct reg_beacon *reg_beacon; |
3ebfa6e76 cfg80211: do not ... |
2597 |
bool processing; |
e38f8a7a8 cfg80211: Add AP ... |
2598 |
|
1a9193185 regulatory: code ... |
2599 2600 |
if (beacon_chan->beacon_found || beacon_chan->flags & IEEE80211_CHAN_RADAR || |
57fbcce37 cfg80211: remove ... |
2601 |
(beacon_chan->band == NL80211_BAND_2GHZ && |
1a9193185 regulatory: code ... |
2602 |
!freq_is_chan_12_13_14(beacon_chan->center_freq))) |
e38f8a7a8 cfg80211: Add AP ... |
2603 |
return 0; |
3ebfa6e76 cfg80211: do not ... |
2604 2605 2606 2607 2608 |
spin_lock_bh(®_pending_beacons_lock); processing = pending_reg_beacon(beacon_chan); spin_unlock_bh(®_pending_beacons_lock); if (processing) |
e38f8a7a8 cfg80211: Add AP ... |
2609 2610 2611 2612 2613 |
return 0; reg_beacon = kzalloc(sizeof(struct reg_beacon), gfp); if (!reg_beacon) return -ENOMEM; |
c799ba6ea cfg80211: remove ... |
2614 2615 2616 2617 2618 |
pr_debug("Found new beacon on frequency: %d MHz (Ch %d) on %s ", beacon_chan->center_freq, ieee80211_frequency_to_channel(beacon_chan->center_freq), wiphy_name(wiphy)); |
4113f7518 cfg80211: add a r... |
2619 |
|
e38f8a7a8 cfg80211: Add AP ... |
2620 |
memcpy(®_beacon->chan, beacon_chan, |
1a9193185 regulatory: code ... |
2621 |
sizeof(struct ieee80211_channel)); |
e38f8a7a8 cfg80211: Add AP ... |
2622 2623 2624 2625 2626 2627 2628 2629 2630 2631 2632 2633 2634 |
/* * 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... |
2635 |
static void print_rd_rules(const struct ieee80211_regdomain *rd) |
b2e1b3029 cfg80211: Add new... |
2636 2637 |
{ unsigned int i; |
a3d2eaf0d cfg80211: fix reg... |
2638 2639 2640 |
const struct ieee80211_reg_rule *reg_rule = NULL; const struct ieee80211_freq_range *freq_range = NULL; const struct ieee80211_power_rule *power_rule = NULL; |
089027e57 cfg80211: regulat... |
2641 |
char bw[32], cac_time[32]; |
b2e1b3029 cfg80211: Add new... |
2642 |
|
94c4fd641 wireless: change ... |
2643 2644 |
pr_debug(" (start_freq - end_freq @ bandwidth), (max_antenna_gain, max_eirp), (dfs_cac_time) "); |
b2e1b3029 cfg80211: Add new... |
2645 2646 2647 2648 2649 |
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; |
b0dfd2ea1 cfg80211: regulat... |
2650 2651 2652 |
if (reg_rule->flags & NL80211_RRF_AUTO_BW) snprintf(bw, sizeof(bw), "%d KHz, %d KHz AUTO", freq_range->max_bandwidth_khz, |
975248208 cfg80211: regulat... |
2653 2654 |
reg_get_max_bandwidth(rd, reg_rule)); else |
b0dfd2ea1 cfg80211: regulat... |
2655 |
snprintf(bw, sizeof(bw), "%d KHz", |
975248208 cfg80211: regulat... |
2656 |
freq_range->max_bandwidth_khz); |
089027e57 cfg80211: regulat... |
2657 2658 2659 2660 2661 |
if (reg_rule->flags & NL80211_RRF_DFS) scnprintf(cac_time, sizeof(cac_time), "%u s", reg_rule->dfs_cac_ms/1000); else scnprintf(cac_time, sizeof(cac_time), "N/A"); |
fb1fc7add cfg80211: comment... |
2662 2663 2664 2665 |
/* * There may not be documentation for max antenna gain * in certain regions */ |
b2e1b3029 cfg80211: Add new... |
2666 |
if (power_rule->max_antenna_gain) |
94c4fd641 wireless: change ... |
2667 2668 |
pr_debug(" (%d KHz - %d KHz @ %s), (%d mBi, %d mBm), (%s) ", |
b2e1b3029 cfg80211: Add new... |
2669 2670 |
freq_range->start_freq_khz, freq_range->end_freq_khz, |
975248208 cfg80211: regulat... |
2671 |
bw, |
b2e1b3029 cfg80211: Add new... |
2672 |
power_rule->max_antenna_gain, |
089027e57 cfg80211: regulat... |
2673 2674 |
power_rule->max_eirp, cac_time); |
b2e1b3029 cfg80211: Add new... |
2675 |
else |
94c4fd641 wireless: change ... |
2676 2677 |
pr_debug(" (%d KHz - %d KHz @ %s), (N/A, %d mBm), (%s) ", |
b2e1b3029 cfg80211: Add new... |
2678 2679 |
freq_range->start_freq_khz, freq_range->end_freq_khz, |
975248208 cfg80211: regulat... |
2680 |
bw, |
089027e57 cfg80211: regulat... |
2681 2682 |
power_rule->max_eirp, cac_time); |
b2e1b3029 cfg80211: Add new... |
2683 2684 |
} } |
4c7d3982a cfg80211: use enu... |
2685 |
bool reg_supported_dfs_region(enum nl80211_dfs_regions dfs_region) |
8b60b0780 cfg80211: process... |
2686 2687 2688 2689 2690 2691 2692 2693 |
{ switch (dfs_region) { case NL80211_DFS_UNSET: case NL80211_DFS_FCC: case NL80211_DFS_ETSI: case NL80211_DFS_JP: return true; default: |
c799ba6ea cfg80211: remove ... |
2694 2695 |
pr_debug("Ignoring uknown DFS master region: %d ", dfs_region); |
8b60b0780 cfg80211: process... |
2696 2697 2698 |
return false; } } |
a3d2eaf0d cfg80211: fix reg... |
2699 |
static void print_regdomain(const struct ieee80211_regdomain *rd) |
b2e1b3029 cfg80211: Add new... |
2700 |
{ |
c492db370 regulatory: use R... |
2701 |
struct regulatory_request *lr = get_last_request(); |
b2e1b3029 cfg80211: Add new... |
2702 |
|
3f2355cb9 cfg80211/mac80211... |
2703 |
if (is_intersected_alpha2(rd->alpha2)) { |
c492db370 regulatory: use R... |
2704 |
if (lr->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) { |
79c97e97a cfg80211: clean u... |
2705 |
struct cfg80211_registered_device *rdev; |
c492db370 regulatory: use R... |
2706 |
rdev = cfg80211_rdev_by_wiphy_idx(lr->wiphy_idx); |
79c97e97a cfg80211: clean u... |
2707 |
if (rdev) { |
94c4fd641 wireless: change ... |
2708 2709 |
pr_debug("Current regulatory domain updated by AP to: %c%c ", |
79c97e97a cfg80211: clean u... |
2710 2711 |
rdev->country_ie_alpha2[0], rdev->country_ie_alpha2[1]); |
3f2355cb9 cfg80211/mac80211... |
2712 |
} else |
94c4fd641 wireless: change ... |
2713 2714 |
pr_debug("Current regulatory domain intersected: "); |
3f2355cb9 cfg80211/mac80211... |
2715 |
} else |
94c4fd641 wireless: change ... |
2716 2717 |
pr_debug("Current regulatory domain intersected: "); |
1a9193185 regulatory: code ... |
2718 |
} else if (is_world_regdom(rd->alpha2)) { |
94c4fd641 wireless: change ... |
2719 2720 |
pr_debug("World regulatory domain updated: "); |
1a9193185 regulatory: code ... |
2721 |
} else { |
b2e1b3029 cfg80211: Add new... |
2722 |
if (is_unknown_alpha2(rd->alpha2)) |
94c4fd641 wireless: change ... |
2723 2724 |
pr_debug("Regulatory domain changed to driver built-in settings (unknown country) "); |
57b5ce072 cfg80211: add cel... |
2725 |
else { |
c492db370 regulatory: use R... |
2726 |
if (reg_request_cell_base(lr)) |
94c4fd641 wireless: change ... |
2727 2728 |
pr_debug("Regulatory domain changed to country: %c%c by Cell Station ", |
57b5ce072 cfg80211: add cel... |
2729 2730 |
rd->alpha2[0], rd->alpha2[1]); else |
94c4fd641 wireless: change ... |
2731 2732 |
pr_debug("Regulatory domain changed to country: %c%c ", |
57b5ce072 cfg80211: add cel... |
2733 2734 |
rd->alpha2[0], rd->alpha2[1]); } |
b2e1b3029 cfg80211: Add new... |
2735 |
} |
1a9193185 regulatory: code ... |
2736 |
|
94c4fd641 wireless: change ... |
2737 |
pr_debug(" DFS Master region: %s", reg_dfs_region_str(rd->dfs_region)); |
b2e1b3029 cfg80211: Add new... |
2738 2739 |
print_rd_rules(rd); } |
2df78167a wireless: fix a f... |
2740 |
static void print_regdomain_info(const struct ieee80211_regdomain *rd) |
b2e1b3029 cfg80211: Add new... |
2741 |
{ |
94c4fd641 wireless: change ... |
2742 2743 |
pr_debug("Regulatory domain: %c%c ", rd->alpha2[0], rd->alpha2[1]); |
b2e1b3029 cfg80211: Add new... |
2744 2745 |
print_rd_rules(rd); } |
3b9e5aca4 cfg80211: set cor... |
2746 2747 2748 2749 2750 2751 2752 |
static int reg_set_rd_core(const struct ieee80211_regdomain *rd) { if (!is_world_regdom(rd->alpha2)) return -EINVAL; update_world_regdomain(rd); return 0; } |
84721d449 cfg80211: set use... |
2753 2754 2755 2756 |
static int reg_set_rd_user(const struct ieee80211_regdomain *rd, struct regulatory_request *user_request) { const struct ieee80211_regdomain *intersected_rd = NULL; |
84721d449 cfg80211: set use... |
2757 2758 2759 2760 |
if (!regdom_changes(rd->alpha2)) return -EALREADY; if (!is_valid_rd(rd)) { |
94c4fd641 wireless: change ... |
2761 2762 2763 |
pr_err("Invalid regulatory domain detected: %c%c ", rd->alpha2[0], rd->alpha2[1]); |
84721d449 cfg80211: set use... |
2764 2765 2766 2767 2768 2769 2770 2771 2772 2773 2774 2775 2776 2777 2778 2779 2780 2781 2782 |
print_regdomain_info(rd); return -EINVAL; } if (!user_request->intersect) { reset_regdomains(false, rd); return 0; } intersected_rd = regdom_intersect(rd, get_cfg80211_regdom()); if (!intersected_rd) return -EINVAL; kfree(rd); rd = NULL; reset_regdomains(false, intersected_rd); return 0; } |
f5fe32478 cfg80211: set dri... |
2783 2784 |
static int reg_set_rd_driver(const struct ieee80211_regdomain *rd, struct regulatory_request *driver_request) |
b2e1b3029 cfg80211: Add new... |
2785 |
{ |
e9763c3c2 regulatory: clean... |
2786 |
const struct ieee80211_regdomain *regd; |
9c96477d1 cfg80211: Add reg... |
2787 |
const struct ieee80211_regdomain *intersected_rd = NULL; |
f5fe32478 cfg80211: set dri... |
2788 |
const struct ieee80211_regdomain *tmp; |
806a9e396 cfg80211: make re... |
2789 |
struct wiphy *request_wiphy; |
6913b49a5 regulatory: fix r... |
2790 |
|
f5fe32478 cfg80211: set dri... |
2791 |
if (is_world_regdom(rd->alpha2)) |
b2e1b3029 cfg80211: Add new... |
2792 |
return -EINVAL; |
f5fe32478 cfg80211: set dri... |
2793 2794 |
if (!regdom_changes(rd->alpha2)) return -EALREADY; |
b2e1b3029 cfg80211: Add new... |
2795 |
|
8375af3ba cfg80211: remove ... |
2796 |
if (!is_valid_rd(rd)) { |
94c4fd641 wireless: change ... |
2797 2798 2799 |
pr_err("Invalid regulatory domain detected: %c%c ", rd->alpha2[0], rd->alpha2[1]); |
8375af3ba cfg80211: remove ... |
2800 2801 |
print_regdomain_info(rd); return -EINVAL; |
b2e1b3029 cfg80211: Add new... |
2802 |
} |
f5fe32478 cfg80211: set dri... |
2803 |
request_wiphy = wiphy_idx_to_wiphy(driver_request->wiphy_idx); |
922ec58c7 cfg80211: reg: re... |
2804 |
if (!request_wiphy) |
de3584bd6 cfg80211: fix reg... |
2805 |
return -ENODEV; |
806a9e396 cfg80211: make re... |
2806 |
|
f5fe32478 cfg80211: set dri... |
2807 |
if (!driver_request->intersect) { |
558f6d322 cfg80211: fix for... |
2808 2809 |
if (request_wiphy->regd) return -EALREADY; |
3e0c3ff36 cfg80211: allow m... |
2810 |
|
e9763c3c2 regulatory: clean... |
2811 2812 2813 |
regd = reg_copy_regd(rd); if (IS_ERR(regd)) return PTR_ERR(regd); |
3e0c3ff36 cfg80211: allow m... |
2814 |
|
458f4f9e9 regulatory: use R... |
2815 |
rcu_assign_pointer(request_wiphy->regd, regd); |
379b82f4c regulatory: pass ... |
2816 |
reset_regdomains(false, rd); |
b8295acdc cfg80211: separat... |
2817 2818 |
return 0; } |
f5fe32478 cfg80211: set dri... |
2819 2820 2821 |
intersected_rd = regdom_intersect(rd, get_cfg80211_regdom()); if (!intersected_rd) return -EINVAL; |
b8295acdc cfg80211: separat... |
2822 |
|
f5fe32478 cfg80211: set dri... |
2823 2824 2825 2826 2827 2828 2829 2830 |
/* * We can trash what CRDA provided now. * However if a driver requested this specific regulatory * domain we keep it for its private use */ tmp = get_wiphy_regdom(request_wiphy); rcu_assign_pointer(request_wiphy->regd, rd); rcu_free_regdom(tmp); |
b8295acdc cfg80211: separat... |
2831 |
|
f5fe32478 cfg80211: set dri... |
2832 |
rd = NULL; |
b7566fc36 cfg80211: Fix mem... |
2833 |
|
f5fe32478 cfg80211: set dri... |
2834 |
reset_regdomains(false, intersected_rd); |
3e0c3ff36 cfg80211: allow m... |
2835 |
|
f5fe32478 cfg80211: set dri... |
2836 2837 |
return 0; } |
01992406d cfg80211: rename ... |
2838 2839 |
static int reg_set_rd_country_ie(const struct ieee80211_regdomain *rd, struct regulatory_request *country_ie_request) |
f5fe32478 cfg80211: set dri... |
2840 2841 |
{ struct wiphy *request_wiphy; |
b8295acdc cfg80211: separat... |
2842 |
|
f5fe32478 cfg80211: set dri... |
2843 2844 2845 |
if (!is_alpha2_set(rd->alpha2) && !is_an_alpha2(rd->alpha2) && !is_unknown_alpha2(rd->alpha2)) return -EINVAL; |
b8295acdc cfg80211: separat... |
2846 |
|
f5fe32478 cfg80211: set dri... |
2847 2848 2849 2850 2851 2852 2853 |
/* * Lets only bother proceeding on the same alpha2 if the current * rd is non static (it means CRDA was present and was used last) * and the pending request came in from a country IE */ if (!is_valid_rd(rd)) { |
94c4fd641 wireless: change ... |
2854 2855 2856 |
pr_err("Invalid regulatory domain detected: %c%c ", rd->alpha2[0], rd->alpha2[1]); |
f5fe32478 cfg80211: set dri... |
2857 2858 |
print_regdomain_info(rd); return -EINVAL; |
9c96477d1 cfg80211: Add reg... |
2859 |
} |
01992406d cfg80211: rename ... |
2860 |
request_wiphy = wiphy_idx_to_wiphy(country_ie_request->wiphy_idx); |
922ec58c7 cfg80211: reg: re... |
2861 |
if (!request_wiphy) |
f5fe32478 cfg80211: set dri... |
2862 |
return -ENODEV; |
b2e1b3029 cfg80211: Add new... |
2863 |
|
01992406d cfg80211: rename ... |
2864 |
if (country_ie_request->intersect) |
f5fe32478 cfg80211: set dri... |
2865 2866 2867 2868 2869 |
return -EINVAL; reset_regdomains(false, rd); return 0; } |
b2e1b3029 cfg80211: Add new... |
2870 |
|
fb1fc7add cfg80211: comment... |
2871 2872 |
/* * Use this call to set the current regulatory domain. Conflicts with |
b2e1b3029 cfg80211: Add new... |
2873 |
* multiple drivers can be ironed out later. Caller must've already |
458f4f9e9 regulatory: use R... |
2874 |
* kmalloc'd the rd structure. |
fb1fc7add cfg80211: comment... |
2875 |
*/ |
c37722bd1 cfg80211: Stop ca... |
2876 2877 |
int set_regdom(const struct ieee80211_regdomain *rd, enum ieee80211_regd_source regd_src) |
b2e1b3029 cfg80211: Add new... |
2878 |
{ |
c492db370 regulatory: use R... |
2879 |
struct regulatory_request *lr; |
092008abe cfg80211: regulat... |
2880 |
bool user_reset = false; |
b2e1b3029 cfg80211: Add new... |
2881 |
int r; |
3b9e5aca4 cfg80211: set cor... |
2882 2883 2884 2885 |
if (!reg_is_valid_request(rd->alpha2)) { kfree(rd); return -EINVAL; } |
c37722bd1 cfg80211: Stop ca... |
2886 |
if (regd_src == REGD_SOURCE_CRDA) |
b68630369 cfg80211: reg: ma... |
2887 |
reset_crda_timeouts(); |
c37722bd1 cfg80211: Stop ca... |
2888 |
|
c492db370 regulatory: use R... |
2889 |
lr = get_last_request(); |
abc7381bc cfg80211: decoupl... |
2890 |
|
b2e1b3029 cfg80211: Add new... |
2891 |
/* Note that this doesn't update the wiphys, this is done below */ |
3b9e5aca4 cfg80211: set cor... |
2892 2893 2894 2895 2896 |
switch (lr->initiator) { case NL80211_REGDOM_SET_BY_CORE: r = reg_set_rd_core(rd); break; case NL80211_REGDOM_SET_BY_USER: |
84721d449 cfg80211: set use... |
2897 |
r = reg_set_rd_user(rd, lr); |
092008abe cfg80211: regulat... |
2898 |
user_reset = true; |
84721d449 cfg80211: set use... |
2899 |
break; |
3b9e5aca4 cfg80211: set cor... |
2900 |
case NL80211_REGDOM_SET_BY_DRIVER: |
f5fe32478 cfg80211: set dri... |
2901 2902 |
r = reg_set_rd_driver(rd, lr); break; |
3b9e5aca4 cfg80211: set cor... |
2903 |
case NL80211_REGDOM_SET_BY_COUNTRY_IE: |
01992406d cfg80211: rename ... |
2904 |
r = reg_set_rd_country_ie(rd, lr); |
3b9e5aca4 cfg80211: set cor... |
2905 2906 2907 2908 |
break; default: WARN(1, "invalid initiator %d ", lr->initiator); |
09d118008 nl80211: fix a fe... |
2909 |
kfree(rd); |
3b9e5aca4 cfg80211: set cor... |
2910 2911 |
return -EINVAL; } |
d2372b315 wireless: make re... |
2912 |
if (r) { |
092008abe cfg80211: regulat... |
2913 2914 |
switch (r) { case -EALREADY: |
959085352 cfg80211: fix set... |
2915 |
reg_set_request_processed(); |
092008abe cfg80211: regulat... |
2916 2917 2918 2919 2920 |
break; default: /* Back to world regulatory in case of errors */ restore_regulatory_settings(user_reset); } |
959085352 cfg80211: fix set... |
2921 |
|
d2372b315 wireless: make re... |
2922 |
kfree(rd); |
38fd2143f regulatory: remov... |
2923 |
return r; |
d2372b315 wireless: make re... |
2924 |
} |
b2e1b3029 cfg80211: Add new... |
2925 |
|
b2e1b3029 cfg80211: Add new... |
2926 |
/* This would make this whole thing pointless */ |
38fd2143f regulatory: remov... |
2927 2928 |
if (WARN_ON(!lr->intersect && rd != get_cfg80211_regdom())) return -EINVAL; |
b2e1b3029 cfg80211: Add new... |
2929 2930 |
/* update all wiphys now with the new established regulatory domain */ |
c492db370 regulatory: use R... |
2931 |
update_all_wiphy_regulatory(lr->initiator); |
b2e1b3029 cfg80211: Add new... |
2932 |
|
458f4f9e9 regulatory: use R... |
2933 |
print_regdomain(get_cfg80211_regdom()); |
b2e1b3029 cfg80211: Add new... |
2934 |
|
c492db370 regulatory: use R... |
2935 |
nl80211_send_reg_change_event(lr); |
73d54c9e7 cfg80211: add reg... |
2936 |
|
b2e253cf3 cfg80211: Fix reg... |
2937 |
reg_set_request_processed(); |
38fd2143f regulatory: remov... |
2938 |
return 0; |
b2e1b3029 cfg80211: Add new... |
2939 |
} |
2c3e861c9 cfg80211: introdu... |
2940 2941 |
static int __regulatory_set_wiphy_regd(struct wiphy *wiphy, struct ieee80211_regdomain *rd) |
b0d7aa595 cfg80211: allow w... |
2942 2943 2944 2945 2946 2947 2948 2949 2950 2951 2952 2953 2954 2955 2956 2957 2958 2959 2960 2961 2962 2963 2964 2965 2966 2967 2968 2969 2970 2971 2972 |
{ const struct ieee80211_regdomain *regd; const struct ieee80211_regdomain *prev_regd; struct cfg80211_registered_device *rdev; if (WARN_ON(!wiphy || !rd)) return -EINVAL; if (WARN(!(wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED), "wiphy should have REGULATORY_WIPHY_SELF_MANAGED ")) return -EPERM; if (WARN(!is_valid_rd(rd), "Invalid regulatory domain detected ")) { print_regdomain_info(rd); return -EINVAL; } regd = reg_copy_regd(rd); if (IS_ERR(regd)) return PTR_ERR(regd); rdev = wiphy_to_rdev(wiphy); spin_lock(®_requests_lock); prev_regd = rdev->requested_regd; rdev->requested_regd = regd; spin_unlock(®_requests_lock); kfree(prev_regd); |
2c3e861c9 cfg80211: introdu... |
2973 2974 2975 2976 2977 2978 2979 2980 2981 2982 |
return 0; } int regulatory_set_wiphy_regd(struct wiphy *wiphy, struct ieee80211_regdomain *rd) { int ret = __regulatory_set_wiphy_regd(wiphy, rd); if (ret) return ret; |
b0d7aa595 cfg80211: allow w... |
2983 2984 2985 2986 2987 |
schedule_work(®_work); return 0; } EXPORT_SYMBOL(regulatory_set_wiphy_regd); |
2c3e861c9 cfg80211: introdu... |
2988 2989 2990 2991 2992 2993 2994 2995 2996 2997 2998 2999 3000 3001 3002 3003 |
int regulatory_set_wiphy_regd_sync_rtnl(struct wiphy *wiphy, struct ieee80211_regdomain *rd) { int ret; ASSERT_RTNL(); ret = __regulatory_set_wiphy_regd(wiphy, rd); if (ret) return ret; /* process the request immediately */ reg_process_self_managed_hints(); return 0; } EXPORT_SYMBOL(regulatory_set_wiphy_regd_sync_rtnl); |
57b5ce072 cfg80211: add cel... |
3004 3005 |
void wiphy_regulatory_register(struct wiphy *wiphy) { |
23df0b731 regulatory: use c... |
3006 |
struct regulatory_request *lr; |
b0d7aa595 cfg80211: allow w... |
3007 3008 3009 3010 |
/* self-managed devices ignore external hints */ if (wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED) wiphy->regulatory_flags |= REGULATORY_DISABLE_BEACON_HINTS | REGULATORY_COUNTRY_IE_IGNORE; |
57b5ce072 cfg80211: add cel... |
3011 3012 |
if (!reg_dev_ignore_cell_hint(wiphy)) reg_num_devs_support_basehint++; |
23df0b731 regulatory: use c... |
3013 3014 |
lr = get_last_request(); wiphy_update_regulatory(wiphy, lr->initiator); |
897667273 cfg80211: Share C... |
3015 |
wiphy_all_share_dfs_chan_state(wiphy); |
57b5ce072 cfg80211: add cel... |
3016 |
} |
bfead0808 cfg80211: rename ... |
3017 |
void wiphy_regulatory_deregister(struct wiphy *wiphy) |
3f2355cb9 cfg80211/mac80211... |
3018 |
{ |
0ad8acaf4 cfg80211: fix NUL... |
3019 |
struct wiphy *request_wiphy = NULL; |
c492db370 regulatory: use R... |
3020 |
struct regulatory_request *lr; |
761cf7ecf cfg80211: add ass... |
3021 |
|
c492db370 regulatory: use R... |
3022 |
lr = get_last_request(); |
abc7381bc cfg80211: decoupl... |
3023 |
|
57b5ce072 cfg80211: add cel... |
3024 3025 |
if (!reg_dev_ignore_cell_hint(wiphy)) reg_num_devs_support_basehint--; |
458f4f9e9 regulatory: use R... |
3026 |
rcu_free_regdom(get_wiphy_regdom(wiphy)); |
34dd886c1 cfg80211: regulat... |
3027 |
RCU_INIT_POINTER(wiphy->regd, NULL); |
0ef9ccdd9 cfg80211: remove ... |
3028 |
|
c492db370 regulatory: use R... |
3029 3030 |
if (lr) request_wiphy = wiphy_idx_to_wiphy(lr->wiphy_idx); |
806a9e396 cfg80211: make re... |
3031 |
|
0ef9ccdd9 cfg80211: remove ... |
3032 |
if (!request_wiphy || request_wiphy != wiphy) |
38fd2143f regulatory: remov... |
3033 |
return; |
0ef9ccdd9 cfg80211: remove ... |
3034 |
|
c492db370 regulatory: use R... |
3035 3036 |
lr->wiphy_idx = WIPHY_IDX_INVALID; lr->country_ie_env = ENVIRON_ANY; |
3f2355cb9 cfg80211/mac80211... |
3037 |
} |
174e0cd28 cfg80211: Enable ... |
3038 3039 3040 3041 3042 3043 3044 3045 3046 3047 3048 3049 3050 3051 3052 3053 3054 3055 3056 3057 3058 3059 3060 3061 3062 3063 3064 3065 |
/* * See http://www.fcc.gov/document/5-ghz-unlicensed-spectrum-unii, for * UNII band definitions */ int cfg80211_get_unii(int freq) { /* UNII-1 */ if (freq >= 5150 && freq <= 5250) return 0; /* UNII-2A */ if (freq > 5250 && freq <= 5350) return 1; /* UNII-2B */ if (freq > 5350 && freq <= 5470) return 2; /* UNII-2C */ if (freq > 5470 && freq <= 5725) return 3; /* UNII-3 */ if (freq > 5725 && freq <= 5825) return 4; return -EINVAL; } |
c8866e55a cfg80211: Enable ... |
3066 3067 3068 3069 |
bool regulatory_indoor_allowed(void) { return reg_is_indoor; } |
b35a51c7d cfg80211: Make pr... |
3070 3071 3072 3073 3074 3075 3076 3077 3078 3079 3080 3081 3082 3083 3084 3085 3086 3087 3088 3089 3090 3091 3092 3093 3094 3095 3096 |
bool regulatory_pre_cac_allowed(struct wiphy *wiphy) { const struct ieee80211_regdomain *regd = NULL; const struct ieee80211_regdomain *wiphy_regd = NULL; bool pre_cac_allowed = false; rcu_read_lock(); regd = rcu_dereference(cfg80211_regdomain); wiphy_regd = rcu_dereference(wiphy->regd); if (!wiphy_regd) { if (regd->dfs_region == NL80211_DFS_ETSI) pre_cac_allowed = true; rcu_read_unlock(); return pre_cac_allowed; } if (regd->dfs_region == wiphy_regd->dfs_region && wiphy_regd->dfs_region == NL80211_DFS_ETSI) pre_cac_allowed = true; rcu_read_unlock(); return pre_cac_allowed; } |
897667273 cfg80211: Share C... |
3097 3098 3099 3100 3101 3102 3103 3104 3105 3106 3107 |
void regulatory_propagate_dfs_state(struct wiphy *wiphy, struct cfg80211_chan_def *chandef, enum nl80211_dfs_state dfs_state, enum nl80211_radar_event event) { struct cfg80211_registered_device *rdev; ASSERT_RTNL(); if (WARN_ON(!cfg80211_chandef_valid(chandef))) return; |
897667273 cfg80211: Share C... |
3108 3109 3110 3111 3112 3113 3114 3115 3116 3117 3118 3119 3120 3121 3122 3123 3124 3125 3126 3127 |
list_for_each_entry(rdev, &cfg80211_rdev_list, list) { if (wiphy == &rdev->wiphy) continue; if (!reg_dfs_domain_same(wiphy, &rdev->wiphy)) continue; if (!ieee80211_get_channel(&rdev->wiphy, chandef->chan->center_freq)) continue; cfg80211_set_dfs_state(&rdev->wiphy, chandef, dfs_state); if (event == NL80211_RADAR_DETECTED || event == NL80211_RADAR_CAC_FINISHED) cfg80211_sched_dfs_chan_update(rdev); nl80211_radar_notify(rdev, chandef, event, NULL, GFP_KERNEL); } } |
2fcc9f731 wireless: move re... |
3128 |
int __init regulatory_init(void) |
b2e1b3029 cfg80211: Add new... |
3129 |
{ |
bcf4f99b7 cfg80211: propaga... |
3130 |
int err = 0; |
734366dea cfg80211: clean u... |
3131 |
|
b2e1b3029 cfg80211: Add new... |
3132 3133 3134 |
reg_pdev = platform_device_register_simple("regulatory", 0, NULL, 0); if (IS_ERR(reg_pdev)) return PTR_ERR(reg_pdev); |
734366dea cfg80211: clean u... |
3135 |
|
fe33eb390 cfg80211: move al... |
3136 |
spin_lock_init(®_requests_lock); |
e38f8a7a8 cfg80211: Add AP ... |
3137 |
spin_lock_init(®_pending_beacons_lock); |
050507536 cfg80211: Add API... |
3138 |
spin_lock_init(®_indoor_lock); |
fe33eb390 cfg80211: move al... |
3139 |
|
80007efef cfg80211: warn if... |
3140 |
reg_regdb_size_check(); |
458f4f9e9 regulatory: use R... |
3141 |
rcu_assign_pointer(cfg80211_regdomain, cfg80211_world_regdom); |
734366dea cfg80211: clean u... |
3142 |
|
09d989d17 cfg80211: add reg... |
3143 3144 |
user_alpha2[0] = '9'; user_alpha2[1] = '7'; |
ae9e4b0d1 cfg80211: treat i... |
3145 |
/* We always try to get an update for the static regdomain */ |
458f4f9e9 regulatory: use R... |
3146 |
err = regulatory_hint_core(cfg80211_world_regdom->alpha2); |
ba25c1414 cfg80211: add reg... |
3147 |
if (err) { |
09d118008 nl80211: fix a fe... |
3148 3149 |
if (err == -ENOMEM) { platform_device_unregister(reg_pdev); |
bcf4f99b7 cfg80211: propaga... |
3150 |
return err; |
09d118008 nl80211: fix a fe... |
3151 |
} |
bcf4f99b7 cfg80211: propaga... |
3152 3153 3154 3155 3156 3157 3158 |
/* * 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... |
3159 3160 |
pr_err("kobject_uevent_env() was unable to call CRDA during init "); |
bcf4f99b7 cfg80211: propaga... |
3161 |
} |
734366dea cfg80211: clean u... |
3162 |
|
ae9e4b0d1 cfg80211: treat i... |
3163 3164 3165 3166 3167 |
/* * Finally, if the user set the module parameter treat it * as a user hint. */ if (!is_world_regdom(ieee80211_regdom)) |
57b5ce072 cfg80211: add cel... |
3168 3169 |
regulatory_hint_user(ieee80211_regdom, NL80211_USER_REG_HINT_USER); |
ae9e4b0d1 cfg80211: treat i... |
3170 |
|
b2e1b3029 cfg80211: Add new... |
3171 3172 |
return 0; } |
1a9193185 regulatory: code ... |
3173 |
void regulatory_exit(void) |
b2e1b3029 cfg80211: Add new... |
3174 |
{ |
fe33eb390 cfg80211: move al... |
3175 |
struct regulatory_request *reg_request, *tmp; |
e38f8a7a8 cfg80211: Add AP ... |
3176 |
struct reg_beacon *reg_beacon, *btmp; |
fe33eb390 cfg80211: move al... |
3177 3178 |
cancel_work_sync(®_work); |
b68630369 cfg80211: reg: ma... |
3179 |
cancel_crda_timeout_sync(); |
ad932f046 cfg80211: leave i... |
3180 |
cancel_delayed_work_sync(®_check_chans); |
fe33eb390 cfg80211: move al... |
3181 |
|
9027b1493 regulatory: remov... |
3182 |
/* Lock to suppress warnings */ |
38fd2143f regulatory: remov... |
3183 |
rtnl_lock(); |
379b82f4c regulatory: pass ... |
3184 |
reset_regdomains(true, NULL); |
38fd2143f regulatory: remov... |
3185 |
rtnl_unlock(); |
734366dea cfg80211: clean u... |
3186 |
|
58ebacc66 cfg80211: fix bug... |
3187 |
dev_set_uevent_suppress(®_pdev->dev, true); |
f6037d09e wireless: get rid... |
3188 |
|
b2e1b3029 cfg80211: Add new... |
3189 |
platform_device_unregister(reg_pdev); |
734366dea cfg80211: clean u... |
3190 |
|
fea9bcedc regulatory: don't... |
3191 3192 3193 |
list_for_each_entry_safe(reg_beacon, btmp, ®_pending_beacons, list) { list_del(®_beacon->list); kfree(reg_beacon); |
e38f8a7a8 cfg80211: Add AP ... |
3194 |
} |
e38f8a7a8 cfg80211: Add AP ... |
3195 |
|
fea9bcedc regulatory: don't... |
3196 3197 3198 |
list_for_each_entry_safe(reg_beacon, btmp, ®_beacon_list, list) { list_del(®_beacon->list); kfree(reg_beacon); |
e38f8a7a8 cfg80211: Add AP ... |
3199 |
} |
fea9bcedc regulatory: don't... |
3200 3201 3202 |
list_for_each_entry_safe(reg_request, tmp, ®_requests_list, list) { list_del(®_request->list); kfree(reg_request); |
fe33eb390 cfg80211: move al... |
3203 |
} |
8318d78a4 cfg80211 API for ... |
3204 |
} |