developer | faf1ea2 | 2022-04-29 17:53:25 +0800 | [diff] [blame] | 1 | diff --git a/hostapd/config_file.c b/hostapd/config_file.c |
| 2 | index 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) { |
| 14 | diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf |
| 15 | index 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 |
| 32 | diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h |
| 33 | index 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 |
| 44 | diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c |
| 45 | index 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 | |
| 74 | diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h |
| 75 | index 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); |
| 88 | diff --git a/src/ap/dfs.c b/src/ap/dfs.c |
| 89 | index 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 | } |
| 722 | diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h |
| 723 | index 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 |
| 748 | diff --git a/src/drivers/driver.h b/src/drivers/driver.h |
| 749 | index 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) \ |
| 773 | diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c |
| 774 | index 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 | |
| 795 | diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c |
| 796 | index 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 | |
| 810 | diff --git a/src/drivers/nl80211_copy.h b/src/drivers/nl80211_copy.h |
| 811 | index 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, |