[PATCH] Trigger channel switch from driver

Shay Bar shay.bar at celeno.com
Wed Aug 26 09:25:30 EDT 2020


This patch add support for channel switch event from driver to hostap.
same as can be triggered from US via hostapd_cli chan_switch.
using the already existing NL80211_CMD_CHANNEL_SWITCH.

Signed-off-by: Aviad Brikman <aviad.brikman at celeno.com>
Signed-off-by: Shay Bar <shay.bar at celeno.com>
---
 src/ap/drv_callbacks.c             |  17 ++++
 src/ap/hostapd.c                   | 139 +++++++++++++++++++++++++++++
 src/ap/hostapd.h                   |   3 +-
 src/drivers/driver.h               |  10 +++
 src/drivers/driver_common.c        |   1 +
 src/drivers/driver_nl80211_event.c |  78 ++++++++++++++++
 6 files changed, 247 insertions(+), 1 deletion(-)

diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
index 36678e7a3..e809dbb2c 100644
--- a/src/ap/drv_callbacks.c
+++ b/src/ap/drv_callbacks.c
@@ -1697,6 +1697,18 @@ static void hostapd_event_dfs_cac_started(struct hostapd_data *hapd,
                              radar->cf1, radar->cf2);
 }

+
+static void hostapd_event_do_channel_switch(struct hostapd_data *hapd,
+                                           struct ch_switch *chs)
+{
+       wpa_printf(MSG_DEBUG, "Channel switch to freq %d, center freq1 %d, "
+                             "center freq2 %d, width %d, channel ofset %d, "
+                             "csa count %d",
+                  chs->freq, chs->cf1, chs->cf2, chs->ch_width,
+                  chs->ch_offset, chs->csa_count);
+
+       hostapd_do_channel_switch(hapd->iface, chs);
+}
 #endif /* NEED_AP_MLME */


@@ -1931,6 +1943,11 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
                                        data->ch_switch.cf2,
                                        event == EVENT_CH_SWITCH);
                break;
+       case EVENT_DO_CHANNEL_SWITCH:
+               if (!data)
+                       break;
+               hostapd_event_do_channel_switch(hapd, &data->ch_switch);
+               break;
        case EVENT_CONNECT_FAILED_REASON:
                if (!data)
                        break;
diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
index b37f49f9a..eec2d1d5f 100644
--- a/src/ap/hostapd.c
+++ b/src/ap/hostapd.c
@@ -3634,6 +3634,145 @@ hostapd_switch_channel_fallback(struct hostapd_iface *iface,
        hostapd_enable_iface(iface);
 }

+static int hostapd_check_freq_params(struct hostapd_freq_params *params)
+{
+       switch (params->bandwidth) {
+       case 20:
+               if (params->center_freq1 &&
+                   params->center_freq1 != params->freq)
+                       return -1;
+
+               if (params->center_freq2 || params->sec_channel_offset)
+                       return -1;
+               break;
+       case 40:
+               if (params->center_freq2 || !params->sec_channel_offset)
+                       return -1;
+
+               if (!params->center_freq1)
+                       break;
+
+               switch (params->sec_channel_offset) {
+               case 1:
+                       if (params->freq + 10 != params->center_freq1)
+                               return -1;
+                       break;
+               case -1:
+                       if (params->freq - 10 != params->center_freq1)
+                               return -1;
+                       break;
+               default:
+                       return -1;
+               }
+               break;
+       case 80:
+               if (!params->center_freq1)
+                       return 1;
+
+               if ((params->freq < params->center_freq1) &&
+                   (params->freq + 30 < params->center_freq1))
+                       return 1;
+
+               if ((params->freq > params->center_freq1) &&
+                   (params->freq- 30 > params->center_freq1))
+                       return 1;
+
+               /* Adjacent and overlapped are not allowed for 80+80 */
+               if (params->center_freq2 &&
+                   params->center_freq1 - params->center_freq2 <= 80 &&
+                   params->center_freq2 - params->center_freq1 <= 80)
+                       return 1;
+               break;
+       case 160:
+               if (!params->center_freq1)
+                       return -1;
+
+               if ((params->freq < params->center_freq1) &&
+                   (params->freq + 70 < params->center_freq1))
+                       return 1;
+
+               if ((params->freq > params->center_freq1) &&
+                   (params->freq - 70 > params->center_freq1))
+                       return 1;
+               break;
+       default:
+               return -1;
+       }
+
+       return 0;
+}
+
+#define nl80211_chan_width_to_mhz(width) (((width) <= CHAN_WIDTH_20 ? 20 : \
+                                         ((width) == CHAN_WIDTH_40 ? 40 : \
+                                         ((width) <= CHAN_WIDTH_80P80 ? 80 : \
+                                         ((width) == CHAN_WIDTH_160 ? 160 : \
+                                         0)))))
+
+int hostapd_do_channel_switch(struct hostapd_iface *iface,
+                             struct ch_switch *chs)
+{
+       struct csa_settings settings;
+       int ret;
+       unsigned int i;
+       int freq = chs->freq;
+       int center_freq1 = chs->cf1;
+       int center_freq2 = chs->cf2;
+       int ch_width = chs->ch_width;
+       int ch_offset = chs->ch_offset;
+       int csa_count = chs->csa_count;
+
+       wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO,
+                       "freq %d, center_freq1 %d, center_freq2 %d "
+                       "width %d, channel_offset %d, csa_count %d",
+               freq, center_freq1, center_freq2, ch_width, ch_offset,
+               csa_count);
+
+       if (freq == 0) {
+               wpa_printf(MSG_ERROR, "invalid freq provided");
+               return -1;
+       }
+
+       if ((ch_width < CHAN_WIDTH_20_NOHT) || (ch_width > CHAN_WIDTH_160)) {
+               wpa_printf(MSG_ERROR, "invalid chan_width (%d) provided",
+                          ch_width);
+               return -1;
+       }
+
+       memset(&settings, 0, sizeof(settings));
+
+       if (ch_width == CHAN_WIDTH_40)
+               settings.freq_params.sec_channel_offset = ch_offset;
+
+       settings.cs_count = csa_count;
+       settings.freq_params.freq = freq;
+       settings.freq_params.bandwidth = nl80211_chan_width_to_mhz(ch_width);
+       settings.freq_params.center_freq1 = center_freq1;
+       settings.freq_params.center_freq2 = center_freq2;
+
+       if (ch_width == CHAN_WIDTH_20_NOHT) {
+               settings.freq_params.ht_enabled = 0;
+               settings.freq_params.vht_enabled = 0;
+       } else {
+               settings.freq_params.ht_enabled = !!(iface->conf->ieee80211n);
+               settings.freq_params.vht_enabled = !!(iface->conf->ieee80211ac);
+       }
+
+       if (hostapd_check_freq_params(&settings.freq_params)) {
+               wpa_printf(MSG_INFO, "invalid frequency settings provided");
+               return -1;
+       }
+
+       for (i = 0; i < iface->num_bss; i++) {
+               ret = hostapd_switch_channel(iface->bss[i], &settings);
+               if (ret)
+                       wpa_printf(MSG_DEBUG, "Failed to switch channel for "
+                                             "dev %s, ret = %d",
+                                  iface->bss[i]->conf->iface, ret);
+       }
+
+       return 0;
+}
+
 #endif /* NEED_AP_MLME */


diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
index b70d13fba..ca23a280b 100644
--- a/src/ap/hostapd.h
+++ b/src/ap/hostapd.h
@@ -633,7 +633,8 @@ void hostapd_cleanup_cs_params(struct hostapd_data *hapd);
 void hostapd_periodic_iface(struct hostapd_iface *iface);
 int hostapd_owe_trans_get_info(struct hostapd_data *hapd);
 void hostapd_ocv_check_csa_sa_query(void *eloop_ctx, void *timeout_ctx);
-
+int hostapd_do_channel_switch(struct hostapd_iface *iface,
+                             struct ch_switch *chs);
 /* utils.c */
 int hostapd_register_probereq_cb(struct hostapd_data *hapd,
                                 int (*cb)(void *ctx, const u8 *sa,
diff --git a/src/drivers/driver.h b/src/drivers/driver.h
index e8defaba2..2e91e6a38 100644
--- a/src/drivers/driver.h
+++ b/src/drivers/driver.h
@@ -4838,6 +4838,14 @@ enum wpa_event_type {
         * Described in wpa_event_data.ch_switch
         */
        EVENT_CH_SWITCH_STARTED,
+
+       /**
+        * EVENT_DO_CHANNEL_SWITCH - AP or GO want hostapd to switch channel
+        *
+        * Described in wpa_event_data.ch_switch
+        * */
+       EVENT_DO_CHANNEL_SWITCH,
+
        /**
         * EVENT_WNM - Request WNM operation
         *
@@ -5696,6 +5704,7 @@ union wpa_event_data {
         * @ch_width: Channel width
         * @cf1: Center frequency 1
         * @cf2: Center frequency 2
+        * @csa_count: Number of beacons before the channel switch
         */
        struct ch_switch {
                int freq;
@@ -5704,6 +5713,7 @@ union wpa_event_data {
                enum chan_width ch_width;
                int cf1;
                int cf2;
+               int csa_count;
        } ch_switch;

        /**
diff --git a/src/drivers/driver_common.c b/src/drivers/driver_common.c
index a7ebe9566..5aa05b4c9 100644
--- a/src/drivers/driver_common.c
+++ b/src/drivers/driver_common.c
@@ -68,6 +68,7 @@ const char * event_to_string(enum wpa_event_type event)
        E2S(EAPOL_TX_STATUS);
        E2S(CH_SWITCH);
        E2S(CH_SWITCH_STARTED);
+       E2S(DO_CHANNEL_SWITCH);
        E2S(WNM);
        E2S(CONNECT_FAILED_REASON);
        E2S(DFS_RADAR_DETECTED);
diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c
index ce95e9cd3..c8fdc3362 100644
--- a/src/drivers/driver_nl80211_event.c
+++ b/src/drivers/driver_nl80211_event.c
@@ -618,6 +618,74 @@ static void mlme_event_ch_switch(struct wpa_driver_nl80211_data *drv,
 }


+static void mlme_event_do_channel_switch(struct wpa_driver_nl80211_data *drv,
+                                struct nlattr *ifindex, struct nlattr *freq,
+                                struct nlattr *type, struct nlattr *bw,
+                                struct nlattr *cf1, struct nlattr *cf2,
+                                struct nlattr *cs_count)
+{
+       struct i802_bss *bss;
+       union wpa_event_data data;
+       int chan_offset = 0;
+       int ifidx;
+
+       wpa_printf(MSG_DEBUG, "nl80211: Do channel switch event");
+
+       if (!freq)
+               return;
+
+       ifidx = nla_get_u32(ifindex);
+       bss = get_bss_ifindex(drv, ifidx);
+       if (bss == NULL) {
+               wpa_printf(MSG_WARNING, "nl80211: Unknown ifindex (%d) for "
+                                       "channel switch, ignoring", ifidx);
+               return;
+       }
+
+       if (type) {
+               enum nl80211_channel_type ch_type = nla_get_u32(type);
+
+               wpa_printf(MSG_DEBUG, "nl80211: Channel type: %d", ch_type);
+               switch (ch_type) {
+               case NL80211_CHAN_NO_HT:
+                       break;
+               case NL80211_CHAN_HT20:
+                       break;
+               case NL80211_CHAN_HT40PLUS:
+                       chan_offset = 1;
+                       break;
+               case NL80211_CHAN_HT40MINUS:
+                       chan_offset = -1;
+                       break;
+               }
+       } else if (bw && cf1) {
+               /* This can happen for example with VHT80 ch switch */
+               chan_offset = calculate_chan_offset(nla_get_u32(bw),
+                                                   nla_get_u32(freq),
+                                                   nla_get_u32(cf1),
+                                                   cf2 ? nla_get_u32(cf2) : 0);
+       } else {
+               wpa_printf(MSG_WARNING, "nl80211: Unknown secondary channel "
+                          "information - following channel definition "
+                          "calculations may fail");
+       }
+
+       os_memset(&data, 0, sizeof(data));
+       data.ch_switch.freq = nla_get_u32(freq);
+       data.ch_switch.ch_offset = chan_offset;
+       data.ch_switch.csa_count = nla_get_u32(cs_count);
+
+       if (bw)
+               data.ch_switch.ch_width = convert2width(nla_get_u32(bw));
+       if (cf1)
+               data.ch_switch.cf1 = nla_get_u32(cf1);
+       if (cf2)
+               data.ch_switch.cf2 = nla_get_u32(cf2);
+
+       wpa_supplicant_event(bss->ctx, EVENT_DO_CHANNEL_SWITCH, &data);
+}
+
+
 static void mlme_timeout_event(struct wpa_driver_nl80211_data *drv,
                               enum nl80211_commands cmd, struct nlattr *addr)
 {
@@ -2739,6 +2807,16 @@ static void do_process_drv_event(struct i802_bss *bss, int cmd,
                                     tb[NL80211_ATTR_CENTER_FREQ2],
                                     1);
                break;
+       case NL80211_CMD_CHANNEL_SWITCH:
+               mlme_event_do_channel_switch(drv,
+                                            tb[NL80211_ATTR_IFINDEX],
+                                            tb[NL80211_ATTR_WIPHY_FREQ],
+                                            tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE],
+                                            tb[NL80211_ATTR_CHANNEL_WIDTH],
+                                            tb[NL80211_ATTR_CENTER_FREQ1],
+                                            tb[NL80211_ATTR_CENTER_FREQ2],
+                                            tb[NL80211_ATTR_CH_SWITCH_COUNT]);
+               break;
        case NL80211_CMD_DISCONNECT:
                mlme_event_disconnect(drv, tb[NL80211_ATTR_REASON_CODE],
                                      tb[NL80211_ATTR_MAC],
--
2.17.1

________________________________
The information transmitted is intended only for the person or entity to which it is addressed and may contain confidential and/or privileged material. Any retransmission, dissemination, copying or other use of, or taking of any action in reliance upon this information is prohibited. If you received this in error, please contact the sender and delete the material from any computer. Nothing contained herein shall be deemed as a representation, warranty or a commitment by Celeno. No warranties are expressed or implied, including, but not limited to, any implied warranties of non-infringement, merchantability and fitness for a particular purpose.
________________________________




More information about the Hostap mailing list