xref: /freebsd/contrib/wpa/src/ap/interference.c (revision 71e72c9e91c4b8007a4292e09669e8b549c29e97)
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