dfs.c revision 051af73b8f8014eff33330aead0f36944b3403e6
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 "hostapd.h"
15#include "ap_drv_ops.h"
16#include "drivers/driver.h"
17#include "dfs.h"
18
19
20static int dfs_get_used_n_chans(struct hostapd_data *hapd)
21{
22	int n_chans = 1;
23
24	if (hapd->iconf->ieee80211n && hapd->iconf->secondary_channel)
25		n_chans = 2;
26
27	if (hapd->iconf->ieee80211ac) {
28		switch (hapd->iconf->vht_oper_chwidth) {
29		case VHT_CHANWIDTH_USE_HT:
30			break;
31		case VHT_CHANWIDTH_80MHZ:
32			n_chans = 4;
33			break;
34		case VHT_CHANWIDTH_160MHZ:
35			n_chans = 8;
36			break;
37		default:
38			break;
39		}
40	}
41
42	return n_chans;
43}
44
45
46static int dfs_channel_available(struct hostapd_channel_data *chan)
47{
48	if (chan->flag & HOSTAPD_CHAN_DISABLED)
49		return 0;
50	if ((chan->flag & HOSTAPD_CHAN_RADAR) &&
51	    ((chan->flag & HOSTAPD_CHAN_DFS_MASK) ==
52	     HOSTAPD_CHAN_DFS_UNAVAILABLE))
53		return 0;
54	return 1;
55}
56
57
58static int dfs_is_ht40_allowed(struct hostapd_channel_data *chan)
59{
60	int allowed[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 149, 157,
61			  184, 192 };
62	unsigned int i;
63
64	for (i = 0; i < sizeof(allowed) / sizeof(allowed[0]); i++) {
65		if (chan->chan == allowed[i])
66			return 1;
67	}
68
69	return 0;
70}
71
72
73static int dfs_find_channel(struct hostapd_data *hapd,
74			    struct hostapd_channel_data **ret_chan,
75			    int idx)
76{
77	struct hostapd_hw_modes *mode;
78	struct hostapd_channel_data *chan, *next_chan;
79	int i, j, channel_idx = 0, n_chans;
80
81	mode = hapd->iface->current_mode;
82	n_chans = dfs_get_used_n_chans(hapd);
83
84	wpa_printf(MSG_DEBUG, "DFS new chan checking %d channels", n_chans);
85	for (i = 0; i < mode->num_channels; i++) {
86		chan = &mode->channels[i];
87
88		/* Skip not available channels */
89		if (!dfs_channel_available(chan))
90			continue;
91
92		/* Skip HT40/VHT uncompatible channels */
93		if (hapd->iconf->ieee80211n &&
94		    hapd->iconf->secondary_channel) {
95			if (!dfs_is_ht40_allowed(chan))
96				continue;
97
98			for (j = 1; j < n_chans; j++) {
99				next_chan = &mode->channels[i + j];
100				if (!dfs_channel_available(next_chan))
101					break;
102			}
103			if (j != n_chans)
104				continue;
105
106			/* Set HT40+ */
107			hapd->iconf->secondary_channel = 1;
108		}
109
110		if (ret_chan && idx == channel_idx) {
111			wpa_printf(MSG_DEBUG, "Selected ch. #%d", chan->chan);
112			*ret_chan = chan;
113			return idx;
114		}
115		wpa_printf(MSG_DEBUG, "Adding channel: %d", chan->chan);
116		channel_idx++;
117	}
118	return channel_idx;
119}
120
121
122static void dfs_adjust_vht_center_freq(struct hostapd_data *hapd,
123				       struct hostapd_channel_data *chan)
124{
125	if (!hapd->iconf->ieee80211ac)
126		return;
127
128	if (!chan)
129		return;
130
131	switch (hapd->iconf->vht_oper_chwidth) {
132	case VHT_CHANWIDTH_USE_HT:
133		hapd->iconf->vht_oper_centr_freq_seg0_idx = chan->chan + 2;
134		break;
135	case VHT_CHANWIDTH_80MHZ:
136		hapd->iconf->vht_oper_centr_freq_seg0_idx = chan->chan + 6;
137		break;
138	case VHT_CHANWIDTH_160MHZ:
139		hapd->iconf->vht_oper_centr_freq_seg0_idx =
140						chan->chan + 14;
141	default:
142		wpa_printf(MSG_INFO, "DFS only VHT20/40/80/160 is supported now");
143		break;
144	}
145
146	wpa_printf(MSG_DEBUG, "DFS adjusting VHT center frequency: %d",
147		   hapd->iconf->vht_oper_centr_freq_seg0_idx);
148}
149
150
151/* Return start channel idx we will use for mode->channels[idx] */
152static int dfs_get_start_chan_idx(struct hostapd_data *hapd)
153{
154	struct hostapd_hw_modes *mode;
155	struct hostapd_channel_data *chan;
156	int channel_no = hapd->iconf->channel;
157	int res = -1, i;
158
159	/* HT40- */
160	if (hapd->iconf->ieee80211n && hapd->iconf->secondary_channel == -1)
161		channel_no -= 4;
162
163	/* VHT */
164	if (hapd->iconf->ieee80211ac) {
165		switch (hapd->iconf->vht_oper_chwidth) {
166		case VHT_CHANWIDTH_USE_HT:
167			break;
168		case VHT_CHANWIDTH_80MHZ:
169			channel_no =
170				hapd->iconf->vht_oper_centr_freq_seg0_idx - 6;
171			break;
172		case VHT_CHANWIDTH_160MHZ:
173			channel_no =
174				hapd->iconf->vht_oper_centr_freq_seg0_idx - 14;
175			break;
176		default:
177			wpa_printf(MSG_INFO,
178				   "DFS only VHT20/40/80/160 is supported now");
179			channel_no = -1;
180			break;
181		}
182	}
183
184	/* Get idx */
185	mode = hapd->iface->current_mode;
186	for (i = 0; i < mode->num_channels; i++) {
187		chan = &mode->channels[i];
188		if (chan->chan == channel_no) {
189			res = i;
190			break;
191		}
192	}
193
194	if (res == -1)
195		wpa_printf(MSG_DEBUG, "DFS chan_idx seems wrong: -1");
196
197	return res;
198}
199
200
201/* At least one channel have radar flag */
202static int dfs_check_chans_radar(struct hostapd_data *hapd, int start_chan_idx,
203				 int n_chans)
204{
205	struct hostapd_channel_data *channel;
206	struct hostapd_hw_modes *mode;
207	int i, res = 0;
208
209	mode = hapd->iface->current_mode;
210
211	for (i = 0; i < n_chans; i++) {
212		channel = &mode->channels[start_chan_idx + i];
213		if (channel->flag & HOSTAPD_CHAN_RADAR)
214			res++;
215	}
216
217	return res;
218}
219
220
221/* All channels available */
222static int dfs_check_chans_available(struct hostapd_data *hapd,
223				     int start_chan_idx, int n_chans)
224{
225	struct hostapd_channel_data *channel;
226	struct hostapd_hw_modes *mode;
227	int i;
228
229	mode = hapd->iface->current_mode;
230
231	for(i = 0; i < n_chans; i++) {
232		channel = &mode->channels[start_chan_idx + i];
233		if ((channel->flag & HOSTAPD_CHAN_DFS_MASK) !=
234		    HOSTAPD_CHAN_DFS_AVAILABLE)
235			break;
236	}
237
238	return i == n_chans;
239}
240
241
242/* At least one channel unavailable */
243static int dfs_check_chans_unavailable(struct hostapd_data *hapd,
244				       int start_chan_idx,
245				       int n_chans)
246{
247	struct hostapd_channel_data *channel;
248	struct hostapd_hw_modes *mode;
249	int i, res = 0;
250
251	mode = hapd->iface->current_mode;
252
253	for(i = 0; i < n_chans; i++) {
254		channel = &mode->channels[start_chan_idx + i];
255		if (channel->flag & HOSTAPD_CHAN_DISABLED)
256			res++;
257		if ((channel->flag & HOSTAPD_CHAN_DFS_MASK) ==
258		    HOSTAPD_CHAN_DFS_UNAVAILABLE)
259			res++;
260	}
261
262	return res;
263}
264
265
266static struct hostapd_channel_data * dfs_get_valid_channel(
267	struct hostapd_data *hapd)
268{
269	struct hostapd_hw_modes *mode;
270	struct hostapd_channel_data *chan = NULL;
271	int channel_idx, new_channel_idx;
272	u32 _rand;
273
274	wpa_printf(MSG_DEBUG, "DFS: Selecting random channel");
275
276	if (hapd->iface->current_mode == NULL)
277		return NULL;
278
279	mode = hapd->iface->current_mode;
280	if (mode->mode != HOSTAPD_MODE_IEEE80211A)
281		return NULL;
282
283	/* get random available channel */
284	channel_idx = dfs_find_channel(hapd, NULL, 0);
285	if (channel_idx > 0) {
286		os_get_random((u8 *) &_rand, sizeof(_rand));
287		new_channel_idx = _rand % channel_idx;
288		dfs_find_channel(hapd, &chan, new_channel_idx);
289	}
290
291	/* VHT */
292	dfs_adjust_vht_center_freq(hapd, chan);
293
294	return chan;
295}
296
297
298static int set_dfs_state_freq(struct hostapd_data *hapd, int freq, u32 state)
299{
300	struct hostapd_hw_modes *mode;
301	struct hostapd_channel_data *chan = NULL;
302	int i;
303
304	mode = hapd->iface->current_mode;
305	if (mode == NULL)
306		return 0;
307
308	wpa_printf(MSG_DEBUG, "set_dfs_state 0x%X for %d MHz", state, freq);
309	for (i = 0; i < hapd->iface->current_mode->num_channels; i++) {
310		chan = &hapd->iface->current_mode->channels[i];
311		if (chan->freq == freq) {
312			if (chan->flag & HOSTAPD_CHAN_RADAR) {
313				chan->flag &= ~HOSTAPD_CHAN_DFS_MASK;
314				chan->flag |= state;
315				return 1; /* Channel found */
316			}
317		}
318	}
319	wpa_printf(MSG_WARNING, "Can't set DFS state for freq %d MHz", freq);
320	return 0;
321}
322
323
324static int set_dfs_state(struct hostapd_data *hapd, int freq, int ht_enabled,
325			 int chan_offset, int chan_width, int cf1,
326			 int cf2, u32 state)
327{
328	int n_chans = 1, i;
329	struct hostapd_hw_modes *mode;
330	int frequency = freq;
331	int ret = 0;
332
333	mode = hapd->iface->current_mode;
334	if (mode == NULL)
335		return 0;
336
337	if (mode->mode != HOSTAPD_MODE_IEEE80211A) {
338		wpa_printf(MSG_WARNING, "current_mode != IEEE80211A");
339		return 0;
340	}
341
342	/* Seems cf1 and chan_width is enough here */
343	switch (chan_width) {
344	case CHAN_WIDTH_20_NOHT:
345	case CHAN_WIDTH_20:
346		n_chans = 1;
347		frequency = cf1;
348		break;
349	case CHAN_WIDTH_40:
350		n_chans = 2;
351		frequency = cf1 - 10;
352		break;
353	case CHAN_WIDTH_80:
354		n_chans = 4;
355		frequency = cf1 - 30;
356		break;
357	case CHAN_WIDTH_160:
358		n_chans = 8;
359		frequency = cf1 - 70;
360		break;
361	default:
362		wpa_printf(MSG_INFO, "DFS chan_width %d not supported",
363			   chan_width);
364		break;
365	}
366
367	wpa_printf(MSG_DEBUG, "DFS freq: %dMHz, n_chans: %d", frequency,
368		   n_chans);
369	for (i = 0; i < n_chans; i++) {
370		ret += set_dfs_state_freq(hapd, frequency, state);
371		frequency = frequency + 20;
372	}
373
374	return ret;
375}
376
377
378static int dfs_are_channels_overlapped(struct hostapd_data *hapd, int freq,
379				       int chan_width, int cf1, int cf2)
380{
381	int start_chan_idx;
382	struct hostapd_hw_modes *mode;
383	struct hostapd_channel_data *chan;
384	int n_chans, i, j, frequency = freq, radar_n_chans = 1;
385	u8 radar_chan;
386	int res = 0;
387
388	if (hapd->iface->freq == freq)
389		res++;
390
391	/* Our configuration */
392	mode = hapd->iface->current_mode;
393	start_chan_idx = dfs_get_start_chan_idx(hapd);
394	n_chans = dfs_get_used_n_chans(hapd);
395
396	/* Reported via radar event */
397	switch (chan_width) {
398	case CHAN_WIDTH_20_NOHT:
399	case CHAN_WIDTH_20:
400		radar_n_chans = 1;
401		frequency = cf1;
402		break;
403	case CHAN_WIDTH_40:
404		radar_n_chans = 2;
405		frequency = cf1 - 10;
406		break;
407	case CHAN_WIDTH_80:
408		radar_n_chans = 4;
409		frequency = cf1 - 30;
410		break;
411	case CHAN_WIDTH_160:
412		radar_n_chans = 8;
413		frequency = cf1 - 70;
414		break;
415	default:
416		wpa_printf(MSG_INFO, "DFS chan_width %d not supported",
417			   chan_width);
418		break;
419	}
420
421	ieee80211_freq_to_chan(frequency, &radar_chan);
422
423	for (i = 0; i < n_chans; i++) {
424		chan = &mode->channels[start_chan_idx + i];
425		for (j = 0; j < radar_n_chans; j++) {
426			wpa_printf(MSG_DEBUG, "checking our: %d, radar: %d",
427				   chan->chan, radar_chan + j * 4);
428			if (chan->chan == radar_chan + j * 4)
429				res++;
430		}
431	}
432
433	wpa_printf(MSG_DEBUG, "overlapped: %d", res);
434
435	return res;
436}
437
438
439/*
440 * Main DFS handler
441 * 1 - continue channel/ap setup
442 * 0 - channel/ap setup will be continued after CAC
443 * -1 - hit critical error
444 */
445int hostapd_handle_dfs(struct hostapd_data *hapd)
446{
447	struct hostapd_channel_data *channel;
448	int res, n_chans, start_chan_idx;
449
450	do {
451		/* Get start (first) channel for current configuration */
452		start_chan_idx = dfs_get_start_chan_idx(hapd);
453		if (start_chan_idx == -1)
454			return -1;
455
456		/* Get number of used channels, depend on width */
457		n_chans = dfs_get_used_n_chans(hapd);
458
459		/* Check if any of configured channels require DFS */
460		res = dfs_check_chans_radar(hapd, start_chan_idx, n_chans);
461		wpa_printf(MSG_DEBUG,
462			   "DFS %d channels required radar detection",
463			   res);
464		if (!res)
465			return 1;
466
467		/* Check if all channels are DFS available */
468		res = dfs_check_chans_available(hapd, start_chan_idx, n_chans);
469		wpa_printf(MSG_DEBUG,
470			   "DFS all channels available, (SKIP CAC): %s",
471			   res ? "yes" : "no");
472		if (res)
473			return 1;
474
475		/* Check if any of configured channels is unavailable */
476		res = dfs_check_chans_unavailable(hapd, start_chan_idx,
477						  n_chans);
478		wpa_printf(MSG_DEBUG, "DFS %d chans unavailable - choose other channel: %s",
479			   res, res ? "yes": "no");
480		if (res) {
481			channel = dfs_get_valid_channel(hapd);
482			if (!channel) {
483				wpa_printf(MSG_ERROR, "could not get valid channel");
484				return -1;
485			}
486			hapd->iconf->channel = channel->chan;
487			hapd->iface->freq = channel->freq;
488		}
489	} while (res);
490
491	/* Finally start CAC */
492	wpa_printf(MSG_DEBUG, "DFS start CAC on %d MHz", hapd->iface->freq);
493	if (hostapd_start_dfs_cac(hapd, hapd->iconf->hw_mode,
494				  hapd->iface->freq,
495				  hapd->iconf->channel,
496				  hapd->iconf->ieee80211n,
497				  hapd->iconf->ieee80211ac,
498				  hapd->iconf->secondary_channel,
499				  hapd->iconf->vht_oper_chwidth,
500				  hapd->iconf->vht_oper_centr_freq_seg0_idx,
501				  hapd->iconf->vht_oper_centr_freq_seg1_idx)) {
502		wpa_printf(MSG_DEBUG, "DFS start_dfs_cac() failed");
503		return -1;
504	}
505
506	return 0;
507}
508
509
510int hostapd_dfs_complete_cac(struct hostapd_data *hapd, int success, int freq,
511			     int ht_enabled, int chan_offset, int chan_width,
512			     int cf1, int cf2)
513{
514	struct hostapd_channel_data *channel;
515	int err = 1;
516
517	if (success) {
518		/* Complete iface/ap configuration */
519		set_dfs_state(hapd, freq, ht_enabled, chan_offset,
520			      chan_width, cf1, cf2,
521			      HOSTAPD_CHAN_DFS_AVAILABLE);
522		hostapd_setup_interface_complete(hapd->iface, 0);
523	} else {
524		/* Switch to new channel */
525		set_dfs_state(hapd, freq, ht_enabled, chan_offset,
526			      chan_width, cf1, cf2,
527			      HOSTAPD_CHAN_DFS_UNAVAILABLE);
528		channel = dfs_get_valid_channel(hapd);
529		if (channel) {
530			hapd->iconf->channel = channel->chan;
531			hapd->iface->freq = channel->freq;
532			err = 0;
533		} else
534			wpa_printf(MSG_ERROR, "No valid channel available");
535
536		hostapd_setup_interface_complete(hapd->iface, err);
537	}
538
539	return 0;
540}
541
542
543static int hostapd_dfs_start_channel_switch(struct hostapd_data *hapd)
544{
545	struct hostapd_channel_data *channel;
546	int err = 1;
547
548	wpa_printf(MSG_DEBUG, "%s called", __func__);
549	channel = dfs_get_valid_channel(hapd);
550	if (channel) {
551		hapd->iconf->channel = channel->chan;
552		hapd->iface->freq = channel->freq;
553		err = 0;
554	}
555
556	hapd->driver->stop_ap(hapd->drv_priv);
557
558	hostapd_setup_interface_complete(hapd->iface, err);
559	return 0;
560}
561
562
563int hostapd_dfs_radar_detected(struct hostapd_data *hapd, int freq,
564			       int ht_enabled, int chan_offset, int chan_width,
565			       int cf1, int cf2)
566{
567	int res;
568
569	if (!hapd->iconf->ieee80211h)
570		return 0;
571
572	/* mark radar frequency as invalid */
573	res = set_dfs_state(hapd, freq, ht_enabled, chan_offset,
574			    chan_width, cf1, cf2,
575			    HOSTAPD_CHAN_DFS_UNAVAILABLE);
576
577	/* Skip if reported radar event not overlapped our channels */
578	res = dfs_are_channels_overlapped(hapd, freq, chan_width, cf1, cf2);
579	if (!res)
580		return 0;
581
582	/* we are working on non-DFS channel - skip event */
583	if (res == 0)
584		return 0;
585
586	/* radar detected while operating, switch the channel. */
587	res = hostapd_dfs_start_channel_switch(hapd);
588
589	return res;
590}
591
592
593int hostapd_dfs_nop_finished(struct hostapd_data *hapd, int freq,
594			     int ht_enabled, int chan_offset, int chan_width,
595			     int cf1, int cf2)
596{
597	/* TODO add correct implementation here */
598	set_dfs_state(hapd, freq, ht_enabled, chan_offset, chan_width, cf1, cf2,
599		      HOSTAPD_CHAN_DFS_USABLE);
600	return 0;
601}
602