| 1 | |
| 2 | |
| 3 | * This file contains helper code to handle channel
|
| 4 | * settings and keeping track of what is possible at
|
| 5 | * any point in time.
|
| 6 | *
|
| 7 | * Copyright 2009 Johannes Berg <[email protected]>
|
| 8 | * Copyright 2013-2014 Intel Mobile Communications GmbH
|
| 9 | * Copyright 2018-2026 Intel Corporation
|
| 10 |
|
| 11 |
|
| 12 | #include <linux/export.h>
|
| 13 | #include <linux/bitfield.h>
|
| 14 | #include <net/cfg80211.h>
|
| 15 | #include "core.h"
|
| 16 | #include "rdev-ops.h"
|
| 17 |
|
| 18 | static bool cfg80211_valid_60g_freq(u32 freq)
|
| 19 | {
|
| 20 | return freq >= 58320 && freq <= 70200;
|
| 21 | }
|
| 22 |
|
| 23 | void cfg80211_chandef_create(struct cfg80211_chan_def *chandef,
|
| 24 | struct ieee80211_channel *chan,
|
| 25 | enum nl80211_channel_type chan_type)
|
| 26 | {
|
| 27 | if (WARN_ON(!chan))
|
| 28 | return;
|
| 29 |
|
| 30 | *chandef = (struct cfg80211_chan_def) {
|
| 31 | .chan = chan,
|
| 32 | };
|
| 33 |
|
| 34 | WARN_ON(chan->band == NL80211_BAND_60GHZ ||
|
| 35 | chan->band == NL80211_BAND_S1GHZ);
|
| 36 |
|
| 37 | switch (chan_type) {
|
| 38 | case NL80211_CHAN_NO_HT:
|
| 39 | chandef->width = NL80211_CHAN_WIDTH_20_NOHT;
|
| 40 | chandef->center_freq1 = chan->center_freq;
|
| 41 | break;
|
| 42 | case NL80211_CHAN_HT20:
|
| 43 | chandef->width = NL80211_CHAN_WIDTH_20;
|
| 44 | chandef->center_freq1 = chan->center_freq;
|
| 45 | break;
|
| 46 | case NL80211_CHAN_HT40PLUS:
|
| 47 | chandef->width = NL80211_CHAN_WIDTH_40;
|
| 48 | chandef->center_freq1 = chan->center_freq + 10;
|
| 49 | break;
|
| 50 | case NL80211_CHAN_HT40MINUS:
|
| 51 | chandef->width = NL80211_CHAN_WIDTH_40;
|
| 52 | chandef->center_freq1 = chan->center_freq - 10;
|
| 53 | break;
|
| 54 | default:
|
| 55 | WARN_ON(1);
|
| 56 | }
|
| 57 | }
|
| 58 | EXPORT_SYMBOL(cfg80211_chandef_create);
|
| 59 |
|
| 60 | static u32 cfg80211_get_start_freq(const struct cfg80211_chan_def *chandef,
|
| 61 | u32 cf)
|
| 62 | {
|
| 63 | u32 start_freq, center_freq, bandwidth;
|
| 64 |
|
| 65 | center_freq = MHZ_TO_KHZ((cf == 1) ?
|
| 66 | chandef->center_freq1 : chandef->center_freq2);
|
| 67 | bandwidth = MHZ_TO_KHZ(cfg80211_chandef_get_width(chandef));
|
| 68 |
|
| 69 | if (bandwidth <= MHZ_TO_KHZ(20))
|
| 70 | start_freq = center_freq;
|
| 71 | else
|
| 72 | start_freq = center_freq - bandwidth / 2 + MHZ_TO_KHZ(10);
|
| 73 |
|
| 74 | return start_freq;
|
| 75 | }
|
| 76 |
|
| 77 | static u32 cfg80211_get_end_freq(const struct cfg80211_chan_def *chandef,
|
| 78 | u32 cf)
|
| 79 | {
|
| 80 | u32 end_freq, center_freq, bandwidth;
|
| 81 |
|
| 82 | center_freq = MHZ_TO_KHZ((cf == 1) ?
|
| 83 | chandef->center_freq1 : chandef->center_freq2);
|
| 84 | bandwidth = MHZ_TO_KHZ(cfg80211_chandef_get_width(chandef));
|
| 85 |
|
| 86 | if (bandwidth <= MHZ_TO_KHZ(20))
|
| 87 | end_freq = center_freq;
|
| 88 | else
|
| 89 | end_freq = center_freq + bandwidth / 2 - MHZ_TO_KHZ(10);
|
| 90 |
|
| 91 | return end_freq;
|
| 92 | }
|
| 93 |
|
| 94 | #define for_each_subchan(chandef, freq, cf) \
|
| 95 | for (u32 punctured = chandef->punctured, \
|
| 96 | cf = 1, freq = cfg80211_get_start_freq(chandef, cf); \
|
| 97 | freq <= cfg80211_get_end_freq(chandef, cf); \
|
| 98 | freq += MHZ_TO_KHZ(20), \
|
| 99 | ((cf == 1 && chandef->center_freq2 != 0 && \
|
| 100 | freq > cfg80211_get_end_freq(chandef, cf)) ? \
|
| 101 | (cf++, freq = cfg80211_get_start_freq(chandef, cf), \
|
| 102 | punctured = 0) : (punctured >>= 1))) \
|
| 103 | if (!(punctured & 1))
|
| 104 |
|
| 105 | #define for_each_s1g_subchan(chandef, freq_khz) \
|
| 106 | for (freq_khz = cfg80211_s1g_get_start_freq_khz(chandef); \
|
| 107 | freq_khz <= cfg80211_s1g_get_end_freq_khz(chandef); \
|
| 108 | freq_khz += MHZ_TO_KHZ(1))
|
| 109 |
|
| 110 | struct cfg80211_per_bw_puncturing_values {
|
| 111 | u8 len;
|
| 112 | const u16 *valid_values;
|
| 113 | };
|
| 114 |
|
| 115 | static const u16 puncturing_values_80mhz[] = {
|
| 116 | 0x8, 0x4, 0x2, 0x1
|
| 117 | };
|
| 118 |
|
| 119 | static const u16 puncturing_values_160mhz[] = {
|
| 120 | 0x80, 0x40, 0x20, 0x10, 0x8, 0x4, 0x2, 0x1, 0xc0, 0x30, 0xc, 0x3
|
| 121 | };
|
| 122 |
|
| 123 | static const u16 puncturing_values_320mhz[] = {
|
| 124 | 0xc000, 0x3000, 0xc00, 0x300, 0xc0, 0x30, 0xc, 0x3, 0xf000, 0xf00,
|
| 125 | 0xf0, 0xf, 0xfc00, 0xf300, 0xf0c0, 0xf030, 0xf00c, 0xf003, 0xc00f,
|
| 126 | 0x300f, 0xc0f, 0x30f, 0xcf, 0x3f
|
| 127 | };
|
| 128 |
|
| 129 | #define CFG80211_PER_BW_VALID_PUNCTURING_VALUES(_bw) \
|
| 130 | { \
|
| 131 | .len = ARRAY_SIZE(puncturing_values_ ## _bw ## mhz), \
|
| 132 | .valid_values = puncturing_values_ ## _bw ## mhz \
|
| 133 | }
|
| 134 |
|
| 135 | static const struct cfg80211_per_bw_puncturing_values per_bw_puncturing[] = {
|
| 136 | CFG80211_PER_BW_VALID_PUNCTURING_VALUES(80),
|
| 137 | CFG80211_PER_BW_VALID_PUNCTURING_VALUES(160),
|
| 138 | CFG80211_PER_BW_VALID_PUNCTURING_VALUES(320)
|
| 139 | };
|
| 140 |
|
| 141 | static bool valid_puncturing_bitmap(const struct cfg80211_chan_def *chandef,
|
| 142 | u32 primary_center, u32 punctured)
|
| 143 | {
|
| 144 | u32 idx, i, start_freq;
|
| 145 |
|
| 146 | switch (chandef->width) {
|
| 147 | case NL80211_CHAN_WIDTH_80:
|
| 148 | idx = 0;
|
| 149 | start_freq = chandef->center_freq1 - 40;
|
| 150 | break;
|
| 151 | case NL80211_CHAN_WIDTH_160:
|
| 152 | idx = 1;
|
| 153 | start_freq = chandef->center_freq1 - 80;
|
| 154 | break;
|
| 155 | case NL80211_CHAN_WIDTH_320:
|
| 156 | idx = 2;
|
| 157 | start_freq = chandef->center_freq1 - 160;
|
| 158 | break;
|
| 159 | default:
|
| 160 | return punctured == 0;
|
| 161 | }
|
| 162 |
|
| 163 | if (!punctured)
|
| 164 | return true;
|
| 165 |
|
| 166 |
|
| 167 | if (punctured & (u16)BIT((primary_center - start_freq) / 20))
|
| 168 | return false;
|
| 169 |
|
| 170 | for (i = 0; i < per_bw_puncturing[idx].len; i++) {
|
| 171 | if (per_bw_puncturing[idx].valid_values[i] == punctured)
|
| 172 | return true;
|
| 173 | }
|
| 174 |
|
| 175 | return false;
|
| 176 | }
|
| 177 |
|
| 178 | static bool cfg80211_edmg_chandef_valid(const struct cfg80211_chan_def *chandef)
|
| 179 | {
|
| 180 | int max_contiguous = 0;
|
| 181 | int num_of_enabled = 0;
|
| 182 | int contiguous = 0;
|
| 183 | int i;
|
| 184 |
|
| 185 | if (!chandef->edmg.channels || !chandef->edmg.bw_config)
|
| 186 | return false;
|
| 187 |
|
| 188 | if (!cfg80211_valid_60g_freq(chandef->chan->center_freq))
|
| 189 | return false;
|
| 190 |
|
| 191 | for (i = 0; i < 6; i++) {
|
| 192 | if (chandef->edmg.channels & BIT(i)) {
|
| 193 | contiguous++;
|
| 194 | num_of_enabled++;
|
| 195 | } else {
|
| 196 | contiguous = 0;
|
| 197 | }
|
| 198 |
|
| 199 | max_contiguous = max(contiguous, max_contiguous);
|
| 200 | }
|
| 201 | |
| 202 | * IEEE P802.11ay/D4.0 section 9.4.2.251
|
| 203 |
|
| 204 |
|
| 205 | switch (chandef->edmg.bw_config) {
|
| 206 | case IEEE80211_EDMG_BW_CONFIG_4:
|
| 207 | case IEEE80211_EDMG_BW_CONFIG_8:
|
| 208 | case IEEE80211_EDMG_BW_CONFIG_12:
|
| 209 | if (max_contiguous < 1)
|
| 210 | return false;
|
| 211 | break;
|
| 212 | case IEEE80211_EDMG_BW_CONFIG_5:
|
| 213 | case IEEE80211_EDMG_BW_CONFIG_9:
|
| 214 | case IEEE80211_EDMG_BW_CONFIG_13:
|
| 215 | if (max_contiguous < 2)
|
| 216 | return false;
|
| 217 | break;
|
| 218 | case IEEE80211_EDMG_BW_CONFIG_6:
|
| 219 | case IEEE80211_EDMG_BW_CONFIG_10:
|
| 220 | case IEEE80211_EDMG_BW_CONFIG_14:
|
| 221 | if (max_contiguous < 3)
|
| 222 | return false;
|
| 223 | break;
|
| 224 | case IEEE80211_EDMG_BW_CONFIG_7:
|
| 225 | case IEEE80211_EDMG_BW_CONFIG_11:
|
| 226 | case IEEE80211_EDMG_BW_CONFIG_15:
|
| 227 | if (max_contiguous < 4)
|
| 228 | return false;
|
| 229 | break;
|
| 230 |
|
| 231 | default:
|
| 232 | return false;
|
| 233 | }
|
| 234 |
|
| 235 |
|
| 236 | switch (chandef->edmg.bw_config) {
|
| 237 | case IEEE80211_EDMG_BW_CONFIG_4:
|
| 238 | case IEEE80211_EDMG_BW_CONFIG_5:
|
| 239 | case IEEE80211_EDMG_BW_CONFIG_6:
|
| 240 | case IEEE80211_EDMG_BW_CONFIG_7:
|
| 241 | break;
|
| 242 | case IEEE80211_EDMG_BW_CONFIG_8:
|
| 243 | case IEEE80211_EDMG_BW_CONFIG_9:
|
| 244 | case IEEE80211_EDMG_BW_CONFIG_10:
|
| 245 | case IEEE80211_EDMG_BW_CONFIG_11:
|
| 246 | if (num_of_enabled < 2)
|
| 247 | return false;
|
| 248 | break;
|
| 249 | case IEEE80211_EDMG_BW_CONFIG_12:
|
| 250 | case IEEE80211_EDMG_BW_CONFIG_13:
|
| 251 | case IEEE80211_EDMG_BW_CONFIG_14:
|
| 252 | case IEEE80211_EDMG_BW_CONFIG_15:
|
| 253 | if (num_of_enabled < 4 || max_contiguous < 2)
|
| 254 | return false;
|
| 255 | break;
|
| 256 | default:
|
| 257 | return false;
|
| 258 | }
|
| 259 |
|
| 260 | return true;
|
| 261 | }
|
| 262 |
|
| 263 | int nl80211_chan_width_to_mhz(enum nl80211_chan_width chan_width)
|
| 264 | {
|
| 265 | int mhz;
|
| 266 |
|
| 267 | switch (chan_width) {
|
| 268 | case NL80211_CHAN_WIDTH_1:
|
| 269 | mhz = 1;
|
| 270 | break;
|
| 271 | case NL80211_CHAN_WIDTH_2:
|
| 272 | mhz = 2;
|
| 273 | break;
|
| 274 | case NL80211_CHAN_WIDTH_4:
|
| 275 | mhz = 4;
|
| 276 | break;
|
| 277 | case NL80211_CHAN_WIDTH_8:
|
| 278 | mhz = 8;
|
| 279 | break;
|
| 280 | case NL80211_CHAN_WIDTH_16:
|
| 281 | mhz = 16;
|
| 282 | break;
|
| 283 | case NL80211_CHAN_WIDTH_20:
|
| 284 | case NL80211_CHAN_WIDTH_20_NOHT:
|
| 285 | mhz = 20;
|
| 286 | break;
|
| 287 | case NL80211_CHAN_WIDTH_40:
|
| 288 | mhz = 40;
|
| 289 | break;
|
| 290 | case NL80211_CHAN_WIDTH_80P80:
|
| 291 | case NL80211_CHAN_WIDTH_80:
|
| 292 | mhz = 80;
|
| 293 | break;
|
| 294 | case NL80211_CHAN_WIDTH_160:
|
| 295 | mhz = 160;
|
| 296 | break;
|
| 297 | case NL80211_CHAN_WIDTH_320:
|
| 298 | mhz = 320;
|
| 299 | break;
|
| 300 | default:
|
| 301 | WARN_ON_ONCE(1);
|
| 302 | return -1;
|
| 303 | }
|
| 304 | return mhz;
|
| 305 | }
|
| 306 | EXPORT_SYMBOL(nl80211_chan_width_to_mhz);
|
| 307 |
|
| 308 | static bool cfg80211_valid_center_freq(u32 center,
|
| 309 | enum nl80211_chan_width width)
|
| 310 | {
|
| 311 | int bw;
|
| 312 | int step;
|
| 313 |
|
| 314 |
|
| 315 | if (center < 5955 || center > 7215)
|
| 316 | return true;
|
| 317 |
|
| 318 | bw = nl80211_chan_width_to_mhz(width);
|
| 319 | if (bw < 0)
|
| 320 | return false;
|
| 321 |
|
| 322 |
|
| 323 | if (center - bw / 2 < 5945 || center + bw / 2 > 7225)
|
| 324 | return false;
|
| 325 |
|
| 326 |
|
| 327 | if (bw == 320)
|
| 328 | step = 160;
|
| 329 | else
|
| 330 | step = bw;
|
| 331 |
|
| 332 | |
| 333 | * Valid channels are packed from lowest frequency towards higher ones.
|
| 334 | * So test that the lower frequency aligns with one of these steps.
|
| 335 |
|
| 336 | return (center - bw / 2 - 5945) % step == 0;
|
| 337 | }
|
| 338 |
|
| 339 | static bool
|
| 340 | cfg80211_chandef_valid_control_freq(const struct cfg80211_chan_def *chandef,
|
| 341 | u32 control_freq)
|
| 342 | {
|
| 343 | switch (chandef->width) {
|
| 344 | case NL80211_CHAN_WIDTH_20:
|
| 345 | case NL80211_CHAN_WIDTH_20_NOHT:
|
| 346 | case NL80211_CHAN_WIDTH_1:
|
| 347 | case NL80211_CHAN_WIDTH_2:
|
| 348 | case NL80211_CHAN_WIDTH_4:
|
| 349 | case NL80211_CHAN_WIDTH_8:
|
| 350 | case NL80211_CHAN_WIDTH_16:
|
| 351 |
|
| 352 | break;
|
| 353 | case NL80211_CHAN_WIDTH_320:
|
| 354 | if (chandef->center_freq1 == control_freq + 150 ||
|
| 355 | chandef->center_freq1 == control_freq + 130 ||
|
| 356 | chandef->center_freq1 == control_freq + 110 ||
|
| 357 | chandef->center_freq1 == control_freq + 90 ||
|
| 358 | chandef->center_freq1 == control_freq - 90 ||
|
| 359 | chandef->center_freq1 == control_freq - 110 ||
|
| 360 | chandef->center_freq1 == control_freq - 130 ||
|
| 361 | chandef->center_freq1 == control_freq - 150)
|
| 362 | break;
|
| 363 | fallthrough;
|
| 364 | case NL80211_CHAN_WIDTH_160:
|
| 365 | if (chandef->center_freq1 == control_freq + 70 ||
|
| 366 | chandef->center_freq1 == control_freq + 50 ||
|
| 367 | chandef->center_freq1 == control_freq - 50 ||
|
| 368 | chandef->center_freq1 == control_freq - 70)
|
| 369 | break;
|
| 370 | fallthrough;
|
| 371 | case NL80211_CHAN_WIDTH_80P80:
|
| 372 | case NL80211_CHAN_WIDTH_80:
|
| 373 | if (chandef->center_freq1 == control_freq + 30 ||
|
| 374 | chandef->center_freq1 == control_freq - 30)
|
| 375 | break;
|
| 376 | fallthrough;
|
| 377 | case NL80211_CHAN_WIDTH_40:
|
| 378 | if (chandef->center_freq1 == control_freq + 10 ||
|
| 379 | chandef->center_freq1 == control_freq - 10)
|
| 380 | break;
|
| 381 | fallthrough;
|
| 382 | default:
|
| 383 | return false;
|
| 384 | }
|
| 385 |
|
| 386 | return true;
|
| 387 | }
|
| 388 |
|
| 389 | bool cfg80211_chandef_valid(const struct cfg80211_chan_def *chandef)
|
| 390 | {
|
| 391 | u32 control_freq, control_freq_khz, start_khz, end_khz;
|
| 392 |
|
| 393 | if (!chandef->chan)
|
| 394 | return false;
|
| 395 |
|
| 396 | if (chandef->freq1_offset >= 1000)
|
| 397 | return false;
|
| 398 |
|
| 399 | control_freq = chandef->chan->center_freq;
|
| 400 |
|
| 401 | if (cfg80211_chandef_is_s1g(chandef) &&
|
| 402 | chandef->width != NL80211_CHAN_WIDTH_1 &&
|
| 403 | chandef->width != NL80211_CHAN_WIDTH_2 &&
|
| 404 | chandef->width != NL80211_CHAN_WIDTH_4 &&
|
| 405 | chandef->width != NL80211_CHAN_WIDTH_8 &&
|
| 406 | chandef->width != NL80211_CHAN_WIDTH_16)
|
| 407 | return false;
|
| 408 |
|
| 409 | switch (chandef->width) {
|
| 410 | case NL80211_CHAN_WIDTH_20:
|
| 411 | case NL80211_CHAN_WIDTH_20_NOHT:
|
| 412 | if (ieee80211_chandef_to_khz(chandef) !=
|
| 413 | ieee80211_channel_to_khz(chandef->chan))
|
| 414 | return false;
|
| 415 | if (chandef->center_freq2)
|
| 416 | return false;
|
| 417 | break;
|
| 418 | case NL80211_CHAN_WIDTH_1:
|
| 419 | case NL80211_CHAN_WIDTH_2:
|
| 420 | case NL80211_CHAN_WIDTH_4:
|
| 421 | case NL80211_CHAN_WIDTH_8:
|
| 422 | case NL80211_CHAN_WIDTH_16:
|
| 423 | if (!cfg80211_chandef_is_s1g(chandef))
|
| 424 | return false;
|
| 425 | if (chandef->center_freq2)
|
| 426 | return false;
|
| 427 |
|
| 428 | control_freq_khz = ieee80211_channel_to_khz(chandef->chan);
|
| 429 | start_khz = cfg80211_s1g_get_start_freq_khz(chandef);
|
| 430 | end_khz = cfg80211_s1g_get_end_freq_khz(chandef);
|
| 431 |
|
| 432 | if (control_freq_khz < start_khz || control_freq_khz > end_khz)
|
| 433 | return false;
|
| 434 | break;
|
| 435 | case NL80211_CHAN_WIDTH_80P80:
|
| 436 | if (!chandef->center_freq2)
|
| 437 | return false;
|
| 438 |
|
| 439 | if (chandef->center_freq1 - chandef->center_freq2 == 80 ||
|
| 440 | chandef->center_freq2 - chandef->center_freq1 == 80)
|
| 441 | return false;
|
| 442 | break;
|
| 443 | default:
|
| 444 | if (chandef->center_freq2)
|
| 445 | return false;
|
| 446 | break;
|
| 447 | }
|
| 448 |
|
| 449 | if (!cfg80211_chandef_valid_control_freq(chandef, control_freq))
|
| 450 | return false;
|
| 451 |
|
| 452 | if (chandef->npca_chan) {
|
| 453 | switch (chandef->width) {
|
| 454 | case NL80211_CHAN_WIDTH_80:
|
| 455 | case NL80211_CHAN_WIDTH_160:
|
| 456 | case NL80211_CHAN_WIDTH_320:
|
| 457 | break;
|
| 458 | default:
|
| 459 | return false;
|
| 460 | }
|
| 461 | } else if (chandef->npca_punctured) {
|
| 462 | return false;
|
| 463 | }
|
| 464 |
|
| 465 | if (!cfg80211_valid_center_freq(chandef->center_freq1, chandef->width))
|
| 466 | return false;
|
| 467 |
|
| 468 | if (chandef->width == NL80211_CHAN_WIDTH_80P80 &&
|
| 469 | !cfg80211_valid_center_freq(chandef->center_freq2, chandef->width))
|
| 470 | return false;
|
| 471 |
|
| 472 |
|
| 473 | if (chandef->center_freq1 == 2484 &&
|
| 474 | chandef->width != NL80211_CHAN_WIDTH_20_NOHT)
|
| 475 | return false;
|
| 476 |
|
| 477 | if (cfg80211_chandef_is_edmg(chandef) &&
|
| 478 | !cfg80211_edmg_chandef_valid(chandef))
|
| 479 | return false;
|
| 480 |
|
| 481 | if (!cfg80211_chandef_is_s1g(chandef) && chandef->s1g_primary_2mhz)
|
| 482 | return false;
|
| 483 |
|
| 484 | return valid_puncturing_bitmap(chandef, control_freq,
|
| 485 | chandef->punctured);
|
| 486 | }
|
| 487 | EXPORT_SYMBOL(cfg80211_chandef_valid);
|
| 488 |
|
| 489 | int cfg80211_chandef_primary(const struct cfg80211_chan_def *c,
|
| 490 | enum nl80211_chan_width primary_chan_width,
|
| 491 | u16 *punctured)
|
| 492 | {
|
| 493 | int pri_width = nl80211_chan_width_to_mhz(primary_chan_width);
|
| 494 | int width = cfg80211_chandef_get_width(c);
|
| 495 | u32 control = c->chan->center_freq;
|
| 496 | u32 center = c->center_freq1;
|
| 497 | u16 _punct = 0;
|
| 498 |
|
| 499 | if (WARN_ON_ONCE(pri_width < 0 || width < 0))
|
| 500 | return -1;
|
| 501 |
|
| 502 |
|
| 503 | if (WARN_ON_ONCE(pri_width > width))
|
| 504 | return -1;
|
| 505 |
|
| 506 | if (!punctured)
|
| 507 | punctured = &_punct;
|
| 508 |
|
| 509 | *punctured = c->punctured;
|
| 510 |
|
| 511 | while (width > pri_width) {
|
| 512 | unsigned int bits_to_drop = width / 20 / 2;
|
| 513 |
|
| 514 | if (control > center) {
|
| 515 | center += width / 4;
|
| 516 | *punctured >>= bits_to_drop;
|
| 517 | } else {
|
| 518 | center -= width / 4;
|
| 519 | *punctured &= (1 << bits_to_drop) - 1;
|
| 520 | }
|
| 521 | width /= 2;
|
| 522 | }
|
| 523 |
|
| 524 | return center;
|
| 525 | }
|
| 526 | EXPORT_SYMBOL(cfg80211_chandef_primary);
|
| 527 |
|
| 528 | bool cfg80211_chandef_npca_valid(struct wiphy *wiphy,
|
| 529 | const struct cfg80211_chan_def *chandef,
|
| 530 | const struct ieee80211_uhr_npca_info *npca)
|
| 531 | {
|
| 532 | struct cfg80211_chan_def tmp = *chandef;
|
| 533 | bool pri_upper, npca_upper;
|
| 534 | u32 cf1;
|
| 535 |
|
| 536 | if (chandef->npca_chan || chandef->npca_punctured)
|
| 537 | return false;
|
| 538 |
|
| 539 | if (!npca)
|
| 540 | return true;
|
| 541 |
|
| 542 | if (cfg80211_chandef_add_npca(wiphy, &tmp, npca))
|
| 543 | return false;
|
| 544 |
|
| 545 | if (!cfg80211_chandef_valid_control_freq(&tmp,
|
| 546 | tmp.npca_chan->center_freq))
|
| 547 | return false;
|
| 548 |
|
| 549 | cf1 = tmp.center_freq1;
|
| 550 | pri_upper = tmp.chan->center_freq > cf1;
|
| 551 | npca_upper = tmp.npca_chan->center_freq > cf1;
|
| 552 |
|
| 553 | if (pri_upper == npca_upper)
|
| 554 | return false;
|
| 555 |
|
| 556 | if (!valid_puncturing_bitmap(&tmp,
|
| 557 | tmp.npca_chan->center_freq,
|
| 558 | tmp.npca_punctured) ||
|
| 559 | (tmp.punctured & tmp.npca_punctured) != tmp.punctured)
|
| 560 | return false;
|
| 561 |
|
| 562 | return true;
|
| 563 | }
|
| 564 | EXPORT_SYMBOL(cfg80211_chandef_npca_valid);
|
| 565 |
|
| 566 | int cfg80211_chandef_add_npca(struct wiphy *wiphy,
|
| 567 | struct cfg80211_chan_def *chandef,
|
| 568 | const struct ieee80211_uhr_npca_info *npca)
|
| 569 | {
|
| 570 | struct cfg80211_chan_def new_chandef = *chandef;
|
| 571 | u32 width, npca_freq;
|
| 572 | u8 offs;
|
| 573 |
|
| 574 | if (chandef->npca_chan || chandef->npca_punctured)
|
| 575 | return -EINVAL;
|
| 576 |
|
| 577 | if (WARN_ON(!cfg80211_chandef_valid(chandef)))
|
| 578 | return -EINVAL;
|
| 579 |
|
| 580 | if (!npca)
|
| 581 | return 0;
|
| 582 |
|
| 583 | switch (chandef->width) {
|
| 584 | case NL80211_CHAN_WIDTH_80:
|
| 585 | case NL80211_CHAN_WIDTH_160:
|
| 586 | case NL80211_CHAN_WIDTH_320:
|
| 587 | break;
|
| 588 | default:
|
| 589 | return -EINVAL;
|
| 590 | }
|
| 591 |
|
| 592 | offs = le32_get_bits(npca->params,
|
| 593 | IEEE80211_UHR_NPCA_PARAMS_PRIMARY_CHAN_OFFS);
|
| 594 |
|
| 595 | width = cfg80211_chandef_get_width(chandef);
|
| 596 | npca_freq = chandef->center_freq1 - width / 2 + 10 + 20 * offs;
|
| 597 | new_chandef.npca_chan = ieee80211_get_channel(wiphy, npca_freq);
|
| 598 | if (!new_chandef.npca_chan)
|
| 599 | return -EINVAL;
|
| 600 |
|
| 601 | if (npca->params & cpu_to_le32(IEEE80211_UHR_NPCA_PARAMS_DIS_SUBCH_BMAP_PRES))
|
| 602 | new_chandef.npca_punctured = le16_to_cpu(npca->dis_subch_bmap[0]);
|
| 603 |
|
| 604 | if (!cfg80211_chandef_valid(&new_chandef))
|
| 605 | return -EINVAL;
|
| 606 |
|
| 607 | *chandef = new_chandef;
|
| 608 | return 0;
|
| 609 | }
|
| 610 | EXPORT_SYMBOL(cfg80211_chandef_add_npca);
|
| 611 |
|
| 612 | int cfg80211_chandef_add_dbe(struct cfg80211_chan_def *chandef,
|
| 613 | const struct ieee80211_uhr_dbe_info *dbe)
|
| 614 | {
|
| 615 | struct cfg80211_chan_def new_chandef = *chandef;
|
| 616 | u16 starting_freq, bw_mhz, start_old, start_new;
|
| 617 | u8 bw, punct_shift;
|
| 618 | int offset, index;
|
| 619 |
|
| 620 | if (!dbe)
|
| 621 | return 0;
|
| 622 |
|
| 623 | if (!cfg80211_chandef_valid(chandef))
|
| 624 | return -EINVAL;
|
| 625 |
|
| 626 | if (chandef->width == NL80211_CHAN_WIDTH_20_NOHT)
|
| 627 | return -EINVAL;
|
| 628 |
|
| 629 | bw = u8_get_bits(dbe->params, IEEE80211_UHR_DBE_OPER_BANDWIDTH);
|
| 630 |
|
| 631 | switch (chandef->chan->band) {
|
| 632 | case NL80211_BAND_5GHZ:
|
| 633 | if (bw > IEEE80211_UHR_DBE_OPER_BW_160)
|
| 634 | return -EINVAL;
|
| 635 | if (chandef->chan->center_freq < 5745)
|
| 636 | starting_freq = 5180;
|
| 637 | else
|
| 638 | starting_freq = 5745;
|
| 639 | break;
|
| 640 | case NL80211_BAND_6GHZ:
|
| 641 | starting_freq = 5955;
|
| 642 | break;
|
| 643 | default:
|
| 644 | return -EINVAL;
|
| 645 | }
|
| 646 |
|
| 647 | switch (bw) {
|
| 648 | case IEEE80211_UHR_DBE_OPER_BW_320_2:
|
| 649 | case IEEE80211_UHR_DBE_OPER_BW_320_1:
|
| 650 | if (chandef->width == NL80211_CHAN_WIDTH_160)
|
| 651 | break;
|
| 652 | fallthrough;
|
| 653 | case IEEE80211_UHR_DBE_OPER_BW_160:
|
| 654 | if (chandef->width == NL80211_CHAN_WIDTH_80)
|
| 655 | break;
|
| 656 | fallthrough;
|
| 657 | case IEEE80211_UHR_DBE_OPER_BW_80:
|
| 658 | if (chandef->width == NL80211_CHAN_WIDTH_40)
|
| 659 | break;
|
| 660 | fallthrough;
|
| 661 | case IEEE80211_UHR_DBE_OPER_BW_40:
|
| 662 | if (chandef->width == NL80211_CHAN_WIDTH_20)
|
| 663 | break;
|
| 664 | fallthrough;
|
| 665 | default:
|
| 666 | return -EINVAL;
|
| 667 | }
|
| 668 |
|
| 669 | switch (bw) {
|
| 670 | case IEEE80211_UHR_DBE_OPER_BW_320_2:
|
| 671 |
|
| 672 | starting_freq += 160;
|
| 673 | fallthrough;
|
| 674 | case IEEE80211_UHR_DBE_OPER_BW_320_1:
|
| 675 | new_chandef.width = NL80211_CHAN_WIDTH_320;
|
| 676 | bw_mhz = 320;
|
| 677 | break;
|
| 678 | case IEEE80211_UHR_DBE_OPER_BW_160:
|
| 679 | new_chandef.width = NL80211_CHAN_WIDTH_160;
|
| 680 | bw_mhz = 160;
|
| 681 | break;
|
| 682 | case IEEE80211_UHR_DBE_OPER_BW_80:
|
| 683 | new_chandef.width = NL80211_CHAN_WIDTH_80;
|
| 684 | bw_mhz = 80;
|
| 685 | break;
|
| 686 | case IEEE80211_UHR_DBE_OPER_BW_40:
|
| 687 | new_chandef.width = NL80211_CHAN_WIDTH_40;
|
| 688 | bw_mhz = 40;
|
| 689 | break;
|
| 690 | }
|
| 691 |
|
| 692 |
|
| 693 | if (chandef->chan->center_freq < starting_freq)
|
| 694 | return -EINVAL;
|
| 695 |
|
| 696 | offset = chandef->chan->center_freq - starting_freq;
|
| 697 | index = offset / bw_mhz;
|
| 698 | start_new = starting_freq - 10 + index * bw_mhz;
|
| 699 | new_chandef.center_freq1 = start_new + bw_mhz / 2;
|
| 700 |
|
| 701 | start_old = chandef->center_freq1 -
|
| 702 | cfg80211_chandef_get_width(chandef) / 2;
|
| 703 |
|
| 704 | |
| 705 | * If the DBE channel extends downward below the lower
|
| 706 | * edge of the BSS channel, we need to shift puncturing
|
| 707 | * bitmaps up to adjust for that.
|
| 708 |
|
| 709 | if (start_new < start_old)
|
| 710 | punct_shift = (start_old - start_new) / 20;
|
| 711 | else
|
| 712 | punct_shift = 0;
|
| 713 |
|
| 714 | new_chandef.punctured <<= punct_shift;
|
| 715 | new_chandef.npca_punctured <<= punct_shift;
|
| 716 |
|
| 717 | if (dbe->params & IEEE80211_UHR_DBE_OPER_DIS_SUBCHANNEL_BITMAP_PRES) {
|
| 718 | u16 punct_mask = ((1 << (bw_mhz / 40)) - 1) << punct_shift;
|
| 719 | u16 punctured = le16_to_cpu(dbe->dis_subch_bmap[0]);
|
| 720 |
|
| 721 | if ((punctured & punct_mask) != (new_chandef.punctured & punct_mask))
|
| 722 | return -EINVAL;
|
| 723 |
|
| 724 | new_chandef.punctured = punctured;
|
| 725 | }
|
| 726 |
|
| 727 | if (!cfg80211_chandef_valid(&new_chandef))
|
| 728 | return -EINVAL;
|
| 729 |
|
| 730 | |
| 731 | * If e.g. a 40 MHz BSS channel (erroneously) occupies the center of the
|
| 732 | * DBE 80 MHz channel, they would be incompatible; check and reject.
|
| 733 |
|
| 734 | if (!cfg80211_chandef_compatible(&new_chandef, chandef))
|
| 735 | return -EINVAL;
|
| 736 |
|
| 737 | *chandef = new_chandef;
|
| 738 | return 0;
|
| 739 | }
|
| 740 | EXPORT_SYMBOL(cfg80211_chandef_add_dbe);
|
| 741 |
|
| 742 | static const struct cfg80211_chan_def *
|
| 743 | check_chandef_primary_compat(const struct cfg80211_chan_def *c1,
|
| 744 | const struct cfg80211_chan_def *c2,
|
| 745 | enum nl80211_chan_width primary_chan_width)
|
| 746 | {
|
| 747 | u16 punct_c1 = 0, punct_c2 = 0;
|
| 748 |
|
| 749 |
|
| 750 | if (cfg80211_chandef_primary(c1, primary_chan_width, &punct_c1) !=
|
| 751 | cfg80211_chandef_primary(c2, primary_chan_width, &punct_c2))
|
| 752 | return ERR_PTR(-EINVAL);
|
| 753 |
|
| 754 | if (punct_c1 != punct_c2)
|
| 755 | return ERR_PTR(-EINVAL);
|
| 756 |
|
| 757 |
|
| 758 | if (c1->width == primary_chan_width)
|
| 759 | return c2;
|
| 760 |
|
| 761 |
|
| 762 | return NULL;
|
| 763 | }
|
| 764 |
|
| 765 | static const struct cfg80211_chan_def *
|
| 766 | _cfg80211_chandef_compatible(const struct cfg80211_chan_def *c1,
|
| 767 | const struct cfg80211_chan_def *c2)
|
| 768 | {
|
| 769 | const struct cfg80211_chan_def *ret;
|
| 770 |
|
| 771 |
|
| 772 | if (cfg80211_chandef_identical(c1, c2))
|
| 773 | return c2;
|
| 774 |
|
| 775 |
|
| 776 | if (c1->chan != c2->chan)
|
| 777 | return NULL;
|
| 778 |
|
| 779 | |
| 780 | * If they have the same width, but aren't identical,
|
| 781 | * then they can't be compatible.
|
| 782 |
|
| 783 | if (c1->width == c2->width)
|
| 784 | return NULL;
|
| 785 |
|
| 786 | |
| 787 | * We need NPCA to be compatible for some scenarios such as
|
| 788 | * multiple APs, but in this case userspace should configure
|
| 789 | * identical chandefs including NPCA, even if perhaps one of
|
| 790 | * the AP interfaces doesn't even advertise it.
|
| 791 |
|
| 792 | if (c1->npca_chan || c2->npca_chan)
|
| 793 | return NULL;
|
| 794 |
|
| 795 | |
| 796 | * can't be compatible if one of them is S1G
|
| 797 | * but they don't have the same width.
|
| 798 |
|
| 799 | #define IS_S1G(width) ((width) == NL80211_CHAN_WIDTH_1 || \
|
| 800 | (width) == NL80211_CHAN_WIDTH_2 || \
|
| 801 | (width) == NL80211_CHAN_WIDTH_4 || \
|
| 802 | (width) == NL80211_CHAN_WIDTH_8 || \
|
| 803 | (width) == NL80211_CHAN_WIDTH_16)
|
| 804 |
|
| 805 | if (IS_S1G(c1->width) || IS_S1G(c2->width))
|
| 806 | return NULL;
|
| 807 |
|
| 808 | |
| 809 | * Make sure that c1 is always the narrower one, so that later
|
| 810 | * we either return NULL or c2 and don't have to check both
|
| 811 | * directions.
|
| 812 |
|
| 813 | if (c1->width > c2->width)
|
| 814 | swap(c1, c2);
|
| 815 |
|
| 816 | |
| 817 | * No further checks needed if the "narrower" one is only 20 MHz.
|
| 818 | * Here "narrower" includes being a 20 MHz non-HT channel vs. a
|
| 819 | * 20 MHz HT (or later) one.
|
| 820 |
|
| 821 | if (c1->width <= NL80211_CHAN_WIDTH_20)
|
| 822 | return c2;
|
| 823 |
|
| 824 | ret = check_chandef_primary_compat(c1, c2, NL80211_CHAN_WIDTH_40);
|
| 825 | if (ret)
|
| 826 | return ret;
|
| 827 |
|
| 828 | ret = check_chandef_primary_compat(c1, c2, NL80211_CHAN_WIDTH_80);
|
| 829 | if (ret)
|
| 830 | return ret;
|
| 831 |
|
| 832 | |
| 833 | * If c1 is 80+80, then c2 is 160 or higher, but that cannot
|
| 834 | * match. If c2 was also 80+80 it was already either accepted
|
| 835 | * or rejected above (identical or not, respectively.)
|
| 836 |
|
| 837 | if (c1->width == NL80211_CHAN_WIDTH_80P80)
|
| 838 | return NULL;
|
| 839 |
|
| 840 | ret = check_chandef_primary_compat(c1, c2, NL80211_CHAN_WIDTH_160);
|
| 841 | if (ret)
|
| 842 | return ret;
|
| 843 |
|
| 844 | |
| 845 | * Getting here would mean they're both wider than 160, have the
|
| 846 | * same primary 160, but are not identical - this cannot happen
|
| 847 | * since they must be 320 (no wider chandefs exist, at least yet.)
|
| 848 |
|
| 849 | WARN_ON_ONCE(1);
|
| 850 |
|
| 851 | return NULL;
|
| 852 | }
|
| 853 |
|
| 854 | const struct cfg80211_chan_def *
|
| 855 | cfg80211_chandef_compatible(const struct cfg80211_chan_def *c1,
|
| 856 | const struct cfg80211_chan_def *c2)
|
| 857 | {
|
| 858 | const struct cfg80211_chan_def *ret;
|
| 859 |
|
| 860 | ret = _cfg80211_chandef_compatible(c1, c2);
|
| 861 | if (IS_ERR(ret))
|
| 862 | return NULL;
|
| 863 | return ret;
|
| 864 | }
|
| 865 | EXPORT_SYMBOL(cfg80211_chandef_compatible);
|
| 866 |
|
| 867 | void cfg80211_set_dfs_state(struct wiphy *wiphy,
|
| 868 | const struct cfg80211_chan_def *chandef,
|
| 869 | enum nl80211_dfs_state dfs_state)
|
| 870 | {
|
| 871 | struct ieee80211_channel *c;
|
| 872 | int width;
|
| 873 |
|
| 874 | if (WARN_ON(!cfg80211_chandef_valid(chandef)))
|
| 875 | return;
|
| 876 |
|
| 877 | width = cfg80211_chandef_get_width(chandef);
|
| 878 | if (width < 0)
|
| 879 | return;
|
| 880 |
|
| 881 | for_each_subchan(chandef, freq, cf) {
|
| 882 | c = ieee80211_get_channel_khz(wiphy, freq);
|
| 883 | if (!c || !(c->flags & IEEE80211_CHAN_RADAR))
|
| 884 | continue;
|
| 885 |
|
| 886 | c->dfs_state = dfs_state;
|
| 887 | c->dfs_state_entered = jiffies;
|
| 888 | }
|
| 889 | }
|
| 890 |
|
| 891 | void cfg80211_set_cac_state(struct wiphy *wiphy,
|
| 892 | const struct cfg80211_chan_def *chandef,
|
| 893 | bool cac_ongoing)
|
| 894 | {
|
| 895 | struct ieee80211_channel *c;
|
| 896 | int width;
|
| 897 | u64 cac_time;
|
| 898 |
|
| 899 | if (WARN_ON(!cfg80211_chandef_valid(chandef)))
|
| 900 | return;
|
| 901 |
|
| 902 | width = cfg80211_chandef_get_width(chandef);
|
| 903 | if (width < 0)
|
| 904 | return;
|
| 905 |
|
| 906 |
|
| 907 | cac_time = cac_ongoing ? ktime_get_boottime_ns() : 0;
|
| 908 |
|
| 909 | for_each_subchan(chandef, freq, cf) {
|
| 910 | c = ieee80211_get_channel_khz(wiphy, freq);
|
| 911 | if (!c)
|
| 912 | continue;
|
| 913 |
|
| 914 | c->cac_start_time = cac_time;
|
| 915 | }
|
| 916 | }
|
| 917 |
|
| 918 | static bool
|
| 919 | cfg80211_dfs_permissive_check_wdev(struct cfg80211_registered_device *rdev,
|
| 920 | enum nl80211_iftype iftype,
|
| 921 | struct wireless_dev *wdev,
|
| 922 | struct ieee80211_channel *chan)
|
| 923 | {
|
| 924 | unsigned int link_id;
|
| 925 |
|
| 926 | for_each_valid_link(wdev, link_id) {
|
| 927 | struct ieee80211_channel *other_chan = NULL;
|
| 928 | struct cfg80211_chan_def chandef = {};
|
| 929 | int ret;
|
| 930 |
|
| 931 |
|
| 932 | if (wdev->iftype != NL80211_IFTYPE_STATION ||
|
| 933 | !wdev->links[link_id].client.current_bss)
|
| 934 | continue;
|
| 935 |
|
| 936 | other_chan =
|
| 937 | wdev->links[link_id].client.current_bss->pub.channel;
|
| 938 |
|
| 939 | if (!other_chan)
|
| 940 | continue;
|
| 941 |
|
| 942 | if (chan == other_chan)
|
| 943 | return true;
|
| 944 |
|
| 945 |
|
| 946 | ret = rdev_get_channel(rdev, wdev, link_id, &chandef);
|
| 947 | if (ret)
|
| 948 | continue;
|
| 949 |
|
| 950 | if (cfg80211_is_sub_chan(&chandef, chan, false))
|
| 951 | return true;
|
| 952 | }
|
| 953 |
|
| 954 | return false;
|
| 955 | }
|
| 956 |
|
| 957 | |
| 958 | * Check if P2P GO is allowed to operate on a DFS channel
|
| 959 |
|
| 960 | static bool cfg80211_dfs_permissive_chan(struct wiphy *wiphy,
|
| 961 | enum nl80211_iftype iftype,
|
| 962 | struct ieee80211_channel *chan)
|
| 963 | {
|
| 964 | struct wireless_dev *wdev;
|
| 965 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
|
| 966 |
|
| 967 | lockdep_assert_held(&rdev->wiphy.mtx);
|
| 968 |
|
| 969 | if (!wiphy_ext_feature_isset(&rdev->wiphy,
|
| 970 | NL80211_EXT_FEATURE_DFS_CONCURRENT) ||
|
| 971 | !(chan->flags & IEEE80211_CHAN_DFS_CONCURRENT))
|
| 972 | return false;
|
| 973 |
|
| 974 |
|
| 975 | if (iftype != NL80211_IFTYPE_P2P_GO)
|
| 976 | return false;
|
| 977 |
|
| 978 | |
| 979 | * Allow only if there's a concurrent BSS
|
| 980 |
|
| 981 | list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) {
|
| 982 | bool ret = cfg80211_dfs_permissive_check_wdev(rdev, iftype,
|
| 983 | wdev, chan);
|
| 984 | if (ret)
|
| 985 | return ret;
|
| 986 | }
|
| 987 |
|
| 988 | return false;
|
| 989 | }
|
| 990 |
|
| 991 | static int cfg80211_get_chans_dfs_required(struct wiphy *wiphy,
|
| 992 | const struct cfg80211_chan_def *chandef,
|
| 993 | enum nl80211_iftype iftype)
|
| 994 | {
|
| 995 | struct ieee80211_channel *c;
|
| 996 |
|
| 997 |
|
| 998 | if (cfg80211_chandef_is_s1g(chandef))
|
| 999 | return 0;
|
| 1000 |
|
| 1001 | for_each_subchan(chandef, freq, cf) {
|
| 1002 | c = ieee80211_get_channel_khz(wiphy, freq);
|
| 1003 | if (!c)
|
| 1004 | return -EINVAL;
|
| 1005 |
|
| 1006 | if (c->flags & IEEE80211_CHAN_RADAR &&
|
| 1007 | !cfg80211_dfs_permissive_chan(wiphy, iftype, c))
|
| 1008 | return 1;
|
| 1009 | }
|
| 1010 |
|
| 1011 | return 0;
|
| 1012 | }
|
| 1013 |
|
| 1014 |
|
| 1015 | int cfg80211_chandef_dfs_required(struct wiphy *wiphy,
|
| 1016 | const struct cfg80211_chan_def *chandef,
|
| 1017 | enum nl80211_iftype iftype)
|
| 1018 | {
|
| 1019 | int width;
|
| 1020 | int ret;
|
| 1021 |
|
| 1022 | if (WARN_ON(!cfg80211_chandef_valid(chandef)))
|
| 1023 | return -EINVAL;
|
| 1024 |
|
| 1025 | switch (iftype) {
|
| 1026 | case NL80211_IFTYPE_ADHOC:
|
| 1027 | case NL80211_IFTYPE_AP:
|
| 1028 | case NL80211_IFTYPE_P2P_GO:
|
| 1029 | case NL80211_IFTYPE_MESH_POINT:
|
| 1030 | case NL80211_IFTYPE_NAN:
|
| 1031 | width = cfg80211_chandef_get_width(chandef);
|
| 1032 | if (width < 0)
|
| 1033 | return -EINVAL;
|
| 1034 |
|
| 1035 | ret = cfg80211_get_chans_dfs_required(wiphy, chandef, iftype);
|
| 1036 |
|
| 1037 | return (ret > 0) ? BIT(chandef->width) : ret;
|
| 1038 | break;
|
| 1039 | case NL80211_IFTYPE_STATION:
|
| 1040 | case NL80211_IFTYPE_OCB:
|
| 1041 | case NL80211_IFTYPE_P2P_CLIENT:
|
| 1042 | case NL80211_IFTYPE_MONITOR:
|
| 1043 | case NL80211_IFTYPE_AP_VLAN:
|
| 1044 | case NL80211_IFTYPE_P2P_DEVICE:
|
| 1045 | case NL80211_IFTYPE_NAN_DATA:
|
| 1046 | case NL80211_IFTYPE_PD:
|
| 1047 | break;
|
| 1048 | case NL80211_IFTYPE_WDS:
|
| 1049 | case NL80211_IFTYPE_UNSPECIFIED:
|
| 1050 | case NUM_NL80211_IFTYPES:
|
| 1051 | WARN_ON(1);
|
| 1052 | }
|
| 1053 |
|
| 1054 | return 0;
|
| 1055 | }
|
| 1056 | EXPORT_SYMBOL(cfg80211_chandef_dfs_required);
|
| 1057 |
|
| 1058 | bool cfg80211_chandef_dfs_usable(struct wiphy *wiphy,
|
| 1059 | const struct cfg80211_chan_def *chandef)
|
| 1060 | {
|
| 1061 | struct ieee80211_channel *c;
|
| 1062 | int width, count = 0;
|
| 1063 |
|
| 1064 | if (WARN_ON(!cfg80211_chandef_valid(chandef)))
|
| 1065 | return false;
|
| 1066 |
|
| 1067 | width = cfg80211_chandef_get_width(chandef);
|
| 1068 | if (width < 0)
|
| 1069 | return false;
|
| 1070 |
|
| 1071 | |
| 1072 | * Check entire range of channels for the bandwidth.
|
| 1073 | * Check all channels are DFS channels (DFS_USABLE or
|
| 1074 | * DFS_AVAILABLE). Return number of usable channels
|
| 1075 | * (require CAC). Allow DFS and non-DFS channel mix.
|
| 1076 |
|
| 1077 | for_each_subchan(chandef, freq, cf) {
|
| 1078 | c = ieee80211_get_channel_khz(wiphy, freq);
|
| 1079 | if (!c)
|
| 1080 | return false;
|
| 1081 |
|
| 1082 | if (c->flags & IEEE80211_CHAN_DISABLED)
|
| 1083 | return false;
|
| 1084 |
|
| 1085 | if (c->flags & IEEE80211_CHAN_RADAR) {
|
| 1086 | if (c->dfs_state == NL80211_DFS_UNAVAILABLE)
|
| 1087 | return false;
|
| 1088 |
|
| 1089 | if (c->dfs_state == NL80211_DFS_USABLE)
|
| 1090 | count++;
|
| 1091 | }
|
| 1092 | }
|
| 1093 |
|
| 1094 | return count > 0;
|
| 1095 | }
|
| 1096 | EXPORT_SYMBOL(cfg80211_chandef_dfs_usable);
|
| 1097 |
|
| 1098 | |
| 1099 | * Checks if center frequency of chan falls with in the bandwidth
|
| 1100 | * range of chandef.
|
| 1101 |
|
| 1102 | bool cfg80211_is_sub_chan(struct cfg80211_chan_def *chandef,
|
| 1103 | struct ieee80211_channel *chan,
|
| 1104 | bool primary_only)
|
| 1105 | {
|
| 1106 | int width;
|
| 1107 | u32 freq;
|
| 1108 |
|
| 1109 | if (!chandef->chan)
|
| 1110 | return false;
|
| 1111 |
|
| 1112 | if (chandef->chan->center_freq == chan->center_freq)
|
| 1113 | return true;
|
| 1114 |
|
| 1115 | if (primary_only)
|
| 1116 | return false;
|
| 1117 |
|
| 1118 | width = cfg80211_chandef_get_width(chandef);
|
| 1119 | if (width <= 20)
|
| 1120 | return false;
|
| 1121 |
|
| 1122 | for (freq = chandef->center_freq1 - width / 2 + 10;
|
| 1123 | freq <= chandef->center_freq1 + width / 2 - 10; freq += 20) {
|
| 1124 | if (chan->center_freq == freq)
|
| 1125 | return true;
|
| 1126 | }
|
| 1127 |
|
| 1128 | if (!chandef->center_freq2)
|
| 1129 | return false;
|
| 1130 |
|
| 1131 | for (freq = chandef->center_freq2 - width / 2 + 10;
|
| 1132 | freq <= chandef->center_freq2 + width / 2 - 10; freq += 20) {
|
| 1133 | if (chan->center_freq == freq)
|
| 1134 | return true;
|
| 1135 | }
|
| 1136 |
|
| 1137 | return false;
|
| 1138 | }
|
| 1139 |
|
| 1140 | bool cfg80211_beaconing_iface_active(struct wireless_dev *wdev)
|
| 1141 | {
|
| 1142 | unsigned int link;
|
| 1143 |
|
| 1144 | lockdep_assert_wiphy(wdev->wiphy);
|
| 1145 |
|
| 1146 | switch (wdev->iftype) {
|
| 1147 | case NL80211_IFTYPE_AP:
|
| 1148 | case NL80211_IFTYPE_P2P_GO:
|
| 1149 | for_each_valid_link(wdev, link) {
|
| 1150 | if (wdev->links[link].ap.beacon_interval)
|
| 1151 | return true;
|
| 1152 | }
|
| 1153 | break;
|
| 1154 | case NL80211_IFTYPE_ADHOC:
|
| 1155 | if (wdev->u.ibss.ssid_len)
|
| 1156 | return true;
|
| 1157 | break;
|
| 1158 | case NL80211_IFTYPE_MESH_POINT:
|
| 1159 | if (wdev->u.mesh.id_len)
|
| 1160 | return true;
|
| 1161 | break;
|
| 1162 | case NL80211_IFTYPE_STATION:
|
| 1163 | case NL80211_IFTYPE_OCB:
|
| 1164 | case NL80211_IFTYPE_P2P_CLIENT:
|
| 1165 | case NL80211_IFTYPE_MONITOR:
|
| 1166 | case NL80211_IFTYPE_AP_VLAN:
|
| 1167 | case NL80211_IFTYPE_P2P_DEVICE:
|
| 1168 |
|
| 1169 | case NL80211_IFTYPE_NAN:
|
| 1170 | case NL80211_IFTYPE_NAN_DATA:
|
| 1171 | case NL80211_IFTYPE_PD:
|
| 1172 | break;
|
| 1173 | case NL80211_IFTYPE_UNSPECIFIED:
|
| 1174 | case NL80211_IFTYPE_WDS:
|
| 1175 | case NUM_NL80211_IFTYPES:
|
| 1176 | WARN_ON(1);
|
| 1177 | }
|
| 1178 |
|
| 1179 | return false;
|
| 1180 | }
|
| 1181 |
|
| 1182 | bool cfg80211_wdev_on_sub_chan(struct wireless_dev *wdev,
|
| 1183 | struct ieee80211_channel *chan,
|
| 1184 | bool primary_only)
|
| 1185 | {
|
| 1186 | unsigned int link;
|
| 1187 |
|
| 1188 | switch (wdev->iftype) {
|
| 1189 | case NL80211_IFTYPE_AP:
|
| 1190 | case NL80211_IFTYPE_P2P_GO:
|
| 1191 | for_each_valid_link(wdev, link) {
|
| 1192 | if (cfg80211_is_sub_chan(&wdev->links[link].ap.chandef,
|
| 1193 | chan, primary_only))
|
| 1194 | return true;
|
| 1195 | }
|
| 1196 | break;
|
| 1197 | case NL80211_IFTYPE_ADHOC:
|
| 1198 | return cfg80211_is_sub_chan(&wdev->u.ibss.chandef, chan,
|
| 1199 | primary_only);
|
| 1200 | case NL80211_IFTYPE_MESH_POINT:
|
| 1201 | return cfg80211_is_sub_chan(&wdev->u.mesh.chandef, chan,
|
| 1202 | primary_only);
|
| 1203 | default:
|
| 1204 | break;
|
| 1205 | }
|
| 1206 |
|
| 1207 | return false;
|
| 1208 | }
|
| 1209 |
|
| 1210 | static bool cfg80211_is_wiphy_oper_chan(struct wiphy *wiphy,
|
| 1211 | struct ieee80211_channel *chan)
|
| 1212 | {
|
| 1213 | struct wireless_dev *wdev;
|
| 1214 |
|
| 1215 | lockdep_assert_wiphy(wiphy);
|
| 1216 |
|
| 1217 | list_for_each_entry(wdev, &wiphy->wdev_list, list) {
|
| 1218 | if (!cfg80211_beaconing_iface_active(wdev))
|
| 1219 | continue;
|
| 1220 |
|
| 1221 | if (cfg80211_wdev_on_sub_chan(wdev, chan, false))
|
| 1222 | return true;
|
| 1223 | }
|
| 1224 |
|
| 1225 | return false;
|
| 1226 | }
|
| 1227 |
|
| 1228 | static bool
|
| 1229 | cfg80211_offchan_chain_is_active(struct cfg80211_registered_device *rdev,
|
| 1230 | struct ieee80211_channel *channel)
|
| 1231 | {
|
| 1232 | if (!rdev->background_radar_wdev)
|
| 1233 | return false;
|
| 1234 |
|
| 1235 | if (!cfg80211_chandef_valid(&rdev->background_radar_chandef))
|
| 1236 | return false;
|
| 1237 |
|
| 1238 | return cfg80211_is_sub_chan(&rdev->background_radar_chandef, channel,
|
| 1239 | false);
|
| 1240 | }
|
| 1241 |
|
| 1242 | bool cfg80211_any_wiphy_oper_chan(struct wiphy *wiphy,
|
| 1243 | struct ieee80211_channel *chan)
|
| 1244 | {
|
| 1245 | struct cfg80211_registered_device *rdev;
|
| 1246 |
|
| 1247 | ASSERT_RTNL();
|
| 1248 |
|
| 1249 | if (!(chan->flags & IEEE80211_CHAN_RADAR))
|
| 1250 | return false;
|
| 1251 |
|
| 1252 | for_each_rdev(rdev) {
|
| 1253 | bool found;
|
| 1254 |
|
| 1255 | if (!reg_dfs_domain_same(wiphy, &rdev->wiphy))
|
| 1256 | continue;
|
| 1257 |
|
| 1258 | guard(wiphy)(&rdev->wiphy);
|
| 1259 |
|
| 1260 | found = cfg80211_is_wiphy_oper_chan(&rdev->wiphy, chan) ||
|
| 1261 | cfg80211_offchan_chain_is_active(rdev, chan);
|
| 1262 |
|
| 1263 | if (found)
|
| 1264 | return true;
|
| 1265 | }
|
| 1266 |
|
| 1267 | return false;
|
| 1268 | }
|
| 1269 |
|
| 1270 | static bool cfg80211_chandef_dfs_available(struct wiphy *wiphy,
|
| 1271 | const struct cfg80211_chan_def *chandef)
|
| 1272 | {
|
| 1273 | struct ieee80211_channel *c;
|
| 1274 | int width;
|
| 1275 | bool dfs_offload;
|
| 1276 |
|
| 1277 | if (WARN_ON(!cfg80211_chandef_valid(chandef)))
|
| 1278 | return false;
|
| 1279 |
|
| 1280 | width = cfg80211_chandef_get_width(chandef);
|
| 1281 | if (width < 0)
|
| 1282 | return false;
|
| 1283 |
|
| 1284 | dfs_offload = wiphy_ext_feature_isset(wiphy,
|
| 1285 | NL80211_EXT_FEATURE_DFS_OFFLOAD);
|
| 1286 |
|
| 1287 | |
| 1288 | * Check entire range of channels for the bandwidth.
|
| 1289 | * If any channel in between is disabled or has not
|
| 1290 | * had gone through CAC return false
|
| 1291 |
|
| 1292 | for_each_subchan(chandef, freq, cf) {
|
| 1293 | c = ieee80211_get_channel_khz(wiphy, freq);
|
| 1294 | if (!c)
|
| 1295 | return false;
|
| 1296 |
|
| 1297 | if (c->flags & IEEE80211_CHAN_DISABLED)
|
| 1298 | return false;
|
| 1299 |
|
| 1300 | if ((c->flags & IEEE80211_CHAN_RADAR) &&
|
| 1301 | (c->dfs_state != NL80211_DFS_AVAILABLE) &&
|
| 1302 | !(c->dfs_state == NL80211_DFS_USABLE && dfs_offload))
|
| 1303 | return false;
|
| 1304 | }
|
| 1305 |
|
| 1306 | return true;
|
| 1307 | }
|
| 1308 |
|
| 1309 | unsigned int
|
| 1310 | cfg80211_chandef_dfs_cac_time(struct wiphy *wiphy,
|
| 1311 | const struct cfg80211_chan_def *chandef)
|
| 1312 | {
|
| 1313 | struct ieee80211_channel *c;
|
| 1314 | int width;
|
| 1315 | unsigned int t1 = 0, t2 = 0;
|
| 1316 |
|
| 1317 | if (WARN_ON(!cfg80211_chandef_valid(chandef)))
|
| 1318 | return 0;
|
| 1319 |
|
| 1320 | width = cfg80211_chandef_get_width(chandef);
|
| 1321 | if (width < 0)
|
| 1322 | return 0;
|
| 1323 |
|
| 1324 | for_each_subchan(chandef, freq, cf) {
|
| 1325 | c = ieee80211_get_channel_khz(wiphy, freq);
|
| 1326 | if (!c || (c->flags & IEEE80211_CHAN_DISABLED)) {
|
| 1327 | if (cf == 1)
|
| 1328 | t1 = INT_MAX;
|
| 1329 | else
|
| 1330 | t2 = INT_MAX;
|
| 1331 | continue;
|
| 1332 | }
|
| 1333 |
|
| 1334 | if (!(c->flags & IEEE80211_CHAN_RADAR))
|
| 1335 | continue;
|
| 1336 |
|
| 1337 | if (cf == 1 && c->dfs_cac_ms > t1)
|
| 1338 | t1 = c->dfs_cac_ms;
|
| 1339 |
|
| 1340 | if (cf == 2 && c->dfs_cac_ms > t2)
|
| 1341 | t2 = c->dfs_cac_ms;
|
| 1342 | }
|
| 1343 |
|
| 1344 | if (t1 == INT_MAX && t2 == INT_MAX)
|
| 1345 | return 0;
|
| 1346 |
|
| 1347 | if (t1 == INT_MAX)
|
| 1348 | return t2;
|
| 1349 |
|
| 1350 | if (t2 == INT_MAX)
|
| 1351 | return t1;
|
| 1352 |
|
| 1353 | return max(t1, t2);
|
| 1354 | }
|
| 1355 | EXPORT_SYMBOL(cfg80211_chandef_dfs_cac_time);
|
| 1356 |
|
| 1357 |
|
| 1358 | static bool cfg80211_edmg_usable(struct wiphy *wiphy, u8 edmg_channels,
|
| 1359 | enum ieee80211_edmg_bw_config edmg_bw_config,
|
| 1360 | int primary_channel,
|
| 1361 | struct ieee80211_edmg *edmg_cap)
|
| 1362 | {
|
| 1363 | struct ieee80211_channel *chan;
|
| 1364 | int i, freq;
|
| 1365 | int channels_counter = 0;
|
| 1366 |
|
| 1367 | if (!edmg_channels && !edmg_bw_config)
|
| 1368 | return true;
|
| 1369 |
|
| 1370 | if ((!edmg_channels && edmg_bw_config) ||
|
| 1371 | (edmg_channels && !edmg_bw_config))
|
| 1372 | return false;
|
| 1373 |
|
| 1374 | if (!(edmg_channels & BIT(primary_channel - 1)))
|
| 1375 | return false;
|
| 1376 |
|
| 1377 |
|
| 1378 | for (i = 0; i < 6; i++) {
|
| 1379 | if (!(edmg_channels & BIT(i)))
|
| 1380 | continue;
|
| 1381 |
|
| 1382 | if (!(edmg_cap->channels & BIT(i)))
|
| 1383 | return false;
|
| 1384 |
|
| 1385 | channels_counter++;
|
| 1386 |
|
| 1387 | freq = ieee80211_channel_to_frequency(i + 1,
|
| 1388 | NL80211_BAND_60GHZ);
|
| 1389 | chan = ieee80211_get_channel(wiphy, freq);
|
| 1390 | if (!chan || chan->flags & IEEE80211_CHAN_DISABLED)
|
| 1391 | return false;
|
| 1392 | }
|
| 1393 |
|
| 1394 |
|
| 1395 | if (channels_counter > 4)
|
| 1396 | return false;
|
| 1397 |
|
| 1398 | |
| 1399 | * (see IEEE P802.11ay/D4.0 section 9.4.2.251, Table 13)
|
| 1400 |
|
| 1401 | if ((edmg_bw_config % 4) > (edmg_cap->bw_config % 4))
|
| 1402 | return false;
|
| 1403 |
|
| 1404 | if (edmg_bw_config > edmg_cap->bw_config)
|
| 1405 | return false;
|
| 1406 |
|
| 1407 | return true;
|
| 1408 | }
|
| 1409 |
|
| 1410 | static bool cfg80211_s1g_usable(struct wiphy *wiphy,
|
| 1411 | const struct cfg80211_chan_def *chandef)
|
| 1412 | {
|
| 1413 | u32 freq_khz;
|
| 1414 | const struct ieee80211_channel *chan;
|
| 1415 | u32 pri_khz = ieee80211_channel_to_khz(chandef->chan);
|
| 1416 | u32 end_khz = cfg80211_s1g_get_end_freq_khz(chandef);
|
| 1417 | u32 start_khz = cfg80211_s1g_get_start_freq_khz(chandef);
|
| 1418 | int width_mhz = cfg80211_chandef_get_width(chandef);
|
| 1419 | u32 prohibited_flags = IEEE80211_CHAN_DISABLED;
|
| 1420 |
|
| 1421 | if (width_mhz >= 16)
|
| 1422 | prohibited_flags |= IEEE80211_CHAN_NO_16MHZ;
|
| 1423 | if (width_mhz >= 8)
|
| 1424 | prohibited_flags |= IEEE80211_CHAN_NO_8MHZ;
|
| 1425 | if (width_mhz >= 4)
|
| 1426 | prohibited_flags |= IEEE80211_CHAN_NO_4MHZ;
|
| 1427 |
|
| 1428 | if (chandef->chan->flags & IEEE80211_CHAN_S1G_NO_PRIMARY)
|
| 1429 | return false;
|
| 1430 |
|
| 1431 | if (pri_khz < start_khz || pri_khz > end_khz)
|
| 1432 | return false;
|
| 1433 |
|
| 1434 | for_each_s1g_subchan(chandef, freq_khz) {
|
| 1435 | chan = ieee80211_get_channel_khz(wiphy, freq_khz);
|
| 1436 | if (!chan || (chan->flags & prohibited_flags))
|
| 1437 | return false;
|
| 1438 | }
|
| 1439 |
|
| 1440 | if (chandef->s1g_primary_2mhz) {
|
| 1441 | u32 sib_khz;
|
| 1442 | const struct ieee80211_channel *sibling;
|
| 1443 |
|
| 1444 | sibling = cfg80211_s1g_get_primary_sibling(wiphy, chandef);
|
| 1445 | if (!sibling)
|
| 1446 | return false;
|
| 1447 |
|
| 1448 | if (sibling->flags & IEEE80211_CHAN_S1G_NO_PRIMARY)
|
| 1449 | return false;
|
| 1450 |
|
| 1451 | sib_khz = ieee80211_channel_to_khz(sibling);
|
| 1452 | if (sib_khz < start_khz || sib_khz > end_khz)
|
| 1453 | return false;
|
| 1454 | }
|
| 1455 |
|
| 1456 | return true;
|
| 1457 | }
|
| 1458 |
|
| 1459 | bool _cfg80211_chandef_usable(struct wiphy *wiphy,
|
| 1460 | const struct cfg80211_chan_def *chandef,
|
| 1461 | u32 prohibited_flags,
|
| 1462 | u32 permitting_flags)
|
| 1463 | {
|
| 1464 | struct ieee80211_sta_ht_cap *ht_cap;
|
| 1465 | struct ieee80211_sta_vht_cap *vht_cap;
|
| 1466 | struct ieee80211_edmg *edmg_cap;
|
| 1467 | u32 width, control_freq, cap;
|
| 1468 | bool ext_nss_cap, support_80_80 = false, support_320 = false;
|
| 1469 | const struct ieee80211_sband_iftype_data *iftd;
|
| 1470 | struct ieee80211_supported_band *sband;
|
| 1471 | struct ieee80211_channel *c;
|
| 1472 | int i;
|
| 1473 |
|
| 1474 | if (WARN_ON(!cfg80211_chandef_valid(chandef)))
|
| 1475 | return false;
|
| 1476 |
|
| 1477 | ht_cap = &wiphy->bands[chandef->chan->band]->ht_cap;
|
| 1478 | vht_cap = &wiphy->bands[chandef->chan->band]->vht_cap;
|
| 1479 | edmg_cap = &wiphy->bands[chandef->chan->band]->edmg_cap;
|
| 1480 | ext_nss_cap = __le16_to_cpu(vht_cap->vht_mcs.tx_highest) &
|
| 1481 | IEEE80211_VHT_EXT_NSS_BW_CAPABLE;
|
| 1482 |
|
| 1483 | if (cfg80211_chandef_is_s1g(chandef))
|
| 1484 | return cfg80211_s1g_usable(wiphy, chandef);
|
| 1485 |
|
| 1486 | if (edmg_cap->channels &&
|
| 1487 | !cfg80211_edmg_usable(wiphy,
|
| 1488 | chandef->edmg.channels,
|
| 1489 | chandef->edmg.bw_config,
|
| 1490 | chandef->chan->hw_value,
|
| 1491 | edmg_cap))
|
| 1492 | return false;
|
| 1493 |
|
| 1494 | control_freq = chandef->chan->center_freq;
|
| 1495 |
|
| 1496 | switch (chandef->width) {
|
| 1497 | case NL80211_CHAN_WIDTH_20:
|
| 1498 | if (!ht_cap->ht_supported &&
|
| 1499 | chandef->chan->band != NL80211_BAND_6GHZ)
|
| 1500 | return false;
|
| 1501 | fallthrough;
|
| 1502 | case NL80211_CHAN_WIDTH_20_NOHT:
|
| 1503 | prohibited_flags |= IEEE80211_CHAN_NO_20MHZ;
|
| 1504 | width = 20;
|
| 1505 | break;
|
| 1506 | case NL80211_CHAN_WIDTH_40:
|
| 1507 | width = 40;
|
| 1508 | if (chandef->chan->band == NL80211_BAND_6GHZ)
|
| 1509 | break;
|
| 1510 | if (!ht_cap->ht_supported)
|
| 1511 | return false;
|
| 1512 | if (!(ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) ||
|
| 1513 | ht_cap->cap & IEEE80211_HT_CAP_40MHZ_INTOLERANT)
|
| 1514 | return false;
|
| 1515 | if (chandef->center_freq1 < control_freq &&
|
| 1516 | chandef->chan->flags & IEEE80211_CHAN_NO_HT40MINUS)
|
| 1517 | return false;
|
| 1518 | if (chandef->center_freq1 > control_freq &&
|
| 1519 | chandef->chan->flags & IEEE80211_CHAN_NO_HT40PLUS)
|
| 1520 | return false;
|
| 1521 | break;
|
| 1522 | case NL80211_CHAN_WIDTH_80P80:
|
| 1523 | cap = vht_cap->cap;
|
| 1524 | support_80_80 =
|
| 1525 | (cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ) ||
|
| 1526 | (cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ &&
|
| 1527 | cap & IEEE80211_VHT_CAP_EXT_NSS_BW_MASK) ||
|
| 1528 | (ext_nss_cap &&
|
| 1529 | u32_get_bits(cap, IEEE80211_VHT_CAP_EXT_NSS_BW_MASK) > 1);
|
| 1530 | if (chandef->chan->band != NL80211_BAND_6GHZ && !support_80_80)
|
| 1531 | return false;
|
| 1532 | fallthrough;
|
| 1533 | case NL80211_CHAN_WIDTH_80:
|
| 1534 | prohibited_flags |= IEEE80211_CHAN_NO_80MHZ;
|
| 1535 | width = 80;
|
| 1536 | if (chandef->chan->band == NL80211_BAND_6GHZ)
|
| 1537 | break;
|
| 1538 | if (!vht_cap->vht_supported)
|
| 1539 | return false;
|
| 1540 | break;
|
| 1541 | case NL80211_CHAN_WIDTH_160:
|
| 1542 | prohibited_flags |= IEEE80211_CHAN_NO_160MHZ;
|
| 1543 | width = 160;
|
| 1544 | if (chandef->chan->band == NL80211_BAND_6GHZ)
|
| 1545 | break;
|
| 1546 | if (!vht_cap->vht_supported)
|
| 1547 | return false;
|
| 1548 | cap = vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK;
|
| 1549 | if (cap != IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ &&
|
| 1550 | cap != IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ &&
|
| 1551 | !(ext_nss_cap &&
|
| 1552 | (vht_cap->cap & IEEE80211_VHT_CAP_EXT_NSS_BW_MASK)))
|
| 1553 | return false;
|
| 1554 | break;
|
| 1555 | case NL80211_CHAN_WIDTH_320:
|
| 1556 | prohibited_flags |= IEEE80211_CHAN_NO_320MHZ;
|
| 1557 | width = 320;
|
| 1558 |
|
| 1559 | if (chandef->chan->band != NL80211_BAND_6GHZ)
|
| 1560 | return false;
|
| 1561 |
|
| 1562 | sband = wiphy->bands[NL80211_BAND_6GHZ];
|
| 1563 | if (!sband)
|
| 1564 | return false;
|
| 1565 |
|
| 1566 | for_each_sband_iftype_data(sband, i, iftd) {
|
| 1567 | if (!iftd->eht_cap.has_eht)
|
| 1568 | continue;
|
| 1569 |
|
| 1570 | if (iftd->eht_cap.eht_cap_elem.phy_cap_info[0] &
|
| 1571 | IEEE80211_EHT_PHY_CAP0_320MHZ_IN_6GHZ) {
|
| 1572 | support_320 = true;
|
| 1573 | break;
|
| 1574 | }
|
| 1575 | }
|
| 1576 |
|
| 1577 | if (!support_320)
|
| 1578 | return false;
|
| 1579 | break;
|
| 1580 | default:
|
| 1581 | WARN_ON_ONCE(1);
|
| 1582 | return false;
|
| 1583 | }
|
| 1584 |
|
| 1585 | |
| 1586 | * TODO: What if there are only certain 80/160/80+80 MHz channels
|
| 1587 | * allowed by the driver, or only certain combinations?
|
| 1588 | * For 40 MHz the driver can set the NO_HT40 flags, but for
|
| 1589 | * 80/160 MHz and in particular 80+80 MHz this isn't really
|
| 1590 | * feasible and we only have NO_80MHZ/NO_160MHZ so far but
|
| 1591 | * no way to cover 80+80 MHz or more complex restrictions.
|
| 1592 | * Note that such restrictions also need to be advertised to
|
| 1593 | * userspace, for example for P2P channel selection.
|
| 1594 |
|
| 1595 |
|
| 1596 | if (width > 20)
|
| 1597 | prohibited_flags |= IEEE80211_CHAN_NO_OFDM;
|
| 1598 |
|
| 1599 |
|
| 1600 | if (width < 20)
|
| 1601 | prohibited_flags |= IEEE80211_CHAN_NO_OFDM;
|
| 1602 |
|
| 1603 | for_each_subchan(chandef, freq, cf) {
|
| 1604 | c = ieee80211_get_channel_khz(wiphy, freq);
|
| 1605 | if (!c)
|
| 1606 | return false;
|
| 1607 | if (c->flags & permitting_flags)
|
| 1608 | continue;
|
| 1609 | if (c->flags & prohibited_flags)
|
| 1610 | return false;
|
| 1611 | }
|
| 1612 |
|
| 1613 | return true;
|
| 1614 | }
|
| 1615 |
|
| 1616 | bool cfg80211_chandef_usable(struct wiphy *wiphy,
|
| 1617 | const struct cfg80211_chan_def *chandef,
|
| 1618 | u32 prohibited_flags)
|
| 1619 | {
|
| 1620 | return _cfg80211_chandef_usable(wiphy, chandef, prohibited_flags, 0);
|
| 1621 | }
|
| 1622 | EXPORT_SYMBOL(cfg80211_chandef_usable);
|
| 1623 |
|
| 1624 | static bool cfg80211_ir_permissive_check_wdev(enum nl80211_iftype iftype,
|
| 1625 | struct wireless_dev *wdev,
|
| 1626 | struct ieee80211_channel *chan)
|
| 1627 | {
|
| 1628 | struct ieee80211_channel *other_chan = NULL;
|
| 1629 | unsigned int link_id;
|
| 1630 | int r1, r2;
|
| 1631 |
|
| 1632 | for_each_valid_link(wdev, link_id) {
|
| 1633 | if (wdev->iftype == NL80211_IFTYPE_STATION &&
|
| 1634 | wdev->links[link_id].client.current_bss)
|
| 1635 | other_chan = wdev->links[link_id].client.current_bss->pub.channel;
|
| 1636 |
|
| 1637 | |
| 1638 | * If a GO already operates on the same GO_CONCURRENT channel,
|
| 1639 | * this one (maybe the same one) can beacon as well. We allow
|
| 1640 | * the operation even if the station we relied on with
|
| 1641 | * GO_CONCURRENT is disconnected now. But then we must make sure
|
| 1642 | * we're not outdoor on an indoor-only channel.
|
| 1643 |
|
| 1644 | if (iftype == NL80211_IFTYPE_P2P_GO &&
|
| 1645 | wdev->iftype == NL80211_IFTYPE_P2P_GO &&
|
| 1646 | wdev->links[link_id].ap.beacon_interval &&
|
| 1647 | !(chan->flags & IEEE80211_CHAN_INDOOR_ONLY))
|
| 1648 | other_chan = wdev->links[link_id].ap.chandef.chan;
|
| 1649 |
|
| 1650 | if (!other_chan)
|
| 1651 | continue;
|
| 1652 |
|
| 1653 | if (chan == other_chan)
|
| 1654 | return true;
|
| 1655 |
|
| 1656 | if (chan->band != NL80211_BAND_5GHZ &&
|
| 1657 | chan->band != NL80211_BAND_6GHZ)
|
| 1658 | continue;
|
| 1659 |
|
| 1660 | r1 = cfg80211_get_unii(chan->center_freq);
|
| 1661 | r2 = cfg80211_get_unii(other_chan->center_freq);
|
| 1662 |
|
| 1663 | if (r1 != -EINVAL && r1 == r2) {
|
| 1664 | |
| 1665 | * At some locations channels 149-165 are considered a
|
| 1666 | * bundle, but at other locations, e.g., Indonesia,
|
| 1667 | * channels 149-161 are considered a bundle while
|
| 1668 | * channel 165 is left out and considered to be in a
|
| 1669 | * different bundle. Thus, in case that there is a
|
| 1670 | * station interface connected to an AP on channel 165,
|
| 1671 | * it is assumed that channels 149-161 are allowed for
|
| 1672 | * GO operations. However, having a station interface
|
| 1673 | * connected to an AP on channels 149-161, does not
|
| 1674 | * allow GO operation on channel 165.
|
| 1675 |
|
| 1676 | if (chan->center_freq == 5825 &&
|
| 1677 | other_chan->center_freq != 5825)
|
| 1678 | continue;
|
| 1679 | return true;
|
| 1680 | }
|
| 1681 | }
|
| 1682 |
|
| 1683 | return false;
|
| 1684 | }
|
| 1685 |
|
| 1686 | |
| 1687 | * Check if the channel can be used under permissive conditions mandated by
|
| 1688 | * some regulatory bodies, i.e., the channel is marked with
|
| 1689 | * IEEE80211_CHAN_IR_CONCURRENT and there is an additional station interface
|
| 1690 | * associated to an AP on the same channel or on the same UNII band
|
| 1691 | * (assuming that the AP is an authorized master).
|
| 1692 | * In addition allow operation on a channel on which indoor operation is
|
| 1693 | * allowed, iff we are currently operating in an indoor environment.
|
| 1694 |
|
| 1695 | static bool cfg80211_ir_permissive_chan(struct wiphy *wiphy,
|
| 1696 | enum nl80211_iftype iftype,
|
| 1697 | struct ieee80211_channel *chan)
|
| 1698 | {
|
| 1699 | struct wireless_dev *wdev;
|
| 1700 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
|
| 1701 |
|
| 1702 | lockdep_assert_held(&rdev->wiphy.mtx);
|
| 1703 |
|
| 1704 | if (!IS_ENABLED(CONFIG_CFG80211_REG_RELAX_NO_IR) ||
|
| 1705 | !(wiphy->regulatory_flags & REGULATORY_ENABLE_RELAX_NO_IR))
|
| 1706 | return false;
|
| 1707 |
|
| 1708 |
|
| 1709 | if (iftype != NL80211_IFTYPE_P2P_GO &&
|
| 1710 | iftype != NL80211_IFTYPE_STATION &&
|
| 1711 | iftype != NL80211_IFTYPE_P2P_CLIENT)
|
| 1712 | return false;
|
| 1713 |
|
| 1714 | if (regulatory_indoor_allowed() &&
|
| 1715 | (chan->flags & IEEE80211_CHAN_INDOOR_ONLY))
|
| 1716 | return true;
|
| 1717 |
|
| 1718 | if (!(chan->flags & IEEE80211_CHAN_IR_CONCURRENT))
|
| 1719 | return false;
|
| 1720 |
|
| 1721 | |
| 1722 | * Generally, it is possible to rely on another device/driver to allow
|
| 1723 | * the IR concurrent relaxation, however, since the device can further
|
| 1724 | * enforce the relaxation (by doing a similar verifications as this),
|
| 1725 | * and thus fail the GO instantiation, consider only the interfaces of
|
| 1726 | * the current registered device.
|
| 1727 |
|
| 1728 | list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) {
|
| 1729 | bool ret;
|
| 1730 |
|
| 1731 | ret = cfg80211_ir_permissive_check_wdev(iftype, wdev, chan);
|
| 1732 | if (ret)
|
| 1733 | return ret;
|
| 1734 | }
|
| 1735 |
|
| 1736 | return false;
|
| 1737 | }
|
| 1738 |
|
| 1739 | static bool _cfg80211_reg_can_beacon(struct wiphy *wiphy,
|
| 1740 | struct cfg80211_chan_def *chandef,
|
| 1741 | enum nl80211_iftype iftype,
|
| 1742 | u32 prohibited_flags,
|
| 1743 | u32 permitting_flags)
|
| 1744 | {
|
| 1745 | bool res, check_radar;
|
| 1746 | int dfs_required;
|
| 1747 |
|
| 1748 | trace_cfg80211_reg_can_beacon(wiphy, chandef, iftype,
|
| 1749 | prohibited_flags,
|
| 1750 | permitting_flags);
|
| 1751 |
|
| 1752 | if (!_cfg80211_chandef_usable(wiphy, chandef,
|
| 1753 | IEEE80211_CHAN_DISABLED, 0))
|
| 1754 | return false;
|
| 1755 |
|
| 1756 | dfs_required = cfg80211_chandef_dfs_required(wiphy, chandef, iftype);
|
| 1757 | check_radar = dfs_required != 0;
|
| 1758 |
|
| 1759 | if (dfs_required > 0 &&
|
| 1760 | cfg80211_chandef_dfs_available(wiphy, chandef)) {
|
| 1761 |
|
| 1762 | prohibited_flags &= ~IEEE80211_CHAN_NO_IR;
|
| 1763 | check_radar = false;
|
| 1764 | }
|
| 1765 |
|
| 1766 | if (check_radar &&
|
| 1767 | !_cfg80211_chandef_usable(wiphy, chandef,
|
| 1768 | IEEE80211_CHAN_RADAR, 0))
|
| 1769 | return false;
|
| 1770 |
|
| 1771 | res = _cfg80211_chandef_usable(wiphy, chandef,
|
| 1772 | prohibited_flags,
|
| 1773 | permitting_flags);
|
| 1774 |
|
| 1775 | trace_cfg80211_return_bool(res);
|
| 1776 | return res;
|
| 1777 | }
|
| 1778 |
|
| 1779 | bool cfg80211_reg_check_beaconing(struct wiphy *wiphy,
|
| 1780 | struct cfg80211_chan_def *chandef,
|
| 1781 | struct cfg80211_beaconing_check_config *cfg)
|
| 1782 | {
|
| 1783 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
|
| 1784 | u32 permitting_flags = 0;
|
| 1785 | bool check_no_ir = true;
|
| 1786 |
|
| 1787 | |
| 1788 | * Under certain conditions suggested by some regulatory bodies a
|
| 1789 | * GO/STA can IR on channels marked with IEEE80211_NO_IR. Set this flag
|
| 1790 | * only if such relaxations are not enabled and the conditions are not
|
| 1791 | * met.
|
| 1792 |
|
| 1793 | if (cfg->relax) {
|
| 1794 | lockdep_assert_held(&rdev->wiphy.mtx);
|
| 1795 | check_no_ir = !cfg80211_ir_permissive_chan(wiphy, cfg->iftype,
|
| 1796 | chandef->chan);
|
| 1797 | }
|
| 1798 |
|
| 1799 | if (cfg->reg_power == IEEE80211_REG_VLP_AP)
|
| 1800 | permitting_flags |= IEEE80211_CHAN_ALLOW_6GHZ_VLP_AP;
|
| 1801 |
|
| 1802 | if ((cfg->iftype == NL80211_IFTYPE_P2P_GO ||
|
| 1803 | cfg->iftype == NL80211_IFTYPE_AP) &&
|
| 1804 | (chandef->width == NL80211_CHAN_WIDTH_20_NOHT ||
|
| 1805 | chandef->width == NL80211_CHAN_WIDTH_20))
|
| 1806 | permitting_flags |= IEEE80211_CHAN_ALLOW_20MHZ_ACTIVITY;
|
| 1807 |
|
| 1808 | return _cfg80211_reg_can_beacon(wiphy, chandef, cfg->iftype,
|
| 1809 | check_no_ir ? IEEE80211_CHAN_NO_IR : 0,
|
| 1810 | permitting_flags);
|
| 1811 | }
|
| 1812 | EXPORT_SYMBOL(cfg80211_reg_check_beaconing);
|
| 1813 |
|
| 1814 | int cfg80211_set_monitor_channel(struct cfg80211_registered_device *rdev,
|
| 1815 | struct net_device *dev,
|
| 1816 | struct cfg80211_chan_def *chandef)
|
| 1817 | {
|
| 1818 | if (!rdev->ops->set_monitor_channel)
|
| 1819 | return -EOPNOTSUPP;
|
| 1820 | if (!cfg80211_has_monitors_only(rdev))
|
| 1821 | return -EBUSY;
|
| 1822 |
|
| 1823 | return rdev_set_monitor_channel(rdev, dev, chandef);
|
| 1824 | }
|
| 1825 |
|
| 1826 | bool cfg80211_any_usable_channels(struct wiphy *wiphy,
|
| 1827 | unsigned long sband_mask,
|
| 1828 | u32 prohibited_flags)
|
| 1829 | {
|
| 1830 | int idx;
|
| 1831 |
|
| 1832 | prohibited_flags |= IEEE80211_CHAN_DISABLED;
|
| 1833 |
|
| 1834 | for_each_set_bit(idx, &sband_mask, NUM_NL80211_BANDS) {
|
| 1835 | struct ieee80211_supported_band *sband = wiphy->bands[idx];
|
| 1836 | int chanidx;
|
| 1837 |
|
| 1838 | if (!sband)
|
| 1839 | continue;
|
| 1840 |
|
| 1841 | for (chanidx = 0; chanidx < sband->n_channels; chanidx++) {
|
| 1842 | struct ieee80211_channel *chan;
|
| 1843 |
|
| 1844 | chan = &sband->channels[chanidx];
|
| 1845 |
|
| 1846 | if (chan->flags & prohibited_flags)
|
| 1847 | continue;
|
| 1848 |
|
| 1849 | return true;
|
| 1850 | }
|
| 1851 | }
|
| 1852 |
|
| 1853 | return false;
|
| 1854 | }
|
| 1855 | EXPORT_SYMBOL(cfg80211_any_usable_channels);
|
| 1856 |
|
| 1857 | struct cfg80211_chan_def *wdev_chandef(struct wireless_dev *wdev,
|
| 1858 | unsigned int link_id)
|
| 1859 | {
|
| 1860 | lockdep_assert_wiphy(wdev->wiphy);
|
| 1861 |
|
| 1862 | WARN_ON(wdev->valid_links && !(wdev->valid_links & BIT(link_id)));
|
| 1863 | WARN_ON(!wdev->valid_links && link_id > 0);
|
| 1864 |
|
| 1865 | switch (wdev->iftype) {
|
| 1866 | case NL80211_IFTYPE_MESH_POINT:
|
| 1867 | return &wdev->u.mesh.chandef;
|
| 1868 | case NL80211_IFTYPE_ADHOC:
|
| 1869 | return &wdev->u.ibss.chandef;
|
| 1870 | case NL80211_IFTYPE_OCB:
|
| 1871 | return &wdev->u.ocb.chandef;
|
| 1872 | case NL80211_IFTYPE_AP:
|
| 1873 | case NL80211_IFTYPE_P2P_GO:
|
| 1874 | return &wdev->links[link_id].ap.chandef;
|
| 1875 | default:
|
| 1876 | return NULL;
|
| 1877 | }
|
| 1878 | }
|
| 1879 | EXPORT_SYMBOL(wdev_chandef);
|