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