diff options
Diffstat (limited to 'net/wireless/chan.c')
-rw-r--r-- | net/wireless/chan.c | 203 |
1 files changed, 178 insertions, 25 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) { |