[PATCH wireless-next v3 03/21] wifi: nl80211: add SMD BSS Transition sub-state STA flags
From: Pooventhiran G
Date: Thu Nov 05 2026 - 15:13:40 EST
IEEE P802.11bn/D2.0, Aug 2026, subclause 11.3.1, defines three new MLD
state sub-states (4a, 4b, 4c) that apply when a non-AP MLD and its
associated SMD-ME are in state 4 and an SMD BSS Transition is
in progress.
Add the corresponding nl80211 STA flags to track the state changes:
- NL80211_STA_FLAG_SMD_PREP_TARGET - State 4a: non-AP MLD is prepared
with target AP MLD
- NL80211_STA_FLAG_SMD_EXEC_CURRENT - State 4b: ST execution active on
current AP MLD
- NL80211_STA_FLAG_SMD_DL_DRAIN - State 4c: DL draining period
Signed-off-by: Pooventhiran G <pooventhiran.g@xxxxxxxxxxxxxxxx>
---
include/uapi/linux/nl80211.h | 18 ++++++++++++++++++
net/wireless/nl80211.c | 18 ++++++++++++++----
2 files changed, 32 insertions(+), 4 deletions(-)
diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
index cfc1bcad13f0..81c16ea2fd34 100644
--- a/include/uapi/linux/nl80211.h
+++ b/include/uapi/linux/nl80211.h
@@ -366,6 +366,15 @@
* the AP MLD. %NL80211_ATTR_SMD_ROAM_STA is also set on the target AP MLD
* to indicate that it is part of ST Preparation; drivers must keep these
* links in powersave at this point until successful ST Execution.
+ *
+ * When a non-AP MLD undergoes ST, userspace can indicate the current state of
+ * ST at the current AP MLD and target AP MLD using the STA flags
+ * %NL80211_STA_FLAG_SMD_PREP_TARGET, %NL80211_STA_FLAG_SMD_EXEC_CURRENT, and
+ * %NL80211_STA_FLAG_SMD_DL_DRAIN.
+ * %NL80211_STA_FLAG_SMD_PREP_TARGET can be set in %NL80211_CMD_NEW_STATION
+ * on the target AP MLD while creating the station entry.
+ * %NL80211_STA_FLAG_SMD_{EXEC_CURRENT,DL_DRAIN}, on the other hand, can be
+ * set using %NL80211_CMD_SET_STATION on the current AP MLD.
*/
/**
@@ -4035,6 +4044,12 @@ enum nl80211_iftype {
* @NL80211_STA_FLAG_SPP_AMSDU: station supports SPP A-MSDUs
* @NL80211_STA_FLAG_CIP: station has Control Integrity Protocol (CIP) enabled
* @NL80211_STA_FLAG_SMD: station has negotiated SMD.
+ * @NL80211_STA_FLAG_SMD_PREP_TARGET: station is prepared at SMD target AP MLD
+ * (State 4a).
+ * @NL80211_STA_FLAG_SMD_EXEC_CURRENT: current AP MLD is executing SMD BSS
+ * Transition (State 4b).
+ * @NL80211_STA_FLAG_SMD_DL_DRAIN: current AP MLD is in DL draining period
+ * (State 4c).
* @NL80211_STA_FLAG_MAX: highest station flag number currently defined
* @__NL80211_STA_FLAG_AFTER_LAST: internal use
*/
@@ -4050,6 +4065,9 @@ enum nl80211_sta_flags {
NL80211_STA_FLAG_SPP_AMSDU,
NL80211_STA_FLAG_CIP,
NL80211_STA_FLAG_SMD,
+ NL80211_STA_FLAG_SMD_PREP_TARGET,
+ NL80211_STA_FLAG_SMD_EXEC_CURRENT,
+ NL80211_STA_FLAG_SMD_DL_DRAIN,
/* keep last */
__NL80211_STA_FLAG_AFTER_LAST,
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index b06772ec8ac5..5df145ab4ab1 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -8025,6 +8025,9 @@ static const struct nla_policy sta_flags_policy[NL80211_STA_FLAG_MAX + 1] = {
[NL80211_STA_FLAG_AUTHENTICATED] = { .type = NLA_FLAG },
[NL80211_STA_FLAG_TDLS_PEER] = { .type = NLA_FLAG },
[NL80211_STA_FLAG_SMD] = { .type = NLA_FLAG },
+ [NL80211_STA_FLAG_SMD_PREP_TARGET] = { .type = NLA_FLAG },
+ [NL80211_STA_FLAG_SMD_EXEC_CURRENT] = { .type = NLA_FLAG },
+ [NL80211_STA_FLAG_SMD_DL_DRAIN] = { .type = NLA_FLAG },
};
static int parse_station_flags(struct genl_info *info,
@@ -8091,7 +8094,11 @@ static int parse_station_flags(struct genl_info *info,
switch (iftype) {
case NL80211_IFTYPE_AP:
case NL80211_IFTYPE_AP_VLAN:
- params->sta_flags_mask = BIT(NL80211_STA_FLAG_SMD);
+ params->sta_flags_mask =
+ BIT(NL80211_STA_FLAG_SMD) |
+ BIT(NL80211_STA_FLAG_SMD_PREP_TARGET) |
+ BIT(NL80211_STA_FLAG_SMD_EXEC_CURRENT) |
+ BIT(NL80211_STA_FLAG_SMD_DL_DRAIN);
fallthrough;
case NL80211_IFTYPE_P2P_GO:
params->sta_flags_mask |= BIT(NL80211_STA_FLAG_AUTHORIZED) |
@@ -9308,7 +9315,7 @@ int cfg80211_check_station_change(struct wiphy *wiphy,
return -EINVAL;
/* When you run into this, adjust the code below for the new flag */
- BUILD_BUG_ON(NL80211_STA_FLAG_MAX != 10);
+ BUILD_BUG_ON(NL80211_STA_FLAG_MAX != 13);
switch (statype) {
case CFG80211_STA_MESH_PEER_KERNEL:
@@ -9404,7 +9411,10 @@ int cfg80211_check_station_change(struct wiphy *wiphy,
BIT(NL80211_STA_FLAG_MFP) |
BIT(NL80211_STA_FLAG_SPP_AMSDU) |
BIT(NL80211_STA_FLAG_CIP) |
- BIT(NL80211_STA_FLAG_SMD)))
+ BIT(NL80211_STA_FLAG_SMD) |
+ BIT(NL80211_STA_FLAG_SMD_PREP_TARGET) |
+ BIT(NL80211_STA_FLAG_SMD_EXEC_CURRENT) |
+ BIT(NL80211_STA_FLAG_SMD_DL_DRAIN)))
return -EINVAL;
/* but authenticated/associated only if driver handles it */
@@ -10128,7 +10138,7 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
return -EINVAL;
/* When you run into this, adjust the code below for the new flag */
- BUILD_BUG_ON(NL80211_STA_FLAG_MAX != 10);
+ BUILD_BUG_ON(NL80211_STA_FLAG_MAX != 13);
switch (wdev->iftype) {
case NL80211_IFTYPE_AP:
--
2.34.1