]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
DFS: Configure background radar/CAC detection
authorLorenzo Bianconi <lorenzo@kernel.org>
Sun, 6 Mar 2022 19:34:11 +0000 (20:34 +0100)
committerJouni Malinen <j@w1.fi>
Sun, 13 Mar 2022 19:06:51 +0000 (21:06 +0200)
Introduce the capability to perform radar/CAC detection on an offchannel
radar chain available on some hardware (e.g., mt7915). This feature
allows to avoid CAC downtime switching on a different channel during CAC
detection on the selected radar channel.

Tested-by: Owen Peng <owen.peng@mediatek.com>
Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
src/ap/ap_drv_ops.c
src/ap/ap_drv_ops.h
src/ap/dfs.c
src/ap/hostapd.h

index e917736664bda196765fa989f13ec19ea2c7f961..e2aca25d384d6b3c6058ec045eb4dd297e129006 100644 (file)
@@ -812,7 +812,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,
+                         bool radar_background)
 {
        struct hostapd_data *hapd = iface->bss[0];
        struct hostapd_freq_params data;
@@ -838,10 +839,14 @@ int hostapd_start_dfs_cac(struct hostapd_iface *iface,
                wpa_printf(MSG_ERROR, "Can't set freq params");
                return -1;
        }
+       data.radar_background = radar_background;
 
        res = hapd->driver->start_dfs_cac(hapd->drv_priv, &data);
        if (!res) {
-               iface->cac_started = 1;
+               if (radar_background)
+                       iface->radar_background.cac_started = 1;
+               else
+                       iface->cac_started = 1;
                os_get_reltime(&iface->dfs_cac_start);
        }
 
index 61c8f64eb471cbf25a5cee03c5a1fdfd5e001447..b4d6395ae2af25d402121a949f80153c58487da2 100644 (file)
@@ -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,
+                         bool radar_background);
 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);
index 6bde5de0bc17c3bc675144c14386db2f55d350da..b221825a4416963b304d22142333c14b58a22a4a 100644 (file)
 #include "dfs.h"
 
 
+enum dfs_channel_type {
+       DFS_ANY_CHANNEL,
+       DFS_AVAILABLE, /* non-radar or radar-available */
+       DFS_NO_CAC_YET, /* radar-not-yet-available */
+};
+
+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,
+                       enum dfs_channel_type *channel_type);
+
+
+static bool dfs_use_radar_background(struct hostapd_iface *iface)
+{
+       return iface->drv_flags2 & WPA_DRIVER_RADAR_BACKGROUND;
+}
+
+
 static int dfs_get_used_n_chans(struct hostapd_iface *iface, int *seg1)
 {
        int n_chans = 1;
@@ -51,12 +70,6 @@ static int dfs_get_used_n_chans(struct hostapd_iface *iface, int *seg1)
 }
 
 
-enum dfs_channel_type {
-       DFS_ANY_CHANNEL,
-       DFS_AVAILABLE, /* non-radar or radar-available */
-       DFS_NO_CAC_YET, /* radar-not-yet-available */
-};
-
 /* dfs_channel_available: select new channel according to type parameter */
 static int dfs_channel_available(struct hostapd_channel_data *chan,
                                 enum dfs_channel_type type)
@@ -860,7 +873,8 @@ int hostapd_handle_dfs(struct hostapd_iface *iface)
 
        /* Finally start CAC */
        hostapd_set_state(iface, HAPD_IFACE_DFS);
-       wpa_printf(MSG_DEBUG, "DFS start CAC on %d MHz", iface->freq);
+       wpa_printf(MSG_DEBUG, "DFS start CAC on %d MHz%s", iface->freq,
+                  dfs_use_radar_background(iface) ? " (background)" : "");
        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,
@@ -877,13 +891,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),
+               dfs_use_radar_background(iface));
 
        if (res) {
                wpa_printf(MSG_ERROR, "DFS start_dfs_cac() failed, %d", res);
                return -1;
        }
 
+       if (dfs_use_radar_background(iface)) {
+               /* Cache background radar parameters. */
+               iface->radar_background.channel = iface->conf->channel;
+               iface->radar_background.secondary_channel =
+                       iface->conf->secondary_channel;
+               iface->radar_background.freq = iface->freq;
+               iface->radar_background.centr_freq_seg0_idx =
+                       hostapd_get_oper_centr_freq_seg0_idx(iface->conf);
+               iface->radar_background.centr_freq_seg1_idx =
+                       hostapd_get_oper_centr_freq_seg1_idx(iface->conf);
+
+               /*
+                * Let's select a random channel according to the
+                * regulations and perform CAC on dedicated radar chain.
+                */
+               res = dfs_set_valid_channel(iface, 1);
+               if (res < 0)
+                       return res;
+
+               iface->radar_background.temp_ch = 1;
+               return 1;
+       }
+
        return 0;
 }
 
@@ -905,6 +943,86 @@ int hostapd_is_dfs_chan_available(struct hostapd_iface *iface)
 }
 
 
+static void hostpad_dfs_update_background_chain(struct hostapd_iface *iface)
+{
+       int sec = 0;
+       enum dfs_channel_type channel_type = DFS_NO_CAC_YET;
+       struct hostapd_channel_data *channel;
+       u8 oper_centr_freq_seg0_idx = 0;
+       u8 oper_centr_freq_seg1_idx = 0;
+
+       /*
+        * Allow selection of DFS channel in ETSI to comply with
+        * uniform spreading.
+        */
+       if (iface->dfs_domain == HOSTAPD_DFS_REGION_ETSI)
+               channel_type = DFS_ANY_CHANNEL;
+
+       channel = dfs_get_valid_channel(iface, &sec, &oper_centr_freq_seg0_idx,
+                                       &oper_centr_freq_seg1_idx,
+                                       channel_type);
+       if (!channel ||
+           channel->chan == iface->conf->channel ||
+           channel->chan == iface->radar_background.channel)
+               channel = dfs_downgrade_bandwidth(iface, &sec,
+                                                 &oper_centr_freq_seg0_idx,
+                                                 &oper_centr_freq_seg1_idx,
+                                                 &channel_type);
+       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),
+                                 oper_centr_freq_seg0_idx,
+                                 oper_centr_freq_seg1_idx, true)) {
+               wpa_printf(MSG_ERROR, "DFS failed to start CAC offchannel");
+               iface->radar_background.channel = -1;
+               return;
+       }
+
+       iface->radar_background.channel = channel->chan;
+       iface->radar_background.freq = channel->freq;
+       iface->radar_background.secondary_channel = sec;
+       iface->radar_background.centr_freq_seg0_idx = oper_centr_freq_seg0_idx;
+       iface->radar_background.centr_freq_seg1_idx = oper_centr_freq_seg1_idx;
+
+       wpa_printf(MSG_DEBUG,
+                  "%s: setting background chain to chan %d (%d MHz)",
+                  __func__, channel->chan, channel->freq);
+}
+
+
+static bool
+hostapd_dfs_is_background_event(struct hostapd_iface *iface, int freq)
+{
+       return dfs_use_radar_background(iface) &&
+               iface->radar_background.channel != -1 &&
+               iface->radar_background.freq == freq;
+}
+
+
+static int
+hostapd_dfs_start_channel_switch_background(struct hostapd_iface *iface)
+{
+       iface->conf->channel = iface->radar_background.channel;
+       iface->freq = iface->radar_background.freq;
+       iface->conf->secondary_channel =
+               iface->radar_background.secondary_channel;
+       hostapd_set_oper_centr_freq_seg0_idx(
+               iface->conf, iface->radar_background.centr_freq_seg0_idx);
+       hostapd_set_oper_centr_freq_seg1_idx(
+               iface->conf, iface->radar_background.centr_freq_seg1_idx);
+
+       hostpad_dfs_update_background_chain(iface);
+       hostapd_disable_iface(iface);
+       hostapd_enable_iface(iface);
+
+       return 0;
+}
+
+
 int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq,
                             int ht_enabled, int chan_offset, int chan_width,
                             int cf1, int cf2)
@@ -925,6 +1043,22 @@ 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 background chain for the selected
+                        * channel. Perform CSA, move the main chain to the
+                        * selected channel and configure the background chain
+                        * to a new DFS channel.
+                        */
+                       if (hostapd_dfs_is_background_event(iface, freq)) {
+                               iface->radar_background.cac_started = 0;
+                               if (!iface->radar_background.temp_ch)
+                                       return 0;
+
+                               iface->radar_background.temp_ch = 0;
+                               return hostapd_dfs_start_channel_switch_background(iface);
+                       }
+
                        /*
                         * Just mark the channel available when CAC completion
                         * event is received in enabled state. CAC result could
@@ -941,6 +1075,9 @@ int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq,
                                iface->cac_started = 0;
                        }
                }
+       } else if (hostapd_dfs_is_background_event(iface, freq)) {
+               iface->radar_background.cac_started = 0;
+               hostpad_dfs_update_background_chain(iface);
        }
 
        return 0;
@@ -1249,9 +1386,14 @@ int hostapd_dfs_nop_finished(struct hostapd_iface *iface, int freq,
        set_dfs_state(iface, freq, ht_enabled, chan_offset, chan_width,
                      cf1, cf2, HOSTAPD_CHAN_DFS_USABLE);
 
-       /* Handle cases where all channels were initially unavailable */
-       if (iface->state == HAPD_IFACE_DFS && !iface->cac_started)
+       if (iface->state == HAPD_IFACE_DFS && !iface->cac_started) {
+               /* Handle cases where all channels were initially unavailable */
                hostapd_handle_dfs(iface);
+       } else if (dfs_use_radar_background(iface) &&
+                  iface->radar_background.channel == -1) {
+               /* Reset radar background chain if disabled */
+               hostpad_dfs_update_background_chain(iface);
+       }
 
        return 0;
 }
@@ -1289,17 +1431,24 @@ int hostapd_dfs_start_cac(struct hostapd_iface *iface, int freq,
                          int ht_enabled, int chan_offset, int chan_width,
                          int cf1, int cf2)
 {
-       /* This is called when the driver indicates that an offloaded DFS has
-        * started CAC. */
-       hostapd_set_state(iface, HAPD_IFACE_DFS);
+       if (hostapd_dfs_is_background_event(iface, freq)) {
+               iface->radar_background.cac_started = 1;
+       } else {
+               /* This is called when the driver indicates that an offloaded
+                * DFS has started CAC. */
+               hostapd_set_state(iface, HAPD_IFACE_DFS);
+               iface->cac_started = 1;
+       }
        /* TODO: How to check CAC time for ETSI weather channels? */
        iface->dfs_cac_ms = 60000;
        wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_CAC_START
                "freq=%d chan=%d chan_offset=%d width=%d seg0=%d "
-               "seg1=%d cac_time=%ds",
+               "seg1=%d cac_time=%ds%s",
                freq, (freq - 5000) / 5, chan_offset, chan_width, cf1, cf2,
-               iface->dfs_cac_ms / 1000);
-       iface->cac_started = 1;
+               iface->dfs_cac_ms / 1000,
+               hostapd_dfs_is_background_event(iface, freq) ?
+               " (background)" : "");
+
        os_get_reltime(&iface->dfs_cac_start);
        return 0;
 }
index c797a52ecfd9f71f9c36a3ce9b0fab503e09d380..b30aa2ff60fbf1f308b6c5c2c9ba9466d7db73de 100644 (file)
@@ -522,6 +522,21 @@ struct hostapd_iface {
        int *basic_rates;
        int freq;
 
+       /* Background radar 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_background;
+
        u16 hw_flags;
 
        /* Number of associated Non-ERP stations (i.e., stations using 802.11b