blob: cb11aeea757b47e34b19ab742dabd4edf73f2f85 [file] [log] [blame]
developere2cc0fa2022-03-29 17:31:03 +08001diff --git a/hostapd/config_file.c b/hostapd/config_file.c
2index 1e1b685..8f6281a 100644
3--- a/hostapd/config_file.c
4+++ b/hostapd/config_file.c
5@@ -2476,6 +2476,8 @@ static int hostapd_config_fill(struct hostapd_config *conf,
6 conf->ieee80211d = atoi(pos);
7 } else if (os_strcmp(buf, "ieee80211h") == 0) {
8 conf->ieee80211h = atoi(pos);
9+ } else if (os_strcmp(buf, "radar_offchan") == 0) {
10+ conf->radar_offchan = atoi(pos);
11 } else if (os_strcmp(buf, "ieee8021x") == 0) {
12 bss->ieee802_1x = atoi(pos);
13 } else if (os_strcmp(buf, "eapol_version") == 0) {
14diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf
15index a89ce9b..0c951a9 100644
16--- a/hostapd/hostapd.conf
17+++ b/hostapd/hostapd.conf
18@@ -143,6 +143,13 @@ ssid=test
19 # ieee80211d=1 and local_pwr_constraint configured.
20 #spectrum_mgmt_required=1
21
22+# Enable radar/CAC detection through a dedicated offchannel chain available on
23+# some hw. The chain can't be used to transmits or receives frames.
24+# This feature allows to avoid CAC downtime switching on a different channel
25+# during CAC detection on the selected radar channel.
26+# (default: 0 = disabled, 1 = enabled)
27+#radar_offchan=0
28+
29 # Operation mode (a = IEEE 802.11a (5 GHz), b = IEEE 802.11b (2.4 GHz),
30 # g = IEEE 802.11g (2.4 GHz), ad = IEEE 802.11ad (60 GHz); a/g options are used
31 # with IEEE 802.11n (HT), too, to specify band). For IEEE 802.11ac (VHT), this
32diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
33index 28b7efe..ffc3c2c 100644
34--- a/src/ap/ap_config.h
35+++ b/src/ap/ap_config.h
36@@ -993,6 +993,7 @@ struct hostapd_config {
37 int ieee80211d;
38
39 int ieee80211h; /* DFS */
40+ int radar_offchan;
41
42 /*
43 * Local power constraint is an octet encoded as an unsigned integer in
44diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
45index bc49079..c97ee39 100644
46--- a/src/ap/ap_drv_ops.c
47+++ b/src/ap/ap_drv_ops.c
48@@ -810,7 +810,8 @@ int hostapd_start_dfs_cac(struct hostapd_iface *iface,
49 int channel, int ht_enabled, int vht_enabled,
50 int he_enabled,
51 int sec_channel_offset, int oper_chwidth,
52- int center_segment0, int center_segment1)
53+ int center_segment0, int center_segment1,
54+ int radar_offchan)
55 {
56 struct hostapd_data *hapd = iface->bss[0];
57 struct hostapd_freq_params data;
58@@ -836,10 +837,14 @@ int hostapd_start_dfs_cac(struct hostapd_iface *iface,
59 wpa_printf(MSG_ERROR, "Can't set freq params");
60 return -1;
61 }
62+ data.radar_offchan = radar_offchan;
63
64 res = hapd->driver->start_dfs_cac(hapd->drv_priv, &data);
65 if (!res) {
66- iface->cac_started = 1;
67+ if (radar_offchan)
68+ iface->radar_offchan.cac_started = 1;
69+ else
70+ iface->cac_started = 1;
71 os_get_reltime(&iface->dfs_cac_start);
72 }
73
74diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
75index 61c8f64..92842a1 100644
76--- a/src/ap/ap_drv_ops.h
77+++ b/src/ap/ap_drv_ops.h
78@@ -130,7 +130,8 @@ int hostapd_start_dfs_cac(struct hostapd_iface *iface,
79 int channel, int ht_enabled, int vht_enabled,
80 int he_enabled,
81 int sec_channel_offset, int oper_chwidth,
82- int center_segment0, int center_segment1);
83+ int center_segment0, int center_segment1,
84+ int radar_offchan);
85 int hostapd_drv_do_acs(struct hostapd_data *hapd);
86 int hostapd_drv_update_dh_ie(struct hostapd_data *hapd, const u8 *peer,
87 u16 reason_code, const u8 *ie, size_t ielen);
88diff --git a/src/ap/dfs.c b/src/ap/dfs.c
89index eccda1a..3b1276f 100644
90--- a/src/ap/dfs.c
91+++ b/src/ap/dfs.c
92@@ -51,16 +51,31 @@ static int dfs_get_used_n_chans(struct hostapd_iface *iface, int *seg1)
93 return n_chans;
94 }
95
96-
97+/*
98+ * flags:
99+ * - 0: any channel
100+ * - 1: non-radar channel or radar available one
101+ * - 2: radar-only channel not yet available
102+ */
103 static int dfs_channel_available(struct hostapd_channel_data *chan,
104- int skip_radar)
105+ int flags)
106 {
107+ if (flags == 2) {
108+ /* Select only radar channel where CAC has not been
109+ * performed yet
110+ */
111+ if ((chan->flag & HOSTAPD_CHAN_RADAR) &&
112+ (chan->flag & HOSTAPD_CHAN_DFS_MASK) ==
113+ HOSTAPD_CHAN_DFS_USABLE)
114+ return 1;
115+ return 0;
116+ }
117 /*
118 * When radar detection happens, CSA is performed. However, there's no
119 * time for CAC, so radar channels must be skipped when finding a new
120 * channel for CSA, unless they are available for immediate use.
121 */
122- if (skip_radar && (chan->flag & HOSTAPD_CHAN_RADAR) &&
123+ if (flags && (chan->flag & HOSTAPD_CHAN_RADAR) &&
124 ((chan->flag & HOSTAPD_CHAN_DFS_MASK) !=
125 HOSTAPD_CHAN_DFS_AVAILABLE))
126 return 0;
127@@ -136,10 +151,15 @@ dfs_get_chan_data(struct hostapd_hw_modes *mode, int freq, int first_chan_idx)
128 return NULL;
129 }
130
131-
132+/*
133+ * flags:
134+ * - 0: any channel
135+ * - 1: non-radar channel or radar available one
136+ * - 2: radar-only channel not yet available
137+ */
138 static int dfs_chan_range_available(struct hostapd_hw_modes *mode,
139 int first_chan_idx, int num_chans,
140- int skip_radar)
141+ int flags)
142 {
143 struct hostapd_channel_data *first_chan, *chan;
144 int i;
145@@ -178,7 +198,7 @@ static int dfs_chan_range_available(struct hostapd_hw_modes *mode,
146 return 0;
147 }
148
149- if (!dfs_channel_available(chan, skip_radar)) {
150+ if (!dfs_channel_available(chan, flags)) {
151 wpa_printf(MSG_DEBUG, "DFS: channel not available %d",
152 first_chan->freq + i * 20);
153 return 0;
154@@ -205,10 +225,15 @@ static int is_in_chanlist(struct hostapd_iface *iface,
155 * - hapd->secondary_channel
156 * - hapd->vht/he_oper_centr_freq_seg0_idx
157 * - hapd->vht/he_oper_centr_freq_seg1_idx
158+ *
159+ * flags:
160+ * - 0: any channel
161+ * - 1: non-radar channel or radar available one
162+ * - 2: radar-only channel not yet available
163 */
164 static int dfs_find_channel(struct hostapd_iface *iface,
165 struct hostapd_channel_data **ret_chan,
166- int idx, int skip_radar)
167+ int idx, int flags)
168 {
169 struct hostapd_hw_modes *mode;
170 struct hostapd_channel_data *chan;
171@@ -233,7 +258,7 @@ static int dfs_find_channel(struct hostapd_iface *iface,
172 }
173
174 /* Skip incompatible chandefs */
175- if (!dfs_chan_range_available(mode, i, n_chans, skip_radar)) {
176+ if (!dfs_chan_range_available(mode, i, n_chans, flags)) {
177 wpa_printf(MSG_DEBUG,
178 "DFS: range not available for %d (%d)",
179 chan->freq, chan->chan);
180@@ -467,13 +492,18 @@ static int dfs_check_chans_unavailable(struct hostapd_iface *iface,
181 return res;
182 }
183
184-
185+/*
186+ * flags:
187+ * - 0: any channel
188+ * - 1: non-radar channel or radar available one
189+ * - 2: radar-only channel not yet available
190+ */
191 static struct hostapd_channel_data *
192 dfs_get_valid_channel(struct hostapd_iface *iface,
193 int *secondary_channel,
194 u8 *oper_centr_freq_seg0_idx,
195 u8 *oper_centr_freq_seg1_idx,
196- int skip_radar)
197+ int flags)
198 {
199 struct hostapd_hw_modes *mode;
200 struct hostapd_channel_data *chan = NULL;
201@@ -502,7 +532,7 @@ dfs_get_valid_channel(struct hostapd_iface *iface,
202 return NULL;
203
204 /* Get the count first */
205- num_available_chandefs = dfs_find_channel(iface, NULL, 0, skip_radar);
206+ num_available_chandefs = dfs_find_channel(iface, NULL, 0, flags);
207 wpa_printf(MSG_DEBUG, "DFS: num_available_chandefs=%d",
208 num_available_chandefs);
209 if (num_available_chandefs == 0)
210@@ -523,7 +553,7 @@ dfs_get_valid_channel(struct hostapd_iface *iface,
211 return NULL;
212
213 chan_idx = _rand % num_available_chandefs;
214- dfs_find_channel(iface, &chan, chan_idx, skip_radar);
215+ dfs_find_channel(iface, &chan, chan_idx, flags);
216 if (!chan) {
217 wpa_printf(MSG_DEBUG, "DFS: no random channel found");
218 return NULL;
219@@ -552,7 +582,7 @@ dfs_get_valid_channel(struct hostapd_iface *iface,
220 for (i = 0; i < num_available_chandefs - 1; i++) {
221 /* start from chan_idx + 1, end when chan_idx - 1 */
222 chan_idx2 = (chan_idx + 1 + i) % num_available_chandefs;
223- dfs_find_channel(iface, &chan2, chan_idx2, skip_radar);
224+ dfs_find_channel(iface, &chan2, chan_idx2, flags);
225 if (chan2 && abs(chan2->chan - chan->chan) > 12) {
226 /* two channels are not adjacent */
227 sec_chan_idx_80p80 = chan2->chan;
228@@ -582,6 +612,27 @@ dfs_get_valid_channel(struct hostapd_iface *iface,
229 return chan;
230 }
231
232+static int dfs_set_valid_channel(struct hostapd_iface *iface, int skip_radar)
233+{
234+ struct hostapd_channel_data *channel;
235+ u8 cf1 = 0, cf2 = 0;
236+ int sec = 0;
237+
238+ channel = dfs_get_valid_channel(iface, &sec, &cf1, &cf2,
239+ skip_radar);
240+ if (!channel) {
241+ wpa_printf(MSG_ERROR, "could not get valid channel");
242+ return -1;
243+ }
244+
245+ iface->freq = channel->freq;
246+ iface->conf->channel = channel->chan;
247+ iface->conf->secondary_channel = sec;
248+ hostapd_set_oper_centr_freq_seg0_idx(iface->conf, cf1);
249+ hostapd_set_oper_centr_freq_seg1_idx(iface->conf, cf2);
250+
251+ return 0;
252+}
253
254 static int set_dfs_state_freq(struct hostapd_iface *iface, int freq, u32 state)
255 {
256@@ -761,6 +812,11 @@ static unsigned int dfs_get_cac_time(struct hostapd_iface *iface,
257 return cac_time_ms;
258 }
259
260+static int hostapd_is_radar_offchan_enabled(struct hostapd_iface *iface)
261+{
262+ return (iface->drv_flags2 & WPA_DRIVER_RADAR_OFFCHAN) &&
263+ iface->conf->radar_offchan;
264+}
265
266 /*
267 * Main DFS handler
268@@ -770,9 +826,8 @@ static unsigned int dfs_get_cac_time(struct hostapd_iface *iface,
269 */
270 int hostapd_handle_dfs(struct hostapd_iface *iface)
271 {
272- struct hostapd_channel_data *channel;
273 int res, n_chans, n_chans1, start_chan_idx, start_chan_idx1;
274- int skip_radar = 0;
275+ int skip_radar = 0, radar_offchan;
276
277 if (is_6ghz_freq(iface->freq))
278 return 1;
279@@ -825,28 +880,18 @@ int hostapd_handle_dfs(struct hostapd_iface *iface)
280 wpa_printf(MSG_DEBUG, "DFS %d chans unavailable - choose other channel: %s",
281 res, res ? "yes": "no");
282 if (res) {
283- int sec = 0;
284- u8 cf1 = 0, cf2 = 0;
285-
286- channel = dfs_get_valid_channel(iface, &sec, &cf1, &cf2,
287- skip_radar);
288- if (!channel) {
289- wpa_printf(MSG_ERROR, "could not get valid channel");
290+ if (dfs_set_valid_channel(iface, skip_radar) < 0) {
291 hostapd_set_state(iface, HAPD_IFACE_DFS);
292 return 0;
293 }
294-
295- iface->freq = channel->freq;
296- iface->conf->channel = channel->chan;
297- iface->conf->secondary_channel = sec;
298- hostapd_set_oper_centr_freq_seg0_idx(iface->conf, cf1);
299- hostapd_set_oper_centr_freq_seg1_idx(iface->conf, cf2);
300 }
301 } while (res);
302
303 /* Finally start CAC */
304 hostapd_set_state(iface, HAPD_IFACE_DFS);
305- wpa_printf(MSG_DEBUG, "DFS start CAC on %d MHz", iface->freq);
306+ radar_offchan = hostapd_is_radar_offchan_enabled(iface);
307+ wpa_printf(MSG_DEBUG, "DFS start CAC on %d MHz offchan %d",
308+ iface->freq, radar_offchan);
309 wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_CAC_START
310 "freq=%d chan=%d sec_chan=%d, width=%d, seg0=%d, seg1=%d, cac_time=%ds",
311 iface->freq,
312@@ -863,13 +908,37 @@ int hostapd_handle_dfs(struct hostapd_iface *iface)
313 iface->conf->secondary_channel,
314 hostapd_get_oper_chwidth(iface->conf),
315 hostapd_get_oper_centr_freq_seg0_idx(iface->conf),
316- hostapd_get_oper_centr_freq_seg1_idx(iface->conf));
317+ hostapd_get_oper_centr_freq_seg1_idx(iface->conf),
318+ radar_offchan);
319
320 if (res) {
321 wpa_printf(MSG_ERROR, "DFS start_dfs_cac() failed, %d", res);
322 return -1;
323 }
324
325+ if (radar_offchan) {
326+ /* Cache offchannel radar parameters */
327+ iface->radar_offchan.channel = iface->conf->channel;
328+ iface->radar_offchan.secondary_channel =
329+ iface->conf->secondary_channel;
330+ iface->radar_offchan.freq = iface->freq;
331+ iface->radar_offchan.centr_freq_seg0_idx =
332+ hostapd_get_oper_centr_freq_seg0_idx(iface->conf);
333+ iface->radar_offchan.centr_freq_seg1_idx =
334+ hostapd_get_oper_centr_freq_seg1_idx(iface->conf);
335+
336+ /*
337+ * Let's select a random channel for the moment
338+ * and perform CAC on dedicated radar chain
339+ */
340+ res = dfs_set_valid_channel(iface, 1);
341+ if (res < 0)
342+ return res;
343+
344+ iface->radar_offchan.temp_ch = 1;
345+ return 1;
346+ }
347+
348 return 0;
349 }
350
351@@ -890,6 +959,157 @@ int hostapd_is_dfs_chan_available(struct hostapd_iface *iface)
352 return dfs_check_chans_available(iface, start_chan_idx, n_chans);
353 }
354
355+static int hostapd_dfs_request_channel_switch(struct hostapd_iface *iface,
356+ int channel, int freq,
357+ int secondary_channel,
358+ u8 oper_centr_freq_seg0_idx,
359+ u8 oper_centr_freq_seg1_idx)
360+{
361+ struct hostapd_hw_modes *cmode = iface->current_mode;
362+ int ieee80211_mode = IEEE80211_MODE_AP, err, i;
363+ struct csa_settings csa_settings;
364+ u8 new_vht_oper_chwidth;
365+
366+ wpa_printf(MSG_DEBUG, "DFS will switch to a new channel %d", channel);
367+ wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_NEW_CHANNEL
368+ "freq=%d chan=%d sec_chan=%d", freq, channel,
369+ secondary_channel);
370+
371+ new_vht_oper_chwidth = hostapd_get_oper_chwidth(iface->conf);
372+ hostapd_set_oper_chwidth(iface->conf,
373+ hostapd_get_oper_chwidth(iface->conf));
374+
375+ /* Setup CSA request */
376+ os_memset(&csa_settings, 0, sizeof(csa_settings));
377+ csa_settings.cs_count = 5;
378+ csa_settings.block_tx = 1;
379+#ifdef CONFIG_MESH
380+ if (iface->mconf)
381+ ieee80211_mode = IEEE80211_MODE_MESH;
382+#endif /* CONFIG_MESH */
383+ err = hostapd_set_freq_params(&csa_settings.freq_params,
384+ iface->conf->hw_mode,
385+ freq, channel,
386+ iface->conf->enable_edmg,
387+ iface->conf->edmg_channel,
388+ iface->conf->ieee80211n,
389+ iface->conf->ieee80211ac,
390+ iface->conf->ieee80211ax,
391+ secondary_channel,
392+ new_vht_oper_chwidth,
393+ oper_centr_freq_seg0_idx,
394+ oper_centr_freq_seg1_idx,
395+ cmode->vht_capab,
396+ &cmode->he_capab[ieee80211_mode]);
397+
398+ if (err) {
399+ wpa_printf(MSG_ERROR, "DFS failed to calculate CSA freq params");
400+ hostapd_disable_iface(iface);
401+ return err;
402+ }
403+
404+ for (i = 0; i < iface->num_bss; i++) {
405+ err = hostapd_switch_channel(iface->bss[i], &csa_settings);
406+ if (err)
407+ break;
408+ }
409+
410+ if (err) {
411+ wpa_printf(MSG_WARNING, "DFS failed to schedule CSA (%d) - trying fallback",
412+ err);
413+ iface->freq = freq;
414+ iface->conf->channel = channel;
415+ iface->conf->secondary_channel = secondary_channel;
416+ hostapd_set_oper_chwidth(iface->conf, new_vht_oper_chwidth);
417+ hostapd_set_oper_centr_freq_seg0_idx(iface->conf,
418+ oper_centr_freq_seg0_idx);
419+ hostapd_set_oper_centr_freq_seg1_idx(iface->conf,
420+ oper_centr_freq_seg1_idx);
421+
422+ hostapd_disable_iface(iface);
423+ hostapd_enable_iface(iface);
424+
425+ return 0;
426+ }
427+
428+ /* Channel configuration will be updated once CSA completes and
429+ * ch_switch_notify event is received */
430+ wpa_printf(MSG_DEBUG, "DFS waiting channel switch event");
431+
432+ return 0;
433+}
434+
435+static struct hostapd_channel_data *
436+dfs_downgrade_bandwidth(struct hostapd_iface *iface, int *secondary_channel,
437+ u8 *oper_centr_freq_seg0_idx,
438+ u8 *oper_centr_freq_seg1_idx, int *skip_radar);
439+
440+static void
441+hostpad_dfs_update_offchannel_chain(struct hostapd_iface *iface)
442+{
443+ struct hostapd_channel_data *channel;
444+ int sec = 0, flags = 2;
445+ u8 cf1 = 0, cf2 = 0;
446+
447+ channel = dfs_get_valid_channel(iface, &sec, &cf1, &cf2, 2);
448+ if (!channel || channel->chan == iface->conf->channel)
449+ channel = dfs_downgrade_bandwidth(iface, &sec, &cf1, &cf2,
450+ &flags);
451+ if (!channel ||
452+ hostapd_start_dfs_cac(iface, iface->conf->hw_mode,
453+ channel->freq, channel->chan,
454+ iface->conf->ieee80211n,
455+ iface->conf->ieee80211ac,
456+ iface->conf->ieee80211ax,
457+ sec, hostapd_get_oper_chwidth(iface->conf),
458+ cf1, cf2, 1)) {
459+ /*
460+ * Toggle interface state to enter DFS state
461+ * until NOP is finished.
462+ */
463+ wpa_printf(MSG_ERROR, "DFS failed start CAC offchannel");
464+ return;
465+ }
466+
467+ wpa_printf(MSG_DEBUG, "%s: setting offchannel chain to chan %d (%d MHz)",
468+ __func__, channel->chan, channel->freq);
469+
470+ iface->radar_offchan.channel = channel->chan;
471+ iface->radar_offchan.freq = channel->freq;
472+ iface->radar_offchan.secondary_channel = sec;
473+ iface->radar_offchan.centr_freq_seg0_idx = cf1;
474+ iface->radar_offchan.centr_freq_seg1_idx = cf2;
475+}
476+
477+/* FIXME: check if all channel bandwith */
478+static int
479+hostapd_dfs_is_offchan_event(struct hostapd_iface *iface, int freq)
480+{
481+ if (iface->radar_offchan.freq != freq)
482+ return 0;
483+
484+ return 1;
485+}
486+
487+static int
488+hostapd_dfs_start_channel_switch_offchan(struct hostapd_iface *iface)
489+{
490+ iface->conf->channel = iface->radar_offchan.channel;
491+ iface->freq = iface->radar_offchan.freq;
492+ iface->conf->secondary_channel =
493+ iface->radar_offchan.secondary_channel;
494+ hostapd_set_oper_centr_freq_seg0_idx(iface->conf,
495+ iface->radar_offchan.centr_freq_seg0_idx);
496+ hostapd_set_oper_centr_freq_seg1_idx(iface->conf,
497+ iface->radar_offchan.centr_freq_seg1_idx);
498+
499+ hostpad_dfs_update_offchannel_chain(iface);
500+
501+ return hostapd_dfs_request_channel_switch(iface, iface->conf->channel,
502+ iface->freq, iface->conf->secondary_channel,
503+ hostapd_get_oper_centr_freq_seg0_idx(iface->conf),
504+ hostapd_get_oper_centr_freq_seg1_idx(iface->conf));
505+}
506
507 int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq,
508 int ht_enabled, int chan_offset, int chan_width,
509@@ -911,6 +1131,23 @@ int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq,
510 set_dfs_state(iface, freq, ht_enabled, chan_offset,
511 chan_width, cf1, cf2,
512 HOSTAPD_CHAN_DFS_AVAILABLE);
513+
514+ /*
515+ * radar event from offchannel chain for selected
516+ * channel. Perfrom CSA, move main chain to selected
517+ * channel and configure offchannel chain to a new DFS
518+ * channel
519+ */
520+ if (hostapd_is_radar_offchan_enabled(iface) &&
521+ hostapd_dfs_is_offchan_event(iface, freq)) {
522+ iface->radar_offchan.cac_started = 0;
523+ if (iface->radar_offchan.temp_ch) {
524+ iface->radar_offchan.temp_ch = 0;
525+ return hostapd_dfs_start_channel_switch_offchan(iface);
526+ }
527+ return 0;
528+ }
529+
530 /*
531 * Just mark the channel available when CAC completion
532 * event is received in enabled state. CAC result could
533@@ -927,6 +1164,10 @@ int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq,
534 iface->cac_started = 0;
535 }
536 }
537+ } else if (hostapd_is_radar_offchan_enabled(iface) &&
538+ hostapd_dfs_is_offchan_event(iface, freq)) {
539+ iface->radar_offchan.cac_started = 0;
540+ hostpad_dfs_update_offchannel_chain(iface);
541 }
542
543 return 0;
544@@ -1036,6 +1277,44 @@ static int hostapd_dfs_start_channel_switch_cac(struct hostapd_iface *iface)
545 return err;
546 }
547
548+static int
549+hostapd_dfs_offchan_start_channel_switch(struct hostapd_iface *iface, int freq)
550+{
551+ if (!hostapd_is_radar_offchan_enabled(iface))
552+ return -1; /* Offchannel chain not supported */
553+
554+ wpa_printf(MSG_DEBUG,
555+ "%s called (offchannel CAC active: %s, CSA active: %s)",
556+ __func__, iface->radar_offchan.cac_started ? "yes" : "no",
557+ hostapd_csa_in_progress(iface) ? "yes" : "no");
558+
559+ /* Check if CSA in progress */
560+ if (hostapd_csa_in_progress(iface))
561+ return 0;
562+
563+ /*
564+ * If offchannel radar detation is supported and radar channel
565+ * monitored by offchain is available switch to it without waiting
566+ * for the CAC otherwise let's keep a random channel.
567+ * If radar pattern is reported on offchannel chain, just switch to
568+ * monitor another radar channel.
569+ */
570+ if (hostapd_dfs_is_offchan_event(iface, freq)) {
571+ hostpad_dfs_update_offchannel_chain(iface);
572+ return 0;
573+ }
574+
575+ /* Offchannel not availanle yet. Perform CAC on main chain */
576+ if (iface->radar_offchan.cac_started) {
577+ /* We want to switch to monitored channel as soon as
578+ * CAC is completed.
579+ */
580+ iface->radar_offchan.temp_ch = 1;
581+ return -1;
582+ }
583+
584+ return hostapd_dfs_start_channel_switch_offchan(iface);
585+}
586
587 static int hostapd_dfs_start_channel_switch(struct hostapd_iface *iface)
588 {
589@@ -1043,13 +1322,7 @@ static int hostapd_dfs_start_channel_switch(struct hostapd_iface *iface)
590 int secondary_channel;
591 u8 oper_centr_freq_seg0_idx;
592 u8 oper_centr_freq_seg1_idx;
593- u8 new_vht_oper_chwidth;
594 int skip_radar = 1;
595- struct csa_settings csa_settings;
596- unsigned int i;
597- int err = 1;
598- struct hostapd_hw_modes *cmode = iface->current_mode;
599- u8 current_vht_oper_chwidth = hostapd_get_oper_chwidth(iface->conf);
600 int ieee80211_mode = IEEE80211_MODE_AP;
601
602 wpa_printf(MSG_DEBUG, "%s called (CAC active: %s, CSA active: %s)",
603@@ -1113,73 +1386,16 @@ static int hostapd_dfs_start_channel_switch(struct hostapd_iface *iface)
604 }
605 }
606
607- wpa_printf(MSG_DEBUG, "DFS will switch to a new channel %d",
608- channel->chan);
609- wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_NEW_CHANNEL
610- "freq=%d chan=%d sec_chan=%d", channel->freq,
611- channel->chan, secondary_channel);
612-
613- new_vht_oper_chwidth = hostapd_get_oper_chwidth(iface->conf);
614- hostapd_set_oper_chwidth(iface->conf, current_vht_oper_chwidth);
615-
616- /* Setup CSA request */
617- os_memset(&csa_settings, 0, sizeof(csa_settings));
618- csa_settings.cs_count = 5;
619- csa_settings.block_tx = 1;
620 #ifdef CONFIG_MESH
621 if (iface->mconf)
622 ieee80211_mode = IEEE80211_MODE_MESH;
623 #endif /* CONFIG_MESH */
624- err = hostapd_set_freq_params(&csa_settings.freq_params,
625- iface->conf->hw_mode,
626- channel->freq,
627- channel->chan,
628- iface->conf->enable_edmg,
629- iface->conf->edmg_channel,
630- iface->conf->ieee80211n,
631- iface->conf->ieee80211ac,
632- iface->conf->ieee80211ax,
633- secondary_channel,
634- new_vht_oper_chwidth,
635- oper_centr_freq_seg0_idx,
636- oper_centr_freq_seg1_idx,
637- cmode->vht_capab,
638- &cmode->he_capab[ieee80211_mode]);
639-
640- if (err) {
641- wpa_printf(MSG_ERROR, "DFS failed to calculate CSA freq params");
642- hostapd_disable_iface(iface);
643- return err;
644- }
645
646- for (i = 0; i < iface->num_bss; i++) {
647- err = hostapd_switch_channel(iface->bss[i], &csa_settings);
648- if (err)
649- break;
650- }
651-
652- if (err) {
653- wpa_printf(MSG_WARNING, "DFS failed to schedule CSA (%d) - trying fallback",
654- err);
655- iface->freq = channel->freq;
656- iface->conf->channel = channel->chan;
657- iface->conf->secondary_channel = secondary_channel;
658- hostapd_set_oper_chwidth(iface->conf, new_vht_oper_chwidth);
659- hostapd_set_oper_centr_freq_seg0_idx(iface->conf,
660- oper_centr_freq_seg0_idx);
661- hostapd_set_oper_centr_freq_seg1_idx(iface->conf,
662- oper_centr_freq_seg1_idx);
663-
664- hostapd_disable_iface(iface);
665- hostapd_enable_iface(iface);
666- return 0;
667- }
668-
669- /* Channel configuration will be updated once CSA completes and
670- * ch_switch_notify event is received */
671-
672- wpa_printf(MSG_DEBUG, "DFS waiting channel switch event");
673- return 0;
674+ return hostapd_dfs_request_channel_switch(iface, channel->chan,
675+ channel->freq,
676+ secondary_channel,
677+ oper_centr_freq_seg0_idx,
678+ oper_centr_freq_seg1_idx);
679 }
680
681
682@@ -1208,15 +1424,19 @@ int hostapd_dfs_radar_detected(struct hostapd_iface *iface, int freq,
683 if (!res)
684 return 0;
685
686- /* Skip if reported radar event not overlapped our channels */
687- res = dfs_are_channels_overlapped(iface, freq, chan_width, cf1, cf2);
688- if (!res)
689- return 0;
690+ if (!hostapd_dfs_is_offchan_event(iface, freq)) {
691+ /* Skip if reported radar event not overlapped our channels */
692+ res = dfs_are_channels_overlapped(iface, freq, chan_width,
693+ cf1, cf2);
694+ if (!res)
695+ return 0;
696+ }
697
698- /* radar detected while operating, switch the channel. */
699- res = hostapd_dfs_start_channel_switch(iface);
700+ if (hostapd_dfs_offchan_start_channel_switch(iface, freq))
701+ /* radar detected while operating, switch the channel. */
702+ return hostapd_dfs_start_channel_switch(iface);
703
704- return res;
705+ return 0;
706 }
707
708
709@@ -1284,7 +1504,11 @@ int hostapd_dfs_start_cac(struct hostapd_iface *iface, int freq,
710 "seg1=%d cac_time=%ds",
711 freq, (freq - 5000) / 5, chan_offset, chan_width, cf1, cf2,
712 iface->dfs_cac_ms / 1000);
713- iface->cac_started = 1;
714+
715+ if (hostapd_dfs_is_offchan_event(iface, freq))
716+ iface->radar_offchan.cac_started = 1;
717+ else
718+ iface->cac_started = 1;
719 os_get_reltime(&iface->dfs_cac_start);
720 return 0;
721 }
722diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
723index 27b985d..1c6c94e 100644
724--- a/src/ap/hostapd.h
725+++ b/src/ap/hostapd.h
726@@ -521,6 +521,21 @@ struct hostapd_iface {
727 int *basic_rates;
728 int freq;
729
730+ /* Offchanel chain configuration */
731+ struct {
732+ int channel;
733+ int secondary_channel;
734+ int freq;
735+ int centr_freq_seg0_idx;
736+ int centr_freq_seg1_idx;
737+ /* Main chain is on temporary channel during
738+ * CAC detection on radar offchain
739+ */
740+ unsigned int temp_ch:1;
741+ /* CAC started on radar offchain */
742+ unsigned int cac_started:1;
743+ } radar_offchan;
744+
745 u16 hw_flags;
746
747 /* Number of associated Non-ERP stations (i.e., stations using 802.11b
748diff --git a/src/drivers/driver.h b/src/drivers/driver.h
749index 6d9194f..7ed47c0 100644
750--- a/src/drivers/driver.h
751+++ b/src/drivers/driver.h
752@@ -777,6 +777,11 @@ struct hostapd_freq_params {
753 * for IEEE 802.11ay EDMG configuration.
754 */
755 struct ieee80211_edmg_config edmg;
756+
757+ /**
758+ * radar_offchan - Whether radar/CAC offchannel is requested
759+ */
760+ int radar_offchan;
761 };
762
763 /**
764@@ -2026,6 +2031,8 @@ struct wpa_driver_capa {
765 #define WPA_DRIVER_FLAGS2_OCV 0x0000000000000080ULL
766 /** Driver expects user space implementation of SME in AP mode */
767 #define WPA_DRIVER_FLAGS2_AP_SME 0x0000000000000100ULL
768+/** Driver supports offchannel radar/CAC detection */
769+#define WPA_DRIVER_RADAR_OFFCHAN 0x0000000000000200ULL
770 u64 flags2;
771
772 #define FULL_AP_CLIENT_STATE_SUPP(drv_flags) \
773diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
774index 4db8cce..62c3cd8 100644
775--- a/src/drivers/driver_nl80211.c
776+++ b/src/drivers/driver_nl80211.c
777@@ -4885,6 +4885,7 @@ static int nl80211_put_freq_params(struct nl_msg *msg,
778 wpa_printf(MSG_DEBUG, " * he_enabled=%d", freq->he_enabled);
779 wpa_printf(MSG_DEBUG, " * vht_enabled=%d", freq->vht_enabled);
780 wpa_printf(MSG_DEBUG, " * ht_enabled=%d", freq->ht_enabled);
781+ wpa_printf(MSG_DEBUG, " * radar_offchan=%d", freq->radar_offchan);
782
783 hw_mode = ieee80211_freq_to_chan(freq->freq, &channel);
784 is_24ghz = hw_mode == HOSTAPD_MODE_IEEE80211G ||
785@@ -4962,6 +4963,9 @@ static int nl80211_put_freq_params(struct nl_msg *msg,
786 NL80211_CHAN_NO_HT))
787 return -ENOBUFS;
788 }
789+ if (freq->radar_offchan)
790+ nla_put_flag(msg, NL80211_ATTR_RADAR_OFFCHAN);
791+
792 return 0;
793 }
794
795diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
796index cd596e3..e370ef3 100644
797--- a/src/drivers/driver_nl80211_capa.c
798+++ b/src/drivers/driver_nl80211_capa.c
799@@ -665,6 +665,10 @@ static void wiphy_info_ext_feature_flags(struct wiphy_info_data *info,
800 if (ext_feature_isset(ext_features, len,
801 NL80211_EXT_FEATURE_OPERATING_CHANNEL_VALIDATION))
802 capa->flags2 |= WPA_DRIVER_FLAGS2_OCV;
803+
804+ if (ext_feature_isset(ext_features, len,
805+ NL80211_EXT_FEATURE_RADAR_OFFCHAN))
806+ capa->flags2 |= WPA_DRIVER_RADAR_OFFCHAN;
807 }
808
809
810diff --git a/src/drivers/nl80211_copy.h b/src/drivers/nl80211_copy.h
811index f7be755..736b483 100644
812--- a/src/drivers/nl80211_copy.h
813+++ b/src/drivers/nl80211_copy.h
814@@ -2573,6 +2573,10 @@ enum nl80211_commands {
815 * @NL80211_ATTR_WIPHY_ANTENNA_GAIN: Configured antenna gain. Used to reduce
816 * transmit power to stay within regulatory limits. u32, dBi.
817 *
818+ * @NL80211_ATTR_RADAR_OFFCHAN: Configure dedicated chain available for radar
819+ * detection on some hw. The chain can't be used to transmits or receives
820+ * frames. The driver is supposed to implement CAC management in sw or fw.
821+ *
822 * @NUM_NL80211_ATTR: total number of nl80211_attrs available
823 * @NL80211_ATTR_MAX: highest attribute number currently defined
824 * @__NL80211_ATTR_AFTER_LAST: internal use
825@@ -3078,6 +3082,8 @@ enum nl80211_attrs {
826
827 NL80211_ATTR_WIPHY_ANTENNA_GAIN,
828
829+ NL80211_ATTR_RADAR_OFFCHAN,
830+
831 /* add attributes here, update the policy in nl80211.c */
832
833 __NL80211_ATTR_AFTER_LAST,
834@@ -5974,6 +5980,9 @@ enum nl80211_feature_flags {
835 * @NL80211_EXT_FEATURE_BSS_COLOR: The driver supports BSS color collision
836 * detection and change announcemnts.
837 *
838+ * @NL80211_EXT_FEATURE_RADAR_OFFCHAN: Device supports offchannel radar/CAC
839+ * detection.
840+ *
841 * @NUM_NL80211_EXT_FEATURES: number of extended features.
842 * @MAX_NL80211_EXT_FEATURES: highest extended feature index.
843 */
844@@ -6039,6 +6048,7 @@ enum nl80211_ext_feature_index {
845 NL80211_EXT_FEATURE_SECURE_RTT,
846 NL80211_EXT_FEATURE_PROT_RANGE_NEGO_AND_MEASURE,
847 NL80211_EXT_FEATURE_BSS_COLOR,
848+ NL80211_EXT_FEATURE_RADAR_OFFCHAN,
849
850 /* add new features before the definition below */
851 NUM_NL80211_EXT_FEATURES,