developer | c1498a3 | 2022-06-07 16:14:30 +0800 | [diff] [blame^] | 1 | From e1171aa4bd128d019fc5d53431472abddeb5cca0 Mon Sep 17 00:00:00 2001 |
| 2 | From: dzou <dzou@company.com> |
| 3 | Date: Sun, 5 Jun 2022 23:20:30 +0800 |
| 4 | Subject: [PATCH 905/911] DFS: Configure background radar/CAC detection |
| 5 | |
| 6 | Introduce the capability to perform radar/CAC detection on an offchannel |
| 7 | radar chain available on some hardware (e.g., mt7915). This feature |
| 8 | allows to avoid CAC downtime switching on a different channel during CAC |
| 9 | detection on the selected radar channel. |
| 10 | |
| 11 | Tested-by: Owen Peng <owen.peng@mediatek.com> |
| 12 | Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org> |
| 13 | --- |
| 14 | src/ap/ap_drv_ops.c | 9 ++- |
| 15 | src/ap/ap_drv_ops.h | 3 +- |
| 16 | src/ap/dfs.c | 181 ++++++++++++++++++++++++++++++++++++++++---- |
| 17 | src/ap/hostapd.h | 15 ++++ |
| 18 | 4 files changed, 189 insertions(+), 19 deletions(-) |
| 19 | |
| 20 | diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c |
| 21 | index 033ad097e..1fc538104 100644 |
| 22 | --- a/src/ap/ap_drv_ops.c |
| 23 | +++ b/src/ap/ap_drv_ops.c |
| 24 | @@ -810,7 +810,8 @@ int hostapd_start_dfs_cac(struct hostapd_iface *iface, |
| 25 | int channel, int ht_enabled, int vht_enabled, |
| 26 | int he_enabled, |
| 27 | int sec_channel_offset, int oper_chwidth, |
| 28 | - int center_segment0, int center_segment1) |
| 29 | + int center_segment0, int center_segment1, |
| 30 | + bool radar_background) |
| 31 | { |
| 32 | struct hostapd_data *hapd = iface->bss[0]; |
| 33 | struct hostapd_freq_params data; |
| 34 | @@ -836,10 +837,14 @@ int hostapd_start_dfs_cac(struct hostapd_iface *iface, |
| 35 | wpa_printf(MSG_ERROR, "Can't set freq params"); |
| 36 | return -1; |
| 37 | } |
| 38 | + data.radar_background = radar_background; |
| 39 | |
| 40 | res = hapd->driver->start_dfs_cac(hapd->drv_priv, &data); |
| 41 | if (!res) { |
| 42 | - iface->cac_started = 1; |
| 43 | + if (radar_background) |
| 44 | + iface->radar_background.cac_started = 1; |
| 45 | + else |
| 46 | + iface->cac_started = 1; |
| 47 | os_get_reltime(&iface->dfs_cac_start); |
| 48 | } |
| 49 | |
| 50 | diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h |
| 51 | index 61c8f64eb..b4d6395ae 100644 |
| 52 | --- a/src/ap/ap_drv_ops.h |
| 53 | +++ b/src/ap/ap_drv_ops.h |
| 54 | @@ -130,7 +130,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 | + bool radar_background); |
| 61 | int hostapd_drv_do_acs(struct hostapd_data *hapd); |
| 62 | int hostapd_drv_update_dh_ie(struct hostapd_data *hapd, const u8 *peer, |
| 63 | u16 reason_code, const u8 *ie, size_t ielen); |
| 64 | diff --git a/src/ap/dfs.c b/src/ap/dfs.c |
| 65 | index 05cb63ac4..5dc261589 100644 |
| 66 | --- a/src/ap/dfs.c |
| 67 | +++ b/src/ap/dfs.c |
| 68 | @@ -20,6 +20,25 @@ |
| 69 | #include "crypto/crypto.h" |
| 70 | |
| 71 | |
| 72 | +enum dfs_channel_type { |
| 73 | + DFS_ANY_CHANNEL, |
| 74 | + DFS_AVAILABLE, /* non-radar or radar-available */ |
| 75 | + DFS_NO_CAC_YET, /* radar-not-yet-available */ |
| 76 | +}; |
| 77 | + |
| 78 | +static struct hostapd_channel_data * |
| 79 | +dfs_downgrade_bandwidth(struct hostapd_iface *iface, int *secondary_channel, |
| 80 | + u8 *oper_centr_freq_seg0_idx, |
| 81 | + u8 *oper_centr_freq_seg1_idx, |
| 82 | + enum dfs_channel_type *channel_type); |
| 83 | + |
| 84 | + |
| 85 | +static bool dfs_use_radar_background(struct hostapd_iface *iface) |
| 86 | +{ |
| 87 | + return iface->drv_flags2 & WPA_DRIVER_RADAR_BACKGROUND; |
| 88 | +} |
| 89 | + |
| 90 | + |
| 91 | static int dfs_get_used_n_chans(struct hostapd_iface *iface, int *seg1) |
| 92 | { |
| 93 | int n_chans = 1; |
| 94 | @@ -52,12 +71,6 @@ static int dfs_get_used_n_chans(struct hostapd_iface *iface, int *seg1) |
| 95 | } |
| 96 | |
| 97 | |
| 98 | -enum dfs_channel_type { |
| 99 | - DFS_ANY_CHANNEL, |
| 100 | - DFS_AVAILABLE, /* non-radar or radar-available */ |
| 101 | - DFS_NO_CAC_YET, /* radar-not-yet-available */ |
| 102 | -}; |
| 103 | - |
| 104 | /* dfs_channel_available: select new channel according to type parameter */ |
| 105 | static int dfs_channel_available(struct hostapd_channel_data *chan, |
| 106 | enum dfs_channel_type type) |
| 107 | @@ -878,7 +891,8 @@ int hostapd_handle_dfs(struct hostapd_iface *iface) |
| 108 | |
| 109 | /* Finally start CAC */ |
| 110 | hostapd_set_state(iface, HAPD_IFACE_DFS); |
| 111 | - wpa_printf(MSG_DEBUG, "DFS start CAC on %d MHz", iface->freq); |
| 112 | + wpa_printf(MSG_DEBUG, "DFS start CAC on %d MHz%s", iface->freq, |
| 113 | + dfs_use_radar_background(iface) ? " (background)" : ""); |
| 114 | wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_CAC_START |
| 115 | "freq=%d chan=%d sec_chan=%d, width=%d, seg0=%d, seg1=%d, cac_time=%ds", |
| 116 | iface->freq, |
| 117 | @@ -895,13 +909,37 @@ int hostapd_handle_dfs(struct hostapd_iface *iface) |
| 118 | iface->conf->secondary_channel, |
| 119 | hostapd_get_oper_chwidth(iface->conf), |
| 120 | hostapd_get_oper_centr_freq_seg0_idx(iface->conf), |
| 121 | - hostapd_get_oper_centr_freq_seg1_idx(iface->conf)); |
| 122 | + hostapd_get_oper_centr_freq_seg1_idx(iface->conf), |
| 123 | + dfs_use_radar_background(iface)); |
| 124 | |
| 125 | if (res) { |
| 126 | wpa_printf(MSG_ERROR, "DFS start_dfs_cac() failed, %d", res); |
| 127 | return -1; |
| 128 | } |
| 129 | |
| 130 | + if (dfs_use_radar_background(iface)) { |
| 131 | + /* Cache background radar parameters. */ |
| 132 | + iface->radar_background.channel = iface->conf->channel; |
| 133 | + iface->radar_background.secondary_channel = |
| 134 | + iface->conf->secondary_channel; |
| 135 | + iface->radar_background.freq = iface->freq; |
| 136 | + iface->radar_background.centr_freq_seg0_idx = |
| 137 | + hostapd_get_oper_centr_freq_seg0_idx(iface->conf); |
| 138 | + iface->radar_background.centr_freq_seg1_idx = |
| 139 | + hostapd_get_oper_centr_freq_seg1_idx(iface->conf); |
| 140 | + |
| 141 | + /* |
| 142 | + * Let's select a random channel according to the |
| 143 | + * regulations and perform CAC on dedicated radar chain. |
| 144 | + */ |
| 145 | + res = dfs_set_valid_channel(iface, 1); |
| 146 | + if (res < 0) |
| 147 | + return res; |
| 148 | + |
| 149 | + iface->radar_background.temp_ch = 1; |
| 150 | + return 1; |
| 151 | + } |
| 152 | + |
| 153 | return 0; |
| 154 | } |
| 155 | |
| 156 | @@ -923,6 +961,86 @@ int hostapd_is_dfs_chan_available(struct hostapd_iface *iface) |
| 157 | } |
| 158 | |
| 159 | |
| 160 | +static void hostpad_dfs_update_background_chain(struct hostapd_iface *iface) |
| 161 | +{ |
| 162 | + int sec = 0; |
| 163 | + enum dfs_channel_type channel_type = DFS_NO_CAC_YET; |
| 164 | + struct hostapd_channel_data *channel; |
| 165 | + u8 oper_centr_freq_seg0_idx = 0; |
| 166 | + u8 oper_centr_freq_seg1_idx = 0; |
| 167 | + |
| 168 | + /* |
| 169 | + * Allow selection of DFS channel in ETSI to comply with |
| 170 | + * uniform spreading. |
| 171 | + */ |
| 172 | + if (iface->dfs_domain == HOSTAPD_DFS_REGION_ETSI) |
| 173 | + channel_type = DFS_ANY_CHANNEL; |
| 174 | + |
| 175 | + channel = dfs_get_valid_channel(iface, &sec, &oper_centr_freq_seg0_idx, |
| 176 | + &oper_centr_freq_seg1_idx, |
| 177 | + channel_type); |
| 178 | + if (!channel || |
| 179 | + channel->chan == iface->conf->channel || |
| 180 | + channel->chan == iface->radar_background.channel) |
| 181 | + channel = dfs_downgrade_bandwidth(iface, &sec, |
| 182 | + &oper_centr_freq_seg0_idx, |
| 183 | + &oper_centr_freq_seg1_idx, |
| 184 | + &channel_type); |
| 185 | + if (!channel || |
| 186 | + hostapd_start_dfs_cac(iface, iface->conf->hw_mode, |
| 187 | + channel->freq, channel->chan, |
| 188 | + iface->conf->ieee80211n, |
| 189 | + iface->conf->ieee80211ac, |
| 190 | + iface->conf->ieee80211ax, |
| 191 | + sec, hostapd_get_oper_chwidth(iface->conf), |
| 192 | + oper_centr_freq_seg0_idx, |
| 193 | + oper_centr_freq_seg1_idx, true)) { |
| 194 | + wpa_printf(MSG_ERROR, "DFS failed to start CAC offchannel"); |
| 195 | + iface->radar_background.channel = -1; |
| 196 | + return; |
| 197 | + } |
| 198 | + |
| 199 | + iface->radar_background.channel = channel->chan; |
| 200 | + iface->radar_background.freq = channel->freq; |
| 201 | + iface->radar_background.secondary_channel = sec; |
| 202 | + iface->radar_background.centr_freq_seg0_idx = oper_centr_freq_seg0_idx; |
| 203 | + iface->radar_background.centr_freq_seg1_idx = oper_centr_freq_seg1_idx; |
| 204 | + |
| 205 | + wpa_printf(MSG_DEBUG, |
| 206 | + "%s: setting background chain to chan %d (%d MHz)", |
| 207 | + __func__, channel->chan, channel->freq); |
| 208 | +} |
| 209 | + |
| 210 | + |
| 211 | +static bool |
| 212 | +hostapd_dfs_is_background_event(struct hostapd_iface *iface, int freq) |
| 213 | +{ |
| 214 | + return dfs_use_radar_background(iface) && |
| 215 | + iface->radar_background.channel != -1 && |
| 216 | + iface->radar_background.freq == freq; |
| 217 | +} |
| 218 | + |
| 219 | + |
| 220 | +static int |
| 221 | +hostapd_dfs_start_channel_switch_background(struct hostapd_iface *iface) |
| 222 | +{ |
| 223 | + iface->conf->channel = iface->radar_background.channel; |
| 224 | + iface->freq = iface->radar_background.freq; |
| 225 | + iface->conf->secondary_channel = |
| 226 | + iface->radar_background.secondary_channel; |
| 227 | + hostapd_set_oper_centr_freq_seg0_idx( |
| 228 | + iface->conf, iface->radar_background.centr_freq_seg0_idx); |
| 229 | + hostapd_set_oper_centr_freq_seg1_idx( |
| 230 | + iface->conf, iface->radar_background.centr_freq_seg1_idx); |
| 231 | + |
| 232 | + hostpad_dfs_update_background_chain(iface); |
| 233 | + hostapd_disable_iface(iface); |
| 234 | + hostapd_enable_iface(iface); |
| 235 | + |
| 236 | + return 0; |
| 237 | +} |
| 238 | + |
| 239 | + |
| 240 | int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq, |
| 241 | int ht_enabled, int chan_offset, int chan_width, |
| 242 | int cf1, int cf2) |
| 243 | @@ -943,6 +1061,22 @@ int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq, |
| 244 | set_dfs_state(iface, freq, ht_enabled, chan_offset, |
| 245 | chan_width, cf1, cf2, |
| 246 | HOSTAPD_CHAN_DFS_AVAILABLE); |
| 247 | + |
| 248 | + /* |
| 249 | + * Radar event from background chain for the selected |
| 250 | + * channel. Perform CSA, move the main chain to the |
| 251 | + * selected channel and configure the background chain |
| 252 | + * to a new DFS channel. |
| 253 | + */ |
| 254 | + if (hostapd_dfs_is_background_event(iface, freq)) { |
| 255 | + iface->radar_background.cac_started = 0; |
| 256 | + if (!iface->radar_background.temp_ch) |
| 257 | + return 0; |
| 258 | + |
| 259 | + iface->radar_background.temp_ch = 0; |
| 260 | + return hostapd_dfs_start_channel_switch_background(iface); |
| 261 | + } |
| 262 | + |
| 263 | /* |
| 264 | * Just mark the channel available when CAC completion |
| 265 | * event is received in enabled state. CAC result could |
| 266 | @@ -959,6 +1093,9 @@ int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq, |
| 267 | iface->cac_started = 0; |
| 268 | } |
| 269 | } |
| 270 | + } else if (hostapd_dfs_is_background_event(iface, freq)) { |
| 271 | + iface->radar_background.cac_started = 0; |
| 272 | + hostpad_dfs_update_background_chain(iface); |
| 273 | } |
| 274 | |
| 275 | return 0; |
| 276 | @@ -1269,9 +1406,14 @@ int hostapd_dfs_nop_finished(struct hostapd_iface *iface, int freq, |
| 277 | set_dfs_state(iface, freq, ht_enabled, chan_offset, chan_width, |
| 278 | cf1, cf2, HOSTAPD_CHAN_DFS_USABLE); |
| 279 | |
| 280 | - /* Handle cases where all channels were initially unavailable */ |
| 281 | - if (iface->state == HAPD_IFACE_DFS && !iface->cac_started) |
| 282 | + if (iface->state == HAPD_IFACE_DFS && !iface->cac_started) { |
| 283 | + /* Handle cases where all channels were initially unavailable */ |
| 284 | hostapd_handle_dfs(iface); |
| 285 | + } else if (dfs_use_radar_background(iface) && |
| 286 | + iface->radar_background.channel == -1) { |
| 287 | + /* Reset radar background chain if disabled */ |
| 288 | + hostpad_dfs_update_background_chain(iface); |
| 289 | + } |
| 290 | |
| 291 | return 0; |
| 292 | } |
| 293 | @@ -1309,17 +1451,24 @@ int hostapd_dfs_start_cac(struct hostapd_iface *iface, int freq, |
| 294 | int ht_enabled, int chan_offset, int chan_width, |
| 295 | int cf1, int cf2) |
| 296 | { |
| 297 | - /* This is called when the driver indicates that an offloaded DFS has |
| 298 | - * started CAC. */ |
| 299 | - hostapd_set_state(iface, HAPD_IFACE_DFS); |
| 300 | + if (hostapd_dfs_is_background_event(iface, freq)) { |
| 301 | + iface->radar_background.cac_started = 1; |
| 302 | + } else { |
| 303 | + /* This is called when the driver indicates that an offloaded |
| 304 | + * DFS has started CAC. */ |
| 305 | + hostapd_set_state(iface, HAPD_IFACE_DFS); |
| 306 | + iface->cac_started = 1; |
| 307 | + } |
| 308 | /* TODO: How to check CAC time for ETSI weather channels? */ |
| 309 | iface->dfs_cac_ms = 60000; |
| 310 | wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_CAC_START |
| 311 | "freq=%d chan=%d chan_offset=%d width=%d seg0=%d " |
| 312 | - "seg1=%d cac_time=%ds", |
| 313 | + "seg1=%d cac_time=%ds%s", |
| 314 | freq, (freq - 5000) / 5, chan_offset, chan_width, cf1, cf2, |
| 315 | - iface->dfs_cac_ms / 1000); |
| 316 | - iface->cac_started = 1; |
| 317 | + iface->dfs_cac_ms / 1000, |
| 318 | + hostapd_dfs_is_background_event(iface, freq) ? |
| 319 | + " (background)" : ""); |
| 320 | + |
| 321 | os_get_reltime(&iface->dfs_cac_start); |
| 322 | return 0; |
| 323 | } |
| 324 | diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h |
| 325 | index a4ea32517..095524e49 100644 |
| 326 | --- a/src/ap/hostapd.h |
| 327 | +++ b/src/ap/hostapd.h |
| 328 | @@ -541,6 +541,21 @@ struct hostapd_iface { |
| 329 | int *basic_rates; |
| 330 | int freq; |
| 331 | |
| 332 | + /* Background radar configuration */ |
| 333 | + struct { |
| 334 | + int channel; |
| 335 | + int secondary_channel; |
| 336 | + int freq; |
| 337 | + int centr_freq_seg0_idx; |
| 338 | + int centr_freq_seg1_idx; |
| 339 | + /* Main chain is on temporary channel during |
| 340 | + * CAC detection on radar offchain. |
| 341 | + */ |
| 342 | + unsigned int temp_ch:1; |
| 343 | + /* CAC started on radar offchain */ |
| 344 | + unsigned int cac_started:1; |
| 345 | + } radar_background; |
| 346 | + |
| 347 | u16 hw_flags; |
| 348 | |
| 349 | /* Number of associated Non-ERP stations (i.e., stations using 802.11b |
| 350 | -- |
| 351 | 2.29.2 |
| 352 | |