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