}
+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)
{
} 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);
}
+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[])
{
"<cs_count> <freq> [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,
+ "<channel_width> = 0 - 20 MHz, 1 - 40 MHz, 2 - 80 MHz, 3 - 160 MHz" },
{ "hs20_wnm_notif", hostapd_cli_cmd_hs20_wnm_notif, NULL,
"<addr> <url>\n"
" = send WNM-Notification Subscription Remediation Request" },
#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
#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