[PATCH 3/7] ap: support BW changes via CLI

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



From: Johannes Berg <johannes.berg@xxxxxxxxx>

At least for testing, it's useful to be able to change the bandwidth
on the fly without doing a full channel switch announcement, by just
rebuilding the HT/VHT operation elements. Add support for a command
("SET_BW") to do just that.

Co-developed-by: Ilan Peer <ilan.peer@xxxxxxxxx>
Signed-off-by: Ilan Peer <ilan.peer@xxxxxxxxx>
Signed-off-by: Johannes Berg <johannes.berg@xxxxxxxxx>
Signed-off-by: Benjamin Berg <benjamin.berg@xxxxxxxxx>
---
 hostapd/ctrl_iface.c              | 53 +++++++++++++++++++++++++++++++
 hostapd/hostapd_cli.c             | 36 +++++++++++++++++++++
 src/ap/ctrl_iface_ap.c            |  2 ++
 src/ap/hostapd.c                  |  8 ++---
 src/ap/hostapd.h                  |  4 +++
 src/drivers/driver.h              |  3 ++
 src/drivers/driver_nl80211_capa.c |  4 ++-
 tests/hwsim/test_ap_ht.py         | 41 ++++++++++++++++++++++++
 tests/hwsim/test_ap_vht.py        | 38 ++++++++++++++++++++++
 9 files changed, 184 insertions(+), 5 deletions(-)

diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
index 3b410ac77a..db6e44a61b 100644
--- a/hostapd/ctrl_iface.c
+++ b/hostapd/ctrl_iface.c
@@ -3046,6 +3046,55 @@ static char hostapd_ctrl_iface_notify_cw_change(struct hostapd_data *hapd,
 }
 
 
+static int hostapd_ctrl_iface_set_bw(struct hostapd_iface *iface, char *pos)
+{
+#ifdef NEED_AP_MLME
+	struct hostapd_freq_params freq_params;
+	int ret;
+	enum oper_chan_width chanwidth;
+	u8 chan, oper_class;
+
+	if (!(iface->drv_flags2 & WPA_DRIVER_FLAGS2_AP_CHANWIDTH_CHANGE))
+		return -1;
+
+	ret = hostapd_parse_freq_params(pos, &freq_params, iface->freq);
+	if (ret)
+		return ret;
+
+	chanwidth = hostapd_chan_width_from_freq_params(&freq_params);
+
+	if (ieee80211_freq_to_channel_ext(
+		    freq_params.freq,
+		    freq_params.sec_channel_offset,
+		    chanwidth, &oper_class,
+		    &chan) == NUM_HOSTAPD_MODES) {
+		wpa_printf(MSG_DEBUG,
+			   "invalid channel: (freq=%d, sec_channel_offset=%d, vht_enabled=%d, he_enabled=%d)",
+			   freq_params.freq,
+			   freq_params.sec_channel_offset,
+			   freq_params.vht_enabled,
+			   freq_params.he_enabled);
+		return -1;
+	}
+
+	freq_params.channel = chan;
+
+	/* FIXME: what if the newly extended channel overlaps radar ranges? */
+
+	ret = hostapd_change_config_freq(iface->bss[0], iface->conf,
+					 &freq_params, NULL);
+	if (ret)
+		return ret;
+
+	ieee802_11_set_beacons(iface);
+	return 0;
+
+#else /* NEED_AP_MLME */
+	return -1;
+#endif /* NEED_AP_MLME */
+}
+
+
 static int hostapd_ctrl_iface_mib(struct hostapd_data *hapd, char *reply,
 				  int reply_size, const char *param)
 {
@@ -4346,6 +4395,10 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
 	} else if (os_strncmp(buf, "REGISTER_FRAME ", 15) == 0) {
 		if (hostapd_ctrl_register_frame(hapd, buf + 16) < 0)
 			reply_len = -1;
+	} else if (os_strncmp(buf, "SET_BW ", 7) == 0) {
+		/* note: preserve the space for hostapd_parse_freq_params() */
+		if (hostapd_ctrl_iface_set_bw(hapd->iface, buf + 6))
+			reply_len = -1;
 #endif /* CONFIG_TESTING_OPTIONS */
 	} else if (os_strncmp(buf, "CHAN_SWITCH ", 12) == 0) {
 		if (hostapd_ctrl_iface_chan_switch(hapd->iface, buf + 12))
diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
index 57702d93b2..0f6ce5c8b1 100644
--- a/hostapd/hostapd_cli.c
+++ b/hostapd/hostapd_cli.c
@@ -1231,6 +1231,36 @@ static int hostapd_cli_cmd_notify_cw_change(struct wpa_ctrl *ctrl,
 }
 
 
+static int hostapd_cli_cmd_set_bw(struct wpa_ctrl *ctrl,
+				  int argc, char *argv[])
+{
+	char cmd[256];
+	int res;
+	int i;
+	char *tmp;
+	int total;
+
+	res = os_snprintf(cmd, sizeof(cmd), "SET_BW");
+	if (os_snprintf_error(sizeof(cmd), res)) {
+		printf("Too long CHAN_SWITCH command.\n");
+		return -1;
+	}
+
+	total = res;
+	for (i = 0; i < argc; i++) {
+		tmp = cmd + total;
+		res = os_snprintf(tmp, sizeof(cmd) - total, " %s", argv[i]);
+		if (os_snprintf_error(sizeof(cmd) - total, res)) {
+			printf("Too long SET_BW command.\n");
+			return -1;
+		}
+		total += res;
+	}
+
+	return wpa_ctrl_command(ctrl, cmd);
+}
+
+
 static int hostapd_cli_cmd_enable(struct wpa_ctrl *ctrl, int argc,
 				  char *argv[])
 {
@@ -1770,6 +1800,12 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
 #endif /* CONFIG_IEEE80211AX */
 	{ "notify_cw_change", hostapd_cli_cmd_notify_cw_change, NULL,
 	  "<channel_width> = 0 - 20 MHz, 1 - 40 MHz, 2 - 80 MHz, 3 - 160 MHz" },
+#ifdef CONFIG_TESTING_OPTIONS
+	{ "set_bw", hostapd_cli_cmd_set_bw, NULL,
+	  "[sec_channel_offset=] [center_freq1=]\n"
+	  "  [center_freq2=] [bandwidth=] [ht|vht]\n"
+	  "  = change channel bandwidth" },
+#endif
 	{ "hs20_wnm_notif", hostapd_cli_cmd_hs20_wnm_notif, NULL,
 	  "<addr> <url>\n"
 	  "  = send WNM-Notification Subscription Remediation Request" },
diff --git a/src/ap/ctrl_iface_ap.c b/src/ap/ctrl_iface_ap.c
index 002e7b4938..8072710957 100644
--- a/src/ap/ctrl_iface_ap.c
+++ b/src/ap/ctrl_iface_ap.c
@@ -1131,6 +1131,8 @@ int hostapd_parse_freq_params(const char *pos,
 			      struct hostapd_freq_params *params,
 			      unsigned int freq)
 {
+	memset(params, 0, sizeof(*params));
+
 	if (freq)
 		params->freq = freq;
 	else
diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
index cb7b199366..65df420a06 100644
--- a/src/ap/hostapd.c
+++ b/src/ap/hostapd.c
@@ -4448,10 +4448,10 @@ free_ap_params:
  * the same hw_mode. Any other changes to MAC parameters or provided settings
  * are not supported.
  */
-static int hostapd_change_config_freq(struct hostapd_data *hapd,
-				      struct hostapd_config *conf,
-				      struct hostapd_freq_params *params,
-				      struct hostapd_freq_params *old_params)
+int hostapd_change_config_freq(struct hostapd_data *hapd,
+			       struct hostapd_config *conf,
+			       struct hostapd_freq_params *params,
+			       struct hostapd_freq_params *old_params)
 {
 	int channel;
 	u8 seg0 = 0, seg1 = 0;
diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
index 129e0b5f79..1f5a21cbd7 100644
--- a/src/ap/hostapd.h
+++ b/src/ap/hostapd.h
@@ -845,6 +845,10 @@ void hostapd_event_sta_opmode_changed(struct hostapd_data *hapd, const u8 *addr,
 				      enum smps_mode smps_mode,
 				      enum chan_width chan_width, u8 rx_nss);
 
+int hostapd_change_config_freq(struct hostapd_data *hapd,
+			       struct hostapd_config *conf,
+			       struct hostapd_freq_params *params,
+			       struct hostapd_freq_params *old_params);
 #ifdef CONFIG_FST
 void fst_hostapd_fill_iface_obj(struct hostapd_data *hapd,
 				struct fst_wpa_obj *iface_obj);
diff --git a/src/drivers/driver.h b/src/drivers/driver.h
index 0a05323532..bea48af7d7 100644
--- a/src/drivers/driver.h
+++ b/src/drivers/driver.h
@@ -2417,6 +2417,9 @@ struct wpa_driver_capa {
 #define WPA_DRIVER_FLAGS2_P2P_FEATURE_V2	0x0000000002000000ULL
 /** Driver supports P2P PCC mode */
 #define WPA_DRIVER_FLAGS2_P2P_FEATURE_PCC_MODE	0x0000000004000000ULL
+/** Driver supports arbitrary channel width changes in AP mode */
+#define WPA_DRIVER_FLAGS2_AP_CHANWIDTH_CHANGE	0x0000000008000000ULL
+
 	u64 flags2;
 
 #define FULL_AP_CLIENT_STATE_SUPP(drv_flags) \
diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
index 88ee9e724a..2aba1f9a42 100644
--- a/src/drivers/driver_nl80211_capa.c
+++ b/src/drivers/driver_nl80211_capa.c
@@ -747,8 +747,10 @@ static void wiphy_info_feature_flags(struct wiphy_info_data *info,
 	if (flags & NL80211_FEATURE_NEED_OBSS_SCAN)
 		capa->flags |= WPA_DRIVER_FLAGS_OBSS_SCAN;
 
-	if (flags & NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE)
+	if (flags & NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE) {
 		capa->flags |= WPA_DRIVER_FLAGS_HT_2040_COEX;
+		capa->flags2 |= WPA_DRIVER_FLAGS2_AP_CHANWIDTH_CHANGE;
+	}
 
 	if (flags & NL80211_FEATURE_TDLS_CHANNEL_SWITCH) {
 		wpa_printf(MSG_DEBUG, "nl80211: TDLS channel switch");
diff --git a/tests/hwsim/test_ap_ht.py b/tests/hwsim/test_ap_ht.py
index 01e874fc8d..5d8e39ad6b 100644
--- a/tests/hwsim/test_ap_ht.py
+++ b/tests/hwsim/test_ap_ht.py
@@ -972,6 +972,47 @@ def test_ap_ht_40mhz_intolerant_sta(dev, apdev):
     if hapd.get_status_field("secondary_channel") != "-1":
         raise Exception("Unexpected secondary_channel (did not re-enable 40 MHz)")
 
+def test_ap_ht_20_40_switch(dev, apdev):
+    """Do a bandwidth switch from 20 to 40 MHz without doing a CSA"""
+    params = {"ssid": "ht_20_40",
+              "channel": "6",
+              "ht_capab": "[HT40-]"}
+    hapd = hostapd.add_ap(apdev[0], params)
+    if hapd.get_status_field("secondary_channel") != "-1":
+        raise Exception("Unexpected secondary_channel")
+
+    dev[0].connect("ht_20_40", key_mgmt="NONE", scan_freq="2437")
+    if hapd.get_status_field("secondary_channel") != "-1":
+        raise Exception("Unexpected secondary_channel")
+
+    hapd.request("SET_BW bandwidth=20 ht")
+    time.sleep(1)
+    if hapd.get_status_field("secondary_channel") != "0":
+        raise Exception("Unexpected secondary_channel")
+    sig = dev[0].request('SIGNAL_POLL').splitlines()
+    expected = (
+        'FREQUENCY=2437',
+        'WIDTH=20 MHz',
+        'CENTER_FRQ1=2437',
+    )
+    for e in expected:
+        if not e in sig:
+            raise Exception("%s not found in %r" % (e, sig))
+
+    hapd.request("SET_BW bandwidth=40 sec_channel_offset=-1 ht")
+    time.sleep(1)
+    if hapd.get_status_field("secondary_channel") != "-1":
+        raise Exception("Unexpected secondary_channel")
+    sig = dev[0].request('SIGNAL_POLL').splitlines()
+    expected = (
+        'FREQUENCY=2437',
+        'WIDTH=40 MHz',
+        'CENTER_FRQ1=2427',
+    )
+    for e in expected:
+        if not e in sig:
+            raise Exception("%s not found in %r" % (e, sig))
+
 def test_ap_ht_40mhz_intolerant_sta_deinit(dev, apdev):
     """Associated STA indicating 40 MHz intolerant and hostapd deinit"""
     clear_scan_cache(apdev[0])
diff --git a/tests/hwsim/test_ap_vht.py b/tests/hwsim/test_ap_vht.py
index d0bd6468ef..3149d51177 100644
--- a/tests/hwsim/test_ap_vht.py
+++ b/tests/hwsim/test_ap_vht.py
@@ -1339,3 +1339,41 @@ def test_ap_vht_csa_invalid(dev, apdev):
         time.sleep(1)
     finally:
         clear_regdom(hapd, dev)
+
+def test_ap_vht_bwswitch(dev, apdev):
+    """Do a bandwidth switch without a CSA"""
+    try:
+        params = {"ssid": "vht",
+                  "country_code": "FI",
+                  "hw_mode": "a",
+                  "channel": "36",
+                  "ht_capab": "[HT40+]",
+                  "ieee80211n": "1",
+                  "ieee80211ac": "1",
+                  "vht_oper_chwidth": "1",
+                  "vht_capab": "[MAX-MPDU-11454]",
+                  "vht_oper_centr_freq_seg0_idx": "42"}
+        hapd = hostapd.add_ap(apdev[0], params)
+
+        dev[0].connect("vht", key_mgmt="NONE", scan_freq='5180')
+
+        for request, expected in [
+            (None,
+             ('FREQUENCY=5180', 'WIDTH=80 MHz', 'CENTER_FRQ1=5210')),
+            ('center_freq1=5210 sec_channel_offset=1 bandwidth=40 ht vht',
+             ('FREQUENCY=5180', 'WIDTH=40 MHz', 'CENTER_FRQ1=5190')),
+            ('center_freq1=5210 sec_channel_offset=0 bandwidth=20 ht vht',
+             ('FREQUENCY=5180', 'WIDTH=20 MHz', 'CENTER_FRQ1=5180')),
+            ('bandwidth=80 sec_channel_offset=1 center_freq1=5210 ht vht',
+             ('FREQUENCY=5180', 'WIDTH=80 MHz', 'CENTER_FRQ1=5210')),
+        ]:
+            if request is not None:
+                if 'OK' not in hapd.request("SET_BW " + request):
+                    raise Exception("SET_BW request failed")
+            time.sleep(1)
+            sig = dev[0].request('SIGNAL_POLL').splitlines()
+            for e in expected:
+                if not e in sig:
+                    raise Exception("%s not found in %r" % (e, sig))
+    finally:
+        clear_regdom(hapd, dev)
-- 
2.50.1


_______________________________________________
Hostap mailing list
Hostap@xxxxxxxxxxxxxxxxxxx
http://lists.infradead.org/mailman/listinfo/hostap



[Index of Archives]     [Linux Wireless]     [Linux Kernel]     [ATH6KL]     [Linux Bluetooth]     [Linux Netdev]     [Kernel Newbies]     [IDE]     [Security]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux ATA RAID]     [Samba]     [Device Mapper]

  Powered by Linux