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