diff --git a/src/drivers/driver.h b/src/drivers/driver.h index 01204bed2..338a56eeb 100644 --- a/src/drivers/driver.h +++ b/src/drivers/driver.h @@ -1877,6 +1877,17 @@ enum wnm_oper { WNM_SLEEP_TFS_IE_DEL /* AP delete the TFS IE */ }; +/* enum smps_mode - SMPS mode definitions */ +enum smps_mode { + SMPS_AUTOMATIC, + SMPS_OFF, + SMPS_DYNAMIC, + SMPS_STATIC, + + /* Keep last */ + SMPS_INVALID, +}; + /* enum chan_width - Channel width definitions */ enum chan_width { CHAN_WIDTH_20_NOHT, @@ -4568,6 +4579,12 @@ enum wpa_event_type { * indicates the completion of IEEE 802.11 association. */ EVENT_PORT_AUTHORIZED, + + /** + * EVENT_STATION_OPMODE_CHANGED - Notify STA's HT/VHT operation mode + * change event. + */ + EVENT_STATION_OPMODE_CHANGED, }; @@ -5373,6 +5390,22 @@ union wpa_event_data { /* For EVENT_EXTERNAL_AUTH */ struct external_auth external_auth; + + /** + * struct sta_opmode - Station's operation mode change event + * @addr: The station MAC address + * @smps_mode: SMPS mode of the station + * @chan_width: Channel width of the station + * @rx_nss: RX_NSS of the station + * + * This is used as data with EVENT_STATION_OPMODE_CHANGED. + */ + struct sta_opmode { + const u8 *addr; + enum smps_mode smps_mode; + enum chan_width chan_width; + u8 rx_nss; + } sta_opmode; }; /** diff --git a/src/drivers/driver_common.c b/src/drivers/driver_common.c index e4806ad5e..9de9693f5 100644 --- a/src/drivers/driver_common.c +++ b/src/drivers/driver_common.c @@ -84,6 +84,7 @@ const char * event_to_string(enum wpa_event_type event) E2S(DFS_PRE_CAC_EXPIRED); E2S(EXTERNAL_AUTH); E2S(PORT_AUTHORIZED); + E2S(STATION_OPMODE_CHANGED); } return "UNKNOWN"; diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c index dd9b001e4..fd9eac970 100644 --- a/src/drivers/driver_nl80211_event.c +++ b/src/drivers/driver_nl80211_event.c @@ -132,6 +132,7 @@ static const char * nl80211_command_to_string(enum nl80211_commands cmd) C2S(NL80211_CMD_ADD_TX_TS) C2S(NL80211_CMD_DEL_TX_TS) C2S(NL80211_CMD_EXTERNAL_AUTH) + C2S(NL80211_CMD_STA_OPMODE_CHANGED) default: return "NL80211_CMD_UNKNOWN"; } @@ -2246,6 +2247,76 @@ static void nl80211_port_authorized(struct wpa_driver_nl80211_data *drv, } +static void nl80211_sta_opmode_change_event(struct wpa_driver_nl80211_data *drv, + struct nlattr **tb) +{ + union wpa_event_data ed; + u8 smps_mode, max_bw; + + if (!tb[NL80211_ATTR_MAC] || + (!tb[NL80211_ATTR_CHANNEL_WIDTH] && + !tb[NL80211_ATTR_SMPS_MODE] && + !tb[NL80211_ATTR_NSS])) + return; + + ed.sta_opmode.smps_mode = SMPS_INVALID; + ed.sta_opmode.chan_width = CHAN_WIDTH_UNKNOWN; + ed.sta_opmode.rx_nss = 0xff; + ed.sta_opmode.addr = nla_data(tb[NL80211_ATTR_MAC]); + + if (tb[NL80211_ATTR_SMPS_MODE]) { + smps_mode = nla_get_u32(tb[NL80211_ATTR_SMPS_MODE]); + switch (smps_mode) { + case NL80211_SMPS_OFF: + ed.sta_opmode.smps_mode = SMPS_OFF; + break; + case NL80211_SMPS_STATIC: + ed.sta_opmode.smps_mode = SMPS_STATIC; + break; + case NL80211_SMPS_DYNAMIC: + ed.sta_opmode.smps_mode = SMPS_DYNAMIC; + break; + default: + ed.sta_opmode.smps_mode = SMPS_INVALID; + break; + } + } + + if (tb[NL80211_ATTR_CHANNEL_WIDTH]) { + max_bw = nla_get_u32(tb[NL80211_ATTR_CHANNEL_WIDTH]); + switch (max_bw) { + case NL80211_CHAN_WIDTH_20_NOHT: + ed.sta_opmode.chan_width = CHAN_WIDTH_20_NOHT; + break; + case NL80211_CHAN_WIDTH_20: + ed.sta_opmode.chan_width = CHAN_WIDTH_20; + break; + case NL80211_CHAN_WIDTH_40: + ed.sta_opmode.chan_width = CHAN_WIDTH_40; + break; + case NL80211_CHAN_WIDTH_80: + ed.sta_opmode.chan_width = CHAN_WIDTH_80; + break; + case NL80211_CHAN_WIDTH_80P80: + ed.sta_opmode.chan_width = CHAN_WIDTH_80P80; + break; + case NL80211_CHAN_WIDTH_160: + ed.sta_opmode.chan_width = CHAN_WIDTH_160; + break; + default: + ed.sta_opmode.chan_width = CHAN_WIDTH_UNKNOWN; + break; + + } + } + + if (tb[NL80211_ATTR_NSS]) + ed.sta_opmode.rx_nss = nla_get_u8(tb[NL80211_ATTR_NSS]); + + wpa_supplicant_event(drv->ctx, EVENT_STATION_OPMODE_CHANGED, &ed); +} + + static void do_process_drv_event(struct i802_bss *bss, int cmd, struct nlattr **tb) { @@ -2447,6 +2518,9 @@ static void do_process_drv_event(struct i802_bss *bss, int cmd, case NL80211_CMD_PORT_AUTHORIZED: nl80211_port_authorized(drv, tb); break; + case NL80211_CMD_STA_OPMODE_CHANGED: + nl80211_sta_opmode_change_event(drv, tb); + break; default: wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Ignored unknown event " "(cmd=%d)", cmd);