dfs.c revision cce06667447b5aec83452adb0c15100ada531095
1/*
2 * DFS - Dynamic Frequency Selection
3 * Copyright (c) 2002-2013, Jouni Malinen <j@w1.fi>
4 * Copyright (c) 2013, Qualcomm Atheros, Inc.
5 *
6 * This software may be distributed under the terms of the BSD license.
7 * See README for more details.
8 */
9
10#include "utils/includes.h"
11
12#include "utils/common.h"
13#include "common/ieee802_11_defs.h"
14#include "common/wpa_ctrl.h"
15#include "hostapd.h"
16#include "ap_drv_ops.h"
17#include "drivers/driver.h"
18#include "dfs.h"
19
20
21static int dfs_get_used_n_chans(struct hostapd_iface *iface)
22{
23	int n_chans = 1;
24
25	if (iface->conf->ieee80211n && iface->conf->secondary_channel)
26		n_chans = 2;
27
28	if (iface->conf->ieee80211ac) {
29		switch (iface->conf->vht_oper_chwidth) {
30		case VHT_CHANWIDTH_USE_HT:
31			break;
32		case VHT_CHANWIDTH_80MHZ:
33			n_chans = 4;
34			break;
35		case VHT_CHANWIDTH_160MHZ:
36			n_chans = 8;
37			break;
38		default:
39			break;
40		}
41	}
42
43	return n_chans;
44}
45
46
47static int dfs_channel_available(struct hostapd_channel_data *chan)
48{
49	if (chan->flag & HOSTAPD_CHAN_DISABLED)
50		return 0;
51	if ((chan->flag & HOSTAPD_CHAN_RADAR) &&
52	    ((chan->flag & HOSTAPD_CHAN_DFS_MASK) ==
53	     HOSTAPD_CHAN_DFS_UNAVAILABLE))
54		return 0;
55	return 1;
56}
57
58
59static int dfs_is_chan_allowed(struct hostapd_channel_data *chan, int n_chans)
60{
61	/*
62	 * The tables contain first valid channel number based on channel width.
63	 * We will also choose this first channel as the control one.
64	 */
65	int allowed_40[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 149, 157,
66			     184, 192 };
67	/*
68	 * VHT80, valid channels based on center frequency:
69	 * 42, 58, 106, 122, 138, 155
70	 */
71	int allowed_80[] = { 36, 52, 100, 116, 132, 149 };
72	int *allowed = allowed_40;
73	unsigned int i, allowed_no = 0;
74
75	switch (n_chans) {
76	case 2:
77		allowed = allowed_40;
78		allowed_no = ARRAY_SIZE(allowed_40);
79		break;
80	case 4:
81		allowed = allowed_80;
82		allowed_no = ARRAY_SIZE(allowed_80);
83		break;
84	default:
85		wpa_printf(MSG_DEBUG, "Unknown width for %d channels", n_chans);
86		break;
87	}
88
89	for (i = 0; i < allowed_no; i++) {
90		if (chan->chan == allowed[i])
91			return 1;
92	}
93
94	return 0;
95}
96
97
98static int dfs_chan_range_available(struct hostapd_hw_modes *mode,
99				    int first_chan_idx, int num_chans)
100{
101	struct hostapd_channel_data *first_chan, *chan;
102	int i;
103
104	if (first_chan_idx + num_chans >= mode->num_channels)
105		return 0;
106
107	first_chan = &mode->channels[first_chan_idx];
108
109	for (i = 0; i < num_chans; i++) {
110		chan = &mode->channels[first_chan_idx + i];
111
112		if (first_chan->freq + i * 20 != chan->freq)
113			return 0;
114
115		if (!dfs_channel_available(chan))
116			return 0;
117	}
118
119	return 1;
120}
121
122
123/*
124 * The function assumes HT40+ operation.
125 * Make sure to adjust the following variables after calling this:
126 *  - hapd->secondary_channel
127 *  - hapd->vht_oper_centr_freq_seg0_idx
128 *  - hapd->vht_oper_centr_freq_seg1_idx
129 */
130static int dfs_find_channel(struct hostapd_iface *iface,
131			    struct hostapd_channel_data **ret_chan,
132			    int idx)
133{
134	struct hostapd_hw_modes *mode;
135	struct hostapd_channel_data *chan;
136	int i, channel_idx = 0, n_chans;
137
138	mode = iface->current_mode;
139	n_chans = dfs_get_used_n_chans(iface);
140
141	wpa_printf(MSG_DEBUG, "DFS new chan checking %d channels", n_chans);
142	for (i = 0; i < mode->num_channels; i++) {
143		chan = &mode->channels[i];
144
145		/* Skip HT40/VHT incompatible channels */
146		if (iface->conf->ieee80211n &&
147		    iface->conf->secondary_channel &&
148		    !dfs_is_chan_allowed(chan, n_chans))
149			continue;
150
151		/* Skip incompatible chandefs */
152		if (!dfs_chan_range_available(mode, i, n_chans))
153			continue;
154
155		if (ret_chan && idx == channel_idx) {
156			wpa_printf(MSG_DEBUG, "Selected ch. #%d", chan->chan);
157			*ret_chan = chan;
158			return idx;
159		}
160		wpa_printf(MSG_DEBUG, "Adding channel: %d", chan->chan);
161		channel_idx++;
162	}
163	return channel_idx;
164}
165
166
167static void dfs_adjust_vht_center_freq(struct hostapd_iface *iface,
168				       struct hostapd_channel_data *chan,
169				       u8 *vht_oper_centr_freq_seg0_idx,
170				       u8 *vht_oper_centr_freq_seg1_idx)
171{
172	if (!iface->conf->ieee80211ac)
173		return;
174
175	if (!chan)
176		return;
177
178	*vht_oper_centr_freq_seg1_idx = 0;
179
180	switch (iface->conf->vht_oper_chwidth) {
181	case VHT_CHANWIDTH_USE_HT:
182		if (iface->conf->secondary_channel == 1)
183			*vht_oper_centr_freq_seg0_idx = chan->chan + 2;
184		else if (iface->conf->secondary_channel == -1)
185			*vht_oper_centr_freq_seg0_idx = chan->chan - 2;
186		else
187			*vht_oper_centr_freq_seg0_idx = chan->chan;
188		break;
189	case VHT_CHANWIDTH_80MHZ:
190		*vht_oper_centr_freq_seg0_idx = chan->chan + 6;
191		break;
192	case VHT_CHANWIDTH_160MHZ:
193		*vht_oper_centr_freq_seg0_idx = chan->chan + 14;
194		break;
195	default:
196		wpa_printf(MSG_INFO, "DFS only VHT20/40/80/160 is supported now");
197		break;
198	}
199
200	wpa_printf(MSG_DEBUG, "DFS adjusting VHT center frequency: %d, %d",
201		   *vht_oper_centr_freq_seg0_idx,
202		   *vht_oper_centr_freq_seg1_idx);
203}
204
205
206/* Return start channel idx we will use for mode->channels[idx] */
207static int dfs_get_start_chan_idx(struct hostapd_iface *iface)
208{
209	struct hostapd_hw_modes *mode;
210	struct hostapd_channel_data *chan;
211	int channel_no = iface->conf->channel;
212	int res = -1, i;
213
214	/* HT40- */
215	if (iface->conf->ieee80211n && iface->conf->secondary_channel == -1)
216		channel_no -= 4;
217
218	/* VHT */
219	if (iface->conf->ieee80211ac) {
220		switch (iface->conf->vht_oper_chwidth) {
221		case VHT_CHANWIDTH_USE_HT:
222			break;
223		case VHT_CHANWIDTH_80MHZ:
224			channel_no =
225				iface->conf->vht_oper_centr_freq_seg0_idx - 6;
226			break;
227		case VHT_CHANWIDTH_160MHZ:
228			channel_no =
229				iface->conf->vht_oper_centr_freq_seg0_idx - 14;
230			break;
231		default:
232			wpa_printf(MSG_INFO,
233				   "DFS only VHT20/40/80/160 is supported now");
234			channel_no = -1;
235			break;
236		}
237	}
238
239	/* Get idx */
240	mode = iface->current_mode;
241	for (i = 0; i < mode->num_channels; i++) {
242		chan = &mode->channels[i];
243		if (chan->chan == channel_no) {
244			res = i;
245			break;
246		}
247	}
248
249	if (res == -1)
250		wpa_printf(MSG_DEBUG, "DFS chan_idx seems wrong: -1");
251
252	return res;
253}
254
255
256/* At least one channel have radar flag */
257static int dfs_check_chans_radar(struct hostapd_iface *iface,
258				 int start_chan_idx, int n_chans)
259{
260	struct hostapd_channel_data *channel;
261	struct hostapd_hw_modes *mode;
262	int i, res = 0;
263
264	mode = iface->current_mode;
265
266	for (i = 0; i < n_chans; i++) {
267		channel = &mode->channels[start_chan_idx + i];
268		if (channel->flag & HOSTAPD_CHAN_RADAR)
269			res++;
270	}
271
272	return res;
273}
274
275
276/* All channels available */
277static int dfs_check_chans_available(struct hostapd_iface *iface,
278				     int start_chan_idx, int n_chans)
279{
280	struct hostapd_channel_data *channel;
281	struct hostapd_hw_modes *mode;
282	int i;
283
284	mode = iface->current_mode;
285
286	for(i = 0; i < n_chans; i++) {
287		channel = &mode->channels[start_chan_idx + i];
288		if ((channel->flag & HOSTAPD_CHAN_DFS_MASK) !=
289		    HOSTAPD_CHAN_DFS_AVAILABLE)
290			break;
291	}
292
293	return i == n_chans;
294}
295
296
297/* At least one channel unavailable */
298static int dfs_check_chans_unavailable(struct hostapd_iface *iface,
299				       int start_chan_idx,
300				       int n_chans)
301{
302	struct hostapd_channel_data *channel;
303	struct hostapd_hw_modes *mode;
304	int i, res = 0;
305
306	mode = iface->current_mode;
307
308	for(i = 0; i < n_chans; i++) {
309		channel = &mode->channels[start_chan_idx + i];
310		if (channel->flag & HOSTAPD_CHAN_DISABLED)
311			res++;
312		if ((channel->flag & HOSTAPD_CHAN_DFS_MASK) ==
313		    HOSTAPD_CHAN_DFS_UNAVAILABLE)
314			res++;
315	}
316
317	return res;
318}
319
320
321static struct hostapd_channel_data *
322dfs_get_valid_channel(struct hostapd_iface *iface,
323		      int *secondary_channel,
324		      u8 *vht_oper_centr_freq_seg0_idx,
325		      u8 *vht_oper_centr_freq_seg1_idx)
326{
327	struct hostapd_hw_modes *mode;
328	struct hostapd_channel_data *chan = NULL;
329	int num_available_chandefs;
330	int chan_idx;
331	u32 _rand;
332
333	wpa_printf(MSG_DEBUG, "DFS: Selecting random channel");
334
335	if (iface->current_mode == NULL)
336		return NULL;
337
338	mode = iface->current_mode;
339	if (mode->mode != HOSTAPD_MODE_IEEE80211A)
340		return NULL;
341
342	/* Get the count first */
343	num_available_chandefs = dfs_find_channel(iface, NULL, 0);
344	if (num_available_chandefs == 0)
345		return NULL;
346
347	os_get_random((u8 *) &_rand, sizeof(_rand));
348	chan_idx = _rand % num_available_chandefs;
349	dfs_find_channel(iface, &chan, chan_idx);
350
351	/* dfs_find_channel() calculations assume HT40+ */
352	if (iface->conf->secondary_channel)
353		*secondary_channel = 1;
354	else
355		*secondary_channel = 0;
356
357	dfs_adjust_vht_center_freq(iface, chan,
358				   vht_oper_centr_freq_seg0_idx,
359				   vht_oper_centr_freq_seg1_idx);
360
361	return chan;
362}
363
364
365static int set_dfs_state_freq(struct hostapd_iface *iface, int freq, u32 state)
366{
367	struct hostapd_hw_modes *mode;
368	struct hostapd_channel_data *chan = NULL;
369	int i;
370
371	mode = iface->current_mode;
372	if (mode == NULL)
373		return 0;
374
375	wpa_printf(MSG_DEBUG, "set_dfs_state 0x%X for %d MHz", state, freq);
376	for (i = 0; i < iface->current_mode->num_channels; i++) {
377		chan = &iface->current_mode->channels[i];
378		if (chan->freq == freq) {
379			if (chan->flag & HOSTAPD_CHAN_RADAR) {
380				chan->flag &= ~HOSTAPD_CHAN_DFS_MASK;
381				chan->flag |= state;
382				return 1; /* Channel found */
383			}
384		}
385	}
386	wpa_printf(MSG_WARNING, "Can't set DFS state for freq %d MHz", freq);
387	return 0;
388}
389
390
391static int set_dfs_state(struct hostapd_iface *iface, int freq, int ht_enabled,
392			 int chan_offset, int chan_width, int cf1,
393			 int cf2, u32 state)
394{
395	int n_chans = 1, i;
396	struct hostapd_hw_modes *mode;
397	int frequency = freq;
398	int ret = 0;
399
400	mode = iface->current_mode;
401	if (mode == NULL)
402		return 0;
403
404	if (mode->mode != HOSTAPD_MODE_IEEE80211A) {
405		wpa_printf(MSG_WARNING, "current_mode != IEEE80211A");
406		return 0;
407	}
408
409	/* Seems cf1 and chan_width is enough here */
410	switch (chan_width) {
411	case CHAN_WIDTH_20_NOHT:
412	case CHAN_WIDTH_20:
413		n_chans = 1;
414		if (frequency == 0)
415			frequency = cf1;
416		break;
417	case CHAN_WIDTH_40:
418		n_chans = 2;
419		frequency = cf1 - 10;
420		break;
421	case CHAN_WIDTH_80:
422		n_chans = 4;
423		frequency = cf1 - 30;
424		break;
425	case CHAN_WIDTH_160:
426		n_chans = 8;
427		frequency = cf1 - 70;
428		break;
429	default:
430		wpa_printf(MSG_INFO, "DFS chan_width %d not supported",
431			   chan_width);
432		break;
433	}
434
435	wpa_printf(MSG_DEBUG, "DFS freq: %dMHz, n_chans: %d", frequency,
436		   n_chans);
437	for (i = 0; i < n_chans; i++) {
438		ret += set_dfs_state_freq(iface, frequency, state);
439		frequency = frequency + 20;
440	}
441
442	return ret;
443}
444
445
446static int dfs_are_channels_overlapped(struct hostapd_iface *iface, int freq,
447				       int chan_width, int cf1, int cf2)
448{
449	int start_chan_idx;
450	struct hostapd_hw_modes *mode;
451	struct hostapd_channel_data *chan;
452	int n_chans, i, j, frequency = freq, radar_n_chans = 1;
453	u8 radar_chan;
454	int res = 0;
455
456	/* Our configuration */
457	mode = iface->current_mode;
458	start_chan_idx = dfs_get_start_chan_idx(iface);
459	n_chans = dfs_get_used_n_chans(iface);
460
461	/* Check we are on DFS channel(s) */
462	if (!dfs_check_chans_radar(iface, start_chan_idx, n_chans))
463		return 0;
464
465	/* Reported via radar event */
466	switch (chan_width) {
467	case CHAN_WIDTH_20_NOHT:
468	case CHAN_WIDTH_20:
469		radar_n_chans = 1;
470		if (frequency == 0)
471			frequency = cf1;
472		break;
473	case CHAN_WIDTH_40:
474		radar_n_chans = 2;
475		frequency = cf1 - 10;
476		break;
477	case CHAN_WIDTH_80:
478		radar_n_chans = 4;
479		frequency = cf1 - 30;
480		break;
481	case CHAN_WIDTH_160:
482		radar_n_chans = 8;
483		frequency = cf1 - 70;
484		break;
485	default:
486		wpa_printf(MSG_INFO, "DFS chan_width %d not supported",
487			   chan_width);
488		break;
489	}
490
491	ieee80211_freq_to_chan(frequency, &radar_chan);
492
493	for (i = 0; i < n_chans; i++) {
494		chan = &mode->channels[start_chan_idx + i];
495		if (!(chan->flag & HOSTAPD_CHAN_RADAR))
496			continue;
497		for (j = 0; j < radar_n_chans; j++) {
498			wpa_printf(MSG_DEBUG, "checking our: %d, radar: %d",
499				   chan->chan, radar_chan + j * 4);
500			if (chan->chan == radar_chan + j * 4)
501				res++;
502		}
503	}
504
505	wpa_printf(MSG_DEBUG, "overlapped: %d", res);
506
507	return res;
508}
509
510
511/*
512 * Main DFS handler
513 * 1 - continue channel/ap setup
514 * 0 - channel/ap setup will be continued after CAC
515 * -1 - hit critical error
516 */
517int hostapd_handle_dfs(struct hostapd_iface *iface)
518{
519	struct hostapd_channel_data *channel;
520	int res, n_chans, start_chan_idx;
521
522	iface->cac_started = 0;
523
524	do {
525		/* Get start (first) channel for current configuration */
526		start_chan_idx = dfs_get_start_chan_idx(iface);
527		if (start_chan_idx == -1)
528			return -1;
529
530		/* Get number of used channels, depend on width */
531		n_chans = dfs_get_used_n_chans(iface);
532
533		/* Check if any of configured channels require DFS */
534		res = dfs_check_chans_radar(iface, start_chan_idx, n_chans);
535		wpa_printf(MSG_DEBUG,
536			   "DFS %d channels required radar detection",
537			   res);
538		if (!res)
539			return 1;
540
541		/* Check if all channels are DFS available */
542		res = dfs_check_chans_available(iface, start_chan_idx, n_chans);
543		wpa_printf(MSG_DEBUG,
544			   "DFS all channels available, (SKIP CAC): %s",
545			   res ? "yes" : "no");
546		if (res)
547			return 1;
548
549		/* Check if any of configured channels is unavailable */
550		res = dfs_check_chans_unavailable(iface, start_chan_idx,
551						  n_chans);
552		wpa_printf(MSG_DEBUG, "DFS %d chans unavailable - choose other channel: %s",
553			   res, res ? "yes": "no");
554		if (res) {
555			int sec;
556			u8 cf1, cf2;
557
558			channel = dfs_get_valid_channel(iface, &sec, &cf1, &cf2);
559			if (!channel) {
560				wpa_printf(MSG_ERROR, "could not get valid channel");
561				return -1;
562			}
563
564			iface->freq = channel->freq;
565			iface->conf->channel = channel->chan;
566			iface->conf->secondary_channel = sec;
567			iface->conf->vht_oper_centr_freq_seg0_idx = cf1;
568			iface->conf->vht_oper_centr_freq_seg1_idx = cf2;
569		}
570	} while (res);
571
572	/* Finally start CAC */
573	hostapd_set_state(iface, HAPD_IFACE_DFS);
574	wpa_printf(MSG_DEBUG, "DFS start CAC on %d MHz", iface->freq);
575	wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_CAC_START
576		"freq=%d chan=%d sec_chan=%d",
577		iface->freq,
578		iface->conf->channel, iface->conf->secondary_channel);
579	if (hostapd_start_dfs_cac(iface, iface->conf->hw_mode,
580				  iface->freq,
581				  iface->conf->channel,
582				  iface->conf->ieee80211n,
583				  iface->conf->ieee80211ac,
584				  iface->conf->secondary_channel,
585				  iface->conf->vht_oper_chwidth,
586				  iface->conf->vht_oper_centr_freq_seg0_idx,
587				  iface->conf->vht_oper_centr_freq_seg1_idx)) {
588		wpa_printf(MSG_DEBUG, "DFS start_dfs_cac() failed");
589		return -1;
590	}
591
592	return 0;
593}
594
595
596int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq,
597			     int ht_enabled, int chan_offset, int chan_width,
598			     int cf1, int cf2)
599{
600	wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_CAC_COMPLETED
601		"success=%d freq=%d ht_enabled=%d chan_offset=%d chan_width=%d cf1=%d cf2=%d",
602		success, freq, ht_enabled, chan_offset, chan_width, cf1, cf2);
603
604	if (success) {
605		/* Complete iface/ap configuration */
606		set_dfs_state(iface, freq, ht_enabled, chan_offset,
607			      chan_width, cf1, cf2,
608			      HOSTAPD_CHAN_DFS_AVAILABLE);
609		iface->cac_started = 0;
610		hostapd_setup_interface_complete(iface, 0);
611	}
612
613	return 0;
614}
615
616
617static int hostapd_dfs_start_channel_switch(struct hostapd_iface *iface)
618{
619	struct hostapd_channel_data *channel;
620	int err = 1;
621	int secondary_channel;
622	u8 vht_oper_centr_freq_seg0_idx;
623	u8 vht_oper_centr_freq_seg1_idx;
624
625	wpa_printf(MSG_DEBUG, "%s called", __func__);
626	channel = dfs_get_valid_channel(iface, &secondary_channel,
627					&vht_oper_centr_freq_seg0_idx,
628					&vht_oper_centr_freq_seg1_idx);
629	if (channel) {
630		wpa_printf(MSG_DEBUG, "DFS will switch to a new channel %d",
631			   channel->chan);
632		wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_NEW_CHANNEL
633			"freq=%d chan=%d sec_chan=%d", channel->freq,
634			channel->chan, secondary_channel);
635
636		iface->freq = channel->freq;
637		iface->conf->channel = channel->chan;
638		iface->conf->secondary_channel = secondary_channel;
639		iface->conf->vht_oper_centr_freq_seg0_idx =
640			vht_oper_centr_freq_seg0_idx;
641		iface->conf->vht_oper_centr_freq_seg1_idx =
642			vht_oper_centr_freq_seg1_idx;
643		err = 0;
644	} else {
645		wpa_printf(MSG_ERROR, "No valid channel available");
646	}
647
648	if (iface->cac_started) {
649		wpa_printf(MSG_DEBUG, "DFS radar detected during CAC");
650		iface->cac_started = 0;
651		/* FIXME: Wait for channel(s) to become available if no channel
652		 * has been found */
653		hostapd_setup_interface_complete(iface, err);
654		return err;
655	}
656
657	if (err) {
658		/* FIXME: Wait for channel(s) to become available */
659		hostapd_disable_iface(iface);
660		return err;
661	}
662
663	wpa_printf(MSG_DEBUG, "DFS radar detected");
664	hostapd_disable_iface(iface);
665	hostapd_enable_iface(iface);
666	return 0;
667}
668
669
670int hostapd_dfs_radar_detected(struct hostapd_iface *iface, int freq,
671			       int ht_enabled, int chan_offset, int chan_width,
672			       int cf1, int cf2)
673{
674	int res;
675
676	if (!iface->conf->ieee80211h)
677		return 0;
678
679	wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_RADAR_DETECTED
680		"freq=%d ht_enabled=%d chan_offset=%d chan_width=%d cf1=%d cf2=%d",
681		freq, ht_enabled, chan_offset, chan_width, cf1, cf2);
682
683	/* mark radar frequency as invalid */
684	res = set_dfs_state(iface, freq, ht_enabled, chan_offset,
685			    chan_width, cf1, cf2,
686			    HOSTAPD_CHAN_DFS_UNAVAILABLE);
687
688	/* Skip if reported radar event not overlapped our channels */
689	res = dfs_are_channels_overlapped(iface, freq, chan_width, cf1, cf2);
690	if (!res)
691		return 0;
692
693	/* radar detected while operating, switch the channel. */
694	res = hostapd_dfs_start_channel_switch(iface);
695
696	return res;
697}
698
699
700int hostapd_dfs_nop_finished(struct hostapd_iface *iface, int freq,
701			     int ht_enabled, int chan_offset, int chan_width,
702			     int cf1, int cf2)
703{
704	wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_NOP_FINISHED
705		"freq=%d ht_enabled=%d chan_offset=%d chan_width=%d cf1=%d cf2=%d",
706		freq, ht_enabled, chan_offset, chan_width, cf1, cf2);
707	/* TODO add correct implementation here */
708	set_dfs_state(iface, freq, ht_enabled, chan_offset, chan_width,
709		      cf1, cf2, HOSTAPD_CHAN_DFS_USABLE);
710	return 0;
711}
712