diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c index f90eb22c4..83efdee15 100644 --- a/hostapd/ctrl_iface.c +++ b/hostapd/ctrl_iface.c @@ -2715,6 +2715,155 @@ static int hostapd_ctrl_iface_chan_switch(struct hostapd_iface *iface, } +static u8 hostapd_maxnss(struct hostapd_data *hapd, struct sta_info *sta) +{ + u8 *mcs_set = NULL; + u16 mcs_map; + u8 ht_rx_nss = 0; + u8 vht_rx_nss = 1; + u8 mcs; + bool ht_supported = false; + bool vht_supported = false; + int i; + + if (sta->ht_capabilities && (sta->flags & WLAN_STA_HT)) { + mcs_set = sta->ht_capabilities->supported_mcs_set; + ht_supported = true; + } + + if (sta->vht_capabilities && (sta->flags & WLAN_STA_VHT)) { + mcs_map = le_to_host16( + sta->vht_capabilities->vht_supported_mcs_set.rx_map); + vht_supported = true; + } + + if (ht_supported && mcs_set) { + if (mcs_set[0]) + ht_rx_nss++; + if (mcs_set[1]) + ht_rx_nss++; + if (mcs_set[2]) + ht_rx_nss++; + if (mcs_set[3]) + ht_rx_nss++; + } + if (vht_supported) { + for (i = 7; i >= 0; i--) { + mcs = (mcs_map >> (2 * i)) & 0x03; + if (mcs != 0x03) { + vht_rx_nss = i + 1; + break; + } + } + } + + return ht_rx_nss > vht_rx_nss ? ht_rx_nss : vht_rx_nss; +} + + +static char hostapd_ctrl_iface_notify_cw_htaction(struct hostapd_data *hapd, + const u8 *addr, u8 width) +{ + u8 buf[3]; + char ret; + + width = width >= 1 ? 1 : 0; + + buf[0] = WLAN_ACTION_HT; + buf[1] = WLAN_HT_ACTION_NOTIFY_CHANWIDTH; + buf[2] = width; + + ret = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr, + buf, sizeof(buf)); + if (ret) + wpa_printf(MSG_DEBUG, + "Failed to send Notify Channel Width frame to " + MACSTR, MAC2STR(addr)); + + return ret; +} + + +static char hostapd_ctrl_iface_notify_cw_vhtaction(struct hostapd_data *hapd, + const u8 *addr, u8 width) +{ + u8 buf[3]; + char ret; + + buf[0] = WLAN_ACTION_VHT; + buf[1] = WLAN_VHT_ACTION_OPMODE_NOTIF; + buf[2] = width; + + ret = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr, + buf, sizeof(buf)); + if (ret) + wpa_printf(MSG_DEBUG, + "Failed to send Opeating Mode Notification frame to " + MACSTR, MAC2STR(addr)); + + return ret; +} + + +static char hostapd_ctrl_iface_notify_cw_change(struct hostapd_data *hapd, + const char *cmd) +{ + u8 cw, operating_mode = 0, nss; + struct sta_info *sta; + enum hostapd_hw_mode hw_mode; + + if (is_6ghz_freq(hapd->iface->freq)) { + wpa_printf(MSG_ERROR, "20/40 BSS coex not supported in 6 GHz"); + return -1; + } + + cw = atoi(cmd); + hw_mode = hapd->iface->current_mode->mode; + if ((hw_mode == HOSTAPD_MODE_IEEE80211G || + hw_mode == HOSTAPD_MODE_IEEE80211B) && + !(cw == 0 || cw == 1)) { + wpa_printf(MSG_ERROR, + "Channel width should be either 20 MHz or 40 MHz for 2.4 GHz band"); + return -1; + } + + switch (cw) { + case 0: + operating_mode = 0; + break; + case 1: + operating_mode = VHT_OPMODE_CHANNEL_40MHZ; + break; + case 2: + operating_mode = VHT_OPMODE_CHANNEL_80MHZ; + break; + case 3: + operating_mode = VHT_OPMODE_CHANNEL_160MHZ; + break; + default: + wpa_printf(MSG_ERROR, "Channel width should be between 0 to 3"); + return -1; + } + + for (sta = hapd->sta_list; sta; sta = sta->next) { + if ((sta->flags & WLAN_STA_VHT) && sta->vht_capabilities) { + nss = hostapd_maxnss(hapd, sta) - 1; + hostapd_ctrl_iface_notify_cw_vhtaction(hapd, sta->addr, + operating_mode | + (u8) (nss << 4)); + continue; + } + + if ((sta->flags & (WLAN_STA_HT | WLAN_STA_VHT)) == + WLAN_STA_HT && sta->ht_capabilities) + hostapd_ctrl_iface_notify_cw_htaction(hapd, sta->addr, + cw); + } + + return 0; +} + + static int hostapd_ctrl_iface_mib(struct hostapd_data *hapd, char *reply, int reply_size, const char *param) { @@ -3561,6 +3710,9 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd, } else if (os_strncmp(buf, "CHAN_SWITCH ", 12) == 0) { if (hostapd_ctrl_iface_chan_switch(hapd->iface, buf + 12)) reply_len = -1; + } else if (os_strncmp(buf, "NOTIFY_CW_CHANGE ", 17) == 0) { + if (hostapd_ctrl_iface_notify_cw_change(hapd, buf + 17)) + reply_len = -1; } else if (os_strncmp(buf, "VENDOR ", 7) == 0) { reply_len = hostapd_ctrl_iface_vendor(hapd, buf + 7, reply, reply_size); diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c index 646dfc598..01fe34fa0 100644 --- a/hostapd/hostapd_cli.c +++ b/hostapd/hostapd_cli.c @@ -1207,6 +1207,13 @@ static int hostapd_cli_cmd_chan_switch(struct wpa_ctrl *ctrl, } +static int hostapd_cli_cmd_notify_cw_change(struct wpa_ctrl *ctrl, + int argc, char *argv[]) +{ + return hostapd_cli_cmd(ctrl, "NOTIFY_CW_CHANGE", 1, argc, argv); +} + + static int hostapd_cli_cmd_enable(struct wpa_ctrl *ctrl, int argc, char *argv[]) { @@ -1679,6 +1686,8 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = { " [sec_channel_offset=] [center_freq1=]\n" " [center_freq2=] [bandwidth=] [blocktx] [ht|vht]\n" " = initiate channel switch announcement" }, + { "notify_cw_change", hostapd_cli_cmd_notify_cw_change, NULL, + " = 0 - 20 MHz, 1 - 40 MHz, 2 - 80 MHz, 3 - 160 MHz" }, { "hs20_wnm_notif", hostapd_cli_cmd_hs20_wnm_notif, NULL, " \n" " = send WNM-Notification Subscription Remediation Request" }, diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h index 805a4024f..110f2b1e1 100644 --- a/src/common/ieee802_11_defs.h +++ b/src/common/ieee802_11_defs.h @@ -678,6 +678,19 @@ #define WLAN_PA_FILS_DISCOVERY 34 #define WLAN_PA_LOCATION_MEASUREMENT_REPORT 47 +/* HT Action field values (IEEE P802.11-REVme/D4.0, 9.6.11.1, Table 9-491) */ +#define WLAN_HT_ACTION_NOTIFY_CHANWIDTH 0 +#define WLAN_HT_ACTION_SMPS 1 +#define WLAN_HT_ACTION_CSI 4 +#define WLAN_HT_ACTION_NONCOMPRESSED_BF 5 +#define WLAN_HT_ACTION_COMPRESSED_BF 6 +#define WLAN_HT_ACTION_ASEL_IDX_FEEDBACK 7 + +/* VHT Action field values (IEEE P802.11-REVme/D4.0, 9.6.22.1, Table 9-579) */ +#define WLAN_VHT_ACTION_COMPRESSED_BF 0 +#define WLAN_VHT_ACTION_GROUP_ID_MGMT 1 +#define WLAN_VHT_ACTION_OPMODE_NOTIF 2 + /* Protected Dual of Public Action frames (IEEE Std 802.11-2016, 9.6.11, * Table 9-332) */ #define WLAN_PROT_DSE_ENABLEMENT 1 @@ -1357,6 +1370,10 @@ struct ieee80211_ampe_ie { #define VHT_RX_NSS_MAX_STREAMS 8 +#define VHT_OPMODE_CHANNEL_40MHZ ((u8) BIT(0)) +#define VHT_OPMODE_CHANNEL_80MHZ ((u8) BIT(1)) +#define VHT_OPMODE_CHANNEL_160MHZ ((u8) BIT(1) | BIT(2)) + /* VHT operation information - channel widths */ #define CHANWIDTH_USE_HT 0 #define CHANWIDTH_80MHZ 1