13ff40c12SJohn Marino /*
23ff40c12SJohn Marino * DFS - Dynamic Frequency Selection
33ff40c12SJohn Marino * Copyright (c) 2002-2013, Jouni Malinen <j@w1.fi>
4*a1157835SDaniel Fojt * Copyright (c) 2013-2017, Qualcomm Atheros, Inc.
53ff40c12SJohn Marino *
63ff40c12SJohn Marino * This software may be distributed under the terms of the BSD license.
73ff40c12SJohn Marino * See README for more details.
83ff40c12SJohn Marino */
93ff40c12SJohn Marino
103ff40c12SJohn Marino #include "utils/includes.h"
113ff40c12SJohn Marino
123ff40c12SJohn Marino #include "utils/common.h"
133ff40c12SJohn Marino #include "common/ieee802_11_defs.h"
14*a1157835SDaniel Fojt #include "common/hw_features_common.h"
153ff40c12SJohn Marino #include "common/wpa_ctrl.h"
163ff40c12SJohn Marino #include "hostapd.h"
173ff40c12SJohn Marino #include "ap_drv_ops.h"
183ff40c12SJohn Marino #include "drivers/driver.h"
193ff40c12SJohn Marino #include "dfs.h"
203ff40c12SJohn Marino
213ff40c12SJohn Marino
dfs_get_used_n_chans(struct hostapd_iface * iface,int * seg1)22*a1157835SDaniel Fojt static int dfs_get_used_n_chans(struct hostapd_iface *iface, int *seg1)
233ff40c12SJohn Marino {
243ff40c12SJohn Marino int n_chans = 1;
253ff40c12SJohn Marino
26*a1157835SDaniel Fojt *seg1 = 0;
27*a1157835SDaniel Fojt
283ff40c12SJohn Marino if (iface->conf->ieee80211n && iface->conf->secondary_channel)
293ff40c12SJohn Marino n_chans = 2;
303ff40c12SJohn Marino
31*a1157835SDaniel Fojt if (iface->conf->ieee80211ac || iface->conf->ieee80211ax) {
32*a1157835SDaniel Fojt switch (hostapd_get_oper_chwidth(iface->conf)) {
33*a1157835SDaniel Fojt case CHANWIDTH_USE_HT:
343ff40c12SJohn Marino break;
35*a1157835SDaniel Fojt case CHANWIDTH_80MHZ:
363ff40c12SJohn Marino n_chans = 4;
373ff40c12SJohn Marino break;
38*a1157835SDaniel Fojt case CHANWIDTH_160MHZ:
393ff40c12SJohn Marino n_chans = 8;
403ff40c12SJohn Marino break;
41*a1157835SDaniel Fojt case CHANWIDTH_80P80MHZ:
42*a1157835SDaniel Fojt n_chans = 4;
43*a1157835SDaniel Fojt *seg1 = 4;
44*a1157835SDaniel Fojt break;
453ff40c12SJohn Marino default:
463ff40c12SJohn Marino break;
473ff40c12SJohn Marino }
483ff40c12SJohn Marino }
493ff40c12SJohn Marino
503ff40c12SJohn Marino return n_chans;
513ff40c12SJohn Marino }
523ff40c12SJohn Marino
533ff40c12SJohn Marino
dfs_channel_available(struct hostapd_channel_data * chan,int skip_radar)543ff40c12SJohn Marino static int dfs_channel_available(struct hostapd_channel_data *chan,
553ff40c12SJohn Marino int skip_radar)
563ff40c12SJohn Marino {
573ff40c12SJohn Marino /*
583ff40c12SJohn Marino * When radar detection happens, CSA is performed. However, there's no
593ff40c12SJohn Marino * time for CAC, so radar channels must be skipped when finding a new
60*a1157835SDaniel Fojt * channel for CSA, unless they are available for immediate use.
613ff40c12SJohn Marino */
62*a1157835SDaniel Fojt if (skip_radar && (chan->flag & HOSTAPD_CHAN_RADAR) &&
63*a1157835SDaniel Fojt ((chan->flag & HOSTAPD_CHAN_DFS_MASK) !=
64*a1157835SDaniel Fojt HOSTAPD_CHAN_DFS_AVAILABLE))
653ff40c12SJohn Marino return 0;
663ff40c12SJohn Marino
673ff40c12SJohn Marino if (chan->flag & HOSTAPD_CHAN_DISABLED)
683ff40c12SJohn Marino return 0;
693ff40c12SJohn Marino if ((chan->flag & HOSTAPD_CHAN_RADAR) &&
703ff40c12SJohn Marino ((chan->flag & HOSTAPD_CHAN_DFS_MASK) ==
713ff40c12SJohn Marino HOSTAPD_CHAN_DFS_UNAVAILABLE))
723ff40c12SJohn Marino return 0;
733ff40c12SJohn Marino return 1;
743ff40c12SJohn Marino }
753ff40c12SJohn Marino
763ff40c12SJohn Marino
dfs_is_chan_allowed(struct hostapd_channel_data * chan,int n_chans)773ff40c12SJohn Marino static int dfs_is_chan_allowed(struct hostapd_channel_data *chan, int n_chans)
783ff40c12SJohn Marino {
793ff40c12SJohn Marino /*
803ff40c12SJohn Marino * The tables contain first valid channel number based on channel width.
813ff40c12SJohn Marino * We will also choose this first channel as the control one.
823ff40c12SJohn Marino */
833ff40c12SJohn Marino int allowed_40[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 149, 157,
843ff40c12SJohn Marino 184, 192 };
853ff40c12SJohn Marino /*
863ff40c12SJohn Marino * VHT80, valid channels based on center frequency:
873ff40c12SJohn Marino * 42, 58, 106, 122, 138, 155
883ff40c12SJohn Marino */
893ff40c12SJohn Marino int allowed_80[] = { 36, 52, 100, 116, 132, 149 };
90*a1157835SDaniel Fojt /*
91*a1157835SDaniel Fojt * VHT160 valid channels based on center frequency:
92*a1157835SDaniel Fojt * 50, 114
93*a1157835SDaniel Fojt */
94*a1157835SDaniel Fojt int allowed_160[] = { 36, 100 };
953ff40c12SJohn Marino int *allowed = allowed_40;
963ff40c12SJohn Marino unsigned int i, allowed_no = 0;
973ff40c12SJohn Marino
983ff40c12SJohn Marino switch (n_chans) {
993ff40c12SJohn Marino case 2:
1003ff40c12SJohn Marino allowed = allowed_40;
1013ff40c12SJohn Marino allowed_no = ARRAY_SIZE(allowed_40);
1023ff40c12SJohn Marino break;
1033ff40c12SJohn Marino case 4:
1043ff40c12SJohn Marino allowed = allowed_80;
1053ff40c12SJohn Marino allowed_no = ARRAY_SIZE(allowed_80);
1063ff40c12SJohn Marino break;
107*a1157835SDaniel Fojt case 8:
108*a1157835SDaniel Fojt allowed = allowed_160;
109*a1157835SDaniel Fojt allowed_no = ARRAY_SIZE(allowed_160);
110*a1157835SDaniel Fojt break;
1113ff40c12SJohn Marino default:
1123ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "Unknown width for %d channels", n_chans);
1133ff40c12SJohn Marino break;
1143ff40c12SJohn Marino }
1153ff40c12SJohn Marino
1163ff40c12SJohn Marino for (i = 0; i < allowed_no; i++) {
1173ff40c12SJohn Marino if (chan->chan == allowed[i])
1183ff40c12SJohn Marino return 1;
1193ff40c12SJohn Marino }
1203ff40c12SJohn Marino
1213ff40c12SJohn Marino return 0;
1223ff40c12SJohn Marino }
1233ff40c12SJohn Marino
1243ff40c12SJohn Marino
125*a1157835SDaniel Fojt static struct hostapd_channel_data *
dfs_get_chan_data(struct hostapd_hw_modes * mode,int freq,int first_chan_idx)126*a1157835SDaniel Fojt dfs_get_chan_data(struct hostapd_hw_modes *mode, int freq, int first_chan_idx)
127*a1157835SDaniel Fojt {
128*a1157835SDaniel Fojt int i;
129*a1157835SDaniel Fojt
130*a1157835SDaniel Fojt for (i = first_chan_idx; i < mode->num_channels; i++) {
131*a1157835SDaniel Fojt if (mode->channels[i].freq == freq)
132*a1157835SDaniel Fojt return &mode->channels[i];
133*a1157835SDaniel Fojt }
134*a1157835SDaniel Fojt
135*a1157835SDaniel Fojt return NULL;
136*a1157835SDaniel Fojt }
137*a1157835SDaniel Fojt
138*a1157835SDaniel Fojt
dfs_chan_range_available(struct hostapd_hw_modes * mode,int first_chan_idx,int num_chans,int skip_radar)1393ff40c12SJohn Marino static int dfs_chan_range_available(struct hostapd_hw_modes *mode,
1403ff40c12SJohn Marino int first_chan_idx, int num_chans,
1413ff40c12SJohn Marino int skip_radar)
1423ff40c12SJohn Marino {
1433ff40c12SJohn Marino struct hostapd_channel_data *first_chan, *chan;
1443ff40c12SJohn Marino int i;
145*a1157835SDaniel Fojt u32 bw = num_chan_to_bw(num_chans);
1463ff40c12SJohn Marino
147*a1157835SDaniel Fojt if (first_chan_idx + num_chans > mode->num_channels)
1483ff40c12SJohn Marino return 0;
1493ff40c12SJohn Marino
1503ff40c12SJohn Marino first_chan = &mode->channels[first_chan_idx];
1513ff40c12SJohn Marino
152*a1157835SDaniel Fojt /* hostapd DFS implementation assumes the first channel as primary.
153*a1157835SDaniel Fojt * If it's not allowed to use the first channel as primary, decline the
154*a1157835SDaniel Fojt * whole channel range. */
155*a1157835SDaniel Fojt if (!chan_pri_allowed(first_chan))
156*a1157835SDaniel Fojt return 0;
1573ff40c12SJohn Marino
158*a1157835SDaniel Fojt for (i = 0; i < num_chans; i++) {
159*a1157835SDaniel Fojt chan = dfs_get_chan_data(mode, first_chan->freq + i * 20,
160*a1157835SDaniel Fojt first_chan_idx);
161*a1157835SDaniel Fojt if (!chan)
162*a1157835SDaniel Fojt return 0;
163*a1157835SDaniel Fojt
164*a1157835SDaniel Fojt /* HT 40 MHz secondary channel availability checked only for
165*a1157835SDaniel Fojt * primary channel */
166*a1157835SDaniel Fojt if (!chan_bw_allowed(chan, bw, 1, !i))
1673ff40c12SJohn Marino return 0;
1683ff40c12SJohn Marino
1693ff40c12SJohn Marino if (!dfs_channel_available(chan, skip_radar))
1703ff40c12SJohn Marino return 0;
1713ff40c12SJohn Marino }
1723ff40c12SJohn Marino
1733ff40c12SJohn Marino return 1;
1743ff40c12SJohn Marino }
1753ff40c12SJohn Marino
1763ff40c12SJohn Marino
is_in_chanlist(struct hostapd_iface * iface,struct hostapd_channel_data * chan)177*a1157835SDaniel Fojt static int is_in_chanlist(struct hostapd_iface *iface,
178*a1157835SDaniel Fojt struct hostapd_channel_data *chan)
179*a1157835SDaniel Fojt {
180*a1157835SDaniel Fojt if (!iface->conf->acs_ch_list.num)
181*a1157835SDaniel Fojt return 1;
182*a1157835SDaniel Fojt
183*a1157835SDaniel Fojt return freq_range_list_includes(&iface->conf->acs_ch_list, chan->chan);
184*a1157835SDaniel Fojt }
185*a1157835SDaniel Fojt
186*a1157835SDaniel Fojt
1873ff40c12SJohn Marino /*
1883ff40c12SJohn Marino * The function assumes HT40+ operation.
1893ff40c12SJohn Marino * Make sure to adjust the following variables after calling this:
1903ff40c12SJohn Marino * - hapd->secondary_channel
191*a1157835SDaniel Fojt * - hapd->vht/he_oper_centr_freq_seg0_idx
192*a1157835SDaniel Fojt * - hapd->vht/he_oper_centr_freq_seg1_idx
1933ff40c12SJohn Marino */
dfs_find_channel(struct hostapd_iface * iface,struct hostapd_channel_data ** ret_chan,int idx,int skip_radar)1943ff40c12SJohn Marino static int dfs_find_channel(struct hostapd_iface *iface,
1953ff40c12SJohn Marino struct hostapd_channel_data **ret_chan,
1963ff40c12SJohn Marino int idx, int skip_radar)
1973ff40c12SJohn Marino {
1983ff40c12SJohn Marino struct hostapd_hw_modes *mode;
1993ff40c12SJohn Marino struct hostapd_channel_data *chan;
200*a1157835SDaniel Fojt int i, channel_idx = 0, n_chans, n_chans1;
2013ff40c12SJohn Marino
2023ff40c12SJohn Marino mode = iface->current_mode;
203*a1157835SDaniel Fojt n_chans = dfs_get_used_n_chans(iface, &n_chans1);
2043ff40c12SJohn Marino
2053ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "DFS new chan checking %d channels", n_chans);
2063ff40c12SJohn Marino for (i = 0; i < mode->num_channels; i++) {
2073ff40c12SJohn Marino chan = &mode->channels[i];
2083ff40c12SJohn Marino
2093ff40c12SJohn Marino /* Skip HT40/VHT incompatible channels */
2103ff40c12SJohn Marino if (iface->conf->ieee80211n &&
2113ff40c12SJohn Marino iface->conf->secondary_channel &&
212*a1157835SDaniel Fojt (!dfs_is_chan_allowed(chan, n_chans) ||
213*a1157835SDaniel Fojt !(chan->allowed_bw & HOSTAPD_CHAN_WIDTH_40P)))
2143ff40c12SJohn Marino continue;
2153ff40c12SJohn Marino
2163ff40c12SJohn Marino /* Skip incompatible chandefs */
2173ff40c12SJohn Marino if (!dfs_chan_range_available(mode, i, n_chans, skip_radar))
2183ff40c12SJohn Marino continue;
2193ff40c12SJohn Marino
220*a1157835SDaniel Fojt if (!is_in_chanlist(iface, chan))
221*a1157835SDaniel Fojt continue;
222*a1157835SDaniel Fojt
2233ff40c12SJohn Marino if (ret_chan && idx == channel_idx) {
2243ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "Selected ch. #%d", chan->chan);
2253ff40c12SJohn Marino *ret_chan = chan;
2263ff40c12SJohn Marino return idx;
2273ff40c12SJohn Marino }
2283ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "Adding channel: %d", chan->chan);
2293ff40c12SJohn Marino channel_idx++;
2303ff40c12SJohn Marino }
2313ff40c12SJohn Marino return channel_idx;
2323ff40c12SJohn Marino }
2333ff40c12SJohn Marino
2343ff40c12SJohn Marino
dfs_adjust_center_freq(struct hostapd_iface * iface,struct hostapd_channel_data * chan,int secondary_channel,u8 * oper_centr_freq_seg0_idx,u8 * oper_centr_freq_seg1_idx)235*a1157835SDaniel Fojt static void dfs_adjust_center_freq(struct hostapd_iface *iface,
2363ff40c12SJohn Marino struct hostapd_channel_data *chan,
2373ff40c12SJohn Marino int secondary_channel,
238*a1157835SDaniel Fojt u8 *oper_centr_freq_seg0_idx,
239*a1157835SDaniel Fojt u8 *oper_centr_freq_seg1_idx)
2403ff40c12SJohn Marino {
241*a1157835SDaniel Fojt if (!iface->conf->ieee80211ac && !iface->conf->ieee80211ax)
2423ff40c12SJohn Marino return;
2433ff40c12SJohn Marino
2443ff40c12SJohn Marino if (!chan)
2453ff40c12SJohn Marino return;
2463ff40c12SJohn Marino
247*a1157835SDaniel Fojt *oper_centr_freq_seg1_idx = 0;
2483ff40c12SJohn Marino
249*a1157835SDaniel Fojt switch (hostapd_get_oper_chwidth(iface->conf)) {
250*a1157835SDaniel Fojt case CHANWIDTH_USE_HT:
2513ff40c12SJohn Marino if (secondary_channel == 1)
252*a1157835SDaniel Fojt *oper_centr_freq_seg0_idx = chan->chan + 2;
2533ff40c12SJohn Marino else if (secondary_channel == -1)
254*a1157835SDaniel Fojt *oper_centr_freq_seg0_idx = chan->chan - 2;
2553ff40c12SJohn Marino else
256*a1157835SDaniel Fojt *oper_centr_freq_seg0_idx = chan->chan;
2573ff40c12SJohn Marino break;
258*a1157835SDaniel Fojt case CHANWIDTH_80MHZ:
259*a1157835SDaniel Fojt *oper_centr_freq_seg0_idx = chan->chan + 6;
2603ff40c12SJohn Marino break;
261*a1157835SDaniel Fojt case CHANWIDTH_160MHZ:
262*a1157835SDaniel Fojt *oper_centr_freq_seg0_idx = chan->chan + 14;
2633ff40c12SJohn Marino break;
2643ff40c12SJohn Marino default:
2653ff40c12SJohn Marino wpa_printf(MSG_INFO, "DFS only VHT20/40/80/160 is supported now");
266*a1157835SDaniel Fojt *oper_centr_freq_seg0_idx = 0;
2673ff40c12SJohn Marino break;
2683ff40c12SJohn Marino }
2693ff40c12SJohn Marino
2703ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "DFS adjusting VHT center frequency: %d, %d",
271*a1157835SDaniel Fojt *oper_centr_freq_seg0_idx,
272*a1157835SDaniel Fojt *oper_centr_freq_seg1_idx);
2733ff40c12SJohn Marino }
2743ff40c12SJohn Marino
2753ff40c12SJohn Marino
2763ff40c12SJohn Marino /* Return start channel idx we will use for mode->channels[idx] */
dfs_get_start_chan_idx(struct hostapd_iface * iface,int * seg1_start)277*a1157835SDaniel Fojt static int dfs_get_start_chan_idx(struct hostapd_iface *iface, int *seg1_start)
2783ff40c12SJohn Marino {
2793ff40c12SJohn Marino struct hostapd_hw_modes *mode;
2803ff40c12SJohn Marino struct hostapd_channel_data *chan;
2813ff40c12SJohn Marino int channel_no = iface->conf->channel;
2823ff40c12SJohn Marino int res = -1, i;
283*a1157835SDaniel Fojt int chan_seg1 = -1;
284*a1157835SDaniel Fojt
285*a1157835SDaniel Fojt *seg1_start = -1;
2863ff40c12SJohn Marino
2873ff40c12SJohn Marino /* HT40- */
2883ff40c12SJohn Marino if (iface->conf->ieee80211n && iface->conf->secondary_channel == -1)
2893ff40c12SJohn Marino channel_no -= 4;
2903ff40c12SJohn Marino
291*a1157835SDaniel Fojt /* VHT/HE */
292*a1157835SDaniel Fojt if (iface->conf->ieee80211ac || iface->conf->ieee80211ax) {
293*a1157835SDaniel Fojt switch (hostapd_get_oper_chwidth(iface->conf)) {
294*a1157835SDaniel Fojt case CHANWIDTH_USE_HT:
2953ff40c12SJohn Marino break;
296*a1157835SDaniel Fojt case CHANWIDTH_80MHZ:
297*a1157835SDaniel Fojt channel_no = hostapd_get_oper_centr_freq_seg0_idx(
298*a1157835SDaniel Fojt iface->conf) - 6;
2993ff40c12SJohn Marino break;
300*a1157835SDaniel Fojt case CHANWIDTH_160MHZ:
301*a1157835SDaniel Fojt channel_no = hostapd_get_oper_centr_freq_seg0_idx(
302*a1157835SDaniel Fojt iface->conf) - 14;
303*a1157835SDaniel Fojt break;
304*a1157835SDaniel Fojt case CHANWIDTH_80P80MHZ:
305*a1157835SDaniel Fojt channel_no = hostapd_get_oper_centr_freq_seg0_idx(
306*a1157835SDaniel Fojt iface->conf) - 6;
307*a1157835SDaniel Fojt chan_seg1 = hostapd_get_oper_centr_freq_seg1_idx(
308*a1157835SDaniel Fojt iface->conf) - 6;
3093ff40c12SJohn Marino break;
3103ff40c12SJohn Marino default:
3113ff40c12SJohn Marino wpa_printf(MSG_INFO,
312*a1157835SDaniel Fojt "DFS only VHT20/40/80/160/80+80 is supported now");
3133ff40c12SJohn Marino channel_no = -1;
3143ff40c12SJohn Marino break;
3153ff40c12SJohn Marino }
3163ff40c12SJohn Marino }
3173ff40c12SJohn Marino
3183ff40c12SJohn Marino /* Get idx */
3193ff40c12SJohn Marino mode = iface->current_mode;
3203ff40c12SJohn Marino for (i = 0; i < mode->num_channels; i++) {
3213ff40c12SJohn Marino chan = &mode->channels[i];
3223ff40c12SJohn Marino if (chan->chan == channel_no) {
3233ff40c12SJohn Marino res = i;
3243ff40c12SJohn Marino break;
3253ff40c12SJohn Marino }
3263ff40c12SJohn Marino }
3273ff40c12SJohn Marino
328*a1157835SDaniel Fojt if (res != -1 && chan_seg1 > -1) {
329*a1157835SDaniel Fojt int found = 0;
330*a1157835SDaniel Fojt
331*a1157835SDaniel Fojt /* Get idx for seg1 */
332*a1157835SDaniel Fojt mode = iface->current_mode;
333*a1157835SDaniel Fojt for (i = 0; i < mode->num_channels; i++) {
334*a1157835SDaniel Fojt chan = &mode->channels[i];
335*a1157835SDaniel Fojt if (chan->chan == chan_seg1) {
336*a1157835SDaniel Fojt *seg1_start = i;
337*a1157835SDaniel Fojt found = 1;
338*a1157835SDaniel Fojt break;
339*a1157835SDaniel Fojt }
340*a1157835SDaniel Fojt }
341*a1157835SDaniel Fojt if (!found)
342*a1157835SDaniel Fojt res = -1;
343*a1157835SDaniel Fojt }
344*a1157835SDaniel Fojt
345*a1157835SDaniel Fojt if (res == -1) {
346*a1157835SDaniel Fojt wpa_printf(MSG_DEBUG,
347*a1157835SDaniel Fojt "DFS chan_idx seems wrong; num-ch: %d ch-no: %d conf-ch-no: %d 11n: %d sec-ch: %d vht-oper-width: %d",
348*a1157835SDaniel Fojt mode->num_channels, channel_no, iface->conf->channel,
349*a1157835SDaniel Fojt iface->conf->ieee80211n,
350*a1157835SDaniel Fojt iface->conf->secondary_channel,
351*a1157835SDaniel Fojt hostapd_get_oper_chwidth(iface->conf));
352*a1157835SDaniel Fojt
353*a1157835SDaniel Fojt for (i = 0; i < mode->num_channels; i++) {
354*a1157835SDaniel Fojt wpa_printf(MSG_DEBUG, "Available channel: %d",
355*a1157835SDaniel Fojt mode->channels[i].chan);
356*a1157835SDaniel Fojt }
357*a1157835SDaniel Fojt }
3583ff40c12SJohn Marino
3593ff40c12SJohn Marino return res;
3603ff40c12SJohn Marino }
3613ff40c12SJohn Marino
3623ff40c12SJohn Marino
3633ff40c12SJohn Marino /* At least one channel have radar flag */
dfs_check_chans_radar(struct hostapd_iface * iface,int start_chan_idx,int n_chans)3643ff40c12SJohn Marino static int dfs_check_chans_radar(struct hostapd_iface *iface,
3653ff40c12SJohn Marino int start_chan_idx, int n_chans)
3663ff40c12SJohn Marino {
3673ff40c12SJohn Marino struct hostapd_channel_data *channel;
3683ff40c12SJohn Marino struct hostapd_hw_modes *mode;
3693ff40c12SJohn Marino int i, res = 0;
3703ff40c12SJohn Marino
3713ff40c12SJohn Marino mode = iface->current_mode;
3723ff40c12SJohn Marino
3733ff40c12SJohn Marino for (i = 0; i < n_chans; i++) {
3743ff40c12SJohn Marino channel = &mode->channels[start_chan_idx + i];
3753ff40c12SJohn Marino if (channel->flag & HOSTAPD_CHAN_RADAR)
3763ff40c12SJohn Marino res++;
3773ff40c12SJohn Marino }
3783ff40c12SJohn Marino
3793ff40c12SJohn Marino return res;
3803ff40c12SJohn Marino }
3813ff40c12SJohn Marino
3823ff40c12SJohn Marino
3833ff40c12SJohn Marino /* All channels available */
dfs_check_chans_available(struct hostapd_iface * iface,int start_chan_idx,int n_chans)3843ff40c12SJohn Marino static int dfs_check_chans_available(struct hostapd_iface *iface,
3853ff40c12SJohn Marino int start_chan_idx, int n_chans)
3863ff40c12SJohn Marino {
3873ff40c12SJohn Marino struct hostapd_channel_data *channel;
3883ff40c12SJohn Marino struct hostapd_hw_modes *mode;
3893ff40c12SJohn Marino int i;
3903ff40c12SJohn Marino
3913ff40c12SJohn Marino mode = iface->current_mode;
3923ff40c12SJohn Marino
3933ff40c12SJohn Marino for (i = 0; i < n_chans; i++) {
3943ff40c12SJohn Marino channel = &mode->channels[start_chan_idx + i];
395*a1157835SDaniel Fojt
396*a1157835SDaniel Fojt if (channel->flag & HOSTAPD_CHAN_DISABLED)
397*a1157835SDaniel Fojt break;
398*a1157835SDaniel Fojt
399*a1157835SDaniel Fojt if (!(channel->flag & HOSTAPD_CHAN_RADAR))
400*a1157835SDaniel Fojt continue;
401*a1157835SDaniel Fojt
4023ff40c12SJohn Marino if ((channel->flag & HOSTAPD_CHAN_DFS_MASK) !=
4033ff40c12SJohn Marino HOSTAPD_CHAN_DFS_AVAILABLE)
4043ff40c12SJohn Marino break;
4053ff40c12SJohn Marino }
4063ff40c12SJohn Marino
4073ff40c12SJohn Marino return i == n_chans;
4083ff40c12SJohn Marino }
4093ff40c12SJohn Marino
4103ff40c12SJohn Marino
4113ff40c12SJohn Marino /* At least one channel unavailable */
dfs_check_chans_unavailable(struct hostapd_iface * iface,int start_chan_idx,int n_chans)4123ff40c12SJohn Marino static int dfs_check_chans_unavailable(struct hostapd_iface *iface,
4133ff40c12SJohn Marino int start_chan_idx,
4143ff40c12SJohn Marino int n_chans)
4153ff40c12SJohn Marino {
4163ff40c12SJohn Marino struct hostapd_channel_data *channel;
4173ff40c12SJohn Marino struct hostapd_hw_modes *mode;
4183ff40c12SJohn Marino int i, res = 0;
4193ff40c12SJohn Marino
4203ff40c12SJohn Marino mode = iface->current_mode;
4213ff40c12SJohn Marino
4223ff40c12SJohn Marino for (i = 0; i < n_chans; i++) {
4233ff40c12SJohn Marino channel = &mode->channels[start_chan_idx + i];
4243ff40c12SJohn Marino if (channel->flag & HOSTAPD_CHAN_DISABLED)
4253ff40c12SJohn Marino res++;
4263ff40c12SJohn Marino if ((channel->flag & HOSTAPD_CHAN_DFS_MASK) ==
4273ff40c12SJohn Marino HOSTAPD_CHAN_DFS_UNAVAILABLE)
4283ff40c12SJohn Marino res++;
4293ff40c12SJohn Marino }
4303ff40c12SJohn Marino
4313ff40c12SJohn Marino return res;
4323ff40c12SJohn Marino }
4333ff40c12SJohn Marino
4343ff40c12SJohn Marino
4353ff40c12SJohn Marino static struct hostapd_channel_data *
dfs_get_valid_channel(struct hostapd_iface * iface,int * secondary_channel,u8 * oper_centr_freq_seg0_idx,u8 * oper_centr_freq_seg1_idx,int skip_radar)4363ff40c12SJohn Marino dfs_get_valid_channel(struct hostapd_iface *iface,
4373ff40c12SJohn Marino int *secondary_channel,
438*a1157835SDaniel Fojt u8 *oper_centr_freq_seg0_idx,
439*a1157835SDaniel Fojt u8 *oper_centr_freq_seg1_idx,
4403ff40c12SJohn Marino int skip_radar)
4413ff40c12SJohn Marino {
4423ff40c12SJohn Marino struct hostapd_hw_modes *mode;
4433ff40c12SJohn Marino struct hostapd_channel_data *chan = NULL;
4443ff40c12SJohn Marino int num_available_chandefs;
4453ff40c12SJohn Marino int chan_idx;
4463ff40c12SJohn Marino u32 _rand;
4473ff40c12SJohn Marino
4483ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "DFS: Selecting random channel");
449*a1157835SDaniel Fojt *secondary_channel = 0;
450*a1157835SDaniel Fojt *oper_centr_freq_seg0_idx = 0;
451*a1157835SDaniel Fojt *oper_centr_freq_seg1_idx = 0;
4523ff40c12SJohn Marino
4533ff40c12SJohn Marino if (iface->current_mode == NULL)
4543ff40c12SJohn Marino return NULL;
4553ff40c12SJohn Marino
4563ff40c12SJohn Marino mode = iface->current_mode;
4573ff40c12SJohn Marino if (mode->mode != HOSTAPD_MODE_IEEE80211A)
4583ff40c12SJohn Marino return NULL;
4593ff40c12SJohn Marino
4603ff40c12SJohn Marino /* Get the count first */
4613ff40c12SJohn Marino num_available_chandefs = dfs_find_channel(iface, NULL, 0, skip_radar);
4623ff40c12SJohn Marino if (num_available_chandefs == 0)
4633ff40c12SJohn Marino return NULL;
4643ff40c12SJohn Marino
465*a1157835SDaniel Fojt if (os_get_random((u8 *) &_rand, sizeof(_rand)) < 0)
466*a1157835SDaniel Fojt return NULL;
4673ff40c12SJohn Marino chan_idx = _rand % num_available_chandefs;
4683ff40c12SJohn Marino dfs_find_channel(iface, &chan, chan_idx, skip_radar);
4693ff40c12SJohn Marino
4703ff40c12SJohn Marino /* dfs_find_channel() calculations assume HT40+ */
4713ff40c12SJohn Marino if (iface->conf->secondary_channel)
4723ff40c12SJohn Marino *secondary_channel = 1;
4733ff40c12SJohn Marino else
4743ff40c12SJohn Marino *secondary_channel = 0;
4753ff40c12SJohn Marino
476*a1157835SDaniel Fojt dfs_adjust_center_freq(iface, chan,
4773ff40c12SJohn Marino *secondary_channel,
478*a1157835SDaniel Fojt oper_centr_freq_seg0_idx,
479*a1157835SDaniel Fojt oper_centr_freq_seg1_idx);
4803ff40c12SJohn Marino
4813ff40c12SJohn Marino return chan;
4823ff40c12SJohn Marino }
4833ff40c12SJohn Marino
4843ff40c12SJohn Marino
set_dfs_state_freq(struct hostapd_iface * iface,int freq,u32 state)4853ff40c12SJohn Marino static int set_dfs_state_freq(struct hostapd_iface *iface, int freq, u32 state)
4863ff40c12SJohn Marino {
4873ff40c12SJohn Marino struct hostapd_hw_modes *mode;
4883ff40c12SJohn Marino struct hostapd_channel_data *chan = NULL;
4893ff40c12SJohn Marino int i;
4903ff40c12SJohn Marino
4913ff40c12SJohn Marino mode = iface->current_mode;
4923ff40c12SJohn Marino if (mode == NULL)
4933ff40c12SJohn Marino return 0;
4943ff40c12SJohn Marino
4953ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "set_dfs_state 0x%X for %d MHz", state, freq);
4963ff40c12SJohn Marino for (i = 0; i < iface->current_mode->num_channels; i++) {
4973ff40c12SJohn Marino chan = &iface->current_mode->channels[i];
4983ff40c12SJohn Marino if (chan->freq == freq) {
4993ff40c12SJohn Marino if (chan->flag & HOSTAPD_CHAN_RADAR) {
5003ff40c12SJohn Marino chan->flag &= ~HOSTAPD_CHAN_DFS_MASK;
5013ff40c12SJohn Marino chan->flag |= state;
5023ff40c12SJohn Marino return 1; /* Channel found */
5033ff40c12SJohn Marino }
5043ff40c12SJohn Marino }
5053ff40c12SJohn Marino }
5063ff40c12SJohn Marino wpa_printf(MSG_WARNING, "Can't set DFS state for freq %d MHz", freq);
5073ff40c12SJohn Marino return 0;
5083ff40c12SJohn Marino }
5093ff40c12SJohn Marino
5103ff40c12SJohn Marino
set_dfs_state(struct hostapd_iface * iface,int freq,int ht_enabled,int chan_offset,int chan_width,int cf1,int cf2,u32 state)5113ff40c12SJohn Marino static int set_dfs_state(struct hostapd_iface *iface, int freq, int ht_enabled,
5123ff40c12SJohn Marino int chan_offset, int chan_width, int cf1,
5133ff40c12SJohn Marino int cf2, u32 state)
5143ff40c12SJohn Marino {
5153ff40c12SJohn Marino int n_chans = 1, i;
5163ff40c12SJohn Marino struct hostapd_hw_modes *mode;
5173ff40c12SJohn Marino int frequency = freq;
5183ff40c12SJohn Marino int ret = 0;
5193ff40c12SJohn Marino
5203ff40c12SJohn Marino mode = iface->current_mode;
5213ff40c12SJohn Marino if (mode == NULL)
5223ff40c12SJohn Marino return 0;
5233ff40c12SJohn Marino
5243ff40c12SJohn Marino if (mode->mode != HOSTAPD_MODE_IEEE80211A) {
5253ff40c12SJohn Marino wpa_printf(MSG_WARNING, "current_mode != IEEE80211A");
5263ff40c12SJohn Marino return 0;
5273ff40c12SJohn Marino }
5283ff40c12SJohn Marino
5293ff40c12SJohn Marino /* Seems cf1 and chan_width is enough here */
5303ff40c12SJohn Marino switch (chan_width) {
5313ff40c12SJohn Marino case CHAN_WIDTH_20_NOHT:
5323ff40c12SJohn Marino case CHAN_WIDTH_20:
5333ff40c12SJohn Marino n_chans = 1;
5343ff40c12SJohn Marino if (frequency == 0)
5353ff40c12SJohn Marino frequency = cf1;
5363ff40c12SJohn Marino break;
5373ff40c12SJohn Marino case CHAN_WIDTH_40:
5383ff40c12SJohn Marino n_chans = 2;
5393ff40c12SJohn Marino frequency = cf1 - 10;
5403ff40c12SJohn Marino break;
5413ff40c12SJohn Marino case CHAN_WIDTH_80:
5423ff40c12SJohn Marino n_chans = 4;
5433ff40c12SJohn Marino frequency = cf1 - 30;
5443ff40c12SJohn Marino break;
5453ff40c12SJohn Marino case CHAN_WIDTH_160:
5463ff40c12SJohn Marino n_chans = 8;
5473ff40c12SJohn Marino frequency = cf1 - 70;
5483ff40c12SJohn Marino break;
5493ff40c12SJohn Marino default:
5503ff40c12SJohn Marino wpa_printf(MSG_INFO, "DFS chan_width %d not supported",
5513ff40c12SJohn Marino chan_width);
5523ff40c12SJohn Marino break;
5533ff40c12SJohn Marino }
5543ff40c12SJohn Marino
5553ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "DFS freq: %dMHz, n_chans: %d", frequency,
5563ff40c12SJohn Marino n_chans);
5573ff40c12SJohn Marino for (i = 0; i < n_chans; i++) {
5583ff40c12SJohn Marino ret += set_dfs_state_freq(iface, frequency, state);
5593ff40c12SJohn Marino frequency = frequency + 20;
5603ff40c12SJohn Marino }
5613ff40c12SJohn Marino
5623ff40c12SJohn Marino return ret;
5633ff40c12SJohn Marino }
5643ff40c12SJohn Marino
5653ff40c12SJohn Marino
dfs_are_channels_overlapped(struct hostapd_iface * iface,int freq,int chan_width,int cf1,int cf2)5663ff40c12SJohn Marino static int dfs_are_channels_overlapped(struct hostapd_iface *iface, int freq,
5673ff40c12SJohn Marino int chan_width, int cf1, int cf2)
5683ff40c12SJohn Marino {
569*a1157835SDaniel Fojt int start_chan_idx, start_chan_idx1;
5703ff40c12SJohn Marino struct hostapd_hw_modes *mode;
5713ff40c12SJohn Marino struct hostapd_channel_data *chan;
572*a1157835SDaniel Fojt int n_chans, n_chans1, i, j, frequency = freq, radar_n_chans = 1;
5733ff40c12SJohn Marino u8 radar_chan;
5743ff40c12SJohn Marino int res = 0;
5753ff40c12SJohn Marino
5763ff40c12SJohn Marino /* Our configuration */
5773ff40c12SJohn Marino mode = iface->current_mode;
578*a1157835SDaniel Fojt start_chan_idx = dfs_get_start_chan_idx(iface, &start_chan_idx1);
579*a1157835SDaniel Fojt n_chans = dfs_get_used_n_chans(iface, &n_chans1);
5803ff40c12SJohn Marino
5813ff40c12SJohn Marino /* Check we are on DFS channel(s) */
5823ff40c12SJohn Marino if (!dfs_check_chans_radar(iface, start_chan_idx, n_chans))
5833ff40c12SJohn Marino return 0;
5843ff40c12SJohn Marino
5853ff40c12SJohn Marino /* Reported via radar event */
5863ff40c12SJohn Marino switch (chan_width) {
5873ff40c12SJohn Marino case CHAN_WIDTH_20_NOHT:
5883ff40c12SJohn Marino case CHAN_WIDTH_20:
5893ff40c12SJohn Marino radar_n_chans = 1;
5903ff40c12SJohn Marino if (frequency == 0)
5913ff40c12SJohn Marino frequency = cf1;
5923ff40c12SJohn Marino break;
5933ff40c12SJohn Marino case CHAN_WIDTH_40:
5943ff40c12SJohn Marino radar_n_chans = 2;
5953ff40c12SJohn Marino frequency = cf1 - 10;
5963ff40c12SJohn Marino break;
5973ff40c12SJohn Marino case CHAN_WIDTH_80:
5983ff40c12SJohn Marino radar_n_chans = 4;
5993ff40c12SJohn Marino frequency = cf1 - 30;
6003ff40c12SJohn Marino break;
6013ff40c12SJohn Marino case CHAN_WIDTH_160:
6023ff40c12SJohn Marino radar_n_chans = 8;
6033ff40c12SJohn Marino frequency = cf1 - 70;
6043ff40c12SJohn Marino break;
6053ff40c12SJohn Marino default:
6063ff40c12SJohn Marino wpa_printf(MSG_INFO, "DFS chan_width %d not supported",
6073ff40c12SJohn Marino chan_width);
6083ff40c12SJohn Marino break;
6093ff40c12SJohn Marino }
6103ff40c12SJohn Marino
6113ff40c12SJohn Marino ieee80211_freq_to_chan(frequency, &radar_chan);
6123ff40c12SJohn Marino
6133ff40c12SJohn Marino for (i = 0; i < n_chans; i++) {
6143ff40c12SJohn Marino chan = &mode->channels[start_chan_idx + i];
6153ff40c12SJohn Marino if (!(chan->flag & HOSTAPD_CHAN_RADAR))
6163ff40c12SJohn Marino continue;
6173ff40c12SJohn Marino for (j = 0; j < radar_n_chans; j++) {
6183ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "checking our: %d, radar: %d",
6193ff40c12SJohn Marino chan->chan, radar_chan + j * 4);
6203ff40c12SJohn Marino if (chan->chan == radar_chan + j * 4)
6213ff40c12SJohn Marino res++;
6223ff40c12SJohn Marino }
6233ff40c12SJohn Marino }
6243ff40c12SJohn Marino
6253ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "overlapped: %d", res);
6263ff40c12SJohn Marino
6273ff40c12SJohn Marino return res;
6283ff40c12SJohn Marino }
6293ff40c12SJohn Marino
6303ff40c12SJohn Marino
dfs_get_cac_time(struct hostapd_iface * iface,int start_chan_idx,int n_chans)631*a1157835SDaniel Fojt static unsigned int dfs_get_cac_time(struct hostapd_iface *iface,
632*a1157835SDaniel Fojt int start_chan_idx, int n_chans)
633*a1157835SDaniel Fojt {
634*a1157835SDaniel Fojt struct hostapd_channel_data *channel;
635*a1157835SDaniel Fojt struct hostapd_hw_modes *mode;
636*a1157835SDaniel Fojt int i;
637*a1157835SDaniel Fojt unsigned int cac_time_ms = 0;
638*a1157835SDaniel Fojt
639*a1157835SDaniel Fojt mode = iface->current_mode;
640*a1157835SDaniel Fojt
641*a1157835SDaniel Fojt for (i = 0; i < n_chans; i++) {
642*a1157835SDaniel Fojt channel = &mode->channels[start_chan_idx + i];
643*a1157835SDaniel Fojt if (!(channel->flag & HOSTAPD_CHAN_RADAR))
644*a1157835SDaniel Fojt continue;
645*a1157835SDaniel Fojt if (channel->dfs_cac_ms > cac_time_ms)
646*a1157835SDaniel Fojt cac_time_ms = channel->dfs_cac_ms;
647*a1157835SDaniel Fojt }
648*a1157835SDaniel Fojt
649*a1157835SDaniel Fojt return cac_time_ms;
650*a1157835SDaniel Fojt }
651*a1157835SDaniel Fojt
652*a1157835SDaniel Fojt
6533ff40c12SJohn Marino /*
6543ff40c12SJohn Marino * Main DFS handler
6553ff40c12SJohn Marino * 1 - continue channel/ap setup
6563ff40c12SJohn Marino * 0 - channel/ap setup will be continued after CAC
6573ff40c12SJohn Marino * -1 - hit critical error
6583ff40c12SJohn Marino */
hostapd_handle_dfs(struct hostapd_iface * iface)6593ff40c12SJohn Marino int hostapd_handle_dfs(struct hostapd_iface *iface)
6603ff40c12SJohn Marino {
6613ff40c12SJohn Marino struct hostapd_channel_data *channel;
662*a1157835SDaniel Fojt int res, n_chans, n_chans1, start_chan_idx, start_chan_idx1;
6633ff40c12SJohn Marino int skip_radar = 0;
6643ff40c12SJohn Marino
665*a1157835SDaniel Fojt if (!iface->current_mode) {
666*a1157835SDaniel Fojt /*
667*a1157835SDaniel Fojt * This can happen with drivers that do not provide mode
668*a1157835SDaniel Fojt * information and as such, cannot really use hostapd for DFS.
669*a1157835SDaniel Fojt */
670*a1157835SDaniel Fojt wpa_printf(MSG_DEBUG,
671*a1157835SDaniel Fojt "DFS: No current_mode information - assume no need to perform DFS operations by hostapd");
672*a1157835SDaniel Fojt return 1;
673*a1157835SDaniel Fojt }
674*a1157835SDaniel Fojt
6753ff40c12SJohn Marino iface->cac_started = 0;
6763ff40c12SJohn Marino
6773ff40c12SJohn Marino do {
6783ff40c12SJohn Marino /* Get start (first) channel for current configuration */
679*a1157835SDaniel Fojt start_chan_idx = dfs_get_start_chan_idx(iface,
680*a1157835SDaniel Fojt &start_chan_idx1);
6813ff40c12SJohn Marino if (start_chan_idx == -1)
6823ff40c12SJohn Marino return -1;
6833ff40c12SJohn Marino
6843ff40c12SJohn Marino /* Get number of used channels, depend on width */
685*a1157835SDaniel Fojt n_chans = dfs_get_used_n_chans(iface, &n_chans1);
686*a1157835SDaniel Fojt
687*a1157835SDaniel Fojt /* Setup CAC time */
688*a1157835SDaniel Fojt iface->dfs_cac_ms = dfs_get_cac_time(iface, start_chan_idx,
689*a1157835SDaniel Fojt n_chans);
6903ff40c12SJohn Marino
6913ff40c12SJohn Marino /* Check if any of configured channels require DFS */
6923ff40c12SJohn Marino res = dfs_check_chans_radar(iface, start_chan_idx, n_chans);
6933ff40c12SJohn Marino wpa_printf(MSG_DEBUG,
6943ff40c12SJohn Marino "DFS %d channels required radar detection",
6953ff40c12SJohn Marino res);
6963ff40c12SJohn Marino if (!res)
6973ff40c12SJohn Marino return 1;
6983ff40c12SJohn Marino
6993ff40c12SJohn Marino /* Check if all channels are DFS available */
7003ff40c12SJohn Marino res = dfs_check_chans_available(iface, start_chan_idx, n_chans);
7013ff40c12SJohn Marino wpa_printf(MSG_DEBUG,
7023ff40c12SJohn Marino "DFS all channels available, (SKIP CAC): %s",
7033ff40c12SJohn Marino res ? "yes" : "no");
7043ff40c12SJohn Marino if (res)
7053ff40c12SJohn Marino return 1;
7063ff40c12SJohn Marino
7073ff40c12SJohn Marino /* Check if any of configured channels is unavailable */
7083ff40c12SJohn Marino res = dfs_check_chans_unavailable(iface, start_chan_idx,
7093ff40c12SJohn Marino n_chans);
7103ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "DFS %d chans unavailable - choose other channel: %s",
7113ff40c12SJohn Marino res, res ? "yes": "no");
7123ff40c12SJohn Marino if (res) {
713*a1157835SDaniel Fojt int sec = 0;
714*a1157835SDaniel Fojt u8 cf1 = 0, cf2 = 0;
7153ff40c12SJohn Marino
7163ff40c12SJohn Marino channel = dfs_get_valid_channel(iface, &sec, &cf1, &cf2,
7173ff40c12SJohn Marino skip_radar);
7183ff40c12SJohn Marino if (!channel) {
7193ff40c12SJohn Marino wpa_printf(MSG_ERROR, "could not get valid channel");
720*a1157835SDaniel Fojt hostapd_set_state(iface, HAPD_IFACE_DFS);
721*a1157835SDaniel Fojt return 0;
7223ff40c12SJohn Marino }
7233ff40c12SJohn Marino
7243ff40c12SJohn Marino iface->freq = channel->freq;
7253ff40c12SJohn Marino iface->conf->channel = channel->chan;
7263ff40c12SJohn Marino iface->conf->secondary_channel = sec;
727*a1157835SDaniel Fojt hostapd_set_oper_centr_freq_seg0_idx(iface->conf, cf1);
728*a1157835SDaniel Fojt hostapd_set_oper_centr_freq_seg1_idx(iface->conf, cf2);
7293ff40c12SJohn Marino }
7303ff40c12SJohn Marino } while (res);
7313ff40c12SJohn Marino
7323ff40c12SJohn Marino /* Finally start CAC */
7333ff40c12SJohn Marino hostapd_set_state(iface, HAPD_IFACE_DFS);
7343ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "DFS start CAC on %d MHz", iface->freq);
7353ff40c12SJohn Marino wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_CAC_START
736*a1157835SDaniel Fojt "freq=%d chan=%d sec_chan=%d, width=%d, seg0=%d, seg1=%d, cac_time=%ds",
7373ff40c12SJohn Marino iface->freq,
738*a1157835SDaniel Fojt iface->conf->channel, iface->conf->secondary_channel,
739*a1157835SDaniel Fojt hostapd_get_oper_chwidth(iface->conf),
740*a1157835SDaniel Fojt hostapd_get_oper_centr_freq_seg0_idx(iface->conf),
741*a1157835SDaniel Fojt hostapd_get_oper_centr_freq_seg1_idx(iface->conf),
742*a1157835SDaniel Fojt iface->dfs_cac_ms / 1000);
743*a1157835SDaniel Fojt
744*a1157835SDaniel Fojt res = hostapd_start_dfs_cac(
745*a1157835SDaniel Fojt iface, iface->conf->hw_mode, iface->freq, iface->conf->channel,
746*a1157835SDaniel Fojt iface->conf->ieee80211n, iface->conf->ieee80211ac,
747*a1157835SDaniel Fojt iface->conf->ieee80211ax,
7483ff40c12SJohn Marino iface->conf->secondary_channel,
749*a1157835SDaniel Fojt hostapd_get_oper_chwidth(iface->conf),
750*a1157835SDaniel Fojt hostapd_get_oper_centr_freq_seg0_idx(iface->conf),
751*a1157835SDaniel Fojt hostapd_get_oper_centr_freq_seg1_idx(iface->conf));
752*a1157835SDaniel Fojt
753*a1157835SDaniel Fojt if (res) {
754*a1157835SDaniel Fojt wpa_printf(MSG_ERROR, "DFS start_dfs_cac() failed, %d", res);
7553ff40c12SJohn Marino return -1;
7563ff40c12SJohn Marino }
7573ff40c12SJohn Marino
7583ff40c12SJohn Marino return 0;
7593ff40c12SJohn Marino }
7603ff40c12SJohn Marino
7613ff40c12SJohn Marino
hostapd_config_dfs_chan_available(struct hostapd_iface * iface)762*a1157835SDaniel Fojt static int hostapd_config_dfs_chan_available(struct hostapd_iface *iface)
763*a1157835SDaniel Fojt {
764*a1157835SDaniel Fojt int n_chans, n_chans1, start_chan_idx, start_chan_idx1;
765*a1157835SDaniel Fojt
766*a1157835SDaniel Fojt /* Get the start (first) channel for current configuration */
767*a1157835SDaniel Fojt start_chan_idx = dfs_get_start_chan_idx(iface, &start_chan_idx1);
768*a1157835SDaniel Fojt if (start_chan_idx < 0)
769*a1157835SDaniel Fojt return 0;
770*a1157835SDaniel Fojt
771*a1157835SDaniel Fojt /* Get the number of used channels, depending on width */
772*a1157835SDaniel Fojt n_chans = dfs_get_used_n_chans(iface, &n_chans1);
773*a1157835SDaniel Fojt
774*a1157835SDaniel Fojt /* Check if all channels are DFS available */
775*a1157835SDaniel Fojt return dfs_check_chans_available(iface, start_chan_idx, n_chans);
776*a1157835SDaniel Fojt }
777*a1157835SDaniel Fojt
778*a1157835SDaniel Fojt
hostapd_dfs_complete_cac(struct hostapd_iface * iface,int success,int freq,int ht_enabled,int chan_offset,int chan_width,int cf1,int cf2)7793ff40c12SJohn Marino int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq,
7803ff40c12SJohn Marino int ht_enabled, int chan_offset, int chan_width,
7813ff40c12SJohn Marino int cf1, int cf2)
7823ff40c12SJohn Marino {
7833ff40c12SJohn Marino wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_CAC_COMPLETED
7843ff40c12SJohn Marino "success=%d freq=%d ht_enabled=%d chan_offset=%d chan_width=%d cf1=%d cf2=%d",
7853ff40c12SJohn Marino success, freq, ht_enabled, chan_offset, chan_width, cf1, cf2);
7863ff40c12SJohn Marino
7873ff40c12SJohn Marino if (success) {
7883ff40c12SJohn Marino /* Complete iface/ap configuration */
789*a1157835SDaniel Fojt if (iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD) {
790*a1157835SDaniel Fojt /* Complete AP configuration for the first bring up. */
791*a1157835SDaniel Fojt if (iface->state != HAPD_IFACE_ENABLED)
792*a1157835SDaniel Fojt hostapd_setup_interface_complete(iface, 0);
793*a1157835SDaniel Fojt else
794*a1157835SDaniel Fojt iface->cac_started = 0;
795*a1157835SDaniel Fojt } else {
7963ff40c12SJohn Marino set_dfs_state(iface, freq, ht_enabled, chan_offset,
7973ff40c12SJohn Marino chan_width, cf1, cf2,
7983ff40c12SJohn Marino HOSTAPD_CHAN_DFS_AVAILABLE);
799*a1157835SDaniel Fojt /*
800*a1157835SDaniel Fojt * Just mark the channel available when CAC completion
801*a1157835SDaniel Fojt * event is received in enabled state. CAC result could
802*a1157835SDaniel Fojt * have been propagated from another radio having the
803*a1157835SDaniel Fojt * same regulatory configuration. When CAC completion is
804*a1157835SDaniel Fojt * received during non-HAPD_IFACE_ENABLED state, make
805*a1157835SDaniel Fojt * sure the configured channel is available because this
806*a1157835SDaniel Fojt * CAC completion event could have been propagated from
807*a1157835SDaniel Fojt * another radio.
808*a1157835SDaniel Fojt */
809*a1157835SDaniel Fojt if (iface->state != HAPD_IFACE_ENABLED &&
810*a1157835SDaniel Fojt hostapd_config_dfs_chan_available(iface)) {
8113ff40c12SJohn Marino hostapd_setup_interface_complete(iface, 0);
812*a1157835SDaniel Fojt iface->cac_started = 0;
8133ff40c12SJohn Marino }
814*a1157835SDaniel Fojt }
815*a1157835SDaniel Fojt }
816*a1157835SDaniel Fojt
817*a1157835SDaniel Fojt return 0;
818*a1157835SDaniel Fojt }
819*a1157835SDaniel Fojt
820*a1157835SDaniel Fojt
hostapd_dfs_pre_cac_expired(struct hostapd_iface * iface,int freq,int ht_enabled,int chan_offset,int chan_width,int cf1,int cf2)821*a1157835SDaniel Fojt int hostapd_dfs_pre_cac_expired(struct hostapd_iface *iface, int freq,
822*a1157835SDaniel Fojt int ht_enabled, int chan_offset, int chan_width,
823*a1157835SDaniel Fojt int cf1, int cf2)
824*a1157835SDaniel Fojt {
825*a1157835SDaniel Fojt wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_PRE_CAC_EXPIRED
826*a1157835SDaniel Fojt "freq=%d ht_enabled=%d chan_offset=%d chan_width=%d cf1=%d cf2=%d",
827*a1157835SDaniel Fojt freq, ht_enabled, chan_offset, chan_width, cf1, cf2);
828*a1157835SDaniel Fojt
829*a1157835SDaniel Fojt /* Proceed only if DFS is not offloaded to the driver */
830*a1157835SDaniel Fojt if (iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD)
831*a1157835SDaniel Fojt return 0;
832*a1157835SDaniel Fojt
833*a1157835SDaniel Fojt set_dfs_state(iface, freq, ht_enabled, chan_offset, chan_width,
834*a1157835SDaniel Fojt cf1, cf2, HOSTAPD_CHAN_DFS_USABLE);
8353ff40c12SJohn Marino
8363ff40c12SJohn Marino return 0;
8373ff40c12SJohn Marino }
8383ff40c12SJohn Marino
8393ff40c12SJohn Marino
hostapd_dfs_start_channel_switch_cac(struct hostapd_iface * iface)8403ff40c12SJohn Marino static int hostapd_dfs_start_channel_switch_cac(struct hostapd_iface *iface)
8413ff40c12SJohn Marino {
8423ff40c12SJohn Marino struct hostapd_channel_data *channel;
8433ff40c12SJohn Marino int secondary_channel;
844*a1157835SDaniel Fojt u8 oper_centr_freq_seg0_idx = 0;
845*a1157835SDaniel Fojt u8 oper_centr_freq_seg1_idx = 0;
8463ff40c12SJohn Marino int skip_radar = 0;
8473ff40c12SJohn Marino int err = 1;
8483ff40c12SJohn Marino
8493ff40c12SJohn Marino /* Radar detected during active CAC */
8503ff40c12SJohn Marino iface->cac_started = 0;
8513ff40c12SJohn Marino channel = dfs_get_valid_channel(iface, &secondary_channel,
852*a1157835SDaniel Fojt &oper_centr_freq_seg0_idx,
853*a1157835SDaniel Fojt &oper_centr_freq_seg1_idx,
8543ff40c12SJohn Marino skip_radar);
8553ff40c12SJohn Marino
8563ff40c12SJohn Marino if (!channel) {
8573ff40c12SJohn Marino wpa_printf(MSG_ERROR, "No valid channel available");
8583ff40c12SJohn Marino return err;
8593ff40c12SJohn Marino }
8603ff40c12SJohn Marino
8613ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "DFS will switch to a new channel %d",
8623ff40c12SJohn Marino channel->chan);
8633ff40c12SJohn Marino wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_NEW_CHANNEL
8643ff40c12SJohn Marino "freq=%d chan=%d sec_chan=%d", channel->freq,
8653ff40c12SJohn Marino channel->chan, secondary_channel);
8663ff40c12SJohn Marino
8673ff40c12SJohn Marino iface->freq = channel->freq;
8683ff40c12SJohn Marino iface->conf->channel = channel->chan;
8693ff40c12SJohn Marino iface->conf->secondary_channel = secondary_channel;
870*a1157835SDaniel Fojt hostapd_set_oper_centr_freq_seg0_idx(iface->conf,
871*a1157835SDaniel Fojt oper_centr_freq_seg0_idx);
872*a1157835SDaniel Fojt hostapd_set_oper_centr_freq_seg1_idx(iface->conf,
873*a1157835SDaniel Fojt oper_centr_freq_seg1_idx);
8743ff40c12SJohn Marino err = 0;
8753ff40c12SJohn Marino
8763ff40c12SJohn Marino hostapd_setup_interface_complete(iface, err);
8773ff40c12SJohn Marino return err;
8783ff40c12SJohn Marino }
8793ff40c12SJohn Marino
8803ff40c12SJohn Marino
hostapd_dfs_start_channel_switch(struct hostapd_iface * iface)8813ff40c12SJohn Marino static int hostapd_dfs_start_channel_switch(struct hostapd_iface *iface)
8823ff40c12SJohn Marino {
8833ff40c12SJohn Marino struct hostapd_channel_data *channel;
8843ff40c12SJohn Marino int secondary_channel;
885*a1157835SDaniel Fojt u8 oper_centr_freq_seg0_idx;
886*a1157835SDaniel Fojt u8 oper_centr_freq_seg1_idx;
8873ff40c12SJohn Marino int skip_radar = 1;
8883ff40c12SJohn Marino struct csa_settings csa_settings;
889*a1157835SDaniel Fojt unsigned int i;
8903ff40c12SJohn Marino int err = 1;
891*a1157835SDaniel Fojt struct hostapd_hw_modes *cmode = iface->current_mode;
8923ff40c12SJohn Marino
8933ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "%s called (CAC active: %s, CSA active: %s)",
8943ff40c12SJohn Marino __func__, iface->cac_started ? "yes" : "no",
895*a1157835SDaniel Fojt hostapd_csa_in_progress(iface) ? "yes" : "no");
8963ff40c12SJohn Marino
8973ff40c12SJohn Marino /* Check if CSA in progress */
898*a1157835SDaniel Fojt if (hostapd_csa_in_progress(iface))
8993ff40c12SJohn Marino return 0;
9003ff40c12SJohn Marino
9013ff40c12SJohn Marino /* Check if active CAC */
9023ff40c12SJohn Marino if (iface->cac_started)
9033ff40c12SJohn Marino return hostapd_dfs_start_channel_switch_cac(iface);
9043ff40c12SJohn Marino
905*a1157835SDaniel Fojt /*
906*a1157835SDaniel Fojt * Allow selection of DFS channel in ETSI to comply with
907*a1157835SDaniel Fojt * uniform spreading.
908*a1157835SDaniel Fojt */
909*a1157835SDaniel Fojt if (iface->dfs_domain == HOSTAPD_DFS_REGION_ETSI)
910*a1157835SDaniel Fojt skip_radar = 0;
911*a1157835SDaniel Fojt
9123ff40c12SJohn Marino /* Perform channel switch/CSA */
9133ff40c12SJohn Marino channel = dfs_get_valid_channel(iface, &secondary_channel,
914*a1157835SDaniel Fojt &oper_centr_freq_seg0_idx,
915*a1157835SDaniel Fojt &oper_centr_freq_seg1_idx,
9163ff40c12SJohn Marino skip_radar);
9173ff40c12SJohn Marino
9183ff40c12SJohn Marino if (!channel) {
919*a1157835SDaniel Fojt /*
920*a1157835SDaniel Fojt * If there is no channel to switch immediately to, check if
921*a1157835SDaniel Fojt * there is another channel where we can switch even if it
922*a1157835SDaniel Fojt * requires to perform a CAC first.
923*a1157835SDaniel Fojt */
924*a1157835SDaniel Fojt skip_radar = 0;
925*a1157835SDaniel Fojt channel = dfs_get_valid_channel(iface, &secondary_channel,
926*a1157835SDaniel Fojt &oper_centr_freq_seg0_idx,
927*a1157835SDaniel Fojt &oper_centr_freq_seg1_idx,
928*a1157835SDaniel Fojt skip_radar);
929*a1157835SDaniel Fojt if (!channel) {
930*a1157835SDaniel Fojt wpa_printf(MSG_INFO,
931*a1157835SDaniel Fojt "%s: no DFS channels left, waiting for NOP to finish",
932*a1157835SDaniel Fojt __func__);
9333ff40c12SJohn Marino return err;
9343ff40c12SJohn Marino }
9353ff40c12SJohn Marino
936*a1157835SDaniel Fojt iface->freq = channel->freq;
937*a1157835SDaniel Fojt iface->conf->channel = channel->chan;
938*a1157835SDaniel Fojt iface->conf->secondary_channel = secondary_channel;
939*a1157835SDaniel Fojt hostapd_set_oper_centr_freq_seg0_idx(iface->conf,
940*a1157835SDaniel Fojt oper_centr_freq_seg0_idx);
941*a1157835SDaniel Fojt hostapd_set_oper_centr_freq_seg1_idx(iface->conf,
942*a1157835SDaniel Fojt oper_centr_freq_seg1_idx);
943*a1157835SDaniel Fojt
944*a1157835SDaniel Fojt hostapd_disable_iface(iface);
945*a1157835SDaniel Fojt hostapd_enable_iface(iface);
946*a1157835SDaniel Fojt return 0;
947*a1157835SDaniel Fojt }
948*a1157835SDaniel Fojt
9493ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "DFS will switch to a new channel %d",
9503ff40c12SJohn Marino channel->chan);
9513ff40c12SJohn Marino wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_NEW_CHANNEL
9523ff40c12SJohn Marino "freq=%d chan=%d sec_chan=%d", channel->freq,
9533ff40c12SJohn Marino channel->chan, secondary_channel);
9543ff40c12SJohn Marino
9553ff40c12SJohn Marino /* Setup CSA request */
9563ff40c12SJohn Marino os_memset(&csa_settings, 0, sizeof(csa_settings));
9573ff40c12SJohn Marino csa_settings.cs_count = 5;
9583ff40c12SJohn Marino csa_settings.block_tx = 1;
9593ff40c12SJohn Marino err = hostapd_set_freq_params(&csa_settings.freq_params,
9603ff40c12SJohn Marino iface->conf->hw_mode,
9613ff40c12SJohn Marino channel->freq,
9623ff40c12SJohn Marino channel->chan,
9633ff40c12SJohn Marino iface->conf->ieee80211n,
9643ff40c12SJohn Marino iface->conf->ieee80211ac,
965*a1157835SDaniel Fojt iface->conf->ieee80211ax,
9663ff40c12SJohn Marino secondary_channel,
967*a1157835SDaniel Fojt hostapd_get_oper_chwidth(iface->conf),
968*a1157835SDaniel Fojt oper_centr_freq_seg0_idx,
969*a1157835SDaniel Fojt oper_centr_freq_seg1_idx,
970*a1157835SDaniel Fojt cmode->vht_capab,
971*a1157835SDaniel Fojt &cmode->he_capab[IEEE80211_MODE_AP]);
9723ff40c12SJohn Marino
9733ff40c12SJohn Marino if (err) {
9743ff40c12SJohn Marino wpa_printf(MSG_ERROR, "DFS failed to calculate CSA freq params");
9753ff40c12SJohn Marino hostapd_disable_iface(iface);
9763ff40c12SJohn Marino return err;
9773ff40c12SJohn Marino }
9783ff40c12SJohn Marino
979*a1157835SDaniel Fojt for (i = 0; i < iface->num_bss; i++) {
980*a1157835SDaniel Fojt err = hostapd_switch_channel(iface->bss[i], &csa_settings);
981*a1157835SDaniel Fojt if (err)
982*a1157835SDaniel Fojt break;
983*a1157835SDaniel Fojt }
984*a1157835SDaniel Fojt
9853ff40c12SJohn Marino if (err) {
9863ff40c12SJohn Marino wpa_printf(MSG_WARNING, "DFS failed to schedule CSA (%d) - trying fallback",
9873ff40c12SJohn Marino err);
9883ff40c12SJohn Marino iface->freq = channel->freq;
9893ff40c12SJohn Marino iface->conf->channel = channel->chan;
9903ff40c12SJohn Marino iface->conf->secondary_channel = secondary_channel;
991*a1157835SDaniel Fojt hostapd_set_oper_centr_freq_seg0_idx(iface->conf,
992*a1157835SDaniel Fojt oper_centr_freq_seg0_idx);
993*a1157835SDaniel Fojt hostapd_set_oper_centr_freq_seg1_idx(iface->conf,
994*a1157835SDaniel Fojt oper_centr_freq_seg1_idx);
9953ff40c12SJohn Marino
9963ff40c12SJohn Marino hostapd_disable_iface(iface);
9973ff40c12SJohn Marino hostapd_enable_iface(iface);
9983ff40c12SJohn Marino return 0;
9993ff40c12SJohn Marino }
10003ff40c12SJohn Marino
10013ff40c12SJohn Marino /* Channel configuration will be updated once CSA completes and
10023ff40c12SJohn Marino * ch_switch_notify event is received */
10033ff40c12SJohn Marino
10043ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "DFS waiting channel switch event");
10053ff40c12SJohn Marino return 0;
10063ff40c12SJohn Marino }
10073ff40c12SJohn Marino
10083ff40c12SJohn Marino
hostapd_dfs_radar_detected(struct hostapd_iface * iface,int freq,int ht_enabled,int chan_offset,int chan_width,int cf1,int cf2)10093ff40c12SJohn Marino int hostapd_dfs_radar_detected(struct hostapd_iface *iface, int freq,
10103ff40c12SJohn Marino int ht_enabled, int chan_offset, int chan_width,
10113ff40c12SJohn Marino int cf1, int cf2)
10123ff40c12SJohn Marino {
10133ff40c12SJohn Marino int res;
10143ff40c12SJohn Marino
10153ff40c12SJohn Marino wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_RADAR_DETECTED
10163ff40c12SJohn Marino "freq=%d ht_enabled=%d chan_offset=%d chan_width=%d cf1=%d cf2=%d",
10173ff40c12SJohn Marino freq, ht_enabled, chan_offset, chan_width, cf1, cf2);
10183ff40c12SJohn Marino
1019*a1157835SDaniel Fojt /* Proceed only if DFS is not offloaded to the driver */
1020*a1157835SDaniel Fojt if (iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD)
1021*a1157835SDaniel Fojt return 0;
1022*a1157835SDaniel Fojt
1023*a1157835SDaniel Fojt if (!iface->conf->ieee80211h)
1024*a1157835SDaniel Fojt return 0;
1025*a1157835SDaniel Fojt
10263ff40c12SJohn Marino /* mark radar frequency as invalid */
1027*a1157835SDaniel Fojt set_dfs_state(iface, freq, ht_enabled, chan_offset, chan_width,
1028*a1157835SDaniel Fojt cf1, cf2, HOSTAPD_CHAN_DFS_UNAVAILABLE);
10293ff40c12SJohn Marino
10303ff40c12SJohn Marino /* Skip if reported radar event not overlapped our channels */
10313ff40c12SJohn Marino res = dfs_are_channels_overlapped(iface, freq, chan_width, cf1, cf2);
10323ff40c12SJohn Marino if (!res)
10333ff40c12SJohn Marino return 0;
10343ff40c12SJohn Marino
10353ff40c12SJohn Marino /* radar detected while operating, switch the channel. */
10363ff40c12SJohn Marino res = hostapd_dfs_start_channel_switch(iface);
10373ff40c12SJohn Marino
10383ff40c12SJohn Marino return res;
10393ff40c12SJohn Marino }
10403ff40c12SJohn Marino
10413ff40c12SJohn Marino
hostapd_dfs_nop_finished(struct hostapd_iface * iface,int freq,int ht_enabled,int chan_offset,int chan_width,int cf1,int cf2)10423ff40c12SJohn Marino int hostapd_dfs_nop_finished(struct hostapd_iface *iface, int freq,
10433ff40c12SJohn Marino int ht_enabled, int chan_offset, int chan_width,
10443ff40c12SJohn Marino int cf1, int cf2)
10453ff40c12SJohn Marino {
10463ff40c12SJohn Marino wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_NOP_FINISHED
10473ff40c12SJohn Marino "freq=%d ht_enabled=%d chan_offset=%d chan_width=%d cf1=%d cf2=%d",
10483ff40c12SJohn Marino freq, ht_enabled, chan_offset, chan_width, cf1, cf2);
1049*a1157835SDaniel Fojt
1050*a1157835SDaniel Fojt /* Proceed only if DFS is not offloaded to the driver */
1051*a1157835SDaniel Fojt if (iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD)
1052*a1157835SDaniel Fojt return 0;
1053*a1157835SDaniel Fojt
10543ff40c12SJohn Marino /* TODO add correct implementation here */
10553ff40c12SJohn Marino set_dfs_state(iface, freq, ht_enabled, chan_offset, chan_width,
10563ff40c12SJohn Marino cf1, cf2, HOSTAPD_CHAN_DFS_USABLE);
1057*a1157835SDaniel Fojt
1058*a1157835SDaniel Fojt /* Handle cases where all channels were initially unavailable */
1059*a1157835SDaniel Fojt if (iface->state == HAPD_IFACE_DFS && !iface->cac_started)
1060*a1157835SDaniel Fojt hostapd_handle_dfs(iface);
1061*a1157835SDaniel Fojt
10623ff40c12SJohn Marino return 0;
10633ff40c12SJohn Marino }
1064*a1157835SDaniel Fojt
1065*a1157835SDaniel Fojt
hostapd_is_dfs_required(struct hostapd_iface * iface)1066*a1157835SDaniel Fojt int hostapd_is_dfs_required(struct hostapd_iface *iface)
1067*a1157835SDaniel Fojt {
1068*a1157835SDaniel Fojt int n_chans, n_chans1, start_chan_idx, start_chan_idx1, res;
1069*a1157835SDaniel Fojt
1070*a1157835SDaniel Fojt if (!iface->conf->ieee80211h || !iface->current_mode ||
1071*a1157835SDaniel Fojt iface->current_mode->mode != HOSTAPD_MODE_IEEE80211A)
1072*a1157835SDaniel Fojt return 0;
1073*a1157835SDaniel Fojt
1074*a1157835SDaniel Fojt /* Get start (first) channel for current configuration */
1075*a1157835SDaniel Fojt start_chan_idx = dfs_get_start_chan_idx(iface, &start_chan_idx1);
1076*a1157835SDaniel Fojt if (start_chan_idx == -1)
1077*a1157835SDaniel Fojt return -1;
1078*a1157835SDaniel Fojt
1079*a1157835SDaniel Fojt /* Get number of used channels, depend on width */
1080*a1157835SDaniel Fojt n_chans = dfs_get_used_n_chans(iface, &n_chans1);
1081*a1157835SDaniel Fojt
1082*a1157835SDaniel Fojt /* Check if any of configured channels require DFS */
1083*a1157835SDaniel Fojt res = dfs_check_chans_radar(iface, start_chan_idx, n_chans);
1084*a1157835SDaniel Fojt if (res)
1085*a1157835SDaniel Fojt return res;
1086*a1157835SDaniel Fojt if (start_chan_idx1 >= 0 && n_chans1 > 0)
1087*a1157835SDaniel Fojt res = dfs_check_chans_radar(iface, start_chan_idx1, n_chans1);
1088*a1157835SDaniel Fojt return res;
1089*a1157835SDaniel Fojt }
1090*a1157835SDaniel Fojt
1091*a1157835SDaniel Fojt
hostapd_dfs_start_cac(struct hostapd_iface * iface,int freq,int ht_enabled,int chan_offset,int chan_width,int cf1,int cf2)1092*a1157835SDaniel Fojt int hostapd_dfs_start_cac(struct hostapd_iface *iface, int freq,
1093*a1157835SDaniel Fojt int ht_enabled, int chan_offset, int chan_width,
1094*a1157835SDaniel Fojt int cf1, int cf2)
1095*a1157835SDaniel Fojt {
1096*a1157835SDaniel Fojt wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_CAC_START
1097*a1157835SDaniel Fojt "freq=%d chan=%d chan_offset=%d width=%d seg0=%d "
1098*a1157835SDaniel Fojt "seg1=%d cac_time=%ds",
1099*a1157835SDaniel Fojt freq, (freq - 5000) / 5, chan_offset, chan_width, cf1, cf2, 60);
1100*a1157835SDaniel Fojt iface->cac_started = 1;
1101*a1157835SDaniel Fojt return 0;
1102*a1157835SDaniel Fojt }
1103*a1157835SDaniel Fojt
1104*a1157835SDaniel Fojt
1105*a1157835SDaniel Fojt /*
1106*a1157835SDaniel Fojt * Main DFS handler for offloaded case.
1107*a1157835SDaniel Fojt * 2 - continue channel/AP setup for non-DFS channel
1108*a1157835SDaniel Fojt * 1 - continue channel/AP setup for DFS channel
1109*a1157835SDaniel Fojt * 0 - channel/AP setup will be continued after CAC
1110*a1157835SDaniel Fojt * -1 - hit critical error
1111*a1157835SDaniel Fojt */
hostapd_handle_dfs_offload(struct hostapd_iface * iface)1112*a1157835SDaniel Fojt int hostapd_handle_dfs_offload(struct hostapd_iface *iface)
1113*a1157835SDaniel Fojt {
1114*a1157835SDaniel Fojt wpa_printf(MSG_DEBUG, "%s: iface->cac_started: %d",
1115*a1157835SDaniel Fojt __func__, iface->cac_started);
1116*a1157835SDaniel Fojt
1117*a1157835SDaniel Fojt /*
1118*a1157835SDaniel Fojt * If DFS has already been started, then we are being called from a
1119*a1157835SDaniel Fojt * callback to continue AP/channel setup. Reset the CAC start flag and
1120*a1157835SDaniel Fojt * return.
1121*a1157835SDaniel Fojt */
1122*a1157835SDaniel Fojt if (iface->cac_started) {
1123*a1157835SDaniel Fojt wpa_printf(MSG_DEBUG, "%s: iface->cac_started: %d",
1124*a1157835SDaniel Fojt __func__, iface->cac_started);
1125*a1157835SDaniel Fojt iface->cac_started = 0;
1126*a1157835SDaniel Fojt return 1;
1127*a1157835SDaniel Fojt }
1128*a1157835SDaniel Fojt
1129*a1157835SDaniel Fojt if (ieee80211_is_dfs(iface->freq, iface->hw_features,
1130*a1157835SDaniel Fojt iface->num_hw_features)) {
1131*a1157835SDaniel Fojt wpa_printf(MSG_DEBUG, "%s: freq %d MHz requires DFS",
1132*a1157835SDaniel Fojt __func__, iface->freq);
1133*a1157835SDaniel Fojt return 0;
1134*a1157835SDaniel Fojt }
1135*a1157835SDaniel Fojt
1136*a1157835SDaniel Fojt wpa_printf(MSG_DEBUG,
1137*a1157835SDaniel Fojt "%s: freq %d MHz does not require DFS. Continue channel/AP setup",
1138*a1157835SDaniel Fojt __func__, iface->freq);
1139*a1157835SDaniel Fojt return 2;
1140*a1157835SDaniel Fojt }
1141