blob: fc37d6cc3279459d94efc9c09331e9aae3ece72d [file] [log] [blame]
developer3f52c302024-04-08 14:36:46 +08001From 5d4d4397d0628a1aa83f83163e97ff34a0a5de43 Mon Sep 17 00:00:00 2001
developerd243af02023-12-21 14:49:33 +08002From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
3Date: Thu, 16 Nov 2023 13:18:48 +0800
4Subject: [PATCH] hostapd: mtk: add support for channel switching with csa sent
5 when background radar is enabled
6
7Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
8---
developer3f52c302024-04-08 14:36:46 +08009 hostapd/ctrl_iface.c | 84 +++++++++++++++++-----
developerd243af02023-12-21 14:49:33 +080010 src/ap/ctrl_iface_ap.c | 5 +-
11 src/ap/dfs.c | 156 +++++++++++++++++++++++++++++++++++------
12 src/ap/dfs.h | 9 ++-
developer3f52c302024-04-08 14:36:46 +080013 src/ap/hostapd.c | 8 ++-
14 5 files changed, 221 insertions(+), 41 deletions(-)
developerd243af02023-12-21 14:49:33 +080015
16diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
developer3f52c302024-04-08 14:36:46 +080017index 96b593a..445cb34 100644
developerd243af02023-12-21 14:49:33 +080018--- a/hostapd/ctrl_iface.c
19+++ b/hostapd/ctrl_iface.c
20@@ -2742,11 +2742,12 @@ static int hostapd_ctrl_iface_chan_switch(struct hostapd_iface *iface,
21 char *pos)
22 {
23 #ifdef NEED_AP_MLME
24- struct csa_settings settings;
25+ struct csa_settings settings, background_settings;
26 int ret;
27- int dfs_range = 0;
28+ int freq, state;
29 unsigned int i;
30- int bandwidth;
31+ int bandwidth, oper_chwidth;
32+ bool background_radar, bw_changed, cac_required = false;
33
34 ret = hostapd_parse_csa_settings(pos, &settings);
35 if (ret)
36@@ -2762,21 +2763,28 @@ static int hostapd_ctrl_iface_chan_switch(struct hostapd_iface *iface,
37 switch (settings.freq_params.bandwidth) {
38 case 40:
39 bandwidth = CHAN_WIDTH_40;
40+ oper_chwidth = CONF_OPER_CHWIDTH_USE_HT;
41 break;
42 case 80:
43- if (settings.freq_params.center_freq2)
44+ if (settings.freq_params.center_freq2) {
45 bandwidth = CHAN_WIDTH_80P80;
46- else
47+ oper_chwidth = CONF_OPER_CHWIDTH_80P80MHZ;
48+ } else {
49 bandwidth = CHAN_WIDTH_80;
50+ oper_chwidth = CONF_OPER_CHWIDTH_80MHZ;
51+ }
52 break;
53 case 160:
54 bandwidth = CHAN_WIDTH_160;
55+ oper_chwidth = CONF_OPER_CHWIDTH_160MHZ;
56 break;
57 case 320:
58 bandwidth = CHAN_WIDTH_320;
59+ oper_chwidth = CONF_OPER_CHWIDTH_320MHZ;
60 break;
61 default:
62 bandwidth = CHAN_WIDTH_20;
63+ oper_chwidth = CONF_OPER_CHWIDTH_USE_HT;
64 break;
65 }
66
67@@ -2797,19 +2805,29 @@ static int hostapd_ctrl_iface_chan_switch(struct hostapd_iface *iface,
68 }
69
70 if (settings.freq_params.center_freq1)
71- dfs_range += hostapd_is_dfs_overlap(
72- iface, bandwidth, settings.freq_params.center_freq1);
73+ freq = settings.freq_params.center_freq1;
74 else
75- dfs_range += hostapd_is_dfs_overlap(
76- iface, bandwidth, settings.freq_params.freq);
77-
78- if (settings.freq_params.center_freq2)
79- dfs_range += hostapd_is_dfs_overlap(
80- iface, bandwidth, settings.freq_params.center_freq2);
81-
82- if (dfs_range) {
83- settings.cs_count = 5;
84- settings.block_tx = 1;
85+ freq = settings.freq_params.freq;
86+
87+ bw_changed = oper_chwidth != hostapd_get_oper_chwidth(iface->conf);
88+ state = hostapd_dfs_get_target_state(iface, bandwidth, freq,
89+ settings.freq_params.center_freq2);
90+ switch (state) {
91+ case HOSTAPD_CHAN_DFS_USABLE:
92+ cac_required = true;
93+ /* fallthrough */
94+ case HOSTAPD_CHAN_DFS_AVAILABLE:
95+ background_radar = hostapd_dfs_handle_csa(iface, &settings,
96+ &background_settings,
97+ cac_required,
98+ bw_changed);
99+ break;
100+ case HOSTAPD_CHAN_DFS_UNAVAILABLE:
101+ wpa_printf(MSG_INFO,
102+ "chanswitch: target channel is UNAVAILABLE, so stop switching");
103+ return -1;
104+ default:
105+ break;
106 }
107
108 for (i = 0; i < iface->num_bss; i++) {
developer3f52c302024-04-08 14:36:46 +0800109@@ -2825,6 +2843,38 @@ static int hostapd_ctrl_iface_chan_switch(struct hostapd_iface *iface,
110 return ret;
developerd243af02023-12-21 14:49:33 +0800111 }
112 }
developer3f52c302024-04-08 14:36:46 +0800113+ /* Clear the CAC flag once all BSSes are switched to the new channel */
114+ iface->cac_started = 0;
115+
developerd243af02023-12-21 14:49:33 +0800116+ if (background_radar) {
117+ u8 seg0, seg1;
118+
119+ ieee80211_freq_to_chan(background_settings.freq_params.center_freq1, &seg0);
120+ ieee80211_freq_to_chan(background_settings.freq_params.center_freq2, &seg1);
121+ ret = hostapd_start_dfs_cac(iface, iface->conf->hw_mode,
122+ background_settings.freq_params.freq,
123+ background_settings.freq_params.channel,
124+ background_settings.freq_params.ht_enabled,
125+ background_settings.freq_params.vht_enabled,
126+ background_settings.freq_params.he_enabled,
127+ background_settings.freq_params.eht_enabled,
128+ background_settings.freq_params.sec_channel_offset,
129+ oper_chwidth, seg0, seg1, true);
130+ if (ret) {
131+ wpa_printf(MSG_ERROR, "Background radar start dfs cac failed, %d",
132+ ret);
133+ iface->radar_background.channel = -1;
134+ return -1;
135+ }
136+
137+ /* Cache background radar parameters. */
138+ iface->radar_background.channel = background_settings.freq_params.channel;
139+ iface->radar_background.secondary_channel =
140+ background_settings.freq_params.sec_channel_offset;
141+ iface->radar_background.freq = background_settings.freq_params.freq;
142+ iface->radar_background.centr_freq_seg0_idx = seg0;
143+ iface->radar_background.centr_freq_seg1_idx = seg1;
144+ }
developer3f52c302024-04-08 14:36:46 +0800145
developerd243af02023-12-21 14:49:33 +0800146 return 0;
147 #else /* NEED_AP_MLME */
developerd243af02023-12-21 14:49:33 +0800148diff --git a/src/ap/ctrl_iface_ap.c b/src/ap/ctrl_iface_ap.c
149index 86e8729..005c9fa 100644
150--- a/src/ap/ctrl_iface_ap.c
151+++ b/src/ap/ctrl_iface_ap.c
152@@ -907,6 +907,7 @@ int hostapd_parse_csa_settings(const char *pos,
153 struct csa_settings *settings)
154 {
155 char *end;
156+ int ret;
157
158 os_memset(settings, 0, sizeof(*settings));
159 settings->cs_count = strtol(pos, &end, 10);
160@@ -916,7 +917,9 @@ int hostapd_parse_csa_settings(const char *pos,
161 }
162
163 settings->freq_params.freq = atoi(end);
164- if (settings->freq_params.freq == 0) {
165+ ret = ieee80211_freq_to_chan(settings->freq_params.freq,
166+ &settings->freq_params.channel);
167+ if (ret == NUM_HOSTAPD_MODES) {
168 wpa_printf(MSG_ERROR, "chanswitch: invalid freq provided");
169 return -1;
170 }
171diff --git a/src/ap/dfs.c b/src/ap/dfs.c
developer3f52c302024-04-08 14:36:46 +0800172index d490032..26ce229 100644
developerd243af02023-12-21 14:49:33 +0800173--- a/src/ap/dfs.c
174+++ b/src/ap/dfs.c
175@@ -248,14 +248,15 @@ static int is_in_chanlist(struct hostapd_iface *iface,
176 */
177 static int dfs_find_channel(struct hostapd_iface *iface,
178 struct hostapd_channel_data **ret_chan,
179- int idx, enum dfs_channel_type type)
180+ int n_chans, int idx, enum dfs_channel_type type)
181 {
182 struct hostapd_hw_modes *mode;
183 struct hostapd_channel_data *chan;
184- int i, channel_idx = 0, n_chans, n_chans1;
185+ int i, channel_idx = 0, n_chans1;
186
187 mode = iface->current_mode;
188- n_chans = dfs_get_used_n_chans(iface, &n_chans1);
189+ if (!n_chans)
190+ n_chans = dfs_get_used_n_chans(iface, &n_chans1);
191
192 wpa_printf(MSG_DEBUG, "DFS new chan checking %d channels", n_chans);
193 for (i = 0; i < mode->num_channels; i++) {
194@@ -548,7 +549,7 @@ dfs_get_valid_channel(struct hostapd_iface *iface,
195 return NULL;
196
197 /* Get the count first */
198- num_available_chandefs = dfs_find_channel(iface, NULL, 0, type);
199+ num_available_chandefs = dfs_find_channel(iface, NULL, 0, 0, type);
200 wpa_printf(MSG_DEBUG, "DFS: num_available_chandefs=%d",
201 num_available_chandefs);
202 if (num_available_chandefs == 0)
203@@ -569,7 +570,7 @@ dfs_get_valid_channel(struct hostapd_iface *iface,
204 return NULL;
205
206 chan_idx = _rand % num_available_chandefs;
207- dfs_find_channel(iface, &chan, chan_idx, type);
208+ dfs_find_channel(iface, &chan, 0, chan_idx, type);
209 if (!chan) {
210 wpa_printf(MSG_DEBUG, "DFS: no random channel found");
211 return NULL;
212@@ -599,7 +600,7 @@ dfs_get_valid_channel(struct hostapd_iface *iface,
213 for (i = 0; i < num_available_chandefs - 1; i++) {
214 /* start from chan_idx + 1, end when chan_idx - 1 */
215 chan_idx2 = (chan_idx + 1 + i) % num_available_chandefs;
216- dfs_find_channel(iface, &chan2, chan_idx2, type);
217+ dfs_find_channel(iface, &chan2, 0, chan_idx2, type);
218 if (chan2 && abs(chan2->chan - chan->chan) > 12) {
219 /* two channels are not adjacent */
220 sec_chan_idx_80p80 = chan2->chan;
developer3f52c302024-04-08 14:36:46 +0800221@@ -1304,6 +1305,9 @@ int hostapd_dfs_pre_cac_expired(struct hostapd_iface *iface, int freq,
developerd243af02023-12-21 14:49:33 +0800222 set_dfs_state(iface, freq, ht_enabled, chan_offset, chan_width,
223 cf1, cf2, HOSTAPD_CHAN_DFS_USABLE);
224
225+ if (dfs_use_radar_background(iface) && iface->radar_background.channel == -1)
226+ hostpad_dfs_update_background_chain(iface);
227+
228 return 0;
229 }
230
developer3f52c302024-04-08 14:36:46 +0800231@@ -1717,14 +1721,15 @@ int hostapd_handle_dfs_offload(struct hostapd_iface *iface)
developerd243af02023-12-21 14:49:33 +0800232 }
233
234
235-int hostapd_is_dfs_overlap(struct hostapd_iface *iface, enum chan_width width,
236- int center_freq)
237+int hostapd_dfs_get_target_state(struct hostapd_iface *iface, enum chan_width width,
238+ int center_freq, int center_freq2)
239 {
240 struct hostapd_channel_data *chan;
241 struct hostapd_hw_modes *mode = iface->current_mode;
242- int half_width;
243- int res = 0;
244+ int half_width, chan_state, state = 0;
245+ int upper, lower;
246 int i;
247+ bool in_range;
248
249 if (!iface->conf->ieee80211h || !mode ||
250 mode->mode != HOSTAPD_MODE_IEEE80211A)
developer3f52c302024-04-08 14:36:46 +0800251@@ -1757,18 +1762,129 @@ int hostapd_is_dfs_overlap(struct hostapd_iface *iface, enum chan_width width,
developerd243af02023-12-21 14:49:33 +0800252 if (!(chan->flag & HOSTAPD_CHAN_RADAR))
253 continue;
254
255- if ((chan->flag & HOSTAPD_CHAN_DFS_MASK) ==
256- HOSTAPD_CHAN_DFS_AVAILABLE)
257- continue;
258+ upper = chan->freq + half_width;
259+ lower = chan->freq - half_width;
260+ in_range = (lower < center_freq && center_freq < upper) ||
261+ (center_freq2 && (lower < center_freq2 && center_freq2 < upper));
262+ if (in_range) {
263+ chan_state = chan->flag & HOSTAPD_CHAN_DFS_MASK;
264+ switch (chan_state) {
265+ case HOSTAPD_CHAN_DFS_USABLE:
266+ state = HOSTAPD_CHAN_DFS_USABLE;
267+ break;
268+ case HOSTAPD_CHAN_DFS_AVAILABLE:
269+ if (state != HOSTAPD_CHAN_DFS_USABLE)
270+ state = HOSTAPD_CHAN_DFS_AVAILABLE;
271+ break;
272+ case HOSTAPD_CHAN_DFS_UNKNOWN:
273+ wpa_printf(MSG_WARNING, "chan %d DFS state: UNKNOWN",
274+ chan->freq);
275+ /* fallthrough */
276+ case HOSTAPD_CHAN_DFS_UNAVAILABLE:
277+ default:
278+ return HOSTAPD_CHAN_DFS_UNAVAILABLE;
279+ }
280+ }
281+ }
282
283- if (center_freq - chan->freq < half_width &&
284- chan->freq - center_freq < half_width)
285- res++;
286+ wpa_printf(MSG_DEBUG, "freq range (%d, %d) has DFS state %d",
287+ center_freq - half_width, center_freq + half_width, state);
288+
289+ return state;
290+}
291+
292+
293+static struct hostapd_channel_data *
294+dfs_get_csa_channel(struct hostapd_iface *iface,
295+ int n_chans, int cur_center,
296+ enum dfs_channel_type type)
297+{
298+ struct hostapd_channel_data *chan;
299+ int avail_chan_num;
300+ u32 _rand, idx;
301+
302+ if (os_get_random((u8 *)&_rand, sizeof(_rand)) < 0)
303+ return NULL;
304+
305+ avail_chan_num = dfs_find_channel(iface, NULL, n_chans, 0, type);
306+ if (!avail_chan_num)
307+ return NULL;
308+
309+ idx = _rand % avail_chan_num;
310+ dfs_find_channel(iface, &chan, n_chans, idx, type);
311+ if (cur_center == chan->freq + (n_chans - 1) * 10) {
312+ if (avail_chan_num == 1)
313+ return NULL;
314+
315+ /* Get the next channel if the found channel is same as current channel */
316+ idx = (idx + 1) % avail_chan_num;
317+ dfs_find_channel(iface, &chan, n_chans, idx, type);
318 }
319
320- wpa_printf(MSG_DEBUG, "DFS CAC required: (%d, %d): in range: %s",
321- center_freq - half_width, center_freq + half_width,
322- res ? "yes" : "no");
323+ return chan;
324+}
325
326- return res;
327+
328+/*
329+ * DFS handler for CSA
330+ * 1 - update background radar with the filled setting
331+ * 0 - background radar is not enabled / background radar remain at the same channel /
332+ * disable background radar
333+ */
334+int hostapd_dfs_handle_csa(struct hostapd_iface *iface,
335+ struct csa_settings *settings,
336+ struct csa_settings *background_settings,
337+ bool cac_required, bool bw_changed)
338+{
339+ struct hostapd_channel_data *chan;
340+ struct hostapd_freq_params *freq_params = &settings->freq_params;
341+ int center = settings->freq_params.center_freq1;
342+ int background_center = 5000 + iface->radar_background.centr_freq_seg0_idx * 5;
343+ int n_chans = settings->freq_params.bandwidth / 20;
344+ bool update_background = false;
345+
346+ if (!dfs_use_radar_background(iface)) {
347+ settings->cs_count = 5;
348+ settings->block_tx = cac_required;
349+ return 0;
350+ }
351+
352+ if (!cac_required) {
353+ if (!bw_changed && center != background_center)
354+ return 0;
355+ /* Update background radar due to bw change or channel overlapping */
356+ update_background = true;
357+ } else {
358+ /*
359+ * Get available channel for main channel.
360+ * If no available channel for main channel, then perform
361+ * the CAC of target channel on the main channel, and select
362+ * an usable channel for background radar.
363+ */
364+ iface->radar_background.temp_ch = 1;
365+ chan = dfs_get_csa_channel(iface, n_chans, 0, DFS_AVAILABLE);
366+ if (!chan)
367+ update_background = true;
368+ }
369+
370+ if (update_background) {
371+ chan = dfs_get_csa_channel(iface, n_chans, center, DFS_NO_CAC_YET);
372+ if (!chan)
373+ goto bkg_disable;
374+ freq_params = &background_settings->freq_params;
375+ iface->radar_background.temp_ch = 0;
376+ }
377+
378+ memcpy(background_settings, settings, sizeof(*settings));
379+ freq_params->freq = chan->freq;
380+ freq_params->channel = chan->chan;
381+ freq_params->sec_channel_offset = 1;
382+ freq_params->center_freq1 = chan->freq + (n_chans - 1) * 10;
383+ freq_params->center_freq2 = 0;
384+
385+ return 1;
386+
387+bkg_disable:
388+ iface->radar_background.channel = -1;
389+ return 0;
390 }
391diff --git a/src/ap/dfs.h b/src/ap/dfs.h
392index 1a0791f..0a7c25d 100644
393--- a/src/ap/dfs.h
394+++ b/src/ap/dfs.h
395@@ -33,7 +33,12 @@ int hostapd_dfs_start_cac(struct hostapd_iface *iface, int freq,
396 int ht_enabled, int chan_offset, int chan_width,
397 int cf1, int cf2);
398 int hostapd_handle_dfs_offload(struct hostapd_iface *iface);
399-int hostapd_is_dfs_overlap(struct hostapd_iface *iface, enum chan_width width,
400- int center_freq);
401+int hostapd_dfs_get_target_state(struct hostapd_iface *iface, enum chan_width width,
402+ int center_freq, int center_freq2);
403+int hostapd_dfs_handle_csa(struct hostapd_iface *iface,
404+ struct csa_settings *settings,
405+ struct csa_settings *background_settings,
406+ bool cac_required, bool bw_changed);
407+
408
409 #endif /* DFS_H */
developer3f52c302024-04-08 14:36:46 +0800410diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
411index 250c168..d983902 100644
412--- a/src/ap/hostapd.c
413+++ b/src/ap/hostapd.c
414@@ -3708,7 +3708,13 @@ static int hostapd_fill_csa_settings(struct hostapd_data *hapd,
415 u8 chan, bandwidth;
416
417 os_memset(&old_freq, 0, sizeof(old_freq));
418- if (!iface || !iface->freq || hapd->csa_in_progress)
419+ if (!iface || !iface->freq)
420+ return -1;
421+
422+ /* Allow another channel switch if the previous
423+ * channel switch is waiting for post-CSA radar detection
424+ */
425+ if (hapd->csa_in_progress && !iface->cac_started)
426 return -1;
427
428 switch (settings->freq_params.bandwidth) {
developerd243af02023-12-21 14:49:33 +0800429--
4302.18.0
431