| diff --git a/package/network/services/hostapd/patches/904-2102-zero-wait_dfs.patch b/package/network/services/hostapd/patches/904-2102-zero-wait_dfs.patch |
| new file mode 100644 |
| index 0000000..417c311 |
| --- /dev/null |
| +++ b/package/network/services/hostapd/patches/904-2102-zero-wait_dfs.patch |
| @@ -0,0 +1,848 @@ |
| +diff --git a/hostapd/config_file.c b/hostapd/config_file.c |
| +index 6b6101f..704afbb 100644 |
| +--- a/hostapd/config_file.c |
| ++++ b/hostapd/config_file.c |
| +@@ -2524,6 +2524,8 @@ static int hostapd_config_fill(struct hostapd_config *conf, |
| + conf->ieee80211d = atoi(pos); |
| + } else if (os_strcmp(buf, "ieee80211h") == 0) { |
| + conf->ieee80211h = atoi(pos); |
| ++ } else if (os_strcmp(buf, "radar_offchan") == 0) { |
| ++ conf->radar_offchan = atoi(pos); |
| + } else if (os_strcmp(buf, "ieee8021x") == 0) { |
| + bss->ieee802_1x = atoi(pos); |
| + } else if (os_strcmp(buf, "eapol_version") == 0) { |
| +diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf |
| +index c71f2ac..58c7034 100644 |
| +--- a/hostapd/hostapd.conf |
| ++++ b/hostapd/hostapd.conf |
| +@@ -143,6 +143,13 @@ ssid=test |
| + # ieee80211d=1 and local_pwr_constraint configured. |
| + #spectrum_mgmt_required=1 |
| + |
| ++# Enable radar/CAC detection through a dedicated offchannel chain available on |
| ++# some hw. The chain can't be used to transmits or receives frames. |
| ++# This feature allows to avoid CAC downtime switching on a different channel |
| ++# during CAC detection on the selected radar channel. |
| ++# (default: 0 = disabled, 1 = enabled) |
| ++#radar_offchan=0 |
| ++ |
| + # Operation mode (a = IEEE 802.11a (5 GHz), b = IEEE 802.11b (2.4 GHz), |
| + # g = IEEE 802.11g (2.4 GHz), ad = IEEE 802.11ad (60 GHz); a/g options are used |
| + # with IEEE 802.11n (HT), too, to specify band). For IEEE 802.11ac (VHT), this |
| +diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h |
| +index 920dba1..44d3b85 100644 |
| +--- a/src/ap/ap_config.h |
| ++++ b/src/ap/ap_config.h |
| +@@ -965,6 +965,7 @@ struct hostapd_config { |
| + int ieee80211d; |
| + |
| + int ieee80211h; /* DFS */ |
| ++ int radar_offchan; |
| + |
| + /* |
| + * Local power constraint is an octet encoded as an unsigned integer in |
| +diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c |
| +index 85728d4..ed16d1f 100644 |
| +--- a/src/ap/ap_drv_ops.c |
| ++++ b/src/ap/ap_drv_ops.c |
| +@@ -810,7 +810,8 @@ int hostapd_start_dfs_cac(struct hostapd_iface *iface, |
| + int channel, int ht_enabled, int vht_enabled, |
| + int he_enabled, |
| + int sec_channel_offset, int oper_chwidth, |
| +- int center_segment0, int center_segment1) |
| ++ int center_segment0, int center_segment1, |
| ++ int radar_offchan) |
| + { |
| + struct hostapd_data *hapd = iface->bss[0]; |
| + struct hostapd_freq_params data; |
| +@@ -836,10 +837,14 @@ int hostapd_start_dfs_cac(struct hostapd_iface *iface, |
| + wpa_printf(MSG_ERROR, "Can't set freq params"); |
| + return -1; |
| + } |
| ++ data.radar_offchan = radar_offchan; |
| + |
| + res = hapd->driver->start_dfs_cac(hapd->drv_priv, &data); |
| + if (!res) { |
| +- iface->cac_started = 1; |
| ++ if (radar_offchan) |
| ++ iface->radar_offchan.cac_started = 1; |
| ++ else |
| ++ iface->cac_started = 1; |
| + os_get_reltime(&iface->dfs_cac_start); |
| + } |
| + |
| +diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h |
| +index 5738c1c..12c87b0 100644 |
| +--- a/src/ap/ap_drv_ops.h |
| ++++ b/src/ap/ap_drv_ops.h |
| +@@ -130,7 +130,8 @@ int hostapd_start_dfs_cac(struct hostapd_iface *iface, |
| + int channel, int ht_enabled, int vht_enabled, |
| + int he_enabled, |
| + int sec_channel_offset, int oper_chwidth, |
| +- int center_segment0, int center_segment1); |
| ++ int center_segment0, int center_segment1, |
| ++ int radar_offchan); |
| + int hostapd_drv_do_acs(struct hostapd_data *hapd); |
| + int hostapd_drv_update_dh_ie(struct hostapd_data *hapd, const u8 *peer, |
| + u16 reason_code, const u8 *ie, size_t ielen); |
| +diff --git a/src/ap/dfs.c b/src/ap/dfs.c |
| +index 54a61b0..217c0fc 100644 |
| +--- a/src/ap/dfs.c |
| ++++ b/src/ap/dfs.c |
| +@@ -51,16 +51,31 @@ static int dfs_get_used_n_chans(struct hostapd_iface *iface, int *seg1) |
| + return n_chans; |
| + } |
| + |
| +- |
| ++/* |
| ++ * flags: |
| ++ * - 0: any channel |
| ++ * - 1: non-radar channel or radar available one |
| ++ * - 2: radar-only channel not yet available |
| ++ */ |
| + static int dfs_channel_available(struct hostapd_channel_data *chan, |
| +- int skip_radar) |
| ++ int flags) |
| + { |
| ++ if (flags == 2) { |
| ++ /* Select only radar channel where CAC has not been |
| ++ * performed yet |
| ++ */ |
| ++ if ((chan->flag & HOSTAPD_CHAN_RADAR) && |
| ++ (chan->flag & HOSTAPD_CHAN_DFS_MASK) == |
| ++ HOSTAPD_CHAN_DFS_USABLE) |
| ++ return 1; |
| ++ return 0; |
| ++ } |
| + /* |
| + * When radar detection happens, CSA is performed. However, there's no |
| + * time for CAC, so radar channels must be skipped when finding a new |
| + * channel for CSA, unless they are available for immediate use. |
| + */ |
| +- if (skip_radar && (chan->flag & HOSTAPD_CHAN_RADAR) && |
| ++ if (flags && (chan->flag & HOSTAPD_CHAN_RADAR) && |
| + ((chan->flag & HOSTAPD_CHAN_DFS_MASK) != |
| + HOSTAPD_CHAN_DFS_AVAILABLE)) |
| + return 0; |
| +@@ -136,10 +151,15 @@ dfs_get_chan_data(struct hostapd_hw_modes *mode, int freq, int first_chan_idx) |
| + return NULL; |
| + } |
| + |
| +- |
| ++/* |
| ++ * flags: |
| ++ * - 0: any channel |
| ++ * - 1: non-radar channel or radar available one |
| ++ * - 2: radar-only channel not yet available |
| ++ */ |
| + static int dfs_chan_range_available(struct hostapd_hw_modes *mode, |
| + int first_chan_idx, int num_chans, |
| +- int skip_radar) |
| ++ int flags) |
| + { |
| + struct hostapd_channel_data *first_chan, *chan; |
| + int i; |
| +@@ -178,7 +198,7 @@ static int dfs_chan_range_available(struct hostapd_hw_modes *mode, |
| + return 0; |
| + } |
| + |
| +- if (!dfs_channel_available(chan, skip_radar)) { |
| ++ if (!dfs_channel_available(chan, flags)) { |
| + wpa_printf(MSG_DEBUG, "DFS: channel not available %d", |
| + first_chan->freq + i * 20); |
| + return 0; |
| +@@ -205,10 +225,15 @@ static int is_in_chanlist(struct hostapd_iface *iface, |
| + * - hapd->secondary_channel |
| + * - hapd->vht/he_oper_centr_freq_seg0_idx |
| + * - hapd->vht/he_oper_centr_freq_seg1_idx |
| ++ * |
| ++ * flags: |
| ++ * - 0: any channel |
| ++ * - 1: non-radar channel or radar available one |
| ++ * - 2: radar-only channel not yet available |
| + */ |
| + static int dfs_find_channel(struct hostapd_iface *iface, |
| + struct hostapd_channel_data **ret_chan, |
| +- int idx, int skip_radar) |
| ++ int idx, int flags) |
| + { |
| + struct hostapd_hw_modes *mode; |
| + struct hostapd_channel_data *chan; |
| +@@ -233,7 +258,7 @@ static int dfs_find_channel(struct hostapd_iface *iface, |
| + } |
| + |
| + /* Skip incompatible chandefs */ |
| +- if (!dfs_chan_range_available(mode, i, n_chans, skip_radar)) { |
| ++ if (!dfs_chan_range_available(mode, i, n_chans, flags)) { |
| + wpa_printf(MSG_DEBUG, |
| + "DFS: range not available for %d (%d)", |
| + chan->freq, chan->chan); |
| +@@ -467,13 +492,18 @@ static int dfs_check_chans_unavailable(struct hostapd_iface *iface, |
| + return res; |
| + } |
| + |
| +- |
| ++/* |
| ++ * flags: |
| ++ * - 0: any channel |
| ++ * - 1: non-radar channel or radar available one |
| ++ * - 2: radar-only channel not yet available |
| ++ */ |
| + static struct hostapd_channel_data * |
| + dfs_get_valid_channel(struct hostapd_iface *iface, |
| + int *secondary_channel, |
| + u8 *oper_centr_freq_seg0_idx, |
| + u8 *oper_centr_freq_seg1_idx, |
| +- int skip_radar) |
| ++ int flags) |
| + { |
| + struct hostapd_hw_modes *mode; |
| + struct hostapd_channel_data *chan = NULL; |
| +@@ -502,7 +532,7 @@ dfs_get_valid_channel(struct hostapd_iface *iface, |
| + return NULL; |
| + |
| + /* Get the count first */ |
| +- num_available_chandefs = dfs_find_channel(iface, NULL, 0, skip_radar); |
| ++ num_available_chandefs = dfs_find_channel(iface, NULL, 0, flags); |
| + wpa_printf(MSG_DEBUG, "DFS: num_available_chandefs=%d", |
| + num_available_chandefs); |
| + if (num_available_chandefs == 0) |
| +@@ -523,7 +553,7 @@ dfs_get_valid_channel(struct hostapd_iface *iface, |
| + return NULL; |
| + |
| + chan_idx = _rand % num_available_chandefs; |
| +- dfs_find_channel(iface, &chan, chan_idx, skip_radar); |
| ++ dfs_find_channel(iface, &chan, chan_idx, flags); |
| + if (!chan) { |
| + wpa_printf(MSG_DEBUG, "DFS: no random channel found"); |
| + return NULL; |
| +@@ -552,7 +582,7 @@ dfs_get_valid_channel(struct hostapd_iface *iface, |
| + for (i = 0; i < num_available_chandefs - 1; i++) { |
| + /* start from chan_idx + 1, end when chan_idx - 1 */ |
| + chan_idx2 = (chan_idx + 1 + i) % num_available_chandefs; |
| +- dfs_find_channel(iface, &chan2, chan_idx2, skip_radar); |
| ++ dfs_find_channel(iface, &chan2, chan_idx2, flags); |
| + if (chan2 && abs(chan2->chan - chan->chan) > 12) { |
| + /* two channels are not adjacent */ |
| + sec_chan_idx_80p80 = chan2->chan; |
| +@@ -582,6 +612,27 @@ dfs_get_valid_channel(struct hostapd_iface *iface, |
| + return chan; |
| + } |
| + |
| ++static int dfs_set_valid_channel(struct hostapd_iface *iface, int skip_radar) |
| ++{ |
| ++ struct hostapd_channel_data *channel; |
| ++ u8 cf1 = 0, cf2 = 0; |
| ++ int sec = 0; |
| ++ |
| ++ channel = dfs_get_valid_channel(iface, &sec, &cf1, &cf2, |
| ++ skip_radar); |
| ++ if (!channel) { |
| ++ wpa_printf(MSG_ERROR, "could not get valid channel"); |
| ++ return -1; |
| ++ } |
| ++ |
| ++ iface->freq = channel->freq; |
| ++ iface->conf->channel = channel->chan; |
| ++ iface->conf->secondary_channel = sec; |
| ++ hostapd_set_oper_centr_freq_seg0_idx(iface->conf, cf1); |
| ++ hostapd_set_oper_centr_freq_seg1_idx(iface->conf, cf2); |
| ++ |
| ++ return 0; |
| ++} |
| + |
| + static int set_dfs_state_freq(struct hostapd_iface *iface, int freq, u32 state) |
| + { |
| +@@ -761,6 +812,11 @@ static unsigned int dfs_get_cac_time(struct hostapd_iface *iface, |
| + return cac_time_ms; |
| + } |
| + |
| ++static int hostapd_is_radar_offchan_enabled(struct hostapd_iface *iface) |
| ++{ |
| ++ return (iface->drv_flags2 & WPA_DRIVER_RADAR_OFFCHAN) && |
| ++ iface->conf->radar_offchan; |
| ++} |
| + |
| + /* |
| + * Main DFS handler |
| +@@ -770,9 +826,8 @@ static unsigned int dfs_get_cac_time(struct hostapd_iface *iface, |
| + */ |
| + int hostapd_handle_dfs(struct hostapd_iface *iface) |
| + { |
| +- struct hostapd_channel_data *channel; |
| + int res, n_chans, n_chans1, start_chan_idx, start_chan_idx1; |
| +- int skip_radar = 0; |
| ++ int skip_radar = 0, radar_offchan; |
| + |
| + if (is_6ghz_freq(iface->freq)) |
| + return 1; |
| +@@ -825,28 +880,18 @@ int hostapd_handle_dfs(struct hostapd_iface *iface) |
| + wpa_printf(MSG_DEBUG, "DFS %d chans unavailable - choose other channel: %s", |
| + res, res ? "yes": "no"); |
| + if (res) { |
| +- int sec = 0; |
| +- u8 cf1 = 0, cf2 = 0; |
| +- |
| +- channel = dfs_get_valid_channel(iface, &sec, &cf1, &cf2, |
| +- skip_radar); |
| +- if (!channel) { |
| +- wpa_printf(MSG_ERROR, "could not get valid channel"); |
| ++ if (dfs_set_valid_channel(iface, skip_radar) < 0) { |
| + hostapd_set_state(iface, HAPD_IFACE_DFS); |
| + return 0; |
| + } |
| +- |
| +- iface->freq = channel->freq; |
| +- iface->conf->channel = channel->chan; |
| +- iface->conf->secondary_channel = sec; |
| +- hostapd_set_oper_centr_freq_seg0_idx(iface->conf, cf1); |
| +- hostapd_set_oper_centr_freq_seg1_idx(iface->conf, cf2); |
| + } |
| + } while (res); |
| + |
| + /* Finally start CAC */ |
| + hostapd_set_state(iface, HAPD_IFACE_DFS); |
| +- wpa_printf(MSG_DEBUG, "DFS start CAC on %d MHz", iface->freq); |
| ++ radar_offchan = hostapd_is_radar_offchan_enabled(iface); |
| ++ wpa_printf(MSG_DEBUG, "DFS start CAC on %d MHz offchan %d", |
| ++ iface->freq, radar_offchan); |
| + wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_CAC_START |
| + "freq=%d chan=%d sec_chan=%d, width=%d, seg0=%d, seg1=%d, cac_time=%ds", |
| + iface->freq, |
| +@@ -863,13 +908,37 @@ int hostapd_handle_dfs(struct hostapd_iface *iface) |
| + iface->conf->secondary_channel, |
| + hostapd_get_oper_chwidth(iface->conf), |
| + hostapd_get_oper_centr_freq_seg0_idx(iface->conf), |
| +- hostapd_get_oper_centr_freq_seg1_idx(iface->conf)); |
| ++ hostapd_get_oper_centr_freq_seg1_idx(iface->conf), |
| ++ radar_offchan); |
| + |
| + if (res) { |
| + wpa_printf(MSG_ERROR, "DFS start_dfs_cac() failed, %d", res); |
| + return -1; |
| + } |
| + |
| ++ if (radar_offchan) { |
| ++ /* Cache offchannel radar parameters */ |
| ++ iface->radar_offchan.channel = iface->conf->channel; |
| ++ iface->radar_offchan.secondary_channel = |
| ++ iface->conf->secondary_channel; |
| ++ iface->radar_offchan.freq = iface->freq; |
| ++ iface->radar_offchan.centr_freq_seg0_idx = |
| ++ hostapd_get_oper_centr_freq_seg0_idx(iface->conf); |
| ++ iface->radar_offchan.centr_freq_seg1_idx = |
| ++ hostapd_get_oper_centr_freq_seg1_idx(iface->conf); |
| ++ |
| ++ /* |
| ++ * Let's select a random channel for the moment |
| ++ * and perform CAC on dedicated radar chain |
| ++ */ |
| ++ res = dfs_set_valid_channel(iface, 1); |
| ++ if (res < 0) |
| ++ return res; |
| ++ |
| ++ iface->radar_offchan.temp_ch = 1; |
| ++ return 1; |
| ++ } |
| ++ |
| + return 0; |
| + } |
| + |
| +@@ -890,6 +959,157 @@ int hostapd_is_dfs_chan_available(struct hostapd_iface *iface) |
| + return dfs_check_chans_available(iface, start_chan_idx, n_chans); |
| + } |
| + |
| ++static int hostapd_dfs_request_channel_switch(struct hostapd_iface *iface, |
| ++ int channel, int freq, |
| ++ int secondary_channel, |
| ++ u8 oper_centr_freq_seg0_idx, |
| ++ u8 oper_centr_freq_seg1_idx) |
| ++{ |
| ++ struct hostapd_hw_modes *cmode = iface->current_mode; |
| ++ int ieee80211_mode = IEEE80211_MODE_AP, err, i; |
| ++ struct csa_settings csa_settings; |
| ++ u8 new_vht_oper_chwidth; |
| ++ |
| ++ wpa_printf(MSG_DEBUG, "DFS will switch to a new channel %d", channel); |
| ++ wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_NEW_CHANNEL |
| ++ "freq=%d chan=%d sec_chan=%d", freq, channel, |
| ++ secondary_channel); |
| ++ |
| ++ new_vht_oper_chwidth = hostapd_get_oper_chwidth(iface->conf); |
| ++ hostapd_set_oper_chwidth(iface->conf, |
| ++ hostapd_get_oper_chwidth(iface->conf)); |
| ++ |
| ++ /* Setup CSA request */ |
| ++ os_memset(&csa_settings, 0, sizeof(csa_settings)); |
| ++ csa_settings.cs_count = 5; |
| ++ csa_settings.block_tx = 1; |
| ++#ifdef CONFIG_MESH |
| ++ if (iface->mconf) |
| ++ ieee80211_mode = IEEE80211_MODE_MESH; |
| ++#endif /* CONFIG_MESH */ |
| ++ err = hostapd_set_freq_params(&csa_settings.freq_params, |
| ++ iface->conf->hw_mode, |
| ++ freq, channel, |
| ++ iface->conf->enable_edmg, |
| ++ iface->conf->edmg_channel, |
| ++ iface->conf->ieee80211n, |
| ++ iface->conf->ieee80211ac, |
| ++ iface->conf->ieee80211ax, |
| ++ secondary_channel, |
| ++ new_vht_oper_chwidth, |
| ++ oper_centr_freq_seg0_idx, |
| ++ oper_centr_freq_seg1_idx, |
| ++ cmode->vht_capab, |
| ++ &cmode->he_capab[ieee80211_mode]); |
| ++ |
| ++ if (err) { |
| ++ wpa_printf(MSG_ERROR, "DFS failed to calculate CSA freq params"); |
| ++ hostapd_disable_iface(iface); |
| ++ return err; |
| ++ } |
| ++ |
| ++ for (i = 0; i < iface->num_bss; i++) { |
| ++ err = hostapd_switch_channel(iface->bss[i], &csa_settings); |
| ++ if (err) |
| ++ break; |
| ++ } |
| ++ |
| ++ if (err) { |
| ++ wpa_printf(MSG_WARNING, "DFS failed to schedule CSA (%d) - trying fallback", |
| ++ err); |
| ++ iface->freq = freq; |
| ++ iface->conf->channel = channel; |
| ++ iface->conf->secondary_channel = secondary_channel; |
| ++ hostapd_set_oper_chwidth(iface->conf, new_vht_oper_chwidth); |
| ++ hostapd_set_oper_centr_freq_seg0_idx(iface->conf, |
| ++ oper_centr_freq_seg0_idx); |
| ++ hostapd_set_oper_centr_freq_seg1_idx(iface->conf, |
| ++ oper_centr_freq_seg1_idx); |
| ++ |
| ++ hostapd_disable_iface(iface); |
| ++ hostapd_enable_iface(iface); |
| ++ |
| ++ return 0; |
| ++ } |
| ++ |
| ++ /* Channel configuration will be updated once CSA completes and |
| ++ * ch_switch_notify event is received */ |
| ++ wpa_printf(MSG_DEBUG, "DFS waiting channel switch event"); |
| ++ |
| ++ return 0; |
| ++} |
| ++ |
| ++static struct hostapd_channel_data * |
| ++dfs_downgrade_bandwidth(struct hostapd_iface *iface, int *secondary_channel, |
| ++ u8 *oper_centr_freq_seg0_idx, |
| ++ u8 *oper_centr_freq_seg1_idx, int *skip_radar); |
| ++ |
| ++static void |
| ++hostpad_dfs_update_offchannel_chain(struct hostapd_iface *iface) |
| ++{ |
| ++ struct hostapd_channel_data *channel; |
| ++ int sec = 0, flags = 2; |
| ++ u8 cf1 = 0, cf2 = 0; |
| ++ |
| ++ channel = dfs_get_valid_channel(iface, &sec, &cf1, &cf2, 2); |
| ++ if (!channel || channel->chan == iface->conf->channel) |
| ++ channel = dfs_downgrade_bandwidth(iface, &sec, &cf1, &cf2, |
| ++ &flags); |
| ++ if (!channel || |
| ++ hostapd_start_dfs_cac(iface, iface->conf->hw_mode, |
| ++ channel->freq, channel->chan, |
| ++ iface->conf->ieee80211n, |
| ++ iface->conf->ieee80211ac, |
| ++ iface->conf->ieee80211ax, |
| ++ sec, hostapd_get_oper_chwidth(iface->conf), |
| ++ cf1, cf2, 1)) { |
| ++ /* |
| ++ * Toggle interface state to enter DFS state |
| ++ * until NOP is finished. |
| ++ */ |
| ++ wpa_printf(MSG_ERROR, "DFS failed start CAC offchannel"); |
| ++ return; |
| ++ } |
| ++ |
| ++ wpa_printf(MSG_DEBUG, "%s: setting offchannel chain to chan %d (%d MHz)", |
| ++ __func__, channel->chan, channel->freq); |
| ++ |
| ++ iface->radar_offchan.channel = channel->chan; |
| ++ iface->radar_offchan.freq = channel->freq; |
| ++ iface->radar_offchan.secondary_channel = sec; |
| ++ iface->radar_offchan.centr_freq_seg0_idx = cf1; |
| ++ iface->radar_offchan.centr_freq_seg1_idx = cf2; |
| ++} |
| ++ |
| ++/* FIXME: check if all channel bandwith */ |
| ++static int |
| ++hostapd_dfs_is_offchan_event(struct hostapd_iface *iface, int freq) |
| ++{ |
| ++ if (iface->radar_offchan.freq != freq) |
| ++ return 0; |
| ++ |
| ++ return 1; |
| ++} |
| ++ |
| ++static int |
| ++hostapd_dfs_start_channel_switch_offchan(struct hostapd_iface *iface) |
| ++{ |
| ++ iface->conf->channel = iface->radar_offchan.channel; |
| ++ iface->freq = iface->radar_offchan.freq; |
| ++ iface->conf->secondary_channel = |
| ++ iface->radar_offchan.secondary_channel; |
| ++ hostapd_set_oper_centr_freq_seg0_idx(iface->conf, |
| ++ iface->radar_offchan.centr_freq_seg0_idx); |
| ++ hostapd_set_oper_centr_freq_seg1_idx(iface->conf, |
| ++ iface->radar_offchan.centr_freq_seg1_idx); |
| ++ |
| ++ hostpad_dfs_update_offchannel_chain(iface); |
| ++ |
| ++ return hostapd_dfs_request_channel_switch(iface, iface->conf->channel, |
| ++ iface->freq, iface->conf->secondary_channel, |
| ++ hostapd_get_oper_centr_freq_seg0_idx(iface->conf), |
| ++ hostapd_get_oper_centr_freq_seg1_idx(iface->conf)); |
| ++} |
| + |
| + int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq, |
| + int ht_enabled, int chan_offset, int chan_width, |
| +@@ -911,6 +1131,23 @@ int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq, |
| + set_dfs_state(iface, freq, ht_enabled, chan_offset, |
| + chan_width, cf1, cf2, |
| + HOSTAPD_CHAN_DFS_AVAILABLE); |
| ++ |
| ++ /* |
| ++ * radar event from offchannel chain for selected |
| ++ * channel. Perfrom CSA, move main chain to selected |
| ++ * channel and configure offchannel chain to a new DFS |
| ++ * channel |
| ++ */ |
| ++ if (hostapd_is_radar_offchan_enabled(iface) && |
| ++ hostapd_dfs_is_offchan_event(iface, freq)) { |
| ++ iface->radar_offchan.cac_started = 0; |
| ++ if (iface->radar_offchan.temp_ch) { |
| ++ iface->radar_offchan.temp_ch = 0; |
| ++ return hostapd_dfs_start_channel_switch_offchan(iface); |
| ++ } |
| ++ return 0; |
| ++ } |
| ++ |
| + /* |
| + * Just mark the channel available when CAC completion |
| + * event is received in enabled state. CAC result could |
| +@@ -927,6 +1164,10 @@ int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq, |
| + iface->cac_started = 0; |
| + } |
| + } |
| ++ } else if (hostapd_is_radar_offchan_enabled(iface) && |
| ++ hostapd_dfs_is_offchan_event(iface, freq)) { |
| ++ iface->radar_offchan.cac_started = 0; |
| ++ hostpad_dfs_update_offchannel_chain(iface); |
| + } |
| + |
| + return 0; |
| +@@ -1035,6 +1276,44 @@ static int hostapd_dfs_start_channel_switch_cac(struct hostapd_iface *iface) |
| + return err; |
| + } |
| + |
| ++static int |
| ++hostapd_dfs_offchan_start_channel_switch(struct hostapd_iface *iface, int freq) |
| ++{ |
| ++ if (!hostapd_is_radar_offchan_enabled(iface)) |
| ++ return -1; /* Offchannel chain not supported */ |
| ++ |
| ++ wpa_printf(MSG_DEBUG, |
| ++ "%s called (offchannel CAC active: %s, CSA active: %s)", |
| ++ __func__, iface->radar_offchan.cac_started ? "yes" : "no", |
| ++ hostapd_csa_in_progress(iface) ? "yes" : "no"); |
| ++ |
| ++ /* Check if CSA in progress */ |
| ++ if (hostapd_csa_in_progress(iface)) |
| ++ return 0; |
| ++ |
| ++ /* |
| ++ * If offchannel radar detation is supported and radar channel |
| ++ * monitored by offchain is available switch to it without waiting |
| ++ * for the CAC otherwise let's keep a random channel. |
| ++ * If radar pattern is reported on offchannel chain, just switch to |
| ++ * monitor another radar channel. |
| ++ */ |
| ++ if (hostapd_dfs_is_offchan_event(iface, freq)) { |
| ++ hostpad_dfs_update_offchannel_chain(iface); |
| ++ return 0; |
| ++ } |
| ++ |
| ++ /* Offchannel not availanle yet. Perform CAC on main chain */ |
| ++ if (iface->radar_offchan.cac_started) { |
| ++ /* We want to switch to monitored channel as soon as |
| ++ * CAC is completed. |
| ++ */ |
| ++ iface->radar_offchan.temp_ch = 1; |
| ++ return -1; |
| ++ } |
| ++ |
| ++ return hostapd_dfs_start_channel_switch_offchan(iface); |
| ++} |
| + |
| + static int hostapd_dfs_start_channel_switch(struct hostapd_iface *iface) |
| + { |
| +@@ -1042,13 +1321,7 @@ static int hostapd_dfs_start_channel_switch(struct hostapd_iface *iface) |
| + int secondary_channel; |
| + u8 oper_centr_freq_seg0_idx; |
| + u8 oper_centr_freq_seg1_idx; |
| +- u8 new_vht_oper_chwidth; |
| + int skip_radar = 1; |
| +- struct csa_settings csa_settings; |
| +- unsigned int i; |
| +- int err = 1; |
| +- struct hostapd_hw_modes *cmode = iface->current_mode; |
| +- u8 current_vht_oper_chwidth = hostapd_get_oper_chwidth(iface->conf); |
| + |
| + wpa_printf(MSG_DEBUG, "%s called (CAC active: %s, CSA active: %s)", |
| + __func__, iface->cac_started ? "yes" : "no", |
| +@@ -1110,69 +1383,11 @@ static int hostapd_dfs_start_channel_switch(struct hostapd_iface *iface) |
| + } |
| + } |
| + |
| +- wpa_printf(MSG_DEBUG, "DFS will switch to a new channel %d", |
| +- channel->chan); |
| +- wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_NEW_CHANNEL |
| +- "freq=%d chan=%d sec_chan=%d", channel->freq, |
| +- channel->chan, secondary_channel); |
| +- |
| +- new_vht_oper_chwidth = hostapd_get_oper_chwidth(iface->conf); |
| +- hostapd_set_oper_chwidth(iface->conf, current_vht_oper_chwidth); |
| +- |
| +- /* Setup CSA request */ |
| +- os_memset(&csa_settings, 0, sizeof(csa_settings)); |
| +- csa_settings.cs_count = 5; |
| +- csa_settings.block_tx = 1; |
| +- err = hostapd_set_freq_params(&csa_settings.freq_params, |
| +- iface->conf->hw_mode, |
| +- channel->freq, |
| +- channel->chan, |
| +- iface->conf->enable_edmg, |
| +- iface->conf->edmg_channel, |
| +- iface->conf->ieee80211n, |
| +- iface->conf->ieee80211ac, |
| +- iface->conf->ieee80211ax, |
| +- secondary_channel, |
| +- new_vht_oper_chwidth, |
| +- oper_centr_freq_seg0_idx, |
| +- oper_centr_freq_seg1_idx, |
| +- cmode->vht_capab, |
| +- &cmode->he_capab[iface->conf->hw_mode]); |
| +- |
| +- if (err) { |
| +- wpa_printf(MSG_ERROR, "DFS failed to calculate CSA freq params"); |
| +- hostapd_disable_iface(iface); |
| +- return err; |
| +- } |
| +- |
| +- for (i = 0; i < iface->num_bss; i++) { |
| +- err = hostapd_switch_channel(iface->bss[i], &csa_settings); |
| +- if (err) |
| +- break; |
| +- } |
| +- |
| +- if (err) { |
| +- wpa_printf(MSG_WARNING, "DFS failed to schedule CSA (%d) - trying fallback", |
| +- err); |
| +- iface->freq = channel->freq; |
| +- iface->conf->channel = channel->chan; |
| +- iface->conf->secondary_channel = secondary_channel; |
| +- hostapd_set_oper_chwidth(iface->conf, new_vht_oper_chwidth); |
| +- hostapd_set_oper_centr_freq_seg0_idx(iface->conf, |
| +- oper_centr_freq_seg0_idx); |
| +- hostapd_set_oper_centr_freq_seg1_idx(iface->conf, |
| +- oper_centr_freq_seg1_idx); |
| +- |
| +- hostapd_disable_iface(iface); |
| +- hostapd_enable_iface(iface); |
| +- return 0; |
| +- } |
| +- |
| +- /* Channel configuration will be updated once CSA completes and |
| +- * ch_switch_notify event is received */ |
| +- |
| +- wpa_printf(MSG_DEBUG, "DFS waiting channel switch event"); |
| +- return 0; |
| ++ return hostapd_dfs_request_channel_switch(iface, channel->chan, |
| ++ channel->freq, |
| ++ secondary_channel, |
| ++ oper_centr_freq_seg0_idx, |
| ++ oper_centr_freq_seg1_idx); |
| + } |
| + |
| + |
| +@@ -1199,15 +1414,19 @@ int hostapd_dfs_radar_detected(struct hostapd_iface *iface, int freq, |
| + if (!res) |
| + return 0; |
| + |
| +- /* Skip if reported radar event not overlapped our channels */ |
| +- res = dfs_are_channels_overlapped(iface, freq, chan_width, cf1, cf2); |
| +- if (!res) |
| +- return 0; |
| ++ if (!hostapd_dfs_is_offchan_event(iface, freq)) { |
| ++ /* Skip if reported radar event not overlapped our channels */ |
| ++ res = dfs_are_channels_overlapped(iface, freq, chan_width, |
| ++ cf1, cf2); |
| ++ if (!res) |
| ++ return 0; |
| ++ } |
| + |
| +- /* radar detected while operating, switch the channel. */ |
| +- res = hostapd_dfs_start_channel_switch(iface); |
| ++ if (hostapd_dfs_offchan_start_channel_switch(iface, freq)) |
| ++ /* radar detected while operating, switch the channel. */ |
| ++ return hostapd_dfs_start_channel_switch(iface); |
| + |
| +- return res; |
| ++ return 0; |
| + } |
| + |
| + |
| +@@ -1275,7 +1494,11 @@ int hostapd_dfs_start_cac(struct hostapd_iface *iface, int freq, |
| + "seg1=%d cac_time=%ds", |
| + freq, (freq - 5000) / 5, chan_offset, chan_width, cf1, cf2, |
| + iface->dfs_cac_ms / 1000); |
| +- iface->cac_started = 1; |
| ++ |
| ++ if (hostapd_dfs_is_offchan_event(iface, freq)) |
| ++ iface->radar_offchan.cac_started = 1; |
| ++ else |
| ++ iface->cac_started = 1; |
| + os_get_reltime(&iface->dfs_cac_start); |
| + return 0; |
| + } |
| +diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h |
| +index 54b3674..e9b5500 100644 |
| +--- a/src/ap/hostapd.h |
| ++++ b/src/ap/hostapd.h |
| +@@ -517,6 +517,21 @@ struct hostapd_iface { |
| + int *basic_rates; |
| + int freq; |
| + |
| ++ /* Offchanel chain configuration */ |
| ++ struct { |
| ++ int channel; |
| ++ int secondary_channel; |
| ++ int freq; |
| ++ int centr_freq_seg0_idx; |
| ++ int centr_freq_seg1_idx; |
| ++ /* Main chain is on temporary channel during |
| ++ * CAC detection on radar offchain |
| ++ */ |
| ++ unsigned int temp_ch:1; |
| ++ /* CAC started on radar offchain */ |
| ++ unsigned int cac_started:1; |
| ++ } radar_offchan; |
| ++ |
| + u16 hw_flags; |
| + |
| + /* Number of associated Non-ERP stations (i.e., stations using 802.11b |
| +diff --git a/src/drivers/driver.h b/src/drivers/driver.h |
| +index 05ec9c2..0521c20 100644 |
| +--- a/src/drivers/driver.h |
| ++++ b/src/drivers/driver.h |
| +@@ -776,6 +776,11 @@ struct hostapd_freq_params { |
| + * for IEEE 802.11ay EDMG configuration. |
| + */ |
| + struct ieee80211_edmg_config edmg; |
| ++ |
| ++ /** |
| ++ * radar_offchan - Whether radar/CAC offchannel is requested |
| ++ */ |
| ++ int radar_offchan; |
| + }; |
| + |
| + /** |
| +@@ -1938,6 +1943,8 @@ struct wpa_driver_capa { |
| + |
| + /** Driver supports a separate control port RX for EAPOL frames */ |
| + #define WPA_DRIVER_FLAGS2_CONTROL_PORT_RX 0x0000000000000001ULL |
| ++/** Driver supports offchannel radar/CAC detection */ |
| ++#define WPA_DRIVER_RADAR_OFFCHAN 0x0000000000000200ULL |
| + u64 flags2; |
| + |
| + #define FULL_AP_CLIENT_STATE_SUPP(drv_flags) \ |
| +diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c |
| +index 72ae074..09e0bc9 100644 |
| +--- a/src/drivers/driver_nl80211.c |
| ++++ b/src/drivers/driver_nl80211.c |
| +@@ -4576,6 +4576,7 @@ static int nl80211_put_freq_params(struct nl_msg *msg, |
| + wpa_printf(MSG_DEBUG, " * he_enabled=%d", freq->he_enabled); |
| + wpa_printf(MSG_DEBUG, " * vht_enabled=%d", freq->vht_enabled); |
| + wpa_printf(MSG_DEBUG, " * ht_enabled=%d", freq->ht_enabled); |
| ++ wpa_printf(MSG_DEBUG, " * radar_offchan=%d", freq->radar_offchan); |
| + |
| + hw_mode = ieee80211_freq_to_chan(freq->freq, &channel); |
| + is_24ghz = hw_mode == HOSTAPD_MODE_IEEE80211G || |
| +@@ -4653,6 +4654,9 @@ static int nl80211_put_freq_params(struct nl_msg *msg, |
| + NL80211_CHAN_NO_HT)) |
| + return -ENOBUFS; |
| + } |
| ++ if (freq->radar_offchan) |
| ++ nla_put_flag(msg, NL80211_ATTR_RADAR_OFFCHAN); |
| ++ |
| + return 0; |
| + } |
| + |
| +diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c |
| +index 3e8dcef..1dcdbe6 100644 |
| +--- a/src/drivers/driver_nl80211_capa.c |
| ++++ b/src/drivers/driver_nl80211_capa.c |
| +@@ -639,6 +639,10 @@ static void wiphy_info_ext_feature_flags(struct wiphy_info_data *info, |
| + if (ext_feature_isset(ext_features, len, |
| + NL80211_EXT_FEATURE_MULTICAST_REGISTRATIONS)) |
| + info->drv->multicast_registrations = 1; |
| ++ |
| ++ if (ext_feature_isset(ext_features, len, |
| ++ NL80211_EXT_FEATURE_RADAR_OFFCHAN)) |
| ++ capa->flags2 |= WPA_DRIVER_RADAR_OFFCHAN; |
| + } |
| + |
| + |
| +diff --git a/src/drivers/nl80211_copy.h b/src/drivers/nl80211_copy.h |
| +index ab84efc..31a227c 100644 |
| +--- a/src/drivers/nl80211_copy.h |
| ++++ b/src/drivers/nl80211_copy.h |
| +@@ -2534,6 +2534,10 @@ enum nl80211_commands { |
| + * @NL80211_ATTR_WIPHY_ANTENNA_GAIN: Configured antenna gain. Used to reduce |
| + * transmit power to stay within regulatory limits. u32, dBi. |
| + * |
| ++ * @NL80211_ATTR_RADAR_OFFCHAN: Configure dedicated chain available for radar |
| ++ * detection on some hw. The chain can't be used to transmits or receives |
| ++ * frames. The driver is supposed to implement CAC management in sw or fw. |
| ++ * |
| + * @NUM_NL80211_ATTR: total number of nl80211_attrs available |
| + * @NL80211_ATTR_MAX: highest attribute number currently defined |
| + * @__NL80211_ATTR_AFTER_LAST: internal use |
| +@@ -3039,6 +3043,8 @@ enum nl80211_attrs { |
| + |
| + NL80211_ATTR_WIPHY_ANTENNA_GAIN, |
| + |
| ++ NL80211_ATTR_RADAR_OFFCHAN, |
| ++ |
| + /* add attributes here, update the policy in nl80211.c */ |
| + |
| + __NL80211_ATTR_AFTER_LAST, |
| +@@ -5823,6 +5829,9 @@ enum nl80211_feature_flags { |
| + * @NL80211_EXT_FEATURE_BSS_COLOR: The driver supports BSS color collision |
| + * detection and change announcemnts. |
| + * |
| ++ * @NL80211_EXT_FEATURE_RADAR_OFFCHAN: Device supports offchannel radar/CAC |
| ++ * detection. |
| ++ * |
| + * @NUM_NL80211_EXT_FEATURES: number of extended features. |
| + * @MAX_NL80211_EXT_FEATURES: highest extended feature index. |
| + */ |
| +@@ -5888,6 +5897,7 @@ enum nl80211_ext_feature_index { |
| + NL80211_EXT_FEATURE_SECURE_RTT, |
| + NL80211_EXT_FEATURE_PROT_RANGE_NEGO_AND_MEASURE, |
| + NL80211_EXT_FEATURE_BSS_COLOR, |
| ++ NL80211_EXT_FEATURE_RADAR_OFFCHAN, |
| + |
| + /* add new features before the definition below */ |
| + NUM_NL80211_EXT_FEATURES, |
| + |