1 /*
2 * Management of interference due to incumbent signals in 6 GHz band
3 * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
4 *
5 * This software may be distributed under the terms of the BSD license.
6 * See README for more details.
7 */
8
9 #include "utils/includes.h"
10 #include "utils/common.h"
11 #include "common/hw_features_common.h"
12 #include "common/ieee802_11_common.h"
13 #include "hostapd.h"
14 #include "ap_drv_ops.h"
15 #include "beacon.h"
16 #include "interference.h"
17
18
19 #define BASE_6GHZ_FREQ 5950
20 /* Channel 2 (op class 136) has a center frequency of 5935 MHz. */
21 #define BASE_6GHZ_CH2_FREQ 5935
22
23 enum chan_seg {
24 SEG_PRI20 = 0x1,
25 SEG_SEC20 = 0x2,
26 SEG_SEC40_LOW = 0x4,
27 SEG_SEC40_UP = 0x8,
28 SEG_SEC40 = 0xC,
29 SEG_SEC80_LOW = 0x10,
30 SEG_SEC80_LOW_UP = 0x20,
31 SEG_SEC80_UP_LOW = 0x40,
32 SEG_SEC80_UP = 0x80,
33 SEG_SEC80 = 0xF0,
34 SEG_SEC160_LOW = 0x0100,
35 SEG_SEC160_LOW_UP = 0x0200,
36 SEG_SEC160_LOW_UP_UP = 0x0400,
37 SEG_SEC160_LOW_UP_UP_UP = 0x0800,
38 SEG_SEC160_UP_LOW_LOW_LOW = 0x1000,
39 SEG_SEC160_UP_LOW_LOW = 0x2000,
40 SEG_SEC160_UP_LOW = 0x4000,
41 SEG_SEC160_UP = 0x8000,
42 SEG_SEC160 = 0xFF00,
43 };
44
45
is_chan_disabled(struct hostapd_hw_modes * mode,int chan_num)46 static bool is_chan_disabled(struct hostapd_hw_modes *mode, int chan_num)
47 {
48 struct hostapd_channel_data *chan;
49 int i;
50
51 for (i = 0; i < mode->num_channels; i++) {
52 chan = &mode->channels[i];
53 if (chan->chan == chan_num)
54 return !!(chan->flag & HOSTAPD_CHAN_DISABLED);
55 }
56
57 return true;
58 }
59
60
61 /*
62 * chan_range_available - Check whether the channel can operate in the given
63 * bandwidth in 6 GHz
64 * @first_chan_idx - Channel index of the first 20 MHz channel in a segment
65 * @num_chans - Number of 20 MHz channels needed for the operating bandwidth
66 * @allowed_arr - Range of channels allowed in the given bandwidth
67 * @allowed_arr_size - Number of channels allowed in the given bandwidth
68 * Returns: Whether the channel can operation in the given bandwidth
69 */
chan_range_available(struct hostapd_hw_modes * mode,unsigned int first_chan_idx,unsigned int num_chans,const int * allowed_arr,unsigned int allowed_arr_size)70 static bool chan_range_available(struct hostapd_hw_modes *mode,
71 unsigned int first_chan_idx,
72 unsigned int num_chans,
73 const int *allowed_arr,
74 unsigned int allowed_arr_size)
75 {
76 struct hostapd_channel_data *first_chan;
77 unsigned int i;
78
79 first_chan = &mode->channels[first_chan_idx];
80
81 if (!chan_pri_allowed(first_chan)) {
82 wpa_printf(MSG_DEBUG, "Intf: Primary channel %d not allowed",
83 first_chan->chan);
84 return false;
85 }
86
87 /* 20 MHz channel, so no need to check the range */
88 if (num_chans == 1)
89 return true;
90
91 if (allowed_arr) {
92 for (i = 0; i < allowed_arr_size; i++) {
93 if (first_chan->chan == allowed_arr[i])
94 break;
95 }
96 if (i == allowed_arr_size)
97 return false;
98 }
99
100 /*
101 * Check whether all the 20 MHz channels in the given operating range
102 * are enabled.
103 */
104 for (i = 1; i < num_chans; i++) {
105 if (is_chan_disabled(mode, first_chan->chan + i * 4))
106 return false;
107 }
108
109 return true;
110 }
111
112
is_in_chanlist(struct hostapd_iface * iface,struct hostapd_channel_data * chan)113 static bool is_in_chanlist(struct hostapd_iface *iface,
114 struct hostapd_channel_data *chan)
115 {
116 if (!iface->conf->acs_ch_list.num)
117 return true;
118
119 return freq_range_list_includes(&iface->conf->acs_ch_list, chan->chan);
120 }
121
122
get_center_freq_6g(int chan_idx,enum chan_width chan_width,int * center_freq)123 static int get_center_freq_6g(int chan_idx, enum chan_width chan_width,
124 int *center_freq)
125 {
126 if (!center_freq)
127 return -1;
128
129 *center_freq = 0;
130
131 switch (chan_width) {
132 case CHAN_WIDTH_20:
133 if (chan_idx == 2) {
134 *center_freq = BASE_6GHZ_CH2_FREQ;
135 break;
136 }
137 if (chan_idx >= 1 && chan_idx <= 233)
138 *center_freq = ((chan_idx / 4) * 4 + 1) * 5 +
139 BASE_6GHZ_FREQ;
140 break;
141 case CHAN_WIDTH_40:
142 if (chan_idx >= 1 && chan_idx <= 229)
143 *center_freq = ((chan_idx / 8) * 8 + 3) * 5 +
144 BASE_6GHZ_FREQ;
145 break;
146 case CHAN_WIDTH_80:
147 if (chan_idx >= 1 && chan_idx <= 221)
148 *center_freq = ((chan_idx / 16) * 16 + 7) * 5 +
149 BASE_6GHZ_FREQ;
150 break;
151 case CHAN_WIDTH_160:
152 if (chan_idx >= 1 && chan_idx <= 221)
153 *center_freq = ((chan_idx / 32) * 32 + 15) * 5 +
154 BASE_6GHZ_FREQ;
155 break;
156 case CHAN_WIDTH_320:
157 if (chan_idx >= 1 && chan_idx <= 221)
158 *center_freq = ((chan_idx / 32) * 32 + 31) * 5 +
159 BASE_6GHZ_FREQ;
160 break;
161 default:
162 break;
163 }
164
165 if (*center_freq == 0)
166 return -1;
167
168 return 0;
169 }
170
171
is_interference_in_chanlist(int freq_start,int freq_end,const int * interference_freqs)172 static bool is_interference_in_chanlist(int freq_start, int freq_end,
173 const int *interference_freqs)
174 {
175 int i;
176
177 for (i = freq_start; i <= freq_end; i += 20) {
178 if (int_array_includes(interference_freqs, i))
179 return true;
180 }
181 return false;
182 }
183
184
185
get_allowed_channel_array(int num_chans,unsigned int * size)186 static const int * get_allowed_channel_array(int num_chans, unsigned int *size)
187 {
188 static const int allowed_40_6g[] = { 1, 9, 17, 25, 33, 41, 49, 57, 65,
189 73, 81, 89, 97, 105, 113, 121, 129,
190 137, 145, 153, 161, 169, 177, 185,
191 193, 201, 209, 217, 225, 233 };
192 static const int allowed_80_6g[] = { 1, 17, 33, 49, 65, 81, 97, 113,
193 129, 145, 161, 177,
194 193, 209 };
195 static const int allowed_160_6g[] = { 1, 33, 65, 97, 129, 161, 193 };
196 static const int allowed_320_6g[] = { 1, 65, 129, 33, 97, 161 };
197
198 switch (num_chans) {
199 case 2:
200 *size = ARRAY_SIZE(allowed_40_6g);
201 return allowed_40_6g;
202 case 4:
203 *size = ARRAY_SIZE(allowed_80_6g);
204 return allowed_80_6g;
205 case 8:
206 *size = ARRAY_SIZE(allowed_160_6g);
207 return allowed_160_6g;
208 case 16:
209 *size = ARRAY_SIZE(allowed_320_6g);
210 return allowed_320_6g;
211 default:
212 *size = 0;
213 return NULL;
214 }
215 }
216
217
218 /**
219 * intf_find_channel_list - Find the list of channels that can operate with
220 * channel width chan_width and not present within the range of current
221 * operating range.
222 * @chan_width - Channel width to be checked
223 * @chandef_list - Pointer array to hold the list of valid available chandef
224 * Returns: The total number of available chandefs that support the provided
225 * bandwidth
226 */
intf_find_channel_list(struct hostapd_iface * iface,int chan_width,struct hostapd_channel_data ** chandef_list,const int * interference_freqs)227 static int intf_find_channel_list(struct hostapd_iface *iface, int chan_width,
228 struct hostapd_channel_data **chandef_list,
229 const int *interference_freqs)
230 {
231 struct hostapd_hw_modes *mode = iface->current_mode;
232 int new_center_freq, new_start_freq, new_end_freq;
233 const int *allowed_arr = NULL;
234 unsigned int allowed_arr_size = 0;
235 struct hostapd_channel_data *chan;
236 int i;
237 unsigned int channel_idx = 0, n_chans;
238 int ret;
239
240 switch (chan_width) {
241 case CHAN_WIDTH_20_NOHT:
242 case CHAN_WIDTH_20:
243 n_chans = 1;
244 break;
245 case CHAN_WIDTH_40:
246 n_chans = 2;
247 break;
248 case CHAN_WIDTH_80:
249 n_chans = 4;
250 break;
251 case CHAN_WIDTH_80P80:
252 case CHAN_WIDTH_160:
253 n_chans = 8;
254 break;
255 case CHAN_WIDTH_320:
256 n_chans = 16;
257 break;
258 default:
259 n_chans = 1;
260 break;
261 }
262
263 allowed_arr = get_allowed_channel_array(n_chans, &allowed_arr_size);
264
265 for (i = 0; i < mode->num_channels; i++) {
266 chan = &mode->channels[i];
267
268 if (!chan_in_current_hw_info(iface->current_hw_info, chan)) {
269 wpa_printf(MSG_DEBUG,
270 "Intf: Channel %d (%d) is not under current hardware index",
271 chan->freq, chan->chan);
272 continue;
273 }
274
275 /* Skip incompatible chandefs */
276 if (!chan_range_available(mode, i, n_chans,
277 allowed_arr, allowed_arr_size)) {
278 wpa_printf(MSG_DEBUG,
279 "Intf: Range not available for chan %d (%d)",
280 chan->freq, chan->chan);
281 continue;
282 }
283
284 if (!is_in_chanlist(iface, chan)) {
285 wpa_printf(MSG_DEBUG,
286 "Intf: Channel %d (%d) not in chanlist",
287 chan->freq, chan->chan);
288 continue;
289 }
290
291 ret = get_center_freq_6g(chan->chan, chan_width,
292 &new_center_freq);
293 if (ret) {
294 wpa_printf(MSG_INFO,
295 "Intf: Couldn't find center freq for chan %d chan_width %d",
296 chan->chan, chan_width);
297 return 0;
298 }
299
300 new_start_freq = (new_center_freq -
301 channel_width_to_int(chan_width) / 2) + 10;
302 new_end_freq = (new_center_freq +
303 channel_width_to_int(chan_width) / 2) - 10;
304
305 if (is_interference_in_chanlist(new_start_freq, new_end_freq,
306 interference_freqs)) {
307 wpa_printf(MSG_DEBUG,
308 "Intf: Found frequency which has interference in channel (%d)",
309 chan->chan);
310 continue;
311 }
312
313 wpa_printf(MSG_DEBUG,
314 "Intf: Adding channel %d (%d) to valid chandef list",
315 chan->freq, chan->chan);
316 chandef_list[channel_idx] = chan;
317 channel_idx++;
318 }
319
320 return channel_idx;
321 }
322
323
downgrade_bandwidth(enum chan_width chan_width)324 static enum chan_width downgrade_bandwidth(enum chan_width chan_width)
325 {
326 switch (chan_width) {
327 case CHAN_WIDTH_320:
328 return CHAN_WIDTH_160;
329 case CHAN_WIDTH_160:
330 return CHAN_WIDTH_80;
331 case CHAN_WIDTH_80:
332 return CHAN_WIDTH_40;
333 case CHAN_WIDTH_40:
334 return CHAN_WIDTH_20;
335 default:
336 return CHAN_WIDTH_20_NOHT;
337 }
338 }
339
340
341 /*
342 * hostapd_incumbt_sig_intf_detected - Incumbent signal interference is detected
343 * in the operating channel. The interference channel information is available
344 * as a bitmap(chan_bw_interference_bitmap). If interference has occurred in
345 * the primary channel, do a complete channel switch to a different channel;
346 * otherwise, reduce the operating bandwidth and continue AP operation using
347 * the same primary channel.
348 */
hostapd_incumbt_sig_intf_detected(struct hostapd_iface * iface,int freq,int chan_width,int cf1,int cf2,u32 chan_bw_interference_bitmap)349 int hostapd_incumbt_sig_intf_detected(struct hostapd_iface *iface, int freq,
350 int chan_width, int cf1, int cf2,
351 u32 chan_bw_interference_bitmap)
352 {
353 struct csa_settings settings;
354 struct hostapd_channel_data *chan_data = NULL;
355 struct hostapd_channel_data *chan_temp;
356 struct hostapd_channel_data **available_chandef_list = NULL;
357 int ret = 0;
358 unsigned int i;
359 u32 _rand;
360 u32 chan_idx;
361 int num_available_chandefs = 0;
362 enum chan_width new_chan_width;
363 int new_center_freq;
364 int current_start_freq;
365 int temp_width;
366 struct hostapd_hw_modes *mode = iface->current_mode;
367 int *interference_freqs = NULL;
368 int primary_chan_bit = -1;
369 int segment_freq;
370 int start_freq = (cf1 - channel_width_to_int(chan_width) / 2) + 10;
371 u8 channel_no;
372 unsigned int num_segments;
373 int new_bw;
374
375 wpa_printf(MSG_DEBUG,
376 "Intf: input freq=%d, chan_width=%d, cf1=%d, cf2=%d, chan_bw_interference_bitmap=0x%x",
377 freq, chan_width, cf1, cf2, chan_bw_interference_bitmap);
378
379 num_segments = channel_width_to_int(chan_width) / 20;
380 if (!num_segments) {
381 wpa_printf(MSG_INFO, "Intf: Invalid channel width %d",
382 chan_width);
383 return -1;
384 }
385
386 /* Determine which 20 MHz segment contains the primary channel */
387 for (i = 0; i < num_segments; i++) {
388 segment_freq = start_freq + i * 20;
389 if (segment_freq == freq) {
390 primary_chan_bit = i;
391 break;
392 }
393 }
394
395 /* Check whether interference has occurred in primary 20 MHz channel */
396 if (primary_chan_bit >= 0 &&
397 (chan_bw_interference_bitmap & BIT(primary_chan_bit))) {
398 available_chandef_list = os_calloc(
399 mode->num_channels,
400 sizeof(struct hostapd_channel_data *));
401 if (!available_chandef_list) {
402 wpa_printf(MSG_ERROR,
403 "Intf: Failed to allocate available_chandef_list");
404 return -1;
405 }
406
407 /* Store frequencies with interference in interference_freqs */
408 current_start_freq =
409 (cf1 - channel_width_to_int(chan_width) / 2) + 10;
410 for (i = 0; i < num_segments; i++) {
411 if (chan_bw_interference_bitmap & BIT(i)) {
412 wpa_printf(MSG_DEBUG,
413 "Intf: Found incumbent signal interference in frequency %d",
414 current_start_freq + 20 * i);
415 int_array_add_unique(&interference_freqs,
416 current_start_freq +
417 20 * i);
418 if (!interference_freqs) {
419 wpa_printf(MSG_ERROR,
420 "Intf: Failed to store interference frequencies");
421 ret = -1;
422 goto exit;
423 }
424 }
425 }
426
427 /* Find a random channel to be switched */
428 temp_width = chan_width;
429
430 while (temp_width > CHAN_WIDTH_20_NOHT) {
431 num_available_chandefs =
432 intf_find_channel_list(iface, temp_width,
433 available_chandef_list,
434 interference_freqs);
435 if (num_available_chandefs > 0)
436 break;
437 wpa_printf(MSG_DEBUG, "Intf: Downgrading bandwidth");
438 temp_width = downgrade_bandwidth(temp_width);
439 }
440
441 if (num_available_chandefs == 0) {
442 wpa_printf(MSG_INFO, "Intf: No available_chandefs");
443 goto exit;
444 }
445
446 if (os_get_random((u8 *) &_rand, sizeof(_rand)) < 0)
447 _rand = os_random();
448
449 chan_idx = _rand % num_available_chandefs;
450 chan_data = available_chandef_list[chan_idx];
451 new_chan_width = temp_width;
452
453 wpa_printf(MSG_DEBUG, "Intf: Got random channel %d (%d)",
454 chan_data->freq, chan_data->chan);
455 } else {
456 /*
457 * Interference is not present in the primary 20 MHz, so
458 * reduce bandwidth.
459 */
460 u8 seg0 = hostapd_get_oper_centr_freq_seg0_idx(iface->conf);
461 u8 seg1 = hostapd_get_oper_centr_freq_seg1_idx(iface->conf);
462 enum oper_chan_width oper_chwidth;
463 int j;
464
465 for (j = 0; j < mode->num_channels; j++) {
466 chan_temp = &mode->channels[j];
467 if (chan_temp->freq == freq)
468 chan_data = chan_temp;
469 }
470 if (!chan_data) {
471 wpa_printf(MSG_INFO, "Intf: No channel found");
472 goto exit;
473 }
474
475 oper_chwidth = chan_width_to_oper_chwidth(chan_width);
476
477 if (chan_width > CHAN_WIDTH_40) {
478 punct_update_legacy_bw(chan_bw_interference_bitmap,
479 iface->conf->channel,
480 &oper_chwidth, &seg0, &seg1);
481 if (oper_chwidth == CONF_OPER_CHWIDTH_160MHZ)
482 new_chan_width = CHAN_WIDTH_160;
483 else if (oper_chwidth == CONF_OPER_CHWIDTH_80MHZ)
484 new_chan_width = CHAN_WIDTH_80;
485 else if (seg0 == 0)
486 new_chan_width = CHAN_WIDTH_20;
487 else
488 new_chan_width = CHAN_WIDTH_40;
489 } else {
490 new_chan_width = CHAN_WIDTH_20;
491 }
492 }
493
494 if (new_chan_width > CHAN_WIDTH_20) {
495 ret = get_center_freq_6g(chan_data->chan, new_chan_width,
496 &new_center_freq);
497 if (ret) {
498 wpa_printf(MSG_ERROR,
499 "Intf: Couldn't find center freq for chan : %d chan_width : %d",
500 chan_data->chan, new_chan_width);
501 goto exit;
502 }
503 } else {
504 new_center_freq = chan_data->freq;
505 }
506
507 ieee80211_freq_to_chan(chan_data->freq, &channel_no);
508
509 os_memset(&settings, 0, sizeof(settings));
510 settings.cs_count = 5;
511 settings.freq_params.freq = chan_data->freq;
512
513 switch (new_chan_width) {
514 case CHAN_WIDTH_40:
515 new_bw = 40;
516 break;
517 case CHAN_WIDTH_80P80:
518 case CHAN_WIDTH_80:
519 new_bw = 80;
520 break;
521 case CHAN_WIDTH_160:
522 new_bw = 160;
523 break;
524 case CHAN_WIDTH_320:
525 new_bw = 320;
526 break;
527 default:
528 new_bw = 20;
529 break;
530 }
531
532 settings.freq_params.bandwidth = new_bw;
533 settings.freq_params.channel = channel_no;
534 settings.freq_params.center_freq1 = new_center_freq;
535 settings.freq_params.ht_enabled = iface->conf->ieee80211n;
536 settings.freq_params.vht_enabled = iface->conf->ieee80211ac;
537 settings.freq_params.he_enabled = iface->conf->ieee80211ax;
538 settings.freq_params.eht_enabled = iface->conf->ieee80211be;
539 wpa_printf(MSG_DEBUG,
540 "Intf: channel=%u, freq=%d, bw=%d, center_freq1=%d",
541 settings.freq_params.channel,
542 settings.freq_params.freq,
543 settings.freq_params.bandwidth,
544 settings.freq_params.center_freq1);
545
546 if (chan_data->freq == iface->freq) {
547 if (hostapd_change_config_freq(iface->bss[0], iface->conf,
548 &settings.freq_params, NULL)) {
549 wpa_printf(MSG_INFO,
550 "Intf: Failed to update bandwidth");
551 ret = -1;
552 goto exit;
553 }
554 if (hostapd_set_freq(
555 iface->bss[0], iface->conf->hw_mode, iface->freq,
556 iface->conf->channel, iface->conf->enable_edmg,
557 iface->conf->edmg_channel, iface->conf->ieee80211n,
558 iface->conf->ieee80211ac,
559 iface->conf->ieee80211ax,
560 iface->conf->ieee80211be,
561 iface->conf->secondary_channel,
562 hostapd_get_oper_chwidth(iface->conf),
563 hostapd_get_oper_centr_freq_seg0_idx(iface->conf),
564 hostapd_get_oper_centr_freq_seg1_idx(iface->conf))) {
565 wpa_printf(MSG_ERROR,
566 "Intf: Failed to apply bandwidth update");
567 ret = -1;
568 goto exit;
569 }
570 ieee802_11_set_beacons(iface);
571 goto exit;
572 }
573
574 /* Channel and bandwidth have been decided, triggering channel switch */
575 for (i = 0; i < iface->num_bss; i++) {
576 /* Save CHAN_SWITCH VHT and HE config */
577 hostapd_chan_switch_config(iface->bss[i],
578 &settings.freq_params);
579
580 ret = hostapd_switch_channel(iface->bss[i], &settings);
581 if (ret) {
582 wpa_printf(MSG_ERROR,
583 "Intf: Channel switch failed");
584 break;
585 }
586 }
587
588 exit:
589 os_free(available_chandef_list);
590 os_free(interference_freqs);
591 return ret;
592 }
593