diff options
author | John W. Linville <linville@tuxdriver.com> | 2013-12-02 14:25:38 -0500 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2013-12-02 14:25:38 -0500 |
commit | 4b074b07625f603d40d4d04937f8874a00415dc4 (patch) | |
tree | 2dffdc46e3fea0320524f483cf5ac2c058ab59f2 /net/wireless | |
parent | 7d68849f40cd9169088249cc889d95c8998c3fb8 (diff) | |
parent | ddcc347b70f298f9d624cd0e250581d831d915fb (diff) |
Merge branch 'for-john' of git://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac80211-next
Diffstat (limited to 'net/wireless')
-rw-r--r-- | net/wireless/chan.c | 203 | ||||
-rw-r--r-- | net/wireless/core.c | 6 | ||||
-rw-r--r-- | net/wireless/core.h | 18 | ||||
-rw-r--r-- | net/wireless/genregdb.awk | 45 | ||||
-rw-r--r-- | net/wireless/ibss.c | 4 | ||||
-rw-r--r-- | net/wireless/mesh.c | 3 | ||||
-rw-r--r-- | net/wireless/mlme.c | 20 | ||||
-rw-r--r-- | net/wireless/nl80211.c | 107 | ||||
-rw-r--r-- | net/wireless/nl80211.h | 2 | ||||
-rw-r--r-- | net/wireless/rdev-ops.h | 12 | ||||
-rw-r--r-- | net/wireless/reg.c | 802 | ||||
-rw-r--r-- | net/wireless/reg.h | 3 | ||||
-rw-r--r-- | net/wireless/trace.h | 15 |
13 files changed, 787 insertions, 453 deletions
diff --git a/net/wireless/chan.c b/net/wireless/chan.c index 9b8cc877eb19..78559b5bbd1f 100644 --- a/net/wireless/chan.c +++ b/net/wireless/chan.c | |||
@@ -277,6 +277,32 @@ void cfg80211_set_dfs_state(struct wiphy *wiphy, | |||
277 | width, dfs_state); | 277 | width, dfs_state); |
278 | } | 278 | } |
279 | 279 | ||
280 | static u32 cfg80211_get_start_freq(u32 center_freq, | ||
281 | u32 bandwidth) | ||
282 | { | ||
283 | u32 start_freq; | ||
284 | |||
285 | if (bandwidth <= 20) | ||
286 | start_freq = center_freq; | ||
287 | else | ||
288 | start_freq = center_freq - bandwidth/2 + 10; | ||
289 | |||
290 | return start_freq; | ||
291 | } | ||
292 | |||
293 | static u32 cfg80211_get_end_freq(u32 center_freq, | ||
294 | u32 bandwidth) | ||
295 | { | ||
296 | u32 end_freq; | ||
297 | |||
298 | if (bandwidth <= 20) | ||
299 | end_freq = center_freq; | ||
300 | else | ||
301 | end_freq = center_freq + bandwidth/2 - 10; | ||
302 | |||
303 | return end_freq; | ||
304 | } | ||
305 | |||
280 | static int cfg80211_get_chans_dfs_required(struct wiphy *wiphy, | 306 | static int cfg80211_get_chans_dfs_required(struct wiphy *wiphy, |
281 | u32 center_freq, | 307 | u32 center_freq, |
282 | u32 bandwidth) | 308 | u32 bandwidth) |
@@ -284,13 +310,8 @@ static int cfg80211_get_chans_dfs_required(struct wiphy *wiphy, | |||
284 | struct ieee80211_channel *c; | 310 | struct ieee80211_channel *c; |
285 | u32 freq, start_freq, end_freq; | 311 | u32 freq, start_freq, end_freq; |
286 | 312 | ||
287 | if (bandwidth <= 20) { | 313 | start_freq = cfg80211_get_start_freq(center_freq, bandwidth); |
288 | start_freq = center_freq; | 314 | end_freq = cfg80211_get_end_freq(center_freq, bandwidth); |
289 | end_freq = center_freq; | ||
290 | } else { | ||
291 | start_freq = center_freq - bandwidth/2 + 10; | ||
292 | end_freq = center_freq + bandwidth/2 - 10; | ||
293 | } | ||
294 | 315 | ||
295 | for (freq = start_freq; freq <= end_freq; freq += 20) { | 316 | for (freq = start_freq; freq <= end_freq; freq += 20) { |
296 | c = ieee80211_get_channel(wiphy, freq); | 317 | c = ieee80211_get_channel(wiphy, freq); |
@@ -330,33 +351,159 @@ int cfg80211_chandef_dfs_required(struct wiphy *wiphy, | |||
330 | } | 351 | } |
331 | EXPORT_SYMBOL(cfg80211_chandef_dfs_required); | 352 | EXPORT_SYMBOL(cfg80211_chandef_dfs_required); |
332 | 353 | ||
333 | static bool cfg80211_secondary_chans_ok(struct wiphy *wiphy, | 354 | static int cfg80211_get_chans_dfs_usable(struct wiphy *wiphy, |
334 | u32 center_freq, u32 bandwidth, | 355 | u32 center_freq, |
335 | u32 prohibited_flags) | 356 | u32 bandwidth) |
336 | { | 357 | { |
337 | struct ieee80211_channel *c; | 358 | struct ieee80211_channel *c; |
338 | u32 freq, start_freq, end_freq; | 359 | u32 freq, start_freq, end_freq; |
360 | int count = 0; | ||
339 | 361 | ||
340 | if (bandwidth <= 20) { | 362 | start_freq = cfg80211_get_start_freq(center_freq, bandwidth); |
341 | start_freq = center_freq; | 363 | end_freq = cfg80211_get_end_freq(center_freq, bandwidth); |
342 | end_freq = center_freq; | 364 | |
343 | } else { | 365 | /* |
344 | start_freq = center_freq - bandwidth/2 + 10; | 366 | * Check entire range of channels for the bandwidth. |
345 | end_freq = center_freq + bandwidth/2 - 10; | 367 | * Check all channels are DFS channels (DFS_USABLE or |
368 | * DFS_AVAILABLE). Return number of usable channels | ||
369 | * (require CAC). Allow DFS and non-DFS channel mix. | ||
370 | */ | ||
371 | for (freq = start_freq; freq <= end_freq; freq += 20) { | ||
372 | c = ieee80211_get_channel(wiphy, freq); | ||
373 | if (!c) | ||
374 | return -EINVAL; | ||
375 | |||
376 | if (c->flags & IEEE80211_CHAN_DISABLED) | ||
377 | return -EINVAL; | ||
378 | |||
379 | if (c->flags & IEEE80211_CHAN_RADAR) { | ||
380 | if (c->dfs_state == NL80211_DFS_UNAVAILABLE) | ||
381 | return -EINVAL; | ||
382 | |||
383 | if (c->dfs_state == NL80211_DFS_USABLE) | ||
384 | count++; | ||
385 | } | ||
346 | } | 386 | } |
347 | 387 | ||
388 | return count; | ||
389 | } | ||
390 | |||
391 | bool cfg80211_chandef_dfs_usable(struct wiphy *wiphy, | ||
392 | const struct cfg80211_chan_def *chandef) | ||
393 | { | ||
394 | int width; | ||
395 | int r1, r2 = 0; | ||
396 | |||
397 | if (WARN_ON(!cfg80211_chandef_valid(chandef))) | ||
398 | return false; | ||
399 | |||
400 | width = cfg80211_chandef_get_width(chandef); | ||
401 | if (width < 0) | ||
402 | return false; | ||
403 | |||
404 | r1 = cfg80211_get_chans_dfs_usable(wiphy, chandef->center_freq1, | ||
405 | width); | ||
406 | |||
407 | if (r1 < 0) | ||
408 | return false; | ||
409 | |||
410 | switch (chandef->width) { | ||
411 | case NL80211_CHAN_WIDTH_80P80: | ||
412 | WARN_ON(!chandef->center_freq2); | ||
413 | r2 = cfg80211_get_chans_dfs_usable(wiphy, | ||
414 | chandef->center_freq2, | ||
415 | width); | ||
416 | if (r2 < 0) | ||
417 | return false; | ||
418 | break; | ||
419 | default: | ||
420 | WARN_ON(chandef->center_freq2); | ||
421 | break; | ||
422 | } | ||
423 | |||
424 | return (r1 + r2 > 0); | ||
425 | } | ||
426 | |||
427 | |||
428 | static bool cfg80211_get_chans_dfs_available(struct wiphy *wiphy, | ||
429 | u32 center_freq, | ||
430 | u32 bandwidth) | ||
431 | { | ||
432 | struct ieee80211_channel *c; | ||
433 | u32 freq, start_freq, end_freq; | ||
434 | |||
435 | start_freq = cfg80211_get_start_freq(center_freq, bandwidth); | ||
436 | end_freq = cfg80211_get_end_freq(center_freq, bandwidth); | ||
437 | |||
438 | /* | ||
439 | * Check entire range of channels for the bandwidth. | ||
440 | * If any channel in between is disabled or has not | ||
441 | * had gone through CAC return false | ||
442 | */ | ||
348 | for (freq = start_freq; freq <= end_freq; freq += 20) { | 443 | for (freq = start_freq; freq <= end_freq; freq += 20) { |
349 | c = ieee80211_get_channel(wiphy, freq); | 444 | c = ieee80211_get_channel(wiphy, freq); |
350 | if (!c) | 445 | if (!c) |
351 | return false; | 446 | return false; |
352 | 447 | ||
353 | /* check for radar flags */ | 448 | if (c->flags & IEEE80211_CHAN_DISABLED) |
354 | if ((prohibited_flags & c->flags & IEEE80211_CHAN_RADAR) && | 449 | return false; |
450 | |||
451 | if ((c->flags & IEEE80211_CHAN_RADAR) && | ||
355 | (c->dfs_state != NL80211_DFS_AVAILABLE)) | 452 | (c->dfs_state != NL80211_DFS_AVAILABLE)) |
356 | return false; | 453 | return false; |
454 | } | ||
455 | |||
456 | return true; | ||
457 | } | ||
458 | |||
459 | static bool cfg80211_chandef_dfs_available(struct wiphy *wiphy, | ||
460 | const struct cfg80211_chan_def *chandef) | ||
461 | { | ||
462 | int width; | ||
463 | int r; | ||
464 | |||
465 | if (WARN_ON(!cfg80211_chandef_valid(chandef))) | ||
466 | return false; | ||
467 | |||
468 | width = cfg80211_chandef_get_width(chandef); | ||
469 | if (width < 0) | ||
470 | return false; | ||
471 | |||
472 | r = cfg80211_get_chans_dfs_available(wiphy, chandef->center_freq1, | ||
473 | width); | ||
474 | |||
475 | /* If any of channels unavailable for cf1 just return */ | ||
476 | if (!r) | ||
477 | return r; | ||
478 | |||
479 | switch (chandef->width) { | ||
480 | case NL80211_CHAN_WIDTH_80P80: | ||
481 | WARN_ON(!chandef->center_freq2); | ||
482 | r = cfg80211_get_chans_dfs_available(wiphy, | ||
483 | chandef->center_freq2, | ||
484 | width); | ||
485 | default: | ||
486 | WARN_ON(chandef->center_freq2); | ||
487 | break; | ||
488 | } | ||
489 | |||
490 | return r; | ||
491 | } | ||
357 | 492 | ||
358 | /* check for the other flags */ | 493 | |
359 | if (c->flags & prohibited_flags & ~IEEE80211_CHAN_RADAR) | 494 | static bool cfg80211_secondary_chans_ok(struct wiphy *wiphy, |
495 | u32 center_freq, u32 bandwidth, | ||
496 | u32 prohibited_flags) | ||
497 | { | ||
498 | struct ieee80211_channel *c; | ||
499 | u32 freq, start_freq, end_freq; | ||
500 | |||
501 | start_freq = cfg80211_get_start_freq(center_freq, bandwidth); | ||
502 | end_freq = cfg80211_get_end_freq(center_freq, bandwidth); | ||
503 | |||
504 | for (freq = start_freq; freq <= end_freq; freq += 20) { | ||
505 | c = ieee80211_get_channel(wiphy, freq); | ||
506 | if (!c || c->flags & prohibited_flags) | ||
360 | return false; | 507 | return false; |
361 | } | 508 | } |
362 | 509 | ||
@@ -462,14 +609,19 @@ bool cfg80211_reg_can_beacon(struct wiphy *wiphy, | |||
462 | struct cfg80211_chan_def *chandef) | 609 | struct cfg80211_chan_def *chandef) |
463 | { | 610 | { |
464 | bool res; | 611 | bool res; |
612 | u32 prohibited_flags = IEEE80211_CHAN_DISABLED | | ||
613 | IEEE80211_CHAN_NO_IR | | ||
614 | IEEE80211_CHAN_RADAR; | ||
465 | 615 | ||
466 | trace_cfg80211_reg_can_beacon(wiphy, chandef); | 616 | trace_cfg80211_reg_can_beacon(wiphy, chandef); |
467 | 617 | ||
468 | res = cfg80211_chandef_usable(wiphy, chandef, | 618 | if (cfg80211_chandef_dfs_required(wiphy, chandef) > 0 && |
469 | IEEE80211_CHAN_DISABLED | | 619 | cfg80211_chandef_dfs_available(wiphy, chandef)) { |
470 | IEEE80211_CHAN_PASSIVE_SCAN | | 620 | /* We can skip IEEE80211_CHAN_NO_IR if chandef dfs available */ |
471 | IEEE80211_CHAN_NO_IBSS | | 621 | prohibited_flags = IEEE80211_CHAN_DISABLED; |
472 | IEEE80211_CHAN_RADAR); | 622 | } |
623 | |||
624 | res = cfg80211_chandef_usable(wiphy, chandef, prohibited_flags); | ||
473 | 625 | ||
474 | trace_cfg80211_return_bool(res); | 626 | trace_cfg80211_return_bool(res); |
475 | return res; | 627 | return res; |
@@ -510,6 +662,7 @@ cfg80211_get_chan_state(struct wireless_dev *wdev, | |||
510 | : CHAN_MODE_EXCLUSIVE; | 662 | : CHAN_MODE_EXCLUSIVE; |
511 | return; | 663 | return; |
512 | } | 664 | } |
665 | break; | ||
513 | case NL80211_IFTYPE_STATION: | 666 | case NL80211_IFTYPE_STATION: |
514 | case NL80211_IFTYPE_P2P_CLIENT: | 667 | case NL80211_IFTYPE_P2P_CLIENT: |
515 | if (wdev->current_bss) { | 668 | if (wdev->current_bss) { |
diff --git a/net/wireless/core.c b/net/wireless/core.c index aff959e5a1b3..fc968c861ee4 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c | |||
@@ -357,8 +357,6 @@ struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv) | |||
357 | rdev->wiphy.rts_threshold = (u32) -1; | 357 | rdev->wiphy.rts_threshold = (u32) -1; |
358 | rdev->wiphy.coverage_class = 0; | 358 | rdev->wiphy.coverage_class = 0; |
359 | 359 | ||
360 | rdev->wiphy.features = NL80211_FEATURE_SCAN_FLUSH; | ||
361 | |||
362 | return &rdev->wiphy; | 360 | return &rdev->wiphy; |
363 | } | 361 | } |
364 | EXPORT_SYMBOL(wiphy_new); | 362 | EXPORT_SYMBOL(wiphy_new); |
@@ -566,6 +564,8 @@ int wiphy_register(struct wiphy *wiphy) | |||
566 | /* check and set up bitrates */ | 564 | /* check and set up bitrates */ |
567 | ieee80211_set_bitrate_flags(wiphy); | 565 | ieee80211_set_bitrate_flags(wiphy); |
568 | 566 | ||
567 | rdev->wiphy.features |= NL80211_FEATURE_SCAN_FLUSH; | ||
568 | |||
569 | rtnl_lock(); | 569 | rtnl_lock(); |
570 | res = device_add(&rdev->wiphy.dev); | 570 | res = device_add(&rdev->wiphy.dev); |
571 | if (res) { | 571 | if (res) { |
@@ -586,7 +586,7 @@ int wiphy_register(struct wiphy *wiphy) | |||
586 | if (IS_ERR(rdev->wiphy.debugfsdir)) | 586 | if (IS_ERR(rdev->wiphy.debugfsdir)) |
587 | rdev->wiphy.debugfsdir = NULL; | 587 | rdev->wiphy.debugfsdir = NULL; |
588 | 588 | ||
589 | if (wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY) { | 589 | if (wiphy->regulatory_flags & REGULATORY_CUSTOM_REG) { |
590 | struct regulatory_request request; | 590 | struct regulatory_request request; |
591 | 591 | ||
592 | request.wiphy_idx = get_wiphy_idx(wiphy); | 592 | request.wiphy_idx = get_wiphy_idx(wiphy); |
diff --git a/net/wireless/core.h b/net/wireless/core.h index af10e59af2d8..0a277c33bb02 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h | |||
@@ -317,9 +317,8 @@ void cfg80211_mlme_unregister_socket(struct wireless_dev *wdev, u32 nlpid); | |||
317 | void cfg80211_mlme_purge_registrations(struct wireless_dev *wdev); | 317 | void cfg80211_mlme_purge_registrations(struct wireless_dev *wdev); |
318 | int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev, | 318 | int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev, |
319 | struct wireless_dev *wdev, | 319 | struct wireless_dev *wdev, |
320 | struct ieee80211_channel *chan, bool offchan, | 320 | struct cfg80211_mgmt_tx_params *params, |
321 | unsigned int wait, const u8 *buf, size_t len, | 321 | u64 *cookie); |
322 | bool no_cck, bool dont_wait_for_ack, u64 *cookie); | ||
323 | void cfg80211_oper_and_ht_capa(struct ieee80211_ht_cap *ht_capa, | 322 | void cfg80211_oper_and_ht_capa(struct ieee80211_ht_cap *ht_capa, |
324 | const struct ieee80211_ht_cap *ht_capa_mask); | 323 | const struct ieee80211_ht_cap *ht_capa_mask); |
325 | void cfg80211_oper_and_vht_capa(struct ieee80211_vht_cap *vht_capa, | 324 | void cfg80211_oper_and_vht_capa(struct ieee80211_vht_cap *vht_capa, |
@@ -382,6 +381,19 @@ int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev, | |||
382 | enum cfg80211_chan_mode chanmode, | 381 | enum cfg80211_chan_mode chanmode, |
383 | u8 radar_detect); | 382 | u8 radar_detect); |
384 | 383 | ||
384 | /** | ||
385 | * cfg80211_chandef_dfs_usable - checks if chandef is DFS usable | ||
386 | * @wiphy: the wiphy to validate against | ||
387 | * @chandef: the channel definition to check | ||
388 | * | ||
389 | * Checks if chandef is usable and we can/need start CAC on such channel. | ||
390 | * | ||
391 | * Return: Return true if all channels available and at least | ||
392 | * one channel require CAC (NL80211_DFS_USABLE) | ||
393 | */ | ||
394 | bool cfg80211_chandef_dfs_usable(struct wiphy *wiphy, | ||
395 | const struct cfg80211_chan_def *chandef); | ||
396 | |||
385 | void cfg80211_set_dfs_state(struct wiphy *wiphy, | 397 | void cfg80211_set_dfs_state(struct wiphy *wiphy, |
386 | const struct cfg80211_chan_def *chandef, | 398 | const struct cfg80211_chan_def *chandef, |
387 | enum nl80211_dfs_state dfs_state); | 399 | enum nl80211_dfs_state dfs_state); |
diff --git a/net/wireless/genregdb.awk b/net/wireless/genregdb.awk index 42ed274e81f4..9a8217d2a908 100644 --- a/net/wireless/genregdb.awk +++ b/net/wireless/genregdb.awk | |||
@@ -33,15 +33,7 @@ BEGIN { | |||
33 | regdb = "const struct ieee80211_regdomain *reg_regdb[] = {\n" | 33 | regdb = "const struct ieee80211_regdomain *reg_regdb[] = {\n" |
34 | } | 34 | } |
35 | 35 | ||
36 | /^[ \t]*#/ { | 36 | function parse_country_head() { |
37 | # Ignore | ||
38 | } | ||
39 | |||
40 | !active && /^[ \t]*$/ { | ||
41 | # Ignore | ||
42 | } | ||
43 | |||
44 | !active && /country/ { | ||
45 | country=$2 | 37 | country=$2 |
46 | sub(/:/, "", country) | 38 | sub(/:/, "", country) |
47 | printf "static const struct ieee80211_regdomain regdom_%s = {\n", country | 39 | printf "static const struct ieee80211_regdomain regdom_%s = {\n", country |
@@ -57,7 +49,8 @@ BEGIN { | |||
57 | regdb = regdb "\t®dom_" country ",\n" | 49 | regdb = regdb "\t®dom_" country ",\n" |
58 | } | 50 | } |
59 | 51 | ||
60 | active && /^[ \t]*\(/ { | 52 | function parse_reg_rule() |
53 | { | ||
61 | start = $1 | 54 | start = $1 |
62 | sub(/\(/, "", start) | 55 | sub(/\(/, "", start) |
63 | end = $3 | 56 | end = $3 |
@@ -107,17 +100,21 @@ active && /^[ \t]*\(/ { | |||
107 | } else if (flagarray[arg] == "PTMP-ONLY") { | 100 | } else if (flagarray[arg] == "PTMP-ONLY") { |
108 | flags = flags "\n\t\t\tNL80211_RRF_PTMP_ONLY | " | 101 | flags = flags "\n\t\t\tNL80211_RRF_PTMP_ONLY | " |
109 | } else if (flagarray[arg] == "PASSIVE-SCAN") { | 102 | } else if (flagarray[arg] == "PASSIVE-SCAN") { |
110 | flags = flags "\n\t\t\tNL80211_RRF_PASSIVE_SCAN | " | 103 | flags = flags "\n\t\t\tNL80211_RRF_NO_IR | " |
111 | } else if (flagarray[arg] == "NO-IBSS") { | 104 | } else if (flagarray[arg] == "NO-IBSS") { |
112 | flags = flags "\n\t\t\tNL80211_RRF_NO_IBSS | " | 105 | flags = flags "\n\t\t\tNL80211_RRF_NO_IR | " |
106 | } else if (flagarray[arg] == "NO-IR") { | ||
107 | flags = flags "\n\t\t\tNL80211_RRF_NO_IR | " | ||
113 | } | 108 | } |
109 | |||
114 | } | 110 | } |
115 | flags = flags "0" | 111 | flags = flags "0" |
116 | printf "\t\tREG_RULE(%d, %d, %d, %d, %d, %s),\n", start, end, bw, gain, power, flags | 112 | printf "\t\tREG_RULE(%d, %d, %d, %d, %d, %s),\n", start, end, bw, gain, power, flags |
117 | rules++ | 113 | rules++ |
118 | } | 114 | } |
119 | 115 | ||
120 | active && /^[ \t]*$/ { | 116 | function print_tail_country() |
117 | { | ||
121 | active = 0 | 118 | active = 0 |
122 | printf "\t},\n" | 119 | printf "\t},\n" |
123 | printf "\t.n_reg_rules = %d\n", rules | 120 | printf "\t.n_reg_rules = %d\n", rules |
@@ -125,7 +122,29 @@ active && /^[ \t]*$/ { | |||
125 | rules = 0; | 122 | rules = 0; |
126 | } | 123 | } |
127 | 124 | ||
125 | /^[ \t]*#/ { | ||
126 | # Ignore | ||
127 | } | ||
128 | |||
129 | !active && /^[ \t]*$/ { | ||
130 | # Ignore | ||
131 | } | ||
132 | |||
133 | !active && /country/ { | ||
134 | parse_country_head() | ||
135 | } | ||
136 | |||
137 | active && /^[ \t]*\(/ { | ||
138 | parse_reg_rule() | ||
139 | } | ||
140 | |||
141 | active && /^[ \t]*$/ { | ||
142 | print_tail_country() | ||
143 | } | ||
144 | |||
128 | END { | 145 | END { |
146 | if (active) | ||
147 | print_tail_country() | ||
129 | print regdb "};" | 148 | print regdb "};" |
130 | print "" | 149 | print "" |
131 | print "int reg_regdb_size = ARRAY_SIZE(reg_regdb);" | 150 | print "int reg_regdb_size = ARRAY_SIZE(reg_regdb);" |
diff --git a/net/wireless/ibss.c b/net/wireless/ibss.c index 9d797df56649..f79105712949 100644 --- a/net/wireless/ibss.c +++ b/net/wireless/ibss.c | |||
@@ -274,7 +274,7 @@ int cfg80211_ibss_wext_join(struct cfg80211_registered_device *rdev, | |||
274 | 274 | ||
275 | for (i = 0; i < sband->n_channels; i++) { | 275 | for (i = 0; i < sband->n_channels; i++) { |
276 | chan = &sband->channels[i]; | 276 | chan = &sband->channels[i]; |
277 | if (chan->flags & IEEE80211_CHAN_NO_IBSS) | 277 | if (chan->flags & IEEE80211_CHAN_NO_IR) |
278 | continue; | 278 | continue; |
279 | if (chan->flags & IEEE80211_CHAN_DISABLED) | 279 | if (chan->flags & IEEE80211_CHAN_DISABLED) |
280 | continue; | 280 | continue; |
@@ -345,7 +345,7 @@ int cfg80211_ibss_wext_siwfreq(struct net_device *dev, | |||
345 | chan = ieee80211_get_channel(wdev->wiphy, freq); | 345 | chan = ieee80211_get_channel(wdev->wiphy, freq); |
346 | if (!chan) | 346 | if (!chan) |
347 | return -EINVAL; | 347 | return -EINVAL; |
348 | if (chan->flags & IEEE80211_CHAN_NO_IBSS || | 348 | if (chan->flags & IEEE80211_CHAN_NO_IR || |
349 | chan->flags & IEEE80211_CHAN_DISABLED) | 349 | chan->flags & IEEE80211_CHAN_DISABLED) |
350 | return -EINVAL; | 350 | return -EINVAL; |
351 | } | 351 | } |
diff --git a/net/wireless/mesh.c b/net/wireless/mesh.c index 0553fd4d85ae..b0e1869de7de 100644 --- a/net/wireless/mesh.c +++ b/net/wireless/mesh.c | |||
@@ -141,8 +141,7 @@ int __cfg80211_join_mesh(struct cfg80211_registered_device *rdev, | |||
141 | 141 | ||
142 | for (i = 0; i < sband->n_channels; i++) { | 142 | for (i = 0; i < sband->n_channels; i++) { |
143 | chan = &sband->channels[i]; | 143 | chan = &sband->channels[i]; |
144 | if (chan->flags & (IEEE80211_CHAN_NO_IBSS | | 144 | if (chan->flags & (IEEE80211_CHAN_NO_IR | |
145 | IEEE80211_CHAN_PASSIVE_SCAN | | ||
146 | IEEE80211_CHAN_DISABLED | | 145 | IEEE80211_CHAN_DISABLED | |
147 | IEEE80211_CHAN_RADAR)) | 146 | IEEE80211_CHAN_RADAR)) |
148 | continue; | 147 | continue; |
diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index 6a6b1c8e907d..52cca05044a8 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c | |||
@@ -520,9 +520,7 @@ void cfg80211_mlme_purge_registrations(struct wireless_dev *wdev) | |||
520 | 520 | ||
521 | int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev, | 521 | int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev, |
522 | struct wireless_dev *wdev, | 522 | struct wireless_dev *wdev, |
523 | struct ieee80211_channel *chan, bool offchan, | 523 | struct cfg80211_mgmt_tx_params *params, u64 *cookie) |
524 | unsigned int wait, const u8 *buf, size_t len, | ||
525 | bool no_cck, bool dont_wait_for_ack, u64 *cookie) | ||
526 | { | 524 | { |
527 | const struct ieee80211_mgmt *mgmt; | 525 | const struct ieee80211_mgmt *mgmt; |
528 | u16 stype; | 526 | u16 stype; |
@@ -533,10 +531,10 @@ int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev, | |||
533 | if (!rdev->ops->mgmt_tx) | 531 | if (!rdev->ops->mgmt_tx) |
534 | return -EOPNOTSUPP; | 532 | return -EOPNOTSUPP; |
535 | 533 | ||
536 | if (len < 24 + 1) | 534 | if (params->len < 24 + 1) |
537 | return -EINVAL; | 535 | return -EINVAL; |
538 | 536 | ||
539 | mgmt = (const struct ieee80211_mgmt *) buf; | 537 | mgmt = (const struct ieee80211_mgmt *)params->buf; |
540 | 538 | ||
541 | if (!ieee80211_is_mgmt(mgmt->frame_control)) | 539 | if (!ieee80211_is_mgmt(mgmt->frame_control)) |
542 | return -EINVAL; | 540 | return -EINVAL; |
@@ -615,9 +613,7 @@ int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev, | |||
615 | return -EINVAL; | 613 | return -EINVAL; |
616 | 614 | ||
617 | /* Transmit the Action frame as requested by user space */ | 615 | /* Transmit the Action frame as requested by user space */ |
618 | return rdev_mgmt_tx(rdev, wdev, chan, offchan, | 616 | return rdev_mgmt_tx(rdev, wdev, params, cookie); |
619 | wait, buf, len, no_cck, dont_wait_for_ack, | ||
620 | cookie); | ||
621 | } | 617 | } |
622 | 618 | ||
623 | bool cfg80211_rx_mgmt(struct wireless_dev *wdev, int freq, int sig_mbm, | 619 | bool cfg80211_rx_mgmt(struct wireless_dev *wdev, int freq, int sig_mbm, |
@@ -763,12 +759,12 @@ void cfg80211_radar_event(struct wiphy *wiphy, | |||
763 | EXPORT_SYMBOL(cfg80211_radar_event); | 759 | EXPORT_SYMBOL(cfg80211_radar_event); |
764 | 760 | ||
765 | void cfg80211_cac_event(struct net_device *netdev, | 761 | void cfg80211_cac_event(struct net_device *netdev, |
762 | const struct cfg80211_chan_def *chandef, | ||
766 | enum nl80211_radar_event event, gfp_t gfp) | 763 | enum nl80211_radar_event event, gfp_t gfp) |
767 | { | 764 | { |
768 | struct wireless_dev *wdev = netdev->ieee80211_ptr; | 765 | struct wireless_dev *wdev = netdev->ieee80211_ptr; |
769 | struct wiphy *wiphy = wdev->wiphy; | 766 | struct wiphy *wiphy = wdev->wiphy; |
770 | struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); | 767 | struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); |
771 | struct cfg80211_chan_def chandef; | ||
772 | unsigned long timeout; | 768 | unsigned long timeout; |
773 | 769 | ||
774 | trace_cfg80211_cac_event(netdev, event); | 770 | trace_cfg80211_cac_event(netdev, event); |
@@ -779,14 +775,12 @@ void cfg80211_cac_event(struct net_device *netdev, | |||
779 | if (WARN_ON(!wdev->channel)) | 775 | if (WARN_ON(!wdev->channel)) |
780 | return; | 776 | return; |
781 | 777 | ||
782 | cfg80211_chandef_create(&chandef, wdev->channel, NL80211_CHAN_NO_HT); | ||
783 | |||
784 | switch (event) { | 778 | switch (event) { |
785 | case NL80211_RADAR_CAC_FINISHED: | 779 | case NL80211_RADAR_CAC_FINISHED: |
786 | timeout = wdev->cac_start_time + | 780 | timeout = wdev->cac_start_time + |
787 | msecs_to_jiffies(IEEE80211_DFS_MIN_CAC_TIME_MS); | 781 | msecs_to_jiffies(IEEE80211_DFS_MIN_CAC_TIME_MS); |
788 | WARN_ON(!time_after_eq(jiffies, timeout)); | 782 | WARN_ON(!time_after_eq(jiffies, timeout)); |
789 | cfg80211_set_dfs_state(wiphy, &chandef, NL80211_DFS_AVAILABLE); | 783 | cfg80211_set_dfs_state(wiphy, chandef, NL80211_DFS_AVAILABLE); |
790 | break; | 784 | break; |
791 | case NL80211_RADAR_CAC_ABORTED: | 785 | case NL80211_RADAR_CAC_ABORTED: |
792 | break; | 786 | break; |
@@ -796,6 +790,6 @@ void cfg80211_cac_event(struct net_device *netdev, | |||
796 | } | 790 | } |
797 | wdev->cac_started = false; | 791 | wdev->cac_started = false; |
798 | 792 | ||
799 | nl80211_radar_notify(rdev, &chandef, event, netdev, gfp); | 793 | nl80211_radar_notify(rdev, chandef, event, netdev, gfp); |
800 | } | 794 | } |
801 | EXPORT_SYMBOL(cfg80211_cac_event); | 795 | EXPORT_SYMBOL(cfg80211_cac_event); |
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index a1eb21073176..efaa23e562b4 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c | |||
@@ -564,12 +564,12 @@ static int nl80211_msg_put_channel(struct sk_buff *msg, | |||
564 | if ((chan->flags & IEEE80211_CHAN_DISABLED) && | 564 | if ((chan->flags & IEEE80211_CHAN_DISABLED) && |
565 | nla_put_flag(msg, NL80211_FREQUENCY_ATTR_DISABLED)) | 565 | nla_put_flag(msg, NL80211_FREQUENCY_ATTR_DISABLED)) |
566 | goto nla_put_failure; | 566 | goto nla_put_failure; |
567 | if ((chan->flags & IEEE80211_CHAN_PASSIVE_SCAN) && | 567 | if (chan->flags & IEEE80211_CHAN_NO_IR) { |
568 | nla_put_flag(msg, NL80211_FREQUENCY_ATTR_PASSIVE_SCAN)) | 568 | if (nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_IR)) |
569 | goto nla_put_failure; | 569 | goto nla_put_failure; |
570 | if ((chan->flags & IEEE80211_CHAN_NO_IBSS) && | 570 | if (nla_put_flag(msg, __NL80211_FREQUENCY_ATTR_NO_IBSS)) |
571 | nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_IBSS)) | 571 | goto nla_put_failure; |
572 | goto nla_put_failure; | 572 | } |
573 | if (chan->flags & IEEE80211_CHAN_RADAR) { | 573 | if (chan->flags & IEEE80211_CHAN_RADAR) { |
574 | if (nla_put_flag(msg, NL80211_FREQUENCY_ATTR_RADAR)) | 574 | if (nla_put_flag(msg, NL80211_FREQUENCY_ATTR_RADAR)) |
575 | goto nla_put_failure; | 575 | goto nla_put_failure; |
@@ -1247,10 +1247,6 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev, | |||
1247 | if ((dev->wiphy.flags & WIPHY_FLAG_TDLS_EXTERNAL_SETUP) && | 1247 | if ((dev->wiphy.flags & WIPHY_FLAG_TDLS_EXTERNAL_SETUP) && |
1248 | nla_put_flag(msg, NL80211_ATTR_TDLS_EXTERNAL_SETUP)) | 1248 | nla_put_flag(msg, NL80211_ATTR_TDLS_EXTERNAL_SETUP)) |
1249 | goto nla_put_failure; | 1249 | goto nla_put_failure; |
1250 | if ((dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_5_10_MHZ) && | ||
1251 | nla_put_flag(msg, WIPHY_FLAG_SUPPORTS_5_10_MHZ)) | ||
1252 | goto nla_put_failure; | ||
1253 | |||
1254 | state->split_start++; | 1250 | state->split_start++; |
1255 | if (state->split) | 1251 | if (state->split) |
1256 | break; | 1252 | break; |
@@ -1579,6 +1575,11 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev, | |||
1579 | if (nl80211_send_coalesce(msg, dev)) | 1575 | if (nl80211_send_coalesce(msg, dev)) |
1580 | goto nla_put_failure; | 1576 | goto nla_put_failure; |
1581 | 1577 | ||
1578 | if ((dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_5_10_MHZ) && | ||
1579 | (nla_put_flag(msg, NL80211_ATTR_SUPPORT_5_MHZ) || | ||
1580 | nla_put_flag(msg, NL80211_ATTR_SUPPORT_10_MHZ))) | ||
1581 | goto nla_put_failure; | ||
1582 | |||
1582 | /* done */ | 1583 | /* done */ |
1583 | state->split_start = 0; | 1584 | state->split_start = 0; |
1584 | break; | 1585 | break; |
@@ -2187,7 +2188,7 @@ static inline u64 wdev_id(struct wireless_dev *wdev) | |||
2187 | } | 2188 | } |
2188 | 2189 | ||
2189 | static int nl80211_send_chandef(struct sk_buff *msg, | 2190 | static int nl80211_send_chandef(struct sk_buff *msg, |
2190 | struct cfg80211_chan_def *chandef) | 2191 | const struct cfg80211_chan_def *chandef) |
2191 | { | 2192 | { |
2192 | WARN_ON(!cfg80211_chandef_valid(chandef)); | 2193 | WARN_ON(!cfg80211_chandef_valid(chandef)); |
2193 | 2194 | ||
@@ -3236,6 +3237,7 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info) | |||
3236 | return PTR_ERR(params.acl); | 3237 | return PTR_ERR(params.acl); |
3237 | } | 3238 | } |
3238 | 3239 | ||
3240 | wdev_lock(wdev); | ||
3239 | err = rdev_start_ap(rdev, dev, ¶ms); | 3241 | err = rdev_start_ap(rdev, dev, ¶ms); |
3240 | if (!err) { | 3242 | if (!err) { |
3241 | wdev->preset_chandef = params.chandef; | 3243 | wdev->preset_chandef = params.chandef; |
@@ -3244,6 +3246,7 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info) | |||
3244 | wdev->ssid_len = params.ssid_len; | 3246 | wdev->ssid_len = params.ssid_len; |
3245 | memcpy(wdev->ssid, params.ssid, wdev->ssid_len); | 3247 | memcpy(wdev->ssid, params.ssid, wdev->ssid_len); |
3246 | } | 3248 | } |
3249 | wdev_unlock(wdev); | ||
3247 | 3250 | ||
3248 | kfree(params.acl); | 3251 | kfree(params.acl); |
3249 | 3252 | ||
@@ -3272,7 +3275,11 @@ static int nl80211_set_beacon(struct sk_buff *skb, struct genl_info *info) | |||
3272 | if (err) | 3275 | if (err) |
3273 | return err; | 3276 | return err; |
3274 | 3277 | ||
3275 | return rdev_change_beacon(rdev, dev, ¶ms); | 3278 | wdev_lock(wdev); |
3279 | err = rdev_change_beacon(rdev, dev, ¶ms); | ||
3280 | wdev_unlock(wdev); | ||
3281 | |||
3282 | return err; | ||
3276 | } | 3283 | } |
3277 | 3284 | ||
3278 | static int nl80211_stop_ap(struct sk_buff *skb, struct genl_info *info) | 3285 | static int nl80211_stop_ap(struct sk_buff *skb, struct genl_info *info) |
@@ -4478,7 +4485,9 @@ static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info) | |||
4478 | { | 4485 | { |
4479 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 4486 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
4480 | struct net_device *dev = info->user_ptr[1]; | 4487 | struct net_device *dev = info->user_ptr[1]; |
4488 | struct wireless_dev *wdev = dev->ieee80211_ptr; | ||
4481 | struct bss_parameters params; | 4489 | struct bss_parameters params; |
4490 | int err; | ||
4482 | 4491 | ||
4483 | memset(¶ms, 0, sizeof(params)); | 4492 | memset(¶ms, 0, sizeof(params)); |
4484 | /* default to not changing parameters */ | 4493 | /* default to not changing parameters */ |
@@ -4544,7 +4553,11 @@ static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info) | |||
4544 | dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) | 4553 | dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) |
4545 | return -EOPNOTSUPP; | 4554 | return -EOPNOTSUPP; |
4546 | 4555 | ||
4547 | return rdev_change_bss(rdev, dev, ¶ms); | 4556 | wdev_lock(wdev); |
4557 | err = rdev_change_bss(rdev, dev, ¶ms); | ||
4558 | wdev_unlock(wdev); | ||
4559 | |||
4560 | return err; | ||
4548 | } | 4561 | } |
4549 | 4562 | ||
4550 | static const struct nla_policy reg_rule_policy[NL80211_REG_RULE_ATTR_MAX + 1] = { | 4563 | static const struct nla_policy reg_rule_policy[NL80211_REG_RULE_ATTR_MAX + 1] = { |
@@ -5098,7 +5111,7 @@ static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info) | |||
5098 | char *alpha2 = NULL; | 5111 | char *alpha2 = NULL; |
5099 | int rem_reg_rules = 0, r = 0; | 5112 | int rem_reg_rules = 0, r = 0; |
5100 | u32 num_rules = 0, rule_idx = 0, size_of_regd; | 5113 | u32 num_rules = 0, rule_idx = 0, size_of_regd; |
5101 | u8 dfs_region = 0; | 5114 | enum nl80211_dfs_regions dfs_region = NL80211_DFS_UNSET; |
5102 | struct ieee80211_regdomain *rd = NULL; | 5115 | struct ieee80211_regdomain *rd = NULL; |
5103 | 5116 | ||
5104 | if (!info->attrs[NL80211_ATTR_REG_ALPHA2]) | 5117 | if (!info->attrs[NL80211_ATTR_REG_ALPHA2]) |
@@ -5119,6 +5132,9 @@ static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info) | |||
5119 | return -EINVAL; | 5132 | return -EINVAL; |
5120 | } | 5133 | } |
5121 | 5134 | ||
5135 | if (!reg_is_valid_request(alpha2)) | ||
5136 | return -EINVAL; | ||
5137 | |||
5122 | size_of_regd = sizeof(struct ieee80211_regdomain) + | 5138 | size_of_regd = sizeof(struct ieee80211_regdomain) + |
5123 | num_rules * sizeof(struct ieee80211_reg_rule); | 5139 | num_rules * sizeof(struct ieee80211_reg_rule); |
5124 | 5140 | ||
@@ -5361,10 +5377,8 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info) | |||
5361 | if (info->attrs[NL80211_ATTR_SCAN_FLAGS]) { | 5377 | if (info->attrs[NL80211_ATTR_SCAN_FLAGS]) { |
5362 | request->flags = nla_get_u32( | 5378 | request->flags = nla_get_u32( |
5363 | info->attrs[NL80211_ATTR_SCAN_FLAGS]); | 5379 | info->attrs[NL80211_ATTR_SCAN_FLAGS]); |
5364 | if (((request->flags & NL80211_SCAN_FLAG_LOW_PRIORITY) && | 5380 | if ((request->flags & NL80211_SCAN_FLAG_LOW_PRIORITY) && |
5365 | !(wiphy->features & NL80211_FEATURE_LOW_PRIORITY_SCAN)) || | 5381 | !(wiphy->features & NL80211_FEATURE_LOW_PRIORITY_SCAN)) { |
5366 | ((request->flags & NL80211_SCAN_FLAG_FLUSH) && | ||
5367 | !(wiphy->features & NL80211_FEATURE_SCAN_FLUSH))) { | ||
5368 | err = -EOPNOTSUPP; | 5382 | err = -EOPNOTSUPP; |
5369 | goto out_free; | 5383 | goto out_free; |
5370 | } | 5384 | } |
@@ -5604,10 +5618,8 @@ static int nl80211_start_sched_scan(struct sk_buff *skb, | |||
5604 | if (info->attrs[NL80211_ATTR_SCAN_FLAGS]) { | 5618 | if (info->attrs[NL80211_ATTR_SCAN_FLAGS]) { |
5605 | request->flags = nla_get_u32( | 5619 | request->flags = nla_get_u32( |
5606 | info->attrs[NL80211_ATTR_SCAN_FLAGS]); | 5620 | info->attrs[NL80211_ATTR_SCAN_FLAGS]); |
5607 | if (((request->flags & NL80211_SCAN_FLAG_LOW_PRIORITY) && | 5621 | if ((request->flags & NL80211_SCAN_FLAG_LOW_PRIORITY) && |
5608 | !(wiphy->features & NL80211_FEATURE_LOW_PRIORITY_SCAN)) || | 5622 | !(wiphy->features & NL80211_FEATURE_LOW_PRIORITY_SCAN)) { |
5609 | ((request->flags & NL80211_SCAN_FLAG_FLUSH) && | ||
5610 | !(wiphy->features & NL80211_FEATURE_SCAN_FLUSH))) { | ||
5611 | err = -EOPNOTSUPP; | 5623 | err = -EOPNOTSUPP; |
5612 | goto out_free; | 5624 | goto out_free; |
5613 | } | 5625 | } |
@@ -5670,7 +5682,7 @@ static int nl80211_start_radar_detection(struct sk_buff *skb, | |||
5670 | if (err == 0) | 5682 | if (err == 0) |
5671 | return -EINVAL; | 5683 | return -EINVAL; |
5672 | 5684 | ||
5673 | if (chandef.chan->dfs_state != NL80211_DFS_USABLE) | 5685 | if (!cfg80211_chandef_dfs_usable(wdev->wiphy, &chandef)) |
5674 | return -EINVAL; | 5686 | return -EINVAL; |
5675 | 5687 | ||
5676 | if (!rdev->ops->start_radar_detection) | 5688 | if (!rdev->ops->start_radar_detection) |
@@ -5810,7 +5822,11 @@ skip_beacons: | |||
5810 | if (info->attrs[NL80211_ATTR_CH_SWITCH_BLOCK_TX]) | 5822 | if (info->attrs[NL80211_ATTR_CH_SWITCH_BLOCK_TX]) |
5811 | params.block_tx = true; | 5823 | params.block_tx = true; |
5812 | 5824 | ||
5813 | return rdev_channel_switch(rdev, dev, ¶ms); | 5825 | wdev_lock(wdev); |
5826 | err = rdev_channel_switch(rdev, dev, ¶ms); | ||
5827 | wdev_unlock(wdev); | ||
5828 | |||
5829 | return err; | ||
5814 | } | 5830 | } |
5815 | 5831 | ||
5816 | static int nl80211_send_bss(struct sk_buff *msg, struct netlink_callback *cb, | 5832 | static int nl80211_send_bss(struct sk_buff *msg, struct netlink_callback *cb, |
@@ -7443,10 +7459,10 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info) | |||
7443 | void *hdr = NULL; | 7459 | void *hdr = NULL; |
7444 | u64 cookie; | 7460 | u64 cookie; |
7445 | struct sk_buff *msg = NULL; | 7461 | struct sk_buff *msg = NULL; |
7446 | unsigned int wait = 0; | 7462 | struct cfg80211_mgmt_tx_params params = { |
7447 | bool offchan, no_cck, dont_wait_for_ack; | 7463 | .dont_wait_for_ack = |
7448 | 7464 | info->attrs[NL80211_ATTR_DONT_WAIT_FOR_ACK], | |
7449 | dont_wait_for_ack = info->attrs[NL80211_ATTR_DONT_WAIT_FOR_ACK]; | 7465 | }; |
7450 | 7466 | ||
7451 | if (!info->attrs[NL80211_ATTR_FRAME]) | 7467 | if (!info->attrs[NL80211_ATTR_FRAME]) |
7452 | return -EINVAL; | 7468 | return -EINVAL; |
@@ -7473,24 +7489,24 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info) | |||
7473 | if (info->attrs[NL80211_ATTR_DURATION]) { | 7489 | if (info->attrs[NL80211_ATTR_DURATION]) { |
7474 | if (!(rdev->wiphy.flags & WIPHY_FLAG_OFFCHAN_TX)) | 7490 | if (!(rdev->wiphy.flags & WIPHY_FLAG_OFFCHAN_TX)) |
7475 | return -EINVAL; | 7491 | return -EINVAL; |
7476 | wait = nla_get_u32(info->attrs[NL80211_ATTR_DURATION]); | 7492 | params.wait = nla_get_u32(info->attrs[NL80211_ATTR_DURATION]); |
7477 | 7493 | ||
7478 | /* | 7494 | /* |
7479 | * We should wait on the channel for at least a minimum amount | 7495 | * We should wait on the channel for at least a minimum amount |
7480 | * of time (10ms) but no longer than the driver supports. | 7496 | * of time (10ms) but no longer than the driver supports. |
7481 | */ | 7497 | */ |
7482 | if (wait < NL80211_MIN_REMAIN_ON_CHANNEL_TIME || | 7498 | if (params.wait < NL80211_MIN_REMAIN_ON_CHANNEL_TIME || |
7483 | wait > rdev->wiphy.max_remain_on_channel_duration) | 7499 | params.wait > rdev->wiphy.max_remain_on_channel_duration) |
7484 | return -EINVAL; | 7500 | return -EINVAL; |
7485 | 7501 | ||
7486 | } | 7502 | } |
7487 | 7503 | ||
7488 | offchan = info->attrs[NL80211_ATTR_OFFCHANNEL_TX_OK]; | 7504 | params.offchan = info->attrs[NL80211_ATTR_OFFCHANNEL_TX_OK]; |
7489 | 7505 | ||
7490 | if (offchan && !(rdev->wiphy.flags & WIPHY_FLAG_OFFCHAN_TX)) | 7506 | if (params.offchan && !(rdev->wiphy.flags & WIPHY_FLAG_OFFCHAN_TX)) |
7491 | return -EINVAL; | 7507 | return -EINVAL; |
7492 | 7508 | ||
7493 | no_cck = nla_get_flag(info->attrs[NL80211_ATTR_TX_NO_CCK_RATE]); | 7509 | params.no_cck = nla_get_flag(info->attrs[NL80211_ATTR_TX_NO_CCK_RATE]); |
7494 | 7510 | ||
7495 | /* get the channel if any has been specified, otherwise pass NULL to | 7511 | /* get the channel if any has been specified, otherwise pass NULL to |
7496 | * the driver. The latter will use the current one | 7512 | * the driver. The latter will use the current one |
@@ -7502,10 +7518,10 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info) | |||
7502 | return err; | 7518 | return err; |
7503 | } | 7519 | } |
7504 | 7520 | ||
7505 | if (!chandef.chan && offchan) | 7521 | if (!chandef.chan && params.offchan) |
7506 | return -EINVAL; | 7522 | return -EINVAL; |
7507 | 7523 | ||
7508 | if (!dont_wait_for_ack) { | 7524 | if (!params.dont_wait_for_ack) { |
7509 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); | 7525 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); |
7510 | if (!msg) | 7526 | if (!msg) |
7511 | return -ENOMEM; | 7527 | return -ENOMEM; |
@@ -7518,10 +7534,10 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info) | |||
7518 | } | 7534 | } |
7519 | } | 7535 | } |
7520 | 7536 | ||
7521 | err = cfg80211_mlme_mgmt_tx(rdev, wdev, chandef.chan, offchan, wait, | 7537 | params.buf = nla_data(info->attrs[NL80211_ATTR_FRAME]); |
7522 | nla_data(info->attrs[NL80211_ATTR_FRAME]), | 7538 | params.len = nla_len(info->attrs[NL80211_ATTR_FRAME]); |
7523 | nla_len(info->attrs[NL80211_ATTR_FRAME]), | 7539 | params.chan = chandef.chan; |
7524 | no_cck, dont_wait_for_ack, &cookie); | 7540 | err = cfg80211_mlme_mgmt_tx(rdev, wdev, ¶ms, &cookie); |
7525 | if (err) | 7541 | if (err) |
7526 | goto free_msg; | 7542 | goto free_msg; |
7527 | 7543 | ||
@@ -10805,21 +10821,18 @@ void cfg80211_ch_switch_notify(struct net_device *dev, | |||
10805 | struct wiphy *wiphy = wdev->wiphy; | 10821 | struct wiphy *wiphy = wdev->wiphy; |
10806 | struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); | 10822 | struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); |
10807 | 10823 | ||
10808 | trace_cfg80211_ch_switch_notify(dev, chandef); | 10824 | ASSERT_WDEV_LOCK(wdev); |
10809 | 10825 | ||
10810 | wdev_lock(wdev); | 10826 | trace_cfg80211_ch_switch_notify(dev, chandef); |
10811 | 10827 | ||
10812 | if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP && | 10828 | if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP && |
10813 | wdev->iftype != NL80211_IFTYPE_P2P_GO && | 10829 | wdev->iftype != NL80211_IFTYPE_P2P_GO && |
10814 | wdev->iftype != NL80211_IFTYPE_ADHOC && | 10830 | wdev->iftype != NL80211_IFTYPE_ADHOC && |
10815 | wdev->iftype != NL80211_IFTYPE_MESH_POINT)) | 10831 | wdev->iftype != NL80211_IFTYPE_MESH_POINT)) |
10816 | goto out; | 10832 | return; |
10817 | 10833 | ||
10818 | wdev->channel = chandef->chan; | 10834 | wdev->channel = chandef->chan; |
10819 | nl80211_ch_switch_notify(rdev, dev, chandef, GFP_KERNEL); | 10835 | nl80211_ch_switch_notify(rdev, dev, chandef, GFP_KERNEL); |
10820 | out: | ||
10821 | wdev_unlock(wdev); | ||
10822 | return; | ||
10823 | } | 10836 | } |
10824 | EXPORT_SYMBOL(cfg80211_ch_switch_notify); | 10837 | EXPORT_SYMBOL(cfg80211_ch_switch_notify); |
10825 | 10838 | ||
@@ -10878,7 +10891,7 @@ EXPORT_SYMBOL(cfg80211_cqm_txe_notify); | |||
10878 | 10891 | ||
10879 | void | 10892 | void |
10880 | nl80211_radar_notify(struct cfg80211_registered_device *rdev, | 10893 | nl80211_radar_notify(struct cfg80211_registered_device *rdev, |
10881 | struct cfg80211_chan_def *chandef, | 10894 | const struct cfg80211_chan_def *chandef, |
10882 | enum nl80211_radar_event event, | 10895 | enum nl80211_radar_event event, |
10883 | struct net_device *netdev, gfp_t gfp) | 10896 | struct net_device *netdev, gfp_t gfp) |
10884 | { | 10897 | { |
diff --git a/net/wireless/nl80211.h b/net/wireless/nl80211.h index 2c0f2b3c07cb..b1b231324e10 100644 --- a/net/wireless/nl80211.h +++ b/net/wireless/nl80211.h | |||
@@ -70,7 +70,7 @@ int nl80211_send_mgmt(struct cfg80211_registered_device *rdev, | |||
70 | 70 | ||
71 | void | 71 | void |
72 | nl80211_radar_notify(struct cfg80211_registered_device *rdev, | 72 | nl80211_radar_notify(struct cfg80211_registered_device *rdev, |
73 | struct cfg80211_chan_def *chandef, | 73 | const struct cfg80211_chan_def *chandef, |
74 | enum nl80211_radar_event event, | 74 | enum nl80211_radar_event event, |
75 | struct net_device *netdev, gfp_t gfp); | 75 | struct net_device *netdev, gfp_t gfp); |
76 | 76 | ||
diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h index 37ce9fdfe934..a6c03ab14a0d 100644 --- a/net/wireless/rdev-ops.h +++ b/net/wireless/rdev-ops.h | |||
@@ -624,16 +624,12 @@ rdev_cancel_remain_on_channel(struct cfg80211_registered_device *rdev, | |||
624 | 624 | ||
625 | static inline int rdev_mgmt_tx(struct cfg80211_registered_device *rdev, | 625 | static inline int rdev_mgmt_tx(struct cfg80211_registered_device *rdev, |
626 | struct wireless_dev *wdev, | 626 | struct wireless_dev *wdev, |
627 | struct ieee80211_channel *chan, bool offchan, | 627 | struct cfg80211_mgmt_tx_params *params, |
628 | unsigned int wait, const u8 *buf, size_t len, | 628 | u64 *cookie) |
629 | bool no_cck, bool dont_wait_for_ack, u64 *cookie) | ||
630 | { | 629 | { |
631 | int ret; | 630 | int ret; |
632 | trace_rdev_mgmt_tx(&rdev->wiphy, wdev, chan, offchan, | 631 | trace_rdev_mgmt_tx(&rdev->wiphy, wdev, params); |
633 | wait, no_cck, dont_wait_for_ack); | 632 | ret = rdev->ops->mgmt_tx(&rdev->wiphy, wdev, params, cookie); |
634 | ret = rdev->ops->mgmt_tx(&rdev->wiphy, wdev, chan, offchan, | ||
635 | wait, buf, len, no_cck, | ||
636 | dont_wait_for_ack, cookie); | ||
637 | trace_rdev_return_int_cookie(&rdev->wiphy, ret, *cookie); | 633 | trace_rdev_return_int_cookie(&rdev->wiphy, ret, *cookie); |
638 | return ret; | 634 | return ret; |
639 | } | 635 | } |
diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 7da67fd0b418..ec54e1aac8e2 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c | |||
@@ -120,6 +120,21 @@ static const struct ieee80211_regdomain *get_wiphy_regdom(struct wiphy *wiphy) | |||
120 | return rtnl_dereference(wiphy->regd); | 120 | return rtnl_dereference(wiphy->regd); |
121 | } | 121 | } |
122 | 122 | ||
123 | static const char *reg_dfs_region_str(enum nl80211_dfs_regions dfs_region) | ||
124 | { | ||
125 | switch (dfs_region) { | ||
126 | case NL80211_DFS_UNSET: | ||
127 | return "unset"; | ||
128 | case NL80211_DFS_FCC: | ||
129 | return "FCC"; | ||
130 | case NL80211_DFS_ETSI: | ||
131 | return "ETSI"; | ||
132 | case NL80211_DFS_JP: | ||
133 | return "JP"; | ||
134 | } | ||
135 | return "Unknown"; | ||
136 | } | ||
137 | |||
123 | static void rcu_free_regdom(const struct ieee80211_regdomain *r) | 138 | static void rcu_free_regdom(const struct ieee80211_regdomain *r) |
124 | { | 139 | { |
125 | if (!r) | 140 | if (!r) |
@@ -163,35 +178,29 @@ static const struct ieee80211_regdomain world_regdom = { | |||
163 | REG_RULE(2412-10, 2462+10, 40, 6, 20, 0), | 178 | REG_RULE(2412-10, 2462+10, 40, 6, 20, 0), |
164 | /* IEEE 802.11b/g, channels 12..13. */ | 179 | /* IEEE 802.11b/g, channels 12..13. */ |
165 | REG_RULE(2467-10, 2472+10, 40, 6, 20, | 180 | REG_RULE(2467-10, 2472+10, 40, 6, 20, |
166 | NL80211_RRF_PASSIVE_SCAN | | 181 | NL80211_RRF_NO_IR), |
167 | NL80211_RRF_NO_IBSS), | ||
168 | /* IEEE 802.11 channel 14 - Only JP enables | 182 | /* IEEE 802.11 channel 14 - Only JP enables |
169 | * this and for 802.11b only */ | 183 | * this and for 802.11b only */ |
170 | REG_RULE(2484-10, 2484+10, 20, 6, 20, | 184 | REG_RULE(2484-10, 2484+10, 20, 6, 20, |
171 | NL80211_RRF_PASSIVE_SCAN | | 185 | NL80211_RRF_NO_IR | |
172 | NL80211_RRF_NO_IBSS | | ||
173 | NL80211_RRF_NO_OFDM), | 186 | NL80211_RRF_NO_OFDM), |
174 | /* IEEE 802.11a, channel 36..48 */ | 187 | /* IEEE 802.11a, channel 36..48 */ |
175 | REG_RULE(5180-10, 5240+10, 160, 6, 20, | 188 | REG_RULE(5180-10, 5240+10, 160, 6, 20, |
176 | NL80211_RRF_PASSIVE_SCAN | | 189 | NL80211_RRF_NO_IR), |
177 | NL80211_RRF_NO_IBSS), | ||
178 | 190 | ||
179 | /* IEEE 802.11a, channel 52..64 - DFS required */ | 191 | /* IEEE 802.11a, channel 52..64 - DFS required */ |
180 | REG_RULE(5260-10, 5320+10, 160, 6, 20, | 192 | REG_RULE(5260-10, 5320+10, 160, 6, 20, |
181 | NL80211_RRF_PASSIVE_SCAN | | 193 | NL80211_RRF_NO_IR | |
182 | NL80211_RRF_NO_IBSS | | ||
183 | NL80211_RRF_DFS), | 194 | NL80211_RRF_DFS), |
184 | 195 | ||
185 | /* IEEE 802.11a, channel 100..144 - DFS required */ | 196 | /* IEEE 802.11a, channel 100..144 - DFS required */ |
186 | REG_RULE(5500-10, 5720+10, 160, 6, 20, | 197 | REG_RULE(5500-10, 5720+10, 160, 6, 20, |
187 | NL80211_RRF_PASSIVE_SCAN | | 198 | NL80211_RRF_NO_IR | |
188 | NL80211_RRF_NO_IBSS | | ||
189 | NL80211_RRF_DFS), | 199 | NL80211_RRF_DFS), |
190 | 200 | ||
191 | /* IEEE 802.11a, channel 149..165 */ | 201 | /* IEEE 802.11a, channel 149..165 */ |
192 | REG_RULE(5745-10, 5825+10, 80, 6, 20, | 202 | REG_RULE(5745-10, 5825+10, 80, 6, 20, |
193 | NL80211_RRF_PASSIVE_SCAN | | 203 | NL80211_RRF_NO_IR), |
194 | NL80211_RRF_NO_IBSS), | ||
195 | 204 | ||
196 | /* IEEE 802.11ad (60gHz), channels 1..3 */ | 205 | /* IEEE 802.11ad (60gHz), channels 1..3 */ |
197 | REG_RULE(56160+2160*1-1080, 56160+2160*3+1080, 2160, 0, 0, 0), | 206 | REG_RULE(56160+2160*1-1080, 56160+2160*3+1080, 2160, 0, 0, 0), |
@@ -208,11 +217,26 @@ static char user_alpha2[2]; | |||
208 | module_param(ieee80211_regdom, charp, 0444); | 217 | module_param(ieee80211_regdom, charp, 0444); |
209 | MODULE_PARM_DESC(ieee80211_regdom, "IEEE 802.11 regulatory domain code"); | 218 | MODULE_PARM_DESC(ieee80211_regdom, "IEEE 802.11 regulatory domain code"); |
210 | 219 | ||
220 | static void reg_kfree_last_request(void) | ||
221 | { | ||
222 | struct regulatory_request *lr; | ||
223 | |||
224 | lr = get_last_request(); | ||
225 | |||
226 | if (lr != &core_request_world && lr) | ||
227 | kfree_rcu(lr, rcu_head); | ||
228 | } | ||
229 | |||
230 | static void reg_update_last_request(struct regulatory_request *request) | ||
231 | { | ||
232 | reg_kfree_last_request(); | ||
233 | rcu_assign_pointer(last_request, request); | ||
234 | } | ||
235 | |||
211 | static void reset_regdomains(bool full_reset, | 236 | static void reset_regdomains(bool full_reset, |
212 | const struct ieee80211_regdomain *new_regdom) | 237 | const struct ieee80211_regdomain *new_regdom) |
213 | { | 238 | { |
214 | const struct ieee80211_regdomain *r; | 239 | const struct ieee80211_regdomain *r; |
215 | struct regulatory_request *lr; | ||
216 | 240 | ||
217 | ASSERT_RTNL(); | 241 | ASSERT_RTNL(); |
218 | 242 | ||
@@ -235,10 +259,7 @@ static void reset_regdomains(bool full_reset, | |||
235 | if (!full_reset) | 259 | if (!full_reset) |
236 | return; | 260 | return; |
237 | 261 | ||
238 | lr = get_last_request(); | 262 | reg_update_last_request(&core_request_world); |
239 | if (lr != &core_request_world && lr) | ||
240 | kfree_rcu(lr, rcu_head); | ||
241 | rcu_assign_pointer(last_request, &core_request_world); | ||
242 | } | 263 | } |
243 | 264 | ||
244 | /* | 265 | /* |
@@ -456,7 +477,15 @@ static int call_crda(const char *alpha2) | |||
456 | return kobject_uevent(®_pdev->dev.kobj, KOBJ_CHANGE); | 477 | return kobject_uevent(®_pdev->dev.kobj, KOBJ_CHANGE); |
457 | } | 478 | } |
458 | 479 | ||
459 | static bool reg_is_valid_request(const char *alpha2) | 480 | static enum reg_request_treatment |
481 | reg_call_crda(struct regulatory_request *request) | ||
482 | { | ||
483 | if (call_crda(request->alpha2)) | ||
484 | return REG_REQ_IGNORE; | ||
485 | return REG_REQ_OK; | ||
486 | } | ||
487 | |||
488 | bool reg_is_valid_request(const char *alpha2) | ||
460 | { | 489 | { |
461 | struct regulatory_request *lr = get_last_request(); | 490 | struct regulatory_request *lr = get_last_request(); |
462 | 491 | ||
@@ -557,6 +586,20 @@ static bool freq_in_rule_band(const struct ieee80211_freq_range *freq_range, | |||
557 | } | 586 | } |
558 | 587 | ||
559 | /* | 588 | /* |
589 | * Later on we can perhaps use the more restrictive DFS | ||
590 | * region but we don't have information for that yet so | ||
591 | * for now simply disallow conflicts. | ||
592 | */ | ||
593 | static enum nl80211_dfs_regions | ||
594 | reg_intersect_dfs_region(const enum nl80211_dfs_regions dfs_region1, | ||
595 | const enum nl80211_dfs_regions dfs_region2) | ||
596 | { | ||
597 | if (dfs_region1 != dfs_region2) | ||
598 | return NL80211_DFS_UNSET; | ||
599 | return dfs_region1; | ||
600 | } | ||
601 | |||
602 | /* | ||
560 | * Helper for regdom_intersect(), this does the real | 603 | * Helper for regdom_intersect(), this does the real |
561 | * mathematical intersection fun | 604 | * mathematical intersection fun |
562 | */ | 605 | */ |
@@ -687,6 +730,8 @@ regdom_intersect(const struct ieee80211_regdomain *rd1, | |||
687 | rd->n_reg_rules = num_rules; | 730 | rd->n_reg_rules = num_rules; |
688 | rd->alpha2[0] = '9'; | 731 | rd->alpha2[0] = '9'; |
689 | rd->alpha2[1] = '8'; | 732 | rd->alpha2[1] = '8'; |
733 | rd->dfs_region = reg_intersect_dfs_region(rd1->dfs_region, | ||
734 | rd2->dfs_region); | ||
690 | 735 | ||
691 | return rd; | 736 | return rd; |
692 | } | 737 | } |
@@ -698,10 +743,8 @@ regdom_intersect(const struct ieee80211_regdomain *rd1, | |||
698 | static u32 map_regdom_flags(u32 rd_flags) | 743 | static u32 map_regdom_flags(u32 rd_flags) |
699 | { | 744 | { |
700 | u32 channel_flags = 0; | 745 | u32 channel_flags = 0; |
701 | if (rd_flags & NL80211_RRF_PASSIVE_SCAN) | 746 | if (rd_flags & NL80211_RRF_NO_IR_ALL) |
702 | channel_flags |= IEEE80211_CHAN_PASSIVE_SCAN; | 747 | channel_flags |= IEEE80211_CHAN_NO_IR; |
703 | if (rd_flags & NL80211_RRF_NO_IBSS) | ||
704 | channel_flags |= IEEE80211_CHAN_NO_IBSS; | ||
705 | if (rd_flags & NL80211_RRF_DFS) | 748 | if (rd_flags & NL80211_RRF_DFS) |
706 | channel_flags |= IEEE80211_CHAN_RADAR; | 749 | channel_flags |= IEEE80211_CHAN_RADAR; |
707 | if (rd_flags & NL80211_RRF_NO_OFDM) | 750 | if (rd_flags & NL80211_RRF_NO_OFDM) |
@@ -854,8 +897,18 @@ static void handle_channel(struct wiphy *wiphy, | |||
854 | PTR_ERR(reg_rule) == -ERANGE) | 897 | PTR_ERR(reg_rule) == -ERANGE) |
855 | return; | 898 | return; |
856 | 899 | ||
857 | REG_DBG_PRINT("Disabling freq %d MHz\n", chan->center_freq); | 900 | if (lr->initiator == NL80211_REGDOM_SET_BY_DRIVER && |
858 | chan->flags |= IEEE80211_CHAN_DISABLED; | 901 | request_wiphy && request_wiphy == wiphy && |
902 | request_wiphy->regulatory_flags & REGULATORY_STRICT_REG) { | ||
903 | REG_DBG_PRINT("Disabling freq %d MHz for good\n", | ||
904 | chan->center_freq); | ||
905 | chan->orig_flags |= IEEE80211_CHAN_DISABLED; | ||
906 | chan->flags = chan->orig_flags; | ||
907 | } else { | ||
908 | REG_DBG_PRINT("Disabling freq %d MHz\n", | ||
909 | chan->center_freq); | ||
910 | chan->flags |= IEEE80211_CHAN_DISABLED; | ||
911 | } | ||
859 | return; | 912 | return; |
860 | } | 913 | } |
861 | 914 | ||
@@ -873,7 +926,7 @@ static void handle_channel(struct wiphy *wiphy, | |||
873 | 926 | ||
874 | if (lr->initiator == NL80211_REGDOM_SET_BY_DRIVER && | 927 | if (lr->initiator == NL80211_REGDOM_SET_BY_DRIVER && |
875 | request_wiphy && request_wiphy == wiphy && | 928 | request_wiphy && request_wiphy == wiphy && |
876 | request_wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY) { | 929 | request_wiphy->regulatory_flags & REGULATORY_STRICT_REG) { |
877 | /* | 930 | /* |
878 | * This guarantees the driver's requested regulatory domain | 931 | * This guarantees the driver's requested regulatory domain |
879 | * will always be used as a base for further regulatory | 932 | * will always be used as a base for further regulatory |
@@ -899,13 +952,11 @@ static void handle_channel(struct wiphy *wiphy, | |||
899 | chan->max_reg_power = (int) MBM_TO_DBM(power_rule->max_eirp); | 952 | chan->max_reg_power = (int) MBM_TO_DBM(power_rule->max_eirp); |
900 | if (chan->orig_mpwr) { | 953 | if (chan->orig_mpwr) { |
901 | /* | 954 | /* |
902 | * Devices that have their own custom regulatory domain | 955 | * Devices that use REGULATORY_COUNTRY_IE_FOLLOW_POWER |
903 | * but also use WIPHY_FLAG_STRICT_REGULATORY will follow the | 956 | * will always follow the passed country IE power settings. |
904 | * passed country IE power settings. | ||
905 | */ | 957 | */ |
906 | if (initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE && | 958 | if (initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE && |
907 | wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY && | 959 | wiphy->regulatory_flags & REGULATORY_COUNTRY_IE_FOLLOW_POWER) |
908 | wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY) | ||
909 | chan->max_power = chan->max_reg_power; | 960 | chan->max_power = chan->max_reg_power; |
910 | else | 961 | else |
911 | chan->max_power = min(chan->orig_mpwr, | 962 | chan->max_power = min(chan->orig_mpwr, |
@@ -975,8 +1026,8 @@ static bool reg_dev_ignore_cell_hint(struct wiphy *wiphy) | |||
975 | 1026 | ||
976 | static bool wiphy_strict_alpha2_regd(struct wiphy *wiphy) | 1027 | static bool wiphy_strict_alpha2_regd(struct wiphy *wiphy) |
977 | { | 1028 | { |
978 | if (wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY && | 1029 | if (wiphy->regulatory_flags & REGULATORY_STRICT_REG && |
979 | !(wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY)) | 1030 | !(wiphy->regulatory_flags & REGULATORY_CUSTOM_REG)) |
980 | return true; | 1031 | return true; |
981 | return false; | 1032 | return false; |
982 | } | 1033 | } |
@@ -994,7 +1045,7 @@ static bool ignore_reg_update(struct wiphy *wiphy, | |||
994 | } | 1045 | } |
995 | 1046 | ||
996 | if (initiator == NL80211_REGDOM_SET_BY_CORE && | 1047 | if (initiator == NL80211_REGDOM_SET_BY_CORE && |
997 | wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY) { | 1048 | wiphy->regulatory_flags & REGULATORY_CUSTOM_REG) { |
998 | REG_DBG_PRINT("Ignoring regulatory request set by %s " | 1049 | REG_DBG_PRINT("Ignoring regulatory request set by %s " |
999 | "since the driver uses its own custom " | 1050 | "since the driver uses its own custom " |
1000 | "regulatory domain\n", | 1051 | "regulatory domain\n", |
@@ -1032,7 +1083,7 @@ static bool reg_is_world_roaming(struct wiphy *wiphy) | |||
1032 | return true; | 1083 | return true; |
1033 | 1084 | ||
1034 | if (lr && lr->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE && | 1085 | if (lr && lr->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE && |
1035 | wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY) | 1086 | wiphy->regulatory_flags & REGULATORY_CUSTOM_REG) |
1036 | return true; | 1087 | return true; |
1037 | 1088 | ||
1038 | return false; | 1089 | return false; |
@@ -1060,19 +1111,14 @@ static void handle_reg_beacon(struct wiphy *wiphy, unsigned int chan_idx, | |||
1060 | if (!reg_is_world_roaming(wiphy)) | 1111 | if (!reg_is_world_roaming(wiphy)) |
1061 | return; | 1112 | return; |
1062 | 1113 | ||
1063 | if (wiphy->flags & WIPHY_FLAG_DISABLE_BEACON_HINTS) | 1114 | if (wiphy->regulatory_flags & REGULATORY_DISABLE_BEACON_HINTS) |
1064 | return; | 1115 | return; |
1065 | 1116 | ||
1066 | chan_before.center_freq = chan->center_freq; | 1117 | chan_before.center_freq = chan->center_freq; |
1067 | chan_before.flags = chan->flags; | 1118 | chan_before.flags = chan->flags; |
1068 | 1119 | ||
1069 | if (chan->flags & IEEE80211_CHAN_PASSIVE_SCAN) { | 1120 | if (chan->flags & IEEE80211_CHAN_NO_IR) { |
1070 | chan->flags &= ~IEEE80211_CHAN_PASSIVE_SCAN; | 1121 | chan->flags &= ~IEEE80211_CHAN_NO_IR; |
1071 | channel_changed = true; | ||
1072 | } | ||
1073 | |||
1074 | if (chan->flags & IEEE80211_CHAN_NO_IBSS) { | ||
1075 | chan->flags &= ~IEEE80211_CHAN_NO_IBSS; | ||
1076 | channel_changed = true; | 1122 | channel_changed = true; |
1077 | } | 1123 | } |
1078 | 1124 | ||
@@ -1205,14 +1251,30 @@ static void reg_process_ht_flags(struct wiphy *wiphy) | |||
1205 | reg_process_ht_flags_band(wiphy, wiphy->bands[band]); | 1251 | reg_process_ht_flags_band(wiphy, wiphy->bands[band]); |
1206 | } | 1252 | } |
1207 | 1253 | ||
1254 | static void reg_call_notifier(struct wiphy *wiphy, | ||
1255 | struct regulatory_request *request) | ||
1256 | { | ||
1257 | if (wiphy->reg_notifier) | ||
1258 | wiphy->reg_notifier(wiphy, request); | ||
1259 | } | ||
1260 | |||
1208 | static void wiphy_update_regulatory(struct wiphy *wiphy, | 1261 | static void wiphy_update_regulatory(struct wiphy *wiphy, |
1209 | enum nl80211_reg_initiator initiator) | 1262 | enum nl80211_reg_initiator initiator) |
1210 | { | 1263 | { |
1211 | enum ieee80211_band band; | 1264 | enum ieee80211_band band; |
1212 | struct regulatory_request *lr = get_last_request(); | 1265 | struct regulatory_request *lr = get_last_request(); |
1213 | 1266 | ||
1214 | if (ignore_reg_update(wiphy, initiator)) | 1267 | if (ignore_reg_update(wiphy, initiator)) { |
1268 | /* | ||
1269 | * Regulatory updates set by CORE are ignored for custom | ||
1270 | * regulatory cards. Let us notify the changes to the driver, | ||
1271 | * as some drivers used this to restore its orig_* reg domain. | ||
1272 | */ | ||
1273 | if (initiator == NL80211_REGDOM_SET_BY_CORE && | ||
1274 | wiphy->regulatory_flags & REGULATORY_CUSTOM_REG) | ||
1275 | reg_call_notifier(wiphy, lr); | ||
1215 | return; | 1276 | return; |
1277 | } | ||
1216 | 1278 | ||
1217 | lr->dfs_region = get_cfg80211_regdom()->dfs_region; | 1279 | lr->dfs_region = get_cfg80211_regdom()->dfs_region; |
1218 | 1280 | ||
@@ -1221,9 +1283,7 @@ static void wiphy_update_regulatory(struct wiphy *wiphy, | |||
1221 | 1283 | ||
1222 | reg_process_beacons(wiphy); | 1284 | reg_process_beacons(wiphy); |
1223 | reg_process_ht_flags(wiphy); | 1285 | reg_process_ht_flags(wiphy); |
1224 | 1286 | reg_call_notifier(wiphy, lr); | |
1225 | if (wiphy->reg_notifier) | ||
1226 | wiphy->reg_notifier(wiphy, lr); | ||
1227 | } | 1287 | } |
1228 | 1288 | ||
1229 | static void update_all_wiphy_regulatory(enum nl80211_reg_initiator initiator) | 1289 | static void update_all_wiphy_regulatory(enum nl80211_reg_initiator initiator) |
@@ -1236,15 +1296,6 @@ static void update_all_wiphy_regulatory(enum nl80211_reg_initiator initiator) | |||
1236 | list_for_each_entry(rdev, &cfg80211_rdev_list, list) { | 1296 | list_for_each_entry(rdev, &cfg80211_rdev_list, list) { |
1237 | wiphy = &rdev->wiphy; | 1297 | wiphy = &rdev->wiphy; |
1238 | wiphy_update_regulatory(wiphy, initiator); | 1298 | wiphy_update_regulatory(wiphy, initiator); |
1239 | /* | ||
1240 | * Regulatory updates set by CORE are ignored for custom | ||
1241 | * regulatory cards. Let us notify the changes to the driver, | ||
1242 | * as some drivers used this to restore its orig_* reg domain. | ||
1243 | */ | ||
1244 | if (initiator == NL80211_REGDOM_SET_BY_CORE && | ||
1245 | wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY && | ||
1246 | wiphy->reg_notifier) | ||
1247 | wiphy->reg_notifier(wiphy, get_last_request()); | ||
1248 | } | 1299 | } |
1249 | } | 1300 | } |
1250 | 1301 | ||
@@ -1263,7 +1314,8 @@ static void handle_channel_custom(struct wiphy *wiphy, | |||
1263 | if (IS_ERR(reg_rule)) { | 1314 | if (IS_ERR(reg_rule)) { |
1264 | REG_DBG_PRINT("Disabling freq %d MHz as custom regd has no rule that fits it\n", | 1315 | REG_DBG_PRINT("Disabling freq %d MHz as custom regd has no rule that fits it\n", |
1265 | chan->center_freq); | 1316 | chan->center_freq); |
1266 | chan->flags = IEEE80211_CHAN_DISABLED; | 1317 | chan->orig_flags |= IEEE80211_CHAN_DISABLED; |
1318 | chan->flags = chan->orig_flags; | ||
1267 | return; | 1319 | return; |
1268 | } | 1320 | } |
1269 | 1321 | ||
@@ -1305,6 +1357,10 @@ void wiphy_apply_custom_regulatory(struct wiphy *wiphy, | |||
1305 | enum ieee80211_band band; | 1357 | enum ieee80211_band band; |
1306 | unsigned int bands_set = 0; | 1358 | unsigned int bands_set = 0; |
1307 | 1359 | ||
1360 | WARN(!(wiphy->regulatory_flags & REGULATORY_CUSTOM_REG), | ||
1361 | "wiphy should have REGULATORY_CUSTOM_REG\n"); | ||
1362 | wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG; | ||
1363 | |||
1308 | for (band = 0; band < IEEE80211_NUM_BANDS; band++) { | 1364 | for (band = 0; band < IEEE80211_NUM_BANDS; band++) { |
1309 | if (!wiphy->bands[band]) | 1365 | if (!wiphy->bands[band]) |
1310 | continue; | 1366 | continue; |
@@ -1320,225 +1376,285 @@ void wiphy_apply_custom_regulatory(struct wiphy *wiphy, | |||
1320 | } | 1376 | } |
1321 | EXPORT_SYMBOL(wiphy_apply_custom_regulatory); | 1377 | EXPORT_SYMBOL(wiphy_apply_custom_regulatory); |
1322 | 1378 | ||
1323 | /* This has the logic which determines when a new request | 1379 | static void reg_set_request_processed(void) |
1324 | * should be ignored. */ | ||
1325 | static enum reg_request_treatment | ||
1326 | get_reg_request_treatment(struct wiphy *wiphy, | ||
1327 | struct regulatory_request *pending_request) | ||
1328 | { | 1380 | { |
1329 | struct wiphy *last_wiphy = NULL; | 1381 | bool need_more_processing = false; |
1330 | struct regulatory_request *lr = get_last_request(); | 1382 | struct regulatory_request *lr = get_last_request(); |
1331 | 1383 | ||
1332 | /* All initial requests are respected */ | 1384 | lr->processed = true; |
1333 | if (!lr) | ||
1334 | return REG_REQ_OK; | ||
1335 | 1385 | ||
1336 | switch (pending_request->initiator) { | 1386 | spin_lock(®_requests_lock); |
1337 | case NL80211_REGDOM_SET_BY_CORE: | 1387 | if (!list_empty(®_requests_list)) |
1338 | return REG_REQ_OK; | 1388 | need_more_processing = true; |
1339 | case NL80211_REGDOM_SET_BY_COUNTRY_IE: | 1389 | spin_unlock(®_requests_lock); |
1340 | if (reg_request_cell_base(lr)) { | ||
1341 | /* Trust a Cell base station over the AP's country IE */ | ||
1342 | if (regdom_changes(pending_request->alpha2)) | ||
1343 | return REG_REQ_IGNORE; | ||
1344 | return REG_REQ_ALREADY_SET; | ||
1345 | } | ||
1346 | 1390 | ||
1347 | last_wiphy = wiphy_idx_to_wiphy(lr->wiphy_idx); | 1391 | if (lr->initiator == NL80211_REGDOM_SET_BY_USER) |
1392 | cancel_delayed_work(®_timeout); | ||
1348 | 1393 | ||
1349 | if (unlikely(!is_an_alpha2(pending_request->alpha2))) | 1394 | if (need_more_processing) |
1350 | return -EINVAL; | 1395 | schedule_work(®_work); |
1351 | if (lr->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) { | 1396 | } |
1352 | if (last_wiphy != wiphy) { | ||
1353 | /* | ||
1354 | * Two cards with two APs claiming different | ||
1355 | * Country IE alpha2s. We could | ||
1356 | * intersect them, but that seems unlikely | ||
1357 | * to be correct. Reject second one for now. | ||
1358 | */ | ||
1359 | if (regdom_changes(pending_request->alpha2)) | ||
1360 | return REG_REQ_IGNORE; | ||
1361 | return REG_REQ_ALREADY_SET; | ||
1362 | } | ||
1363 | /* | ||
1364 | * Two consecutive Country IE hints on the same wiphy. | ||
1365 | * This should be picked up early by the driver/stack | ||
1366 | */ | ||
1367 | if (WARN_ON(regdom_changes(pending_request->alpha2))) | ||
1368 | return REG_REQ_OK; | ||
1369 | return REG_REQ_ALREADY_SET; | ||
1370 | } | ||
1371 | return REG_REQ_OK; | ||
1372 | case NL80211_REGDOM_SET_BY_DRIVER: | ||
1373 | if (lr->initiator == NL80211_REGDOM_SET_BY_CORE) { | ||
1374 | if (regdom_changes(pending_request->alpha2)) | ||
1375 | return REG_REQ_OK; | ||
1376 | return REG_REQ_ALREADY_SET; | ||
1377 | } | ||
1378 | 1397 | ||
1379 | /* | 1398 | /** |
1380 | * This would happen if you unplug and plug your card | 1399 | * reg_process_hint_core - process core regulatory requests |
1381 | * back in or if you add a new device for which the previously | 1400 | * @pending_request: a pending core regulatory request |
1382 | * loaded card also agrees on the regulatory domain. | 1401 | * |
1383 | */ | 1402 | * The wireless subsystem can use this function to process |
1384 | if (lr->initiator == NL80211_REGDOM_SET_BY_DRIVER && | 1403 | * a regulatory request issued by the regulatory core. |
1385 | !regdom_changes(pending_request->alpha2)) | 1404 | * |
1386 | return REG_REQ_ALREADY_SET; | 1405 | * Returns one of the different reg request treatment values. |
1406 | */ | ||
1407 | static enum reg_request_treatment | ||
1408 | reg_process_hint_core(struct regulatory_request *core_request) | ||
1409 | { | ||
1410 | |||
1411 | core_request->intersect = false; | ||
1412 | core_request->processed = false; | ||
1413 | |||
1414 | reg_update_last_request(core_request); | ||
1387 | 1415 | ||
1416 | return reg_call_crda(core_request); | ||
1417 | } | ||
1418 | |||
1419 | static enum reg_request_treatment | ||
1420 | __reg_process_hint_user(struct regulatory_request *user_request) | ||
1421 | { | ||
1422 | struct regulatory_request *lr = get_last_request(); | ||
1423 | |||
1424 | if (reg_request_cell_base(user_request)) | ||
1425 | return reg_ignore_cell_hint(user_request); | ||
1426 | |||
1427 | if (reg_request_cell_base(lr)) | ||
1428 | return REG_REQ_IGNORE; | ||
1429 | |||
1430 | if (lr->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) | ||
1388 | return REG_REQ_INTERSECT; | 1431 | return REG_REQ_INTERSECT; |
1389 | case NL80211_REGDOM_SET_BY_USER: | 1432 | /* |
1390 | if (reg_request_cell_base(pending_request)) | 1433 | * If the user knows better the user should set the regdom |
1391 | return reg_ignore_cell_hint(pending_request); | 1434 | * to their country before the IE is picked up |
1435 | */ | ||
1436 | if (lr->initiator == NL80211_REGDOM_SET_BY_USER && | ||
1437 | lr->intersect) | ||
1438 | return REG_REQ_IGNORE; | ||
1439 | /* | ||
1440 | * Process user requests only after previous user/driver/core | ||
1441 | * requests have been processed | ||
1442 | */ | ||
1443 | if ((lr->initiator == NL80211_REGDOM_SET_BY_CORE || | ||
1444 | lr->initiator == NL80211_REGDOM_SET_BY_DRIVER || | ||
1445 | lr->initiator == NL80211_REGDOM_SET_BY_USER) && | ||
1446 | regdom_changes(lr->alpha2)) | ||
1447 | return REG_REQ_IGNORE; | ||
1392 | 1448 | ||
1393 | if (reg_request_cell_base(lr)) | 1449 | if (!regdom_changes(user_request->alpha2)) |
1394 | return REG_REQ_IGNORE; | 1450 | return REG_REQ_ALREADY_SET; |
1395 | 1451 | ||
1396 | if (lr->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) | 1452 | return REG_REQ_OK; |
1397 | return REG_REQ_INTERSECT; | 1453 | } |
1398 | /* | ||
1399 | * If the user knows better the user should set the regdom | ||
1400 | * to their country before the IE is picked up | ||
1401 | */ | ||
1402 | if (lr->initiator == NL80211_REGDOM_SET_BY_USER && | ||
1403 | lr->intersect) | ||
1404 | return REG_REQ_IGNORE; | ||
1405 | /* | ||
1406 | * Process user requests only after previous user/driver/core | ||
1407 | * requests have been processed | ||
1408 | */ | ||
1409 | if ((lr->initiator == NL80211_REGDOM_SET_BY_CORE || | ||
1410 | lr->initiator == NL80211_REGDOM_SET_BY_DRIVER || | ||
1411 | lr->initiator == NL80211_REGDOM_SET_BY_USER) && | ||
1412 | regdom_changes(lr->alpha2)) | ||
1413 | return REG_REQ_IGNORE; | ||
1414 | 1454 | ||
1415 | if (!regdom_changes(pending_request->alpha2)) | 1455 | /** |
1416 | return REG_REQ_ALREADY_SET; | 1456 | * reg_process_hint_user - process user regulatory requests |
1457 | * @user_request: a pending user regulatory request | ||
1458 | * | ||
1459 | * The wireless subsystem can use this function to process | ||
1460 | * a regulatory request initiated by userspace. | ||
1461 | * | ||
1462 | * Returns one of the different reg request treatment values. | ||
1463 | */ | ||
1464 | static enum reg_request_treatment | ||
1465 | reg_process_hint_user(struct regulatory_request *user_request) | ||
1466 | { | ||
1467 | enum reg_request_treatment treatment; | ||
1417 | 1468 | ||
1418 | return REG_REQ_OK; | 1469 | treatment = __reg_process_hint_user(user_request); |
1470 | if (treatment == REG_REQ_IGNORE || | ||
1471 | treatment == REG_REQ_ALREADY_SET) { | ||
1472 | kfree(user_request); | ||
1473 | return treatment; | ||
1419 | } | 1474 | } |
1420 | 1475 | ||
1421 | return REG_REQ_IGNORE; | 1476 | user_request->intersect = treatment == REG_REQ_INTERSECT; |
1477 | user_request->processed = false; | ||
1478 | |||
1479 | reg_update_last_request(user_request); | ||
1480 | |||
1481 | user_alpha2[0] = user_request->alpha2[0]; | ||
1482 | user_alpha2[1] = user_request->alpha2[1]; | ||
1483 | |||
1484 | return reg_call_crda(user_request); | ||
1422 | } | 1485 | } |
1423 | 1486 | ||
1424 | static void reg_set_request_processed(void) | 1487 | static enum reg_request_treatment |
1488 | __reg_process_hint_driver(struct regulatory_request *driver_request) | ||
1425 | { | 1489 | { |
1426 | bool need_more_processing = false; | ||
1427 | struct regulatory_request *lr = get_last_request(); | 1490 | struct regulatory_request *lr = get_last_request(); |
1428 | 1491 | ||
1429 | lr->processed = true; | 1492 | if (lr->initiator == NL80211_REGDOM_SET_BY_CORE) { |
1430 | 1493 | if (regdom_changes(driver_request->alpha2)) | |
1431 | spin_lock(®_requests_lock); | 1494 | return REG_REQ_OK; |
1432 | if (!list_empty(®_requests_list)) | 1495 | return REG_REQ_ALREADY_SET; |
1433 | need_more_processing = true; | 1496 | } |
1434 | spin_unlock(®_requests_lock); | ||
1435 | 1497 | ||
1436 | if (lr->initiator == NL80211_REGDOM_SET_BY_USER) | 1498 | /* |
1437 | cancel_delayed_work(®_timeout); | 1499 | * This would happen if you unplug and plug your card |
1500 | * back in or if you add a new device for which the previously | ||
1501 | * loaded card also agrees on the regulatory domain. | ||
1502 | */ | ||
1503 | if (lr->initiator == NL80211_REGDOM_SET_BY_DRIVER && | ||
1504 | !regdom_changes(driver_request->alpha2)) | ||
1505 | return REG_REQ_ALREADY_SET; | ||
1438 | 1506 | ||
1439 | if (need_more_processing) | 1507 | return REG_REQ_INTERSECT; |
1440 | schedule_work(®_work); | ||
1441 | } | 1508 | } |
1442 | 1509 | ||
1443 | /** | 1510 | /** |
1444 | * __regulatory_hint - hint to the wireless core a regulatory domain | 1511 | * reg_process_hint_driver - process driver regulatory requests |
1445 | * @wiphy: if the hint comes from country information from an AP, this | 1512 | * @driver_request: a pending driver regulatory request |
1446 | * is required to be set to the wiphy that received the information | ||
1447 | * @pending_request: the regulatory request currently being processed | ||
1448 | * | 1513 | * |
1449 | * The Wireless subsystem can use this function to hint to the wireless core | 1514 | * The wireless subsystem can use this function to process |
1450 | * what it believes should be the current regulatory domain. | 1515 | * a regulatory request issued by an 802.11 driver. |
1451 | * | 1516 | * |
1452 | * Returns one of the different reg request treatment values. | 1517 | * Returns one of the different reg request treatment values. |
1453 | */ | 1518 | */ |
1454 | static enum reg_request_treatment | 1519 | static enum reg_request_treatment |
1455 | __regulatory_hint(struct wiphy *wiphy, | 1520 | reg_process_hint_driver(struct wiphy *wiphy, |
1456 | struct regulatory_request *pending_request) | 1521 | struct regulatory_request *driver_request) |
1457 | { | 1522 | { |
1458 | const struct ieee80211_regdomain *regd; | 1523 | const struct ieee80211_regdomain *regd; |
1459 | bool intersect = false; | ||
1460 | enum reg_request_treatment treatment; | 1524 | enum reg_request_treatment treatment; |
1461 | struct regulatory_request *lr; | ||
1462 | 1525 | ||
1463 | treatment = get_reg_request_treatment(wiphy, pending_request); | 1526 | treatment = __reg_process_hint_driver(driver_request); |
1464 | 1527 | ||
1465 | switch (treatment) { | 1528 | switch (treatment) { |
1466 | case REG_REQ_INTERSECT: | ||
1467 | if (pending_request->initiator == | ||
1468 | NL80211_REGDOM_SET_BY_DRIVER) { | ||
1469 | regd = reg_copy_regd(get_cfg80211_regdom()); | ||
1470 | if (IS_ERR(regd)) { | ||
1471 | kfree(pending_request); | ||
1472 | return PTR_ERR(regd); | ||
1473 | } | ||
1474 | rcu_assign_pointer(wiphy->regd, regd); | ||
1475 | } | ||
1476 | intersect = true; | ||
1477 | break; | ||
1478 | case REG_REQ_OK: | 1529 | case REG_REQ_OK: |
1479 | break; | 1530 | break; |
1480 | default: | 1531 | case REG_REQ_IGNORE: |
1481 | /* | 1532 | kfree(driver_request); |
1482 | * If the regulatory domain being requested by the | ||
1483 | * driver has already been set just copy it to the | ||
1484 | * wiphy | ||
1485 | */ | ||
1486 | if (treatment == REG_REQ_ALREADY_SET && | ||
1487 | pending_request->initiator == NL80211_REGDOM_SET_BY_DRIVER) { | ||
1488 | regd = reg_copy_regd(get_cfg80211_regdom()); | ||
1489 | if (IS_ERR(regd)) { | ||
1490 | kfree(pending_request); | ||
1491 | return REG_REQ_IGNORE; | ||
1492 | } | ||
1493 | treatment = REG_REQ_ALREADY_SET; | ||
1494 | rcu_assign_pointer(wiphy->regd, regd); | ||
1495 | goto new_request; | ||
1496 | } | ||
1497 | kfree(pending_request); | ||
1498 | return treatment; | 1533 | return treatment; |
1534 | case REG_REQ_INTERSECT: | ||
1535 | /* fall through */ | ||
1536 | case REG_REQ_ALREADY_SET: | ||
1537 | regd = reg_copy_regd(get_cfg80211_regdom()); | ||
1538 | if (IS_ERR(regd)) { | ||
1539 | kfree(driver_request); | ||
1540 | return REG_REQ_IGNORE; | ||
1541 | } | ||
1542 | rcu_assign_pointer(wiphy->regd, regd); | ||
1499 | } | 1543 | } |
1500 | 1544 | ||
1501 | new_request: | ||
1502 | lr = get_last_request(); | ||
1503 | if (lr != &core_request_world && lr) | ||
1504 | kfree_rcu(lr, rcu_head); | ||
1505 | 1545 | ||
1506 | pending_request->intersect = intersect; | 1546 | driver_request->intersect = treatment == REG_REQ_INTERSECT; |
1507 | pending_request->processed = false; | 1547 | driver_request->processed = false; |
1508 | rcu_assign_pointer(last_request, pending_request); | ||
1509 | lr = pending_request; | ||
1510 | 1548 | ||
1511 | pending_request = NULL; | 1549 | reg_update_last_request(driver_request); |
1512 | 1550 | ||
1513 | if (lr->initiator == NL80211_REGDOM_SET_BY_USER) { | 1551 | /* |
1514 | user_alpha2[0] = lr->alpha2[0]; | 1552 | * Since CRDA will not be called in this case as we already |
1515 | user_alpha2[1] = lr->alpha2[1]; | 1553 | * have applied the requested regulatory domain before we just |
1554 | * inform userspace we have processed the request | ||
1555 | */ | ||
1556 | if (treatment == REG_REQ_ALREADY_SET) { | ||
1557 | nl80211_send_reg_change_event(driver_request); | ||
1558 | reg_set_request_processed(); | ||
1559 | return treatment; | ||
1516 | } | 1560 | } |
1517 | 1561 | ||
1518 | /* When r == REG_REQ_INTERSECT we do need to call CRDA */ | 1562 | return reg_call_crda(driver_request); |
1519 | if (treatment != REG_REQ_OK && treatment != REG_REQ_INTERSECT) { | 1563 | } |
1564 | |||
1565 | static enum reg_request_treatment | ||
1566 | __reg_process_hint_country_ie(struct wiphy *wiphy, | ||
1567 | struct regulatory_request *country_ie_request) | ||
1568 | { | ||
1569 | struct wiphy *last_wiphy = NULL; | ||
1570 | struct regulatory_request *lr = get_last_request(); | ||
1571 | |||
1572 | if (reg_request_cell_base(lr)) { | ||
1573 | /* Trust a Cell base station over the AP's country IE */ | ||
1574 | if (regdom_changes(country_ie_request->alpha2)) | ||
1575 | return REG_REQ_IGNORE; | ||
1576 | return REG_REQ_ALREADY_SET; | ||
1577 | } else { | ||
1578 | if (wiphy->regulatory_flags & REGULATORY_COUNTRY_IE_IGNORE) | ||
1579 | return REG_REQ_IGNORE; | ||
1580 | } | ||
1581 | |||
1582 | if (unlikely(!is_an_alpha2(country_ie_request->alpha2))) | ||
1583 | return -EINVAL; | ||
1584 | |||
1585 | if (lr->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE) | ||
1586 | return REG_REQ_OK; | ||
1587 | |||
1588 | last_wiphy = wiphy_idx_to_wiphy(lr->wiphy_idx); | ||
1589 | |||
1590 | if (last_wiphy != wiphy) { | ||
1520 | /* | 1591 | /* |
1521 | * Since CRDA will not be called in this case as we already | 1592 | * Two cards with two APs claiming different |
1522 | * have applied the requested regulatory domain before we just | 1593 | * Country IE alpha2s. We could |
1523 | * inform userspace we have processed the request | 1594 | * intersect them, but that seems unlikely |
1595 | * to be correct. Reject second one for now. | ||
1524 | */ | 1596 | */ |
1525 | if (treatment == REG_REQ_ALREADY_SET) { | 1597 | if (regdom_changes(country_ie_request->alpha2)) |
1526 | nl80211_send_reg_change_event(lr); | 1598 | return REG_REQ_IGNORE; |
1527 | reg_set_request_processed(); | 1599 | return REG_REQ_ALREADY_SET; |
1528 | } | ||
1529 | return treatment; | ||
1530 | } | 1600 | } |
1601 | /* | ||
1602 | * Two consecutive Country IE hints on the same wiphy. | ||
1603 | * This should be picked up early by the driver/stack | ||
1604 | */ | ||
1605 | if (WARN_ON(regdom_changes(country_ie_request->alpha2))) | ||
1606 | return REG_REQ_OK; | ||
1607 | return REG_REQ_ALREADY_SET; | ||
1608 | } | ||
1609 | |||
1610 | /** | ||
1611 | * reg_process_hint_country_ie - process regulatory requests from country IEs | ||
1612 | * @country_ie_request: a regulatory request from a country IE | ||
1613 | * | ||
1614 | * The wireless subsystem can use this function to process | ||
1615 | * a regulatory request issued by a country Information Element. | ||
1616 | * | ||
1617 | * Returns one of the different reg request treatment values. | ||
1618 | */ | ||
1619 | static enum reg_request_treatment | ||
1620 | reg_process_hint_country_ie(struct wiphy *wiphy, | ||
1621 | struct regulatory_request *country_ie_request) | ||
1622 | { | ||
1623 | enum reg_request_treatment treatment; | ||
1624 | |||
1625 | treatment = __reg_process_hint_country_ie(wiphy, country_ie_request); | ||
1531 | 1626 | ||
1532 | if (call_crda(lr->alpha2)) | 1627 | switch (treatment) { |
1628 | case REG_REQ_OK: | ||
1629 | break; | ||
1630 | case REG_REQ_IGNORE: | ||
1631 | /* fall through */ | ||
1632 | case REG_REQ_ALREADY_SET: | ||
1633 | kfree(country_ie_request); | ||
1634 | return treatment; | ||
1635 | case REG_REQ_INTERSECT: | ||
1636 | kfree(country_ie_request); | ||
1637 | /* | ||
1638 | * This doesn't happen yet, not sure we | ||
1639 | * ever want to support it for this case. | ||
1640 | */ | ||
1641 | WARN_ONCE(1, "Unexpected intersection for country IEs"); | ||
1533 | return REG_REQ_IGNORE; | 1642 | return REG_REQ_IGNORE; |
1534 | return REG_REQ_OK; | 1643 | } |
1644 | |||
1645 | country_ie_request->intersect = false; | ||
1646 | country_ie_request->processed = false; | ||
1647 | |||
1648 | reg_update_last_request(country_ie_request); | ||
1649 | |||
1650 | return reg_call_crda(country_ie_request); | ||
1535 | } | 1651 | } |
1536 | 1652 | ||
1537 | /* This processes *all* regulatory hints */ | 1653 | /* This processes *all* regulatory hints */ |
1538 | static void reg_process_hint(struct regulatory_request *reg_request, | 1654 | static void reg_process_hint(struct regulatory_request *reg_request) |
1539 | enum nl80211_reg_initiator reg_initiator) | ||
1540 | { | 1655 | { |
1541 | struct wiphy *wiphy = NULL; | 1656 | struct wiphy *wiphy = NULL; |
1657 | enum reg_request_treatment treatment; | ||
1542 | 1658 | ||
1543 | if (WARN_ON(!reg_request->alpha2)) | 1659 | if (WARN_ON(!reg_request->alpha2)) |
1544 | return; | 1660 | return; |
@@ -1546,23 +1662,37 @@ static void reg_process_hint(struct regulatory_request *reg_request, | |||
1546 | if (reg_request->wiphy_idx != WIPHY_IDX_INVALID) | 1662 | if (reg_request->wiphy_idx != WIPHY_IDX_INVALID) |
1547 | wiphy = wiphy_idx_to_wiphy(reg_request->wiphy_idx); | 1663 | wiphy = wiphy_idx_to_wiphy(reg_request->wiphy_idx); |
1548 | 1664 | ||
1549 | if (reg_initiator == NL80211_REGDOM_SET_BY_DRIVER && !wiphy) { | 1665 | if (reg_request->initiator == NL80211_REGDOM_SET_BY_DRIVER && !wiphy) { |
1550 | kfree(reg_request); | 1666 | kfree(reg_request); |
1551 | return; | 1667 | return; |
1552 | } | 1668 | } |
1553 | 1669 | ||
1554 | switch (__regulatory_hint(wiphy, reg_request)) { | 1670 | switch (reg_request->initiator) { |
1555 | case REG_REQ_ALREADY_SET: | 1671 | case NL80211_REGDOM_SET_BY_CORE: |
1556 | /* This is required so that the orig_* parameters are saved */ | 1672 | reg_process_hint_core(reg_request); |
1557 | if (wiphy && wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY) | 1673 | return; |
1558 | wiphy_update_regulatory(wiphy, reg_initiator); | 1674 | case NL80211_REGDOM_SET_BY_USER: |
1675 | treatment = reg_process_hint_user(reg_request); | ||
1676 | if (treatment == REG_REQ_OK || | ||
1677 | treatment == REG_REQ_ALREADY_SET) | ||
1678 | return; | ||
1679 | schedule_delayed_work(®_timeout, msecs_to_jiffies(3142)); | ||
1680 | return; | ||
1681 | case NL80211_REGDOM_SET_BY_DRIVER: | ||
1682 | treatment = reg_process_hint_driver(wiphy, reg_request); | ||
1559 | break; | 1683 | break; |
1560 | default: | 1684 | case NL80211_REGDOM_SET_BY_COUNTRY_IE: |
1561 | if (reg_initiator == NL80211_REGDOM_SET_BY_USER) | 1685 | treatment = reg_process_hint_country_ie(wiphy, reg_request); |
1562 | schedule_delayed_work(®_timeout, | ||
1563 | msecs_to_jiffies(3142)); | ||
1564 | break; | 1686 | break; |
1687 | default: | ||
1688 | WARN(1, "invalid initiator %d\n", reg_request->initiator); | ||
1689 | return; | ||
1565 | } | 1690 | } |
1691 | |||
1692 | /* This is required so that the orig_* parameters are saved */ | ||
1693 | if (treatment == REG_REQ_ALREADY_SET && wiphy && | ||
1694 | wiphy->regulatory_flags & REGULATORY_STRICT_REG) | ||
1695 | wiphy_update_regulatory(wiphy, reg_request->initiator); | ||
1566 | } | 1696 | } |
1567 | 1697 | ||
1568 | /* | 1698 | /* |
@@ -1596,7 +1726,7 @@ static void reg_process_pending_hints(void) | |||
1596 | 1726 | ||
1597 | spin_unlock(®_requests_lock); | 1727 | spin_unlock(®_requests_lock); |
1598 | 1728 | ||
1599 | reg_process_hint(reg_request, reg_request->initiator); | 1729 | reg_process_hint(reg_request); |
1600 | } | 1730 | } |
1601 | 1731 | ||
1602 | /* Processes beacon hints -- this has nothing to do with country IEs */ | 1732 | /* Processes beacon hints -- this has nothing to do with country IEs */ |
@@ -1888,7 +2018,7 @@ static void restore_regulatory_settings(bool reset_user) | |||
1888 | world_alpha2[1] = cfg80211_world_regdom->alpha2[1]; | 2018 | world_alpha2[1] = cfg80211_world_regdom->alpha2[1]; |
1889 | 2019 | ||
1890 | list_for_each_entry(rdev, &cfg80211_rdev_list, list) { | 2020 | list_for_each_entry(rdev, &cfg80211_rdev_list, list) { |
1891 | if (rdev->wiphy.flags & WIPHY_FLAG_CUSTOM_REGULATORY) | 2021 | if (rdev->wiphy.regulatory_flags & REGULATORY_CUSTOM_REG) |
1892 | restore_custom_reg_settings(&rdev->wiphy); | 2022 | restore_custom_reg_settings(&rdev->wiphy); |
1893 | } | 2023 | } |
1894 | 2024 | ||
@@ -2016,7 +2146,7 @@ static void print_rd_rules(const struct ieee80211_regdomain *rd) | |||
2016 | } | 2146 | } |
2017 | } | 2147 | } |
2018 | 2148 | ||
2019 | bool reg_supported_dfs_region(u8 dfs_region) | 2149 | bool reg_supported_dfs_region(enum nl80211_dfs_regions dfs_region) |
2020 | { | 2150 | { |
2021 | switch (dfs_region) { | 2151 | switch (dfs_region) { |
2022 | case NL80211_DFS_UNSET: | 2152 | case NL80211_DFS_UNSET: |
@@ -2031,27 +2161,6 @@ bool reg_supported_dfs_region(u8 dfs_region) | |||
2031 | } | 2161 | } |
2032 | } | 2162 | } |
2033 | 2163 | ||
2034 | static void print_dfs_region(u8 dfs_region) | ||
2035 | { | ||
2036 | if (!dfs_region) | ||
2037 | return; | ||
2038 | |||
2039 | switch (dfs_region) { | ||
2040 | case NL80211_DFS_FCC: | ||
2041 | pr_info(" DFS Master region FCC"); | ||
2042 | break; | ||
2043 | case NL80211_DFS_ETSI: | ||
2044 | pr_info(" DFS Master region ETSI"); | ||
2045 | break; | ||
2046 | case NL80211_DFS_JP: | ||
2047 | pr_info(" DFS Master region JP"); | ||
2048 | break; | ||
2049 | default: | ||
2050 | pr_info(" DFS Master region Unknown"); | ||
2051 | break; | ||
2052 | } | ||
2053 | } | ||
2054 | |||
2055 | static void print_regdomain(const struct ieee80211_regdomain *rd) | 2164 | static void print_regdomain(const struct ieee80211_regdomain *rd) |
2056 | { | 2165 | { |
2057 | struct regulatory_request *lr = get_last_request(); | 2166 | struct regulatory_request *lr = get_last_request(); |
@@ -2083,7 +2192,7 @@ static void print_regdomain(const struct ieee80211_regdomain *rd) | |||
2083 | } | 2192 | } |
2084 | } | 2193 | } |
2085 | 2194 | ||
2086 | print_dfs_region(rd->dfs_region); | 2195 | pr_info(" DFS Master region: %s", reg_dfs_region_str(rd->dfs_region)); |
2087 | print_rd_rules(rd); | 2196 | print_rd_rules(rd); |
2088 | } | 2197 | } |
2089 | 2198 | ||
@@ -2093,48 +2202,60 @@ static void print_regdomain_info(const struct ieee80211_regdomain *rd) | |||
2093 | print_rd_rules(rd); | 2202 | print_rd_rules(rd); |
2094 | } | 2203 | } |
2095 | 2204 | ||
2096 | /* Takes ownership of rd only if it doesn't fail */ | 2205 | static int reg_set_rd_core(const struct ieee80211_regdomain *rd) |
2097 | static int __set_regdom(const struct ieee80211_regdomain *rd) | 2206 | { |
2207 | if (!is_world_regdom(rd->alpha2)) | ||
2208 | return -EINVAL; | ||
2209 | update_world_regdomain(rd); | ||
2210 | return 0; | ||
2211 | } | ||
2212 | |||
2213 | static int reg_set_rd_user(const struct ieee80211_regdomain *rd, | ||
2214 | struct regulatory_request *user_request) | ||
2098 | { | 2215 | { |
2099 | const struct ieee80211_regdomain *regd; | ||
2100 | const struct ieee80211_regdomain *intersected_rd = NULL; | 2216 | const struct ieee80211_regdomain *intersected_rd = NULL; |
2101 | struct wiphy *request_wiphy; | ||
2102 | struct regulatory_request *lr = get_last_request(); | ||
2103 | 2217 | ||
2104 | /* Some basic sanity checks first */ | 2218 | if (is_world_regdom(rd->alpha2)) |
2219 | return -EINVAL; | ||
2220 | |||
2221 | if (!regdom_changes(rd->alpha2)) | ||
2222 | return -EALREADY; | ||
2105 | 2223 | ||
2106 | if (!reg_is_valid_request(rd->alpha2)) | 2224 | if (!is_valid_rd(rd)) { |
2225 | pr_err("Invalid regulatory domain detected:\n"); | ||
2226 | print_regdomain_info(rd); | ||
2107 | return -EINVAL; | 2227 | return -EINVAL; |
2228 | } | ||
2108 | 2229 | ||
2109 | if (is_world_regdom(rd->alpha2)) { | 2230 | if (!user_request->intersect) { |
2110 | update_world_regdomain(rd); | 2231 | reset_regdomains(false, rd); |
2111 | return 0; | 2232 | return 0; |
2112 | } | 2233 | } |
2113 | 2234 | ||
2114 | if (!is_alpha2_set(rd->alpha2) && !is_an_alpha2(rd->alpha2) && | 2235 | intersected_rd = regdom_intersect(rd, get_cfg80211_regdom()); |
2115 | !is_unknown_alpha2(rd->alpha2)) | 2236 | if (!intersected_rd) |
2116 | return -EINVAL; | 2237 | return -EINVAL; |
2117 | 2238 | ||
2118 | /* | 2239 | kfree(rd); |
2119 | * Lets only bother proceeding on the same alpha2 if the current | 2240 | rd = NULL; |
2120 | * rd is non static (it means CRDA was present and was used last) | 2241 | reset_regdomains(false, intersected_rd); |
2121 | * and the pending request came in from a country IE | ||
2122 | */ | ||
2123 | if (lr->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE) { | ||
2124 | /* | ||
2125 | * If someone else asked us to change the rd lets only bother | ||
2126 | * checking if the alpha2 changes if CRDA was already called | ||
2127 | */ | ||
2128 | if (!regdom_changes(rd->alpha2)) | ||
2129 | return -EALREADY; | ||
2130 | } | ||
2131 | 2242 | ||
2132 | /* | 2243 | return 0; |
2133 | * Now lets set the regulatory domain, update all driver channels | 2244 | } |
2134 | * and finally inform them of what we have done, in case they want | 2245 | |
2135 | * to review or adjust their own settings based on their own | 2246 | static int reg_set_rd_driver(const struct ieee80211_regdomain *rd, |
2136 | * internal EEPROM data | 2247 | struct regulatory_request *driver_request) |
2137 | */ | 2248 | { |
2249 | const struct ieee80211_regdomain *regd; | ||
2250 | const struct ieee80211_regdomain *intersected_rd = NULL; | ||
2251 | const struct ieee80211_regdomain *tmp; | ||
2252 | struct wiphy *request_wiphy; | ||
2253 | |||
2254 | if (is_world_regdom(rd->alpha2)) | ||
2255 | return -EINVAL; | ||
2256 | |||
2257 | if (!regdom_changes(rd->alpha2)) | ||
2258 | return -EALREADY; | ||
2138 | 2259 | ||
2139 | if (!is_valid_rd(rd)) { | 2260 | if (!is_valid_rd(rd)) { |
2140 | pr_err("Invalid regulatory domain detected:\n"); | 2261 | pr_err("Invalid regulatory domain detected:\n"); |
@@ -2142,29 +2263,13 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) | |||
2142 | return -EINVAL; | 2263 | return -EINVAL; |
2143 | } | 2264 | } |
2144 | 2265 | ||
2145 | request_wiphy = wiphy_idx_to_wiphy(lr->wiphy_idx); | 2266 | request_wiphy = wiphy_idx_to_wiphy(driver_request->wiphy_idx); |
2146 | if (!request_wiphy && | 2267 | if (!request_wiphy) { |
2147 | (lr->initiator == NL80211_REGDOM_SET_BY_DRIVER || | ||
2148 | lr->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE)) { | ||
2149 | schedule_delayed_work(®_timeout, 0); | 2268 | schedule_delayed_work(®_timeout, 0); |
2150 | return -ENODEV; | 2269 | return -ENODEV; |
2151 | } | 2270 | } |
2152 | 2271 | ||
2153 | if (!lr->intersect) { | 2272 | if (!driver_request->intersect) { |
2154 | if (lr->initiator != NL80211_REGDOM_SET_BY_DRIVER) { | ||
2155 | reset_regdomains(false, rd); | ||
2156 | return 0; | ||
2157 | } | ||
2158 | |||
2159 | /* | ||
2160 | * For a driver hint, lets copy the regulatory domain the | ||
2161 | * driver wanted to the wiphy to deal with conflicts | ||
2162 | */ | ||
2163 | |||
2164 | /* | ||
2165 | * Userspace could have sent two replies with only | ||
2166 | * one kernel request. | ||
2167 | */ | ||
2168 | if (request_wiphy->regd) | 2273 | if (request_wiphy->regd) |
2169 | return -EALREADY; | 2274 | return -EALREADY; |
2170 | 2275 | ||
@@ -2177,38 +2282,59 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) | |||
2177 | return 0; | 2282 | return 0; |
2178 | } | 2283 | } |
2179 | 2284 | ||
2180 | /* Intersection requires a bit more work */ | 2285 | intersected_rd = regdom_intersect(rd, get_cfg80211_regdom()); |
2286 | if (!intersected_rd) | ||
2287 | return -EINVAL; | ||
2181 | 2288 | ||
2182 | if (lr->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE) { | 2289 | /* |
2183 | intersected_rd = regdom_intersect(rd, get_cfg80211_regdom()); | 2290 | * We can trash what CRDA provided now. |
2184 | if (!intersected_rd) | 2291 | * However if a driver requested this specific regulatory |
2185 | return -EINVAL; | 2292 | * domain we keep it for its private use |
2293 | */ | ||
2294 | tmp = get_wiphy_regdom(request_wiphy); | ||
2295 | rcu_assign_pointer(request_wiphy->regd, rd); | ||
2296 | rcu_free_regdom(tmp); | ||
2186 | 2297 | ||
2187 | /* | 2298 | rd = NULL; |
2188 | * We can trash what CRDA provided now. | ||
2189 | * However if a driver requested this specific regulatory | ||
2190 | * domain we keep it for its private use | ||
2191 | */ | ||
2192 | if (lr->initiator == NL80211_REGDOM_SET_BY_DRIVER) { | ||
2193 | const struct ieee80211_regdomain *tmp; | ||
2194 | 2299 | ||
2195 | tmp = get_wiphy_regdom(request_wiphy); | 2300 | reset_regdomains(false, intersected_rd); |
2196 | rcu_assign_pointer(request_wiphy->regd, rd); | ||
2197 | rcu_free_regdom(tmp); | ||
2198 | } else { | ||
2199 | kfree(rd); | ||
2200 | } | ||
2201 | 2301 | ||
2202 | rd = NULL; | 2302 | return 0; |
2303 | } | ||
2203 | 2304 | ||
2204 | reset_regdomains(false, intersected_rd); | 2305 | static int reg_set_rd_country_ie(const struct ieee80211_regdomain *rd, |
2306 | struct regulatory_request *country_ie_request) | ||
2307 | { | ||
2308 | struct wiphy *request_wiphy; | ||
2205 | 2309 | ||
2206 | return 0; | 2310 | if (!is_alpha2_set(rd->alpha2) && !is_an_alpha2(rd->alpha2) && |
2311 | !is_unknown_alpha2(rd->alpha2)) | ||
2312 | return -EINVAL; | ||
2313 | |||
2314 | /* | ||
2315 | * Lets only bother proceeding on the same alpha2 if the current | ||
2316 | * rd is non static (it means CRDA was present and was used last) | ||
2317 | * and the pending request came in from a country IE | ||
2318 | */ | ||
2319 | |||
2320 | if (!is_valid_rd(rd)) { | ||
2321 | pr_err("Invalid regulatory domain detected:\n"); | ||
2322 | print_regdomain_info(rd); | ||
2323 | return -EINVAL; | ||
2207 | } | 2324 | } |
2208 | 2325 | ||
2209 | return -EINVAL; | 2326 | request_wiphy = wiphy_idx_to_wiphy(country_ie_request->wiphy_idx); |
2210 | } | 2327 | if (!request_wiphy) { |
2328 | schedule_delayed_work(®_timeout, 0); | ||
2329 | return -ENODEV; | ||
2330 | } | ||
2331 | |||
2332 | if (country_ie_request->intersect) | ||
2333 | return -EINVAL; | ||
2211 | 2334 | ||
2335 | reset_regdomains(false, rd); | ||
2336 | return 0; | ||
2337 | } | ||
2212 | 2338 | ||
2213 | /* | 2339 | /* |
2214 | * Use this call to set the current regulatory domain. Conflicts with | 2340 | * Use this call to set the current regulatory domain. Conflicts with |
@@ -2220,10 +2346,32 @@ int set_regdom(const struct ieee80211_regdomain *rd) | |||
2220 | struct regulatory_request *lr; | 2346 | struct regulatory_request *lr; |
2221 | int r; | 2347 | int r; |
2222 | 2348 | ||
2349 | if (!reg_is_valid_request(rd->alpha2)) { | ||
2350 | kfree(rd); | ||
2351 | return -EINVAL; | ||
2352 | } | ||
2353 | |||
2223 | lr = get_last_request(); | 2354 | lr = get_last_request(); |
2224 | 2355 | ||
2225 | /* Note that this doesn't update the wiphys, this is done below */ | 2356 | /* Note that this doesn't update the wiphys, this is done below */ |
2226 | r = __set_regdom(rd); | 2357 | switch (lr->initiator) { |
2358 | case NL80211_REGDOM_SET_BY_CORE: | ||
2359 | r = reg_set_rd_core(rd); | ||
2360 | break; | ||
2361 | case NL80211_REGDOM_SET_BY_USER: | ||
2362 | r = reg_set_rd_user(rd, lr); | ||
2363 | break; | ||
2364 | case NL80211_REGDOM_SET_BY_DRIVER: | ||
2365 | r = reg_set_rd_driver(rd, lr); | ||
2366 | break; | ||
2367 | case NL80211_REGDOM_SET_BY_COUNTRY_IE: | ||
2368 | r = reg_set_rd_country_ie(rd, lr); | ||
2369 | break; | ||
2370 | default: | ||
2371 | WARN(1, "invalid initiator %d\n", lr->initiator); | ||
2372 | return -EINVAL; | ||
2373 | } | ||
2374 | |||
2227 | if (r) { | 2375 | if (r) { |
2228 | if (r == -EALREADY) | 2376 | if (r == -EALREADY) |
2229 | reg_set_request_processed(); | 2377 | reg_set_request_processed(); |
diff --git a/net/wireless/reg.h b/net/wireless/reg.h index 9677e3c13da9..cc4c2c0a6723 100644 --- a/net/wireless/reg.h +++ b/net/wireless/reg.h | |||
@@ -18,8 +18,9 @@ | |||
18 | 18 | ||
19 | extern const struct ieee80211_regdomain __rcu *cfg80211_regdomain; | 19 | extern const struct ieee80211_regdomain __rcu *cfg80211_regdomain; |
20 | 20 | ||
21 | bool reg_is_valid_request(const char *alpha2); | ||
21 | bool is_world_regdom(const char *alpha2); | 22 | bool is_world_regdom(const char *alpha2); |
22 | bool reg_supported_dfs_region(u8 dfs_region); | 23 | bool reg_supported_dfs_region(enum nl80211_dfs_regions dfs_region); |
23 | 24 | ||
24 | int regulatory_hint_user(const char *alpha2, | 25 | int regulatory_hint_user(const char *alpha2, |
25 | enum nl80211_user_reg_hint_type user_reg_hint_type); | 26 | enum nl80211_user_reg_hint_type user_reg_hint_type); |
diff --git a/net/wireless/trace.h b/net/wireless/trace.h index ba5f0d6614d5..f7aa7a72d9bc 100644 --- a/net/wireless/trace.h +++ b/net/wireless/trace.h | |||
@@ -1653,9 +1653,8 @@ TRACE_EVENT(rdev_cancel_remain_on_channel, | |||
1653 | 1653 | ||
1654 | TRACE_EVENT(rdev_mgmt_tx, | 1654 | TRACE_EVENT(rdev_mgmt_tx, |
1655 | TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev, | 1655 | TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev, |
1656 | struct ieee80211_channel *chan, bool offchan, | 1656 | struct cfg80211_mgmt_tx_params *params), |
1657 | unsigned int wait, bool no_cck, bool dont_wait_for_ack), | 1657 | TP_ARGS(wiphy, wdev, params), |
1658 | TP_ARGS(wiphy, wdev, chan, offchan, wait, no_cck, dont_wait_for_ack), | ||
1659 | TP_STRUCT__entry( | 1658 | TP_STRUCT__entry( |
1660 | WIPHY_ENTRY | 1659 | WIPHY_ENTRY |
1661 | WDEV_ENTRY | 1660 | WDEV_ENTRY |
@@ -1668,11 +1667,11 @@ TRACE_EVENT(rdev_mgmt_tx, | |||
1668 | TP_fast_assign( | 1667 | TP_fast_assign( |
1669 | WIPHY_ASSIGN; | 1668 | WIPHY_ASSIGN; |
1670 | WDEV_ASSIGN; | 1669 | WDEV_ASSIGN; |
1671 | CHAN_ASSIGN(chan); | 1670 | CHAN_ASSIGN(params->chan); |
1672 | __entry->offchan = offchan; | 1671 | __entry->offchan = params->offchan; |
1673 | __entry->wait = wait; | 1672 | __entry->wait = params->wait; |
1674 | __entry->no_cck = no_cck; | 1673 | __entry->no_cck = params->no_cck; |
1675 | __entry->dont_wait_for_ack = dont_wait_for_ack; | 1674 | __entry->dont_wait_for_ack = params->dont_wait_for_ack; |
1676 | ), | 1675 | ), |
1677 | TP_printk(WIPHY_PR_FMT ", " WDEV_PR_FMT ", " CHAN_PR_FMT ", offchan: %s," | 1676 | TP_printk(WIPHY_PR_FMT ", " WDEV_PR_FMT ", " CHAN_PR_FMT ", offchan: %s," |
1678 | " wait: %u, no cck: %s, dont wait for ack: %s", | 1677 | " wait: %u, no cck: %s, dont wait for ack: %s", |