1: a645b4bd44b2 < -: ------------ wifi: nl80211: Define Seamless Mobility Domain (SMD) device capability  2: 457790d60e69 < -: ------------ wifi: nl80211: Add kernel interfaces for Seamless Mobility Domain setup  3: 42cd5cda9e25 < -: ------------ wifi: cfg80211/mac80211: Configure AP with SMD capabilities  -: ------------ > 1: cdd41c8fc9c8 wifi: nl80211: define Seamless Mobility Domain (SMD) device capability  4: 3f836cc85d0c ! 2: 3df988a34e75 wifi: cfg80211/mac80211: Parse SMD parameters in STA addition/modification @@ Metadata Author: Aditya Sathish   ## Commit message ## - wifi: cfg80211/mac80211: Parse SMD parameters in STA addition/modification + wifi: nl80211/cfg80211: define interfaces for SMD setup and STA update  - When a non-AP MLD is getting added or having its capabilities updated, - handle the SMD parameters passed from userspace. Otherwise, drivers will - not be able to identify that the non-AP MLD has negotiated SMD with this - AP MLD during association nor can they support SMD BSS Transition if it - has been requested later by the non-AP MLD. + Define nl80211 and cfg80211 interfaces to support SMD setup for an AP + MLD and the non-AP STA MLD creation upon SMD negotiation. + + Allow only APs affiliated to an AP MLD to enable SMD while starting. + STA creation can happen in two ways: typical 802.11 association with SMD + negotiated or via SMD BSS Transition Preparation on the target AP MLD; + the latter should be marked explicitly to inform drivers so that they + can handle ST Execution and datapath switch once the ST procedure is + completed.  Signed-off-by: Aditya Sathish  + Co-developed-by: Rohan Dutta  + Signed-off-by: Rohan Dutta  Co-developed-by: Sidhanta Sahu  Signed-off-by: Sidhanta Sahu  - Co-developed-by: Pooventhiran G  Signed-off-by: Pooventhiran G   ## include/net/cfg80211.h ## +@@ include/net/cfg80211.h: struct cfg80211_s1g_short_beacon { + * @mbssid_config: AP settings for multiple bssid + * @s1g_long_beacon_period: S1G long beacon period + * @s1g_short_beacon: S1G short beacon data ++ * @smd: Seamless Mobility Domain (SMD) enabled + */ + struct cfg80211_ap_settings { + struct cfg80211_chan_def chandef; +@@ include/net/cfg80211.h: struct cfg80211_ap_settings { + struct cfg80211_mbssid_config mbssid_config; + u8 s1g_long_beacon_period; + struct cfg80211_s1g_short_beacon s1g_short_beacon; ++ bool smd; + }; +  +   @@ include/net/cfg80211.h: struct cfg80211_ttlm_params { u16 ulink[8]; }; @@ include/net/cfg80211.h: struct cfg80211_ttlm_params {  + * struct cfg80211_sta_smd_params - SMD parameters for an added station  + *  + * @smd_sta: SMD-negotiated STA -+ * @smd_identifier: SMD Identifier -+ * @dl_data_fwd: MSDU forwarding supported ++ * @roam_sta: station was created via SMD BSS Transition procedure  + */  +struct cfg80211_sta_smd_params {  + bool smd_sta; -+ u8 smd_identifier[ETH_ALEN]; -+ bool dl_data_fwd; ++ bool roam_sta;  +};  + /** @@ include/net/cfg80211.h: struct station_parameters {  /**  - ## include/net/mac80211.h ## -@@ include/net/mac80211.h: struct ieee80211_link_sta { - * @nan_sched: NAN peer schedule for this station. Valid only for NMI stations. - * @ext_mld_capa_ops: the MLD's extended MLD capabilities and operations - * NOTE: currently only tracked for AP STAs -+ * @smd_params: SMD parameters of the STA + ## include/uapi/linux/nl80211.h ## +@@ + * SMD capability in %NL80211_CMD_GET_WIPHY. + * %NL80211_CMD_START_AP carries %NL80211_ATTR_SMD_SUPPORT to indicate that + * the AP affiliated to an AP MLD is part of an SMD. ++ * %NL80211_CMD_NEW_STATION carries %NL80211_ATTR_SMD_SUPPORT to indicate ++ * that the non-AP MLD getting added has successfully negotiated SMD with ++ * 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. */ - struct ieee80211_sta { - u8 addr[ETH_ALEN] __aligned(2); -@@ include/net/mac80211.h: struct ieee80211_sta { - /* should only be accessed with the wiphy mutex held */ - struct ieee80211_nan_peer_sched *nan_sched;  -+ struct cfg80211_sta_smd_params smd_params; -+ - /* must be last */ - u8 drv_priv[] __aligned(sizeof(void *)); - }; - - ## include/uapi/linux/nl80211.h ## + /**  @@ include/uapi/linux/nl80211.h: enum nl80211_commands { - * %NL80211_EXT_FEATURE_SMD. - * Used with %NL80211_CMD_START_AP. See &enum nl80211_smd_params_attrs. * -+ * @NL80211_ATTR_PEER_SMD_ENABLED: Flag indicating a peer STA has SMD enabled. -+ * Used with %NL80211_CMD_NEW_STATION and %NL80211_CMD_SET_STATION. -+ * -+ * @NL80211_ATTR_PEER_SMD_MAC_ADDR: Binary attribute with the 6-byte MAC -+ * address of the SMD-capable peer STA. -+ * Used with %NL80211_CMD_NEW_STATION and %NL80211_CMD_SET_STATION. -+ * -+ * @NL80211_ATTR_PEER_SMD_DL_DATA_FORWARDING: Flag indicating the peer STA -+ * supports DL data forwarding within the SMD domain. -+ * Used with %NL80211_CMD_NEW_STATION and %NL80211_CMD_SET_STATION. -+ * + * @NL80211_ATTR_SMD_SUPPORT: Flag attribute to indicate SMD support. Used with + * multiple commands to indicate appropriate SMD support (refer DOC). ++ * @NL80211_ATTR_SMD_ROAM_STA: Flag attribute to indicate that the station is ++ * created as part of SMD BSS Transition and not by typical association. + * * @NUM_NL80211_ATTR: total number of nl80211_attrs available * @NL80211_ATTR_MAX: highest attribute number currently defined - * @__NL80211_ATTR_AFTER_LAST: internal use  @@ include/uapi/linux/nl80211.h: enum nl80211_attrs { + NL80211_ATTR_ASSOC_PROTECTED_TWT,  - NL80211_ATTR_SMD_PARAMS, + NL80211_ATTR_SMD_SUPPORT, ++ NL80211_ATTR_SMD_ROAM_STA,  -+ NL80211_ATTR_PEER_SMD_ENABLED, -+ NL80211_ATTR_PEER_SMD_MAC_ADDR, -+ NL80211_ATTR_PEER_SMD_DL_DATA_FORWARDING, -+ /* add attributes here, update the policy in nl80211.c */  - __NL80211_ATTR_AFTER_LAST,  @@ include/uapi/linux/nl80211.h: enum nl80211_iftype { - * that support %NL80211_FEATURE_FULL_AP_CLIENT_STATE to transition a * previously added station into associated state * @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_MAX: highest station flag number currently defined * @__NL80211_STA_FLAG_AFTER_LAST: internal use */  @@ include/uapi/linux/nl80211.h: enum nl80211_sta_flags { - NL80211_STA_FLAG_TDLS_PEER, NL80211_STA_FLAG_ASSOCIATED, NL80211_STA_FLAG_SPP_AMSDU, + NL80211_STA_FLAG_CIP,  + NL80211_STA_FLAG_SMD,  /* keep last */ __NL80211_STA_FLAG_AFTER_LAST,  - ## net/mac80211/cfg.c ## -@@ net/mac80211/cfg.c: static int sta_apply_parameters(struct ieee80211_local *local, - if (mask & BIT(NL80211_STA_FLAG_SPP_AMSDU)) - sta->sta.spp_amsdu = set & BIT(NL80211_STA_FLAG_SPP_AMSDU); + ## net/wireless/nl80211.c ## +@@ net/wireless/nl80211.c: static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = { + [NL80211_ATTR_CIP_CAPABILITIES] = { .type = NLA_U8 }, + [NL80211_ATTR_ASSOC_PROTECTED_TWT] = { .type = NLA_FLAG }, + [NL80211_ATTR_SMD_SUPPORT] = { .type = NLA_FLAG }, ++ [NL80211_ATTR_SMD_ROAM_STA] = { .type = NLA_FLAG }, + };  -+ if (mask & BIT(NL80211_STA_FLAG_SMD)) { -+ if (set & BIT(NL80211_STA_FLAG_SMD)) -+ set_sta_flag(sta, WLAN_STA_SMD); -+ else if (test_sta_flag(sta, WLAN_STA_SMD)) -+ clear_sta_flag(sta, WLAN_STA_SMD); + /* policy for the key attributes */ +@@ net/wireless/nl80211.c: static int nl80211_check_npca(struct cfg80211_registered_device *rdev, + return -EINVAL; + } +  ++static int nl80211_validate_smd(struct genl_info *info, ++ struct cfg80211_ap_settings *params) ++{ ++ struct cfg80211_registered_device *rdev = info->user_ptr[0]; ++ struct net_device *dev = info->user_ptr[1]; ++ struct wireless_dev *wdev = dev->ieee80211_ptr; ++ ++ if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SMD)) { ++ GENL_SET_ERR_MSG(info, "SMD is not supported"); ++ return -EOPNOTSUPP; ++ } ++ ++ if (!wdev->valid_links) { ++ GENL_SET_ERR_MSG(info, "SMD is not allowed without MLO"); ++ return -EINVAL;  + }  + -+ if (params->smd_params.smd_sta) -+ sta->sta.smd_params = params->smd_params; ++ params->smd = true;  + - /* mark TDLS channel switch support, if the AP allows it */ - if (test_sta_flag(sta, WLAN_STA_TDLS_PEER) && - !sdata->deflink.u.mgd.tdls_chan_switch_prohibited && - - ## net/mac80211/debugfs_sta.c ## -@@ net/mac80211/debugfs_sta.c: static const char * const sta_flag_names[] = { - FLAG(PS_DELIVER), - FLAG(USES_ENCRYPTION), - FLAG(DECAP_OFFLOAD), -+ FLAG(SMD), - #undef FLAG - }; -  - - ## net/mac80211/sta_info.h ## -@@ - * @WLAN_STA_USES_ENCRYPTION: This station was configured for encryption, - * so drop all packets without a key later. - * @WLAN_STA_DECAP_OFFLOAD: This station uses rx decap offload -+ * @WLAN_STA_SMD: this station is associated to an SMD-ME. - * - * @NUM_WLAN_STA_FLAGS: number of defined flags - */ -@@ net/mac80211/sta_info.h: enum ieee80211_sta_info_flags { - WLAN_STA_PS_DELIVER, - WLAN_STA_USES_ENCRYPTION, - WLAN_STA_DECAP_OFFLOAD, -+ WLAN_STA_SMD, ++ return 0; ++} ++ + static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info) + { + struct cfg80211_registered_device *rdev = info->user_ptr[0]; +@@ net/wireless/nl80211.c: static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info)  - NUM_WLAN_STA_FLAGS, - }; - - ## net/wireless/nl80211.c ## -@@ net/wireless/nl80211.c: static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = { - [NL80211_ATTR_FRAME_NO_STA] = { .type = NLA_FLAG }, - [NL80211_ATTR_SMD_PARAMS] = - NLA_POLICY_NESTED(nl80211_smd_params_policy), -+ [NL80211_ATTR_PEER_SMD_ENABLED] = { .type = NLA_FLAG }, -+ [NL80211_ATTR_PEER_SMD_MAC_ADDR] = NLA_POLICY_ETH_ADDR, -+ [NL80211_ATTR_PEER_SMD_DL_DATA_FORWARDING] = { .type = NLA_FLAG }, - }; + /* FIXME: validate MLO/link-id against driver capabilities */  - /* policy for the key attributes */ ++ if (info->attrs[NL80211_ATTR_SMD_SUPPORT]) { ++ err = nl80211_validate_smd(info, params); ++ if (err) ++ goto out; ++ } ++ + err = rdev_start_ap(rdev, dev, params); + if (!err) { + wdev->links[link_id].ap.beacon_interval = params->beacon_interval;  @@ net/wireless/nl80211.c: static const struct nla_policy sta_flags_policy[NL80211_STA_FLAG_MAX + 1] = { [NL80211_STA_FLAG_MFP] = { .type = NLA_FLAG }, [NL80211_STA_FLAG_AUTHENTICATED] = { .type = NLA_FLAG }, @@ net/wireless/nl80211.c: 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 != 8); -+ BUILD_BUG_ON(NL80211_STA_FLAG_MAX != 9); +- BUILD_BUG_ON(NL80211_STA_FLAG_MAX != 9); ++ BUILD_BUG_ON(NL80211_STA_FLAG_MAX != 10);  switch (statype) { case CFG80211_STA_MESH_PEER_KERNEL:  @@ net/wireless/nl80211.c: int cfg80211_check_station_change(struct wiphy *wiphy, - BIT(NL80211_STA_FLAG_SHORT_PREAMBLE) | BIT(NL80211_STA_FLAG_WME) | BIT(NL80211_STA_FLAG_MFP) | -- BIT(NL80211_STA_FLAG_SPP_AMSDU))) -+ BIT(NL80211_STA_FLAG_SPP_AMSDU) | + BIT(NL80211_STA_FLAG_SPP_AMSDU) | +- BIT(NL80211_STA_FLAG_CIP))) ++ BIT(NL80211_STA_FLAG_CIP) |  + BIT(NL80211_STA_FLAG_SMD))) return -EINVAL;  @@ net/wireless/nl80211.c: static int nl80211_parse_sta_txpower_setting(struct genl return 0; }  -+static int nl80211_set_station_smd(struct genl_info *info, -+ struct station_parameters *params) ++static int nl80211_parse_sta_smd(struct genl_info *info, ++ struct station_parameters *params)  +{ -+ struct nlattr *smd_mac; -+  + params->smd_params.smd_sta = -+ !!info->attrs[NL80211_ATTR_PEER_SMD_ENABLED]; ++ !!info->attrs[NL80211_ATTR_SMD_SUPPORT];  +  + if (!params->smd_params.smd_sta)  + return 0;  + -+ smd_mac = info->attrs[NL80211_ATTR_PEER_SMD_MAC_ADDR]; -+ -+ if (!info->attrs[NL80211_ATTR_MLD_ADDR] || !smd_mac) ++ if (!info->attrs[NL80211_ATTR_MLD_ADDR])  + return -EINVAL;  + -+ nla_memcpy(params->smd_params.smd_identifier, smd_mac, ETH_ALEN); -+ -+ params->smd_params.dl_data_fwd = -+ !!info->attrs[NL80211_ATTR_PEER_SMD_DL_DATA_FORWARDING]; ++ params->smd_params.roam_sta = !!info->attrs[NL80211_ATTR_SMD_ROAM_STA];  +  + return 0;  +} @@ net/wireless/nl80211.c: static int nl80211_parse_sta_txpower_setting(struct genl static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; -@@ net/wireless/nl80211.c: static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info) - if (err) - return err; -  -+ err = nl80211_set_station_smd(info, ¶ms); -+ if (err) -+ return err; -+ - /* Include parameters for TDLS peer (will check later) */ - err = nl80211_set_station_tdls(info, ¶ms); - if (err)  @@ net/wireless/nl80211.c: static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) if (parse_station_flags(info, wdev->iftype, ¶ms)) return -EINVAL;  -+ err = nl80211_set_station_smd(info, ¶ms); ++ err = nl80211_parse_sta_smd(info, ¶ms);  + if (err)  + return err;  + @@ net/wireless/nl80211.c: static int nl80211_new_station(struct sk_buff *skb, stru return -EINVAL;  /* When you run into this, adjust the code below for the new flag */ -- BUILD_BUG_ON(NL80211_STA_FLAG_MAX != 8); -+ BUILD_BUG_ON(NL80211_STA_FLAG_MAX != 9); +- BUILD_BUG_ON(NL80211_STA_FLAG_MAX != 9); ++ BUILD_BUG_ON(NL80211_STA_FLAG_MAX != 10);  switch (wdev->iftype) { case NL80211_IFTYPE_AP:  5: ad7b3a2d544f < -: ------------ wifi: nl80211/cfg80211: Indicate STA creation via SMD BSS Transition  -: ------------ > 3: fde33a5067d0 wifi: nl80211: add SMD BSS Transition sub-state STA flags  9: 60c58e616ed0 ! 4: aa57cee0218e wifi: mac80211: Define layouts for SMD BSS Transition context @@ Metadata Author: Pooventhiran G   ## Commit message ## - wifi: mac80211: Define layouts for SMD BSS Transition context + wifi: cfg80211: define layouts for SMD BSS Transition context  IEEE P802.11bn/D2.0, Aug 2026, subclause 37.16.9, defines the context data to be transported during SMD BSS Transition (ST) laid out in @@ Commit message  Define the context data: per-TID sequence numbers (SN) in downlink (DL) and uplink (UL) directions, packet number (PN) in DL, per-TID packet - numbers in UL, and per-TID BlockAck session parameters in DL and UL; - along with this, define an optional driver context. This data is used by - drivers to report the context along with the frame SKB. + numbers in UL, per-TID BlockAck session parameters in DL and UL, + management SN in DL and UL, and management PN in UL. This data is used + by drivers to report the context along with the frame SKB.  Signed-off-by: Pooventhiran G   - ## include/linux/ieee80211-uhr.h ## -@@ include/linux/ieee80211-uhr.h: struct ieee80211_uhr_mode_change_tuple { - u8 variable[]; - } __packed; + ## include/net/cfg80211.h ## +@@ include/net/cfg80211.h: void cfg80211_incumbent_signal_notify(struct wiphy *wiphy, + u32 signal_interference_bitmap, + gfp_t gfp);   +/*  + * Context information carried in SMD BSS Transition (refer IEEE P802.11bn/D2.0,  + * Aug 2026, subclause 37.16.9.  + */ -+#define IEEE80211_SMD_CTX_NUM_VALID_CTX 8 -+ -+#define IEEE80211_SMD_CTX_VALID_DL_SN 0 -+#define IEEE80211_SMD_CTX_VALID_UL_SN 1 -+#define IEEE80211_SMD_CTX_VALID_PN 2 -+#define IEEE80211_SMD_CTX_VALID_BA_PARAMS 3 -+/* Positions 4 to 7 are reserved */  +  +/*  + * Data TIDs transported in the context, IEEE P802.11bn/D2.0, Aug 2026, @@ include/linux/ieee80211-uhr.h: struct ieee80211_uhr_mode_change_tuple {  + * @ext_buffer_size: ADDBA Extension buffer size; combined with @buffer_size as  + * (@ext_buffer_size << 10 | @buffer_size) to get the full reorder  + * buffer size ++ * @winstarto: WinStartO of the BA window  + */  +struct ieee80211_smd_ctx_ba {  + bool amsdu_supported; @@ include/linux/ieee80211-uhr.h: struct ieee80211_uhr_mode_change_tuple {  + bool ext_no_frag;  + u8 extfrag_level;  + u16 ext_buffer_size; ++ u16 winstarto;  +};  +  +/**  + * struct ieee80211_smd_ctx - IEEE 802.11bn SMD Roaming Context (refer  + * IEEE P802.11bn/D2.0, Aug 2026, subclause 37.16.9)  + * -+ * @valid_ctx_bmap: Bitmap indicating which context fields are valid; -+ * bit positions defined by IEEE80211_SMD_CTX_VALID_* constants ++ * @dl_sn_valid: if downlink (DL) SN Transfer is requested or not ++ * @ul_sn_valid: if uplink (UL) SN Transfer is requested or not ++ * @pn_valid: if PN is transferred or not ++ * @ba_params_valid: if BlockAck parameters are transferred or not ++ * @mgmt_tid_valid: if management information is transferred or not. ++ * @{d,u}l.mgmt_sn and @ul.mgmt_pn are still bounded by @{d,u}l_sn_valid ++ * and @pn_valid fields.  + * @pn_len: Length of PN in bytes; varies by cipher type  + * (e.g. CCMP (6), GCMP-256 (16)) -+ * @dl: Down-link context data -+ * @dl.valid_tid_bmap: valid DL TIDs for which context is present -+ * @dl.sn: DL SN per-TID to be assigned next -+ * @dl.pn: DL PN to be assigned next -+ * @dl.ba: DL BlockAck parameters per-TID for the BlockAck session -+ * @ul: Up-link context data -+ * @ul.valid_tid_bmap: valid UL TIDs for which context is present -+ * @ul.sn: UL SN per-TID to be checked next -+ * @ul.pn: UL PN per-TID to be checked next -+ * @ul.ba: UL BlockAck parameters per-TID for the BlockAck session -+ * @drv_ctx_size: Number of valid bytes in @drv_ctx. -+ * @drv_ctx: Variable-sized array of driver-specific context, counted by -+ * @drv_ctx_size. Opaque to the wireless core; interpreted by drivers. ++ * @dl: DL context data such as SN, PN, and BlockAck params. ++ * SN and PN transferred are offset to reserve space for DL frames sent by ++ * the current AP MLD so that the target AP MLD does not repeat them. ++ * @ul: UL context data such as SN, PN, and BlockAck params.  + */  +struct ieee80211_smd_ctx { -+ DECLARE_BITMAP(valid_ctx_bmap, IEEE80211_SMD_CTX_NUM_VALID_CTX); ++ u8 dl_sn_valid:1; ++ u8 ul_sn_valid:1; ++ u8 pn_valid:1; ++ u8 ba_params_valid:1; ++ u8 mgmt_tid_valid:1; ++  + u8 pn_len;  +  + struct { -+ DECLARE_BITMAP(valid_tid_bmap, IEEE80211_SMD_CTX_NUM_TIDS); ++ u8 valid_tid_bmap;  + u16 sn[IEEE80211_SMD_CTX_NUM_TIDS];  + u8 pn[IEEE80211_SMD_CTX_MAX_PN_LEN];  + struct ieee80211_smd_ctx_ba ba[IEEE80211_SMD_CTX_NUM_TIDS]; ++ u16 mgmt_sn;  + } dl;  + struct { -+ DECLARE_BITMAP(valid_tid_bmap, IEEE80211_SMD_CTX_NUM_TIDS); ++ u8 valid_tid_bmap;  + u16 sn[IEEE80211_SMD_CTX_NUM_TIDS];  + u8 pn[IEEE80211_SMD_CTX_NUM_TIDS][IEEE80211_SMD_CTX_MAX_PN_LEN];  + struct ieee80211_smd_ctx_ba ba[IEEE80211_SMD_CTX_NUM_TIDS]; ++ u16 mgmt_sn; ++ u8 mgmt_pn[IEEE80211_SMD_CTX_MAX_PN_LEN];  + } ul; -+ -+ size_t drv_ctx_size; -+ u8 drv_ctx[] ____cacheline_aligned __counted_by(drv_ctx_size);  +};  + - static inline int - ieee80211_uhr_mode_change_tuple_size(const struct ieee80211_uhr_mode_change_tuple *tuple) - { + #endif /* __NET_CFG80211_H */ 10: 14c15b3c39f1 ! 5: 51ec4d3a07e6 wifi: nl80211: Define attributes to pack SMD BSS Transition context @@ Metadata Author: Pooventhiran G   ## Commit message ## - wifi: nl80211: Define attributes to pack SMD BSS Transition context + wifi: nl80211: define interfaces for SMD BSS Transition context transfer  Define nl80211 attributes and policies required to pack SMD BSS Transition context along with NL80211_CMD_FRAME to be sent to userspace, and to set @@ Commit message  ## include/uapi/linux/nl80211.h ##  @@ - * @NL80211_CMD_STOP_PD: Stop the PD operation, identified by - * its %NL80211_ATTR_WDEV interface. - * + * 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. ++ * ++ * Drivers can use %NL80211_ATTR_SMD_CTX to pack the dynamic context of the ++ * non-AP MLD along with the ST Preparation or ST Execution Request frames in ++ * the %NL80211_CMD_FRAME event. ++ * ++ * On the target AP MLD, this collected context, received in the ++ * userspace-managed backhaul between the AP MLDs, can be programmed using ++ * %NL80211_CMD_SET_SMD_CTX. ++ * ++ * In case the non-AP MLD executes directly with the target AP MLD, the current ++ * AP MLD can request the context from the driver on the target's behalf using ++ * %NL80211_CMD_GET_SMD_CTX and the response can be reported back using ++ * %NL80211_CMD_SMD_CTX_EVENT. + */ +  + /** +@@ + * and multiple can be nested in %NL80211_ATTR_MLO_LINKS but then the + * update isn't atomic if any failures happen. + * Only supported with %NL80211_EXT_FEATURE_UPDATE_BEACON. ++ *  + * @NL80211_CMD_SET_SMD_CTX: Set the SMD BSS Transition dynamic context for a  + * non-AP MLD sent from the current AP MLD on the target AP MLD managed by  + * an SMD-ME. This command carries %NL80211_ATTR_MLD_ADDR, -+ * %NL80211_ATTR_SMD_CTX_TYPE and %NL80211_ATTR_SMD_CTX. ++ * %NL80211_ATTR_SMD_CTX_TYPE and %NL80211_ATTR_SMD_CTX. The station must ++ * be present on the target AP MLD in the "prepared" state with a default ++ * key installed.  + * @NL80211_CMD_GET_SMD_CTX: Get the SMD BSS Transition dynamic context for a  + * non-AP MLD associated to an AP MLD managed by an SMD-ME. This command  + * carries %NL80211_ATTR_MLD_ADDR and %NL80211_ATTR_SMD_CTX_TYPE. @@ include/uapi/linux/nl80211.h * @__NL80211_CMD_AFTER_LAST: internal use */  @@ include/uapi/linux/nl80211.h: enum nl80211_commands { - NL80211_CMD_START_PD, - NL80211_CMD_STOP_PD, +  + NL80211_CMD_UPDATE_BEACON,   + NL80211_CMD_SET_SMD_CTX,  + NL80211_CMD_GET_SMD_CTX, @@ include/uapi/linux/nl80211.h: enum nl80211_commands {  /* used to define NL80211_CMD_MAX below */  @@ include/uapi/linux/nl80211.h: enum nl80211_commands { - * IEEE P802.11bn/D2.0, Aug 2026, subclause 37.16.6). - * This flag is carried in %NL80211_CMD_NEW_STATION. + * @NL80211_ATTR_SMD_ROAM_STA: Flag attribute to indicate that the station is + * created as part of SMD BSS Transition and not by typical association. *  + * @NL80211_ATTR_SMD_CTX: Nested attribute associated with UHR SMD BSS -+ * Transition data. Used with %NL80211_CMD_SET_SMD_CTX and -+ * %NL80211_CMD_SMD_CTX_EVENT. See &enum nl80211_smd_ctx. ++ * Transition data. Used with %NL80211_CMD_FRAME, %NL80211_CMD_SET_SMD_CTX, ++ * and %NL80211_CMD_SMD_CTX_EVENT. See &enum nl80211_smd_ctx.  + * @NL80211_ATTR_SMD_CTX_TYPE: u8 attribute specifying the type of SMD context. -+ * Used along with %NL80211_CMD_SET_SMD_CTX, %NL80211_CMD_GET_SMD_CTX, -+ * and %NL80211_CMD_SMD_CTX_EVENT. ++ * Used along with %NL80211_CMD_FRAME, %NL80211_CMD_SET_SMD_CTX, ++ * %NL80211_CMD_GET_SMD_CTX, and %NL80211_CMD_SMD_CTX_EVENT.  + * * @NUM_NL80211_ATTR: total number of nl80211_attrs available * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use  @@ include/uapi/linux/nl80211.h: enum nl80211_attrs { -  + NL80211_ATTR_SMD_SUPPORT, NL80211_ATTR_SMD_ROAM_STA,   + NL80211_ATTR_SMD_CTX, @@ include/uapi/linux/nl80211.h: enum nl80211_attrs { /* add attributes here, update the policy in nl80211.c */  __NL80211_ATTR_AFTER_LAST, -@@ include/uapi/linux/nl80211.h: enum nl80211_smd_params_attrs { - NL80211_SMD_PARAMS_ATTR_MAX = __NL80211_SMD_PARAMS_ATTR_LAST - 1 - }; +@@ include/uapi/linux/nl80211.h: enum nl80211_nan_peer_map_attrs { +  + #define NL80211_NAN_SCHED_NOT_AVAIL_SLOT 0xff   +/**  + * enum nl80211_smd_ctx_type - SMD Request Type values @@ include/uapi/linux/nl80211.h: enum nl80211_smd_params_attrs {  + * @NL80211_SMD_CTX_BA_ATTR_EXT_FRAG_LEVEL: Required (u8) HE fragmentation  + * level.  + * @NL80211_SMD_CTX_BA_ATTR_EXT_BUFF_SIZE: Required (u16) extended buffer size. ++ * @NL80211_SMD_CTX_BA_ATTR_WINSTARTO: Required (u16) Starting Sequence Number ++ * of the BA window. ++ * Applicable only with %NL80211_SMD_CTX_DL_ATTR_BA_PARAMS.  + * @__NL80211_SMD_CTX_BA_ATTR_LAST: Last attribute, used internally.  + * @NL80211_SMD_CTX_BA_ATTR_MAX: Max attribute.  + * @@ include/uapi/linux/nl80211.h: enum nl80211_smd_params_attrs {  + NL80211_SMD_CTX_BA_ATTR_EXT_NO_FRAG,  + NL80211_SMD_CTX_BA_ATTR_EXT_FRAG_LEVEL,  + NL80211_SMD_CTX_BA_ATTR_EXT_BUFF_SIZE, ++ NL80211_SMD_CTX_BA_ATTR_WINSTARTO,  +  + __NL80211_SMD_CTX_BA_ATTR_LAST,  + NL80211_SMD_CTX_BA_ATTR_MAX = __NL80211_SMD_CTX_BA_ATTR_LAST - 1 @@ include/uapi/linux/nl80211.h: enum nl80211_smd_params_attrs {  + * DL individually addressed frames.  + * @NL80211_SMD_CTX_DL_ATTR_BA_PARAMS: Optional (nested) Block Ack parameters,  + * nested array of TIDs (uses &enum nl80211_smd_ctx_ba_param). ++ * @NL80211_SMD_CTX_DL_ATTR_MGMT_SN: Optional (u16) next SN to be assigned for ++ * DL individually addressed management frames. ++ * @NL80211_SMD_CTX_DL_ATTR_MGMT_PN: Optional (binary) starting PN ++ * (max 16 bytes) for DL individually addressed management frames.  + * @__NL80211_SMD_CTX_DL_ATTR_LAST: Last attribute, used internally.  + * @NL80211_SMD_CTX_DL_ATTR_MAX: Max attribute.  + * @@ include/uapi/linux/nl80211.h: enum nl80211_smd_params_attrs {  + NL80211_SMD_CTX_DL_ATTR_SN,  + NL80211_SMD_CTX_DL_ATTR_PN,  + NL80211_SMD_CTX_DL_ATTR_BA_PARAMS, ++ NL80211_SMD_CTX_DL_ATTR_MGMT_SN, ++ NL80211_SMD_CTX_DL_ATTR_MGMT_PN,  +  + __NL80211_SMD_CTX_DL_ATTR_LAST,  + NL80211_SMD_CTX_DL_ATTR_MAX = __NL80211_SMD_CTX_DL_ATTR_LAST - 1 @@ include/uapi/linux/nl80211.h: enum nl80211_smd_params_attrs {  + * for UL individually addressed frames.  + * @NL80211_SMD_CTX_UL_ATTR_BA_PARAMS: Optional (nested) Block Ack parameters  + * (uses &enum nl80211_smd_ctx_ba_param). ++ * @NL80211_SMD_CTX_UL_ATTR_MGMT_SN: Optional (u16) last SN received for ++ * UL individually addressed management frames. ++ * @NL80211_SMD_CTX_UL_ATTR_MGMT_PN: Optional (binary, max 16 bytes) last PN ++ * received for UL individually addressed management frames.  + * @__NL80211_SMD_CTX_UL_ATTR_LAST: Last attribute, used internally.  + * @NL80211_SMD_CTX_UL_ATTR_MAX: Max attribute.  + * @@ include/uapi/linux/nl80211.h: enum nl80211_smd_params_attrs {  + NL80211_SMD_CTX_UL_ATTR_SN,  + NL80211_SMD_CTX_UL_ATTR_PN,  + NL80211_SMD_CTX_UL_ATTR_BA_PARAMS, ++ NL80211_SMD_CTX_UL_ATTR_MGMT_SN, ++ NL80211_SMD_CTX_UL_ATTR_MGMT_PN,  +  + __NL80211_SMD_CTX_UL_ATTR_LAST,  + NL80211_SMD_CTX_UL_ATTR_MAX = __NL80211_SMD_CTX_UL_ATTR_LAST - 1 @@ include/uapi/linux/nl80211.h: enum nl80211_smd_params_attrs {  + * See &enum nl80211_smd_ctx_dl.  + * @NL80211_SMD_CTX_ATTR_UL: Optional (nested) UL parameters.  + * See &enum nl80211_smd_ctx_ul. -+ * @NL80211_SMD_CTX_ATTR_DRV_DATA: Optional (binary) driver-specific blob. -+ * Passed through nl80211 as a blob; parsed only at the driver layer of -+ * the current AP MLD and target AP MLD. First 3 bytes shall be driver OUI -+ * for the driver to parse as required.  + * @__NL80211_SMD_CTX_ATTR_LAST: Last attribute, used internally.  + * @NL80211_SMD_CTX_ATTR_MAX: Max attribute.  + */ @@ include/uapi/linux/nl80211.h: enum nl80211_smd_params_attrs {  + __NL80211_SMD_CTX_ATTR_INVALID,  + NL80211_SMD_CTX_ATTR_DL,  + NL80211_SMD_CTX_ATTR_UL, -+ NL80211_SMD_CTX_ATTR_DRV_DATA,  +  + __NL80211_SMD_CTX_ATTR_LAST,  + NL80211_SMD_CTX_ATTR_MAX = __NL80211_SMD_CTX_ATTR_LAST - 1 @@ include/uapi/linux/nl80211.h: enum nl80211_smd_params_attrs { #endif /* __LINUX_NL80211_H */  ## net/wireless/nl80211.c ## -@@ net/wireless/nl80211.c: nl80211_smd_params_policy[NL80211_SMD_PARAMS_ATTR_MAX + 1] = { - [NL80211_SMD_PARAMS_ATTR_NEIGHBOR_PROBING] = { .type = NLA_FLAG }, +@@ net/wireless/nl80211.c: nl80211_nan_conf_policy[NL80211_NAN_CONF_ATTR_MAX + 1] = { + [NL80211_NAN_CONF_INSTANT_COMM] = { .type = NLA_FLAG }, };   +static const struct nla_policy @@ net/wireless/nl80211.c: nl80211_smd_params_policy[NL80211_SMD_PARAMS_ATTR_MAX +  + [NL80211_SMD_CTX_BA_ATTR_EXT_NO_FRAG] = { .type = NLA_FLAG },  + [NL80211_SMD_CTX_BA_ATTR_EXT_FRAG_LEVEL] = { .type = NLA_U8 },  + [NL80211_SMD_CTX_BA_ATTR_EXT_BUFF_SIZE] = { .type = NLA_U16 }, ++ [NL80211_SMD_CTX_BA_ATTR_WINSTARTO] = { .type = NLA_U16 },  +};  +  +static const struct nla_policy @@ net/wireless/nl80211.c: nl80211_smd_params_policy[NL80211_SMD_PARAMS_ATTR_MAX +  + .len = IEEE80211_SMD_CTX_MAX_PN_LEN },  + [NL80211_SMD_CTX_DL_ATTR_BA_PARAMS] =  + NLA_POLICY_NESTED_ARRAY(nl80211_smd_ctx_ba_policy), ++ [NL80211_SMD_CTX_DL_ATTR_MGMT_SN] = { .type = NLA_U16 },  +};  +  +static const struct nla_policy @@ net/wireless/nl80211.c: nl80211_smd_params_policy[NL80211_SMD_PARAMS_ATTR_MAX +  + [NL80211_SMD_CTX_UL_ATTR_PN] = { .type = NLA_NESTED },  + [NL80211_SMD_CTX_UL_ATTR_BA_PARAMS] =  + NLA_POLICY_NESTED_ARRAY(nl80211_smd_ctx_ba_policy), ++ [NL80211_SMD_CTX_UL_ATTR_MGMT_SN] = { .type = NLA_U16 }, ++ [NL80211_SMD_CTX_UL_ATTR_MGMT_PN] = { ++ .type = NLA_BINARY, ++ .len = IEEE80211_SMD_CTX_MAX_PN_LEN ++ },  +};  +  +static const struct nla_policy @@ net/wireless/nl80211.c: nl80211_smd_params_policy[NL80211_SMD_PARAMS_ATTR_MAX +  + NLA_POLICY_NESTED(nl80211_smd_ctx_dl_policy),  + [NL80211_SMD_CTX_ATTR_UL] =  + NLA_POLICY_NESTED(nl80211_smd_ctx_ul_policy), -+ [NL80211_SMD_CTX_ATTR_DRV_DATA] = -+ NLA_POLICY_RANGE(NLA_BINARY, -+ 3, IEEE80211_MAX_DATA_LEN),  +};  + static const struct netlink_range_validation nl80211_punct_bitmap_range = { .min = 0, .max = 0xffff,  @@ net/wireless/nl80211.c: static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = { - [NL80211_ATTR_PEER_SMD_MAC_ADDR] = NLA_POLICY_ETH_ADDR, - [NL80211_ATTR_PEER_SMD_DL_DATA_FORWARDING] = { .type = NLA_FLAG }, + [NL80211_ATTR_ASSOC_PROTECTED_TWT] = { .type = NLA_FLAG }, + [NL80211_ATTR_SMD_SUPPORT] = { .type = NLA_FLAG }, [NL80211_ATTR_SMD_ROAM_STA] = { .type = NLA_FLAG },  + [NL80211_ATTR_SMD_CTX] = NLA_POLICY_NESTED(nl80211_smd_ctx_policy),  + [NL80211_ATTR_SMD_CTX_TYPE] =  -: ------------ > 6: 20a93b51eeab wifi: ieee80211: define APIs for UHR Link Reconfiguration frame checks  -: ------------ > 7: 3d03de0689db wifi: ieee80211: define API to check if frame is class 3 12: fe44e1d4d066 ! 8: e0b779b7d0ab wifi: nl80211: Pack SMD dynamic context along with frame @@ Metadata Author: Pooventhiran G   ## Commit message ## - wifi: nl80211: Pack SMD dynamic context along with frame + wifi: nl80211/cfg80211: pack SMD dynamic context along with frame  While packing the received management frame in nl80211_send_mgmt() to send to userspace, nest the SMD dynamic context along with the frame @@ Commit message Signed-off-by: Aditya Sathish  Signed-off-by: Pooventhiran G   + ## include/net/cfg80211.h ## +@@ include/net/cfg80211.h: struct mgmt_frame_regs { + u32 global_mcast_stypes, interface_mcast_stypes; + }; +  ++/** ++ * struct cfg80211_smd_transition_info - SMD BSS Transition info ++ * ++ * @ctx: Dynamic context to be transferred as part of ST ++ * @type: Type of ST indication ++ */ ++struct cfg80211_smd_transition_info { ++ struct ieee80211_smd_ctx *ctx; ++ enum nl80211_smd_ctx_type type; ++}; ++ + /** + * struct cfg80211_ops - backend description for wireless configuration + * +@@ include/net/cfg80211.h: void cfg80211_conn_failed(struct net_device *dev, const u8 *mac_addr, + * @rx_tstamp: Hardware timestamp of frame RX in nanoseconds + * @ack_tstamp: Hardware timestamp of ack TX in nanoseconds + * @no_sta: set if no station is known for the frame (relevant for MLD) ++ * @st_info: SMD BSS Transition data + */ + struct cfg80211_rx_info { + int freq; +@@ include/net/cfg80211.h: struct cfg80211_rx_info { + u64 rx_tstamp; + u64 ack_tstamp; + bool no_sta; ++ struct cfg80211_smd_transition_info st_info; + }; +  + /** + ## net/wireless/nl80211.c ##  @@ net/wireless/nl80211.c: nl80211_epcs_cfg(struct sk_buff *skb, struct genl_info *info) return rdev_set_epcs(rdev, dev, val); }  -+static size_t _nl80211_smd_ctx_nl_ba_param_size(u8 n_tids) ++static size_t _nl80211_smd_ctx_nl_ba_param_size(u8 n_tids, bool dl)  +{  + size_t n = 0;  + @@ net/wireless/nl80211.c: nl80211_epcs_cfg(struct sk_buff *skb, struct genl_info *  + nla_total_size(sizeof(u8)) + /* ext_frag_lvl */  + nla_total_size(sizeof(u16))); /* ext_buff_size */  + ++ if (dl) ++ n += n_tids * nla_total_size(sizeof(u16)); /* winstarto */ ++  + return n;  +}  + @@ net/wireless/nl80211.c: nl80211_epcs_cfg(struct sk_buff *skb, struct genl_info *  + size_t n = 0;  +  + /* count valid TIDs */ -+ n_dl_tids = bitmap_weight(ctx->dl.valid_tid_bmap, -+ IEEE80211_SMD_CTX_NUM_TIDS); -+ n_ul_tids = bitmap_weight(ctx->ul.valid_tid_bmap, -+ IEEE80211_SMD_CTX_NUM_TIDS); ++ n_dl_tids = hweight8(ctx->dl.valid_tid_bmap); ++ n_ul_tids = hweight8(ctx->ul.valid_tid_bmap);  +  + n += nla_total_size(sizeof(u8)); /* ST type */  + @@ net/wireless/nl80211.c: nl80211_epcs_cfg(struct sk_buff *skb, struct genl_info *  +  + n += nla_total_size(sizeof(u8)); /* valid_tid_bitmap */  + -+ if (test_bit(IEEE80211_SMD_CTX_VALID_DL_SN, ctx->valid_ctx_bmap)) { ++ if (ctx->dl_sn_valid) {  + n += nla_total_size(0); /* DL SN nest header */  + n += n_dl_tids * nla_total_size(sizeof(u16)); /* DL SN */ ++ if (ctx->mgmt_tid_valid) ++ n += nla_total_size(sizeof(u16)); /* DL mgmt_sn */  + }  + -+ if (test_bit(IEEE80211_SMD_CTX_VALID_PN, ctx->valid_ctx_bmap)) ++ if (ctx->pn_valid)  + n += nla_total_size(pn_len); /* DL PN */  + -+ if (test_bit(IEEE80211_SMD_CTX_VALID_BA_PARAMS, ctx->valid_ctx_bmap)) -+ n += _nl80211_smd_ctx_nl_ba_param_size(n_dl_tids); /* DL BA */ ++ if (ctx->ba_params_valid) ++ n += _nl80211_smd_ctx_nl_ba_param_size(n_dl_tids, true); /* DL BA */  +  + /* UL nest */  + n += nla_total_size(0); /* UL nest header */  +  + n += nla_total_size(sizeof(u8)); /* valid_tid_bitmap */  + -+ if (test_bit(IEEE80211_SMD_CTX_VALID_UL_SN, ctx->valid_ctx_bmap)) { ++ if (ctx->ul_sn_valid) {  + n += nla_total_size(0); /* UL SN nest header */  + n += n_ul_tids * nla_total_size(sizeof(u16)); /* UL SN */ ++ if (ctx->mgmt_tid_valid) ++ n += nla_total_size(sizeof(u16)); /* UL mgmt_sn */  + }  + -+ if (test_bit(IEEE80211_SMD_CTX_VALID_PN, ctx->valid_ctx_bmap)) { ++ if (ctx->pn_valid) {  + n += nla_total_size(0); /* UL PN nest header */  + n += n_ul_tids * nla_total_size(pn_len); /* UL PN */ ++ if (ctx->mgmt_tid_valid) ++ n += nla_total_size(pn_len); /* UL mgmt_pn */  + }  + -+ if (test_bit(IEEE80211_SMD_CTX_VALID_BA_PARAMS, ctx->valid_ctx_bmap)) -+ n += _nl80211_smd_ctx_nl_ba_param_size(n_ul_tids); /* UL BA */ -+ -+ if (ctx->drv_ctx_size) -+ n += nla_total_size(ctx->drv_ctx_size); /* Driver data */ ++ if (ctx->ba_params_valid) ++ n += _nl80211_smd_ctx_nl_ba_param_size(n_ul_tids, false); /* UL BA */  +  + return n;  +} @@ net/wireless/nl80211.c: nl80211_epcs_cfg(struct sk_buff *skb, struct genl_info *  + nla_put_u8(msg, NL80211_SMD_CTX_BA_ATTR_EXT_FRAG_LEVEL,  + ba->extfrag_level) ||  + nla_put_u16(msg, NL80211_SMD_CTX_BA_ATTR_EXT_BUFF_SIZE, -+ ba->ext_buffer_size)) { ++ ba->ext_buffer_size) || ++ (dl && nla_put_u16(msg, NL80211_SMD_CTX_BA_ATTR_WINSTARTO, ++ ba->winstarto))) {  + nla_nest_cancel(msg, tid_ba);  + goto nla_fail;  + } @@ net/wireless/nl80211.c: nl80211_epcs_cfg(struct sk_buff *skb, struct genl_info *  + struct nlattr *dl_sn = NULL, *ul_sn = NULL, *ul_pn = NULL;  + struct nlattr *smd_ctx = NULL, *dl = NULL, *ul = NULL;  + const struct ieee80211_smd_ctx *ctx; -+ u8 dl_tid_bmap = 0, ul_tid_bmap = 0; -+ bool pn_size_valid; ++ unsigned long dl_tid_bmap = 0, ul_tid_bmap = 0; ++ bool pn_valid;  + u8 tid;  +  + /* ST Type */ @@ net/wireless/nl80211.c: nl80211_epcs_cfg(struct sk_buff *skb, struct genl_info *  + ctx = st_info->ctx;  +  + /* Build DL context */ -+ dl_tid_bmap = bitmap_read(ctx->dl.valid_tid_bmap, 0, -+ IEEE80211_SMD_CTX_NUM_TIDS); ++ dl_tid_bmap = ctx->dl.valid_tid_bmap;  +  + dl = nla_nest_start(msg, NL80211_SMD_CTX_ATTR_DL);  + if (!dl)  + goto nla_fail;  +  + if (nla_put_u8(msg, NL80211_SMD_CTX_DL_ATTR_VALID_TID_BITMAP, -+ dl_tid_bmap)) ++ ctx->dl.valid_tid_bmap))  + goto nla_fail_dl;  + -+ if (test_bit(IEEE80211_SMD_CTX_VALID_DL_SN, ctx->valid_ctx_bmap)) { ++ if (ctx->dl_sn_valid) {  + dl_sn = nla_nest_start(msg, NL80211_SMD_CTX_DL_ATTR_SN);  + if (!dl_sn)  + goto nla_fail_dl;  + -+ for_each_set_bit(tid, ctx->dl.valid_tid_bmap, ++ for_each_set_bit(tid, &dl_tid_bmap,  + IEEE80211_SMD_CTX_NUM_TIDS) {  + if (nla_put_u16(msg, tid + 1, ctx->dl.sn[tid])) {  + nla_nest_cancel(msg, dl_sn); @@ net/wireless/nl80211.c: nl80211_epcs_cfg(struct sk_buff *skb, struct genl_info *  + nla_nest_end(msg, dl_sn);  + }  + -+ pn_size_valid = -+ ctx->pn_len && ctx->pn_len <= IEEE80211_SMD_CTX_MAX_PN_LEN; ++ pn_valid = (ctx->pn_len && ctx->pn_len <= IEEE80211_SMD_CTX_MAX_PN_LEN && ++ ctx->pn_valid);  + -+ if (pn_size_valid && -+ test_bit(IEEE80211_SMD_CTX_VALID_PN, ctx->valid_ctx_bmap) && ++ if (pn_valid &&  + nla_put(msg, NL80211_SMD_CTX_DL_ATTR_PN, ctx->pn_len, ctx->dl.pn))  + goto nla_fail_dl;  + -+ if (test_bit(IEEE80211_SMD_CTX_VALID_BA_PARAMS, ctx->valid_ctx_bmap) && -+ nl80211_put_smd_ctx_ba_params(msg, ctx->dl.valid_tid_bmap, ++ if (ctx->ba_params_valid && ++ nl80211_put_smd_ctx_ba_params(msg, &dl_tid_bmap,  + ctx->dl.ba, true))  + goto nla_fail_dl;  + ++ if (ctx->dl_sn_valid && ctx->mgmt_tid_valid && ++ nla_put_u16(msg, NL80211_SMD_CTX_DL_ATTR_MGMT_SN, ctx->dl.mgmt_sn)) ++ goto nla_fail_dl; ++  + nla_nest_end(msg, dl);  +  + /* Build UL context */ -+ ul_tid_bmap = bitmap_read(ctx->ul.valid_tid_bmap, 0, -+ IEEE80211_SMD_CTX_NUM_TIDS); ++ ul_tid_bmap = ctx->ul.valid_tid_bmap;  +  + ul = nla_nest_start(msg, NL80211_SMD_CTX_ATTR_UL);  + if (!ul)  + goto nla_fail;  +  + if (nla_put_u8(msg, NL80211_SMD_CTX_UL_ATTR_VALID_TID_BITMAP, -+ ul_tid_bmap)) ++ ctx->ul.valid_tid_bmap))  + goto nla_fail_ul;  + -+ if (test_bit(IEEE80211_SMD_CTX_VALID_UL_SN, ctx->valid_ctx_bmap)) { ++ if (ctx->ul_sn_valid) {  + ul_sn = nla_nest_start(msg, NL80211_SMD_CTX_UL_ATTR_SN);  + if (!ul_sn)  + goto nla_fail_ul;  + -+ for_each_set_bit(tid, ctx->ul.valid_tid_bmap, ++ for_each_set_bit(tid, &ul_tid_bmap,  + IEEE80211_SMD_CTX_NUM_TIDS) {  + if (nla_put_u16(msg, tid + 1, ctx->ul.sn[tid])) {  + nla_nest_cancel(msg, ul_sn); @@ net/wireless/nl80211.c: nl80211_epcs_cfg(struct sk_buff *skb, struct genl_info *  + nla_nest_end(msg, ul_sn);  + }  + -+ if (pn_size_valid && -+ test_bit(IEEE80211_SMD_CTX_VALID_PN, ctx->valid_ctx_bmap)) { ++ if (pn_valid) {  + ul_pn = nla_nest_start(msg, NL80211_SMD_CTX_UL_ATTR_PN);  + if (!ul_pn)  + goto nla_fail_ul;  + -+ for_each_set_bit(tid, ctx->ul.valid_tid_bmap, ++ for_each_set_bit(tid, &ul_tid_bmap,  + IEEE80211_SMD_CTX_NUM_TIDS) {  + if (nla_put(msg, tid + 1, ctx->pn_len,  + ctx->ul.pn[tid])) { @@ net/wireless/nl80211.c: nl80211_epcs_cfg(struct sk_buff *skb, struct genl_info *  + nla_nest_end(msg, ul_pn);  + }  + -+ if (test_bit(IEEE80211_SMD_CTX_VALID_BA_PARAMS, ctx->valid_ctx_bmap) && -+ nl80211_put_smd_ctx_ba_params(msg, ctx->ul.valid_tid_bmap, ++ if (ctx->ba_params_valid && ++ nl80211_put_smd_ctx_ba_params(msg, &ul_tid_bmap,  + ctx->ul.ba, false))  + goto nla_fail_ul;  + -+ nla_nest_end(msg, ul); ++ if (ctx->mgmt_tid_valid) { ++ if (ctx->ul_sn_valid && ++ nla_put_u16(msg, NL80211_SMD_CTX_UL_ATTR_MGMT_SN, ++ ctx->ul.mgmt_sn)) ++ goto nla_fail_ul;  + -+ /* Driver data blob */ -+ if (ctx->drv_ctx_size && -+ nla_put(msg, NL80211_SMD_CTX_ATTR_DRV_DATA, ctx->drv_ctx_size, -+ ctx->drv_ctx)) -+ goto nla_fail; ++ if (pn_valid && ++ nla_put(msg, NL80211_SMD_CTX_UL_ATTR_MGMT_PN, ++ ctx->pn_len, ctx->ul.mgmt_pn)) ++ goto nla_fail_ul; ++ } ++ ++ nla_nest_end(msg, ul);  +  + nla_nest_end(msg, smd_ctx);  + 13: ac70af58367d ! 9: 91dce91761a9 wifi: nl80211/cfg80211: Add support for SMD context programming @@ Metadata Author: Pooventhiran G   ## Commit message ## - wifi: nl80211/cfg80211: Add support for SMD context programming + wifi: nl80211/cfg80211: add support for SMD context programming  On the target AP MLD to which the STA will be transitioning, the dynamic context collected and sent by the current AP MLD needs to be programmed @@ net/wireless/nl80211.c: static int nl80211_put_smd_ctx(struct sk_buff *msg,   +static int  +nl80211_parse_smd_ctx_ba_params(struct nlattr *tb, -+ struct ieee80211_smd_ctx_ba *ba_tids, -+ unsigned long *valid_ctx_bmap) ++ struct ieee80211_smd_ctx_ba *ba_tids)  +{  + struct nlattr *ba_tb[NL80211_SMD_CTX_BA_ATTR_MAX + 1];  + struct ieee80211_smd_ctx_ba *ba; @@ net/wireless/nl80211.c: static int nl80211_put_smd_ctx(struct sk_buff *msg,  + int n_tids = 0;  + int rem, tid;  + -+ if (!tb) -+ return 0; -+  + nla_for_each_nested(ba_attr, tb, rem) {  + tid = nla_type(ba_attr) - 1;  + if (tid < 0 || tid >= IEEE80211_SMD_CTX_NUM_TIDS) @@ net/wireless/nl80211.c: static int nl80211_put_smd_ctx(struct sk_buff *msg,  + ba->ext_buffer_size =  + nla_get_u16(ba_tb[NL80211_SMD_CTX_BA_ATTR_EXT_BUFF_SIZE]);  + ++ if (ba_tb[NL80211_SMD_CTX_BA_ATTR_WINSTARTO]) ++ ba->winstarto = ++ nla_get_u16(ba_tb[NL80211_SMD_CTX_BA_ATTR_WINSTARTO]); ++  + n_tids++;  + }  +  + if (!n_tids)  + return -EINVAL;  + -+ set_bit(IEEE80211_SMD_CTX_VALID_BA_PARAMS, valid_ctx_bmap); -+  + return 0;  +}  + @@ net/wireless/nl80211.c: static int nl80211_put_smd_ctx(struct sk_buff *msg,  + struct nlattr *tb[NL80211_SMD_CTX_ATTR_MAX + 1];  + struct ieee80211_smd_ctx *ctx;  + struct nlattr *bmap_attr; -+ size_t drv_ctx_size = 0;  +  + st_info->type = nla_get_u8(info->attrs[NL80211_ATTR_SMD_CTX_TYPE]);  + @@ net/wireless/nl80211.c: static int nl80211_put_smd_ctx(struct sk_buff *msg,  + return -EINVAL;  + }  + -+ if (tb[NL80211_SMD_CTX_ATTR_DRV_DATA]) -+ drv_ctx_size = nla_len(tb[NL80211_SMD_CTX_ATTR_DRV_DATA]); -+ -+ ctx = kzalloc(sizeof(*ctx) + drv_ctx_size, GFP_KERNEL); ++ ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);  + if (!ctx)  + return -ENOMEM;  + @@ net/wireless/nl80211.c: static int nl80211_put_smd_ctx(struct sk_buff *msg,  +  + bmap_attr = dl_tb[NL80211_SMD_CTX_DL_ATTR_VALID_TID_BITMAP];  + if (bmap_attr) -+ bitmap_write(ctx->dl.valid_tid_bmap, -+ nla_get_u8(bmap_attr), -+ 0, IEEE80211_SMD_CTX_NUM_TIDS); ++ ctx->dl.valid_tid_bmap = nla_get_u8(bmap_attr);  +  + if (dl_tb[NL80211_SMD_CTX_DL_ATTR_SN]) {  + struct nlattr *sn_attr; @@ net/wireless/nl80211.c: static int nl80211_put_smd_ctx(struct sk_buff *msg,  + goto err_free_ctx;  + }  + -+ set_bit(IEEE80211_SMD_CTX_VALID_DL_SN, -+ ctx->valid_ctx_bmap); ++ ctx->dl_sn_valid = 1;  + }  +  + if (dl_tb[NL80211_SMD_CTX_DL_ATTR_PN]) { @@ net/wireless/nl80211.c: static int nl80211_put_smd_ctx(struct sk_buff *msg,  +  + ctx->pn_len =  + nla_len(dl_tb[NL80211_SMD_CTX_DL_ATTR_PN]); ++ if (ctx->pn_len > IEEE80211_SMD_CTX_MAX_PN_LEN) { ++ GENL_SET_ERR_MSG(info, "invalid DL PN length"); ++ goto err_free_ctx; ++ }  +  + memcpy(ctx->dl.pn, pn_data, ctx->pn_len); -+ set_bit(IEEE80211_SMD_CTX_VALID_PN, -+ ctx->valid_ctx_bmap); ++ ctx->pn_valid = 1;  + }  +  + ba_attr = dl_tb[NL80211_SMD_CTX_DL_ATTR_BA_PARAMS]; -+ if (ba_attr && -+ nl80211_parse_smd_ctx_ba_params(ba_attr, ctx->dl.ba, -+ ctx->valid_ctx_bmap)) { -+ GENL_SET_ERR_MSG(info, -+ "failed to parse DL BlockAck params"); -+ goto err_free_ctx; ++ if (ba_attr) { ++ if (nl80211_parse_smd_ctx_ba_params(ba_attr, ++ ctx->dl.ba)) { ++ GENL_SET_ERR_MSG(info, ++ "failed to parse DL BlockAck params"); ++ goto err_free_ctx; ++ } ++ ++ ctx->ba_params_valid = 1; ++ } ++ ++ if (dl_tb[NL80211_SMD_CTX_DL_ATTR_MGMT_SN]) { ++ ctx->dl.mgmt_sn = ++ nla_get_u16(dl_tb[NL80211_SMD_CTX_DL_ATTR_MGMT_SN]); ++ ctx->mgmt_tid_valid = 1; ++ ctx->dl_sn_valid = 1;  + }  + }  + @@ net/wireless/nl80211.c: static int nl80211_put_smd_ctx(struct sk_buff *msg,  +  + bmap_attr = ul_tb[NL80211_SMD_CTX_UL_ATTR_VALID_TID_BITMAP];  + if (bmap_attr) -+ bitmap_write(ctx->ul.valid_tid_bmap, -+ nla_get_u8(bmap_attr), -+ 0, IEEE80211_SMD_CTX_NUM_TIDS); ++ ctx->ul.valid_tid_bmap = nla_get_u8(bmap_attr);  +  + if (ul_tb[NL80211_SMD_CTX_UL_ATTR_SN]) {  + struct nlattr *sn_attr; @@ net/wireless/nl80211.c: static int nl80211_put_smd_ctx(struct sk_buff *msg,  + goto err_free_ctx;  + }  + -+ set_bit(IEEE80211_SMD_CTX_VALID_UL_SN, -+ ctx->valid_ctx_bmap); ++ ctx->ul_sn_valid = 1;  + }  +  + if (ul_tb[NL80211_SMD_CTX_UL_ATTR_PN]) { @@ net/wireless/nl80211.c: static int nl80211_put_smd_ctx(struct sk_buff *msg,  + goto err_free_ctx;  + }  + -+ set_bit(IEEE80211_SMD_CTX_VALID_PN, -+ ctx->valid_ctx_bmap); ++ ctx->pn_valid = 1;  + }  +  + ba_attr = ul_tb[NL80211_SMD_CTX_UL_ATTR_BA_PARAMS]; -+ if (ba_attr && -+ nl80211_parse_smd_ctx_ba_params(ba_attr, ctx->ul.ba, -+ ctx->valid_ctx_bmap)) { -+ GENL_SET_ERR_MSG(info, -+ "failed to parse UL BlockAck params"); -+ goto err_free_ctx; ++ if (ba_attr) { ++ if (nl80211_parse_smd_ctx_ba_params(ba_attr, ++ ctx->ul.ba)) { ++ GENL_SET_ERR_MSG(info, ++ "failed to parse UL BlockAck params"); ++ goto err_free_ctx; ++ } ++ ++ ctx->ba_params_valid = 1; ++ } ++ ++ if (ul_tb[NL80211_SMD_CTX_UL_ATTR_MGMT_SN]) { ++ ctx->ul.mgmt_sn = ++ nla_get_u16(ul_tb[NL80211_SMD_CTX_UL_ATTR_MGMT_SN]); ++ ctx->mgmt_tid_valid = 1; ++ ctx->ul_sn_valid = 1;  + } -+ }  + -+ /* Driver data */ -+ if (tb[NL80211_SMD_CTX_ATTR_DRV_DATA]) { -+ ctx->drv_ctx_size = drv_ctx_size; -+ memcpy(ctx->drv_ctx, -+ nla_data(tb[NL80211_SMD_CTX_ATTR_DRV_DATA]), -+ drv_ctx_size); ++ if (ul_tb[NL80211_SMD_CTX_UL_ATTR_MGMT_PN]) { ++ u8 pn_len = nla_len(ul_tb[NL80211_SMD_CTX_UL_ATTR_MGMT_PN]); ++ ++ if (pn_len > IEEE80211_SMD_CTX_MAX_PN_LEN || ++ (ctx->pn_len && pn_len != ctx->pn_len)) { ++ GENL_SET_ERR_MSG(info, "invalid UL mgmt PN length"); ++ goto err_free_ctx; ++ } ++ memcpy(ctx->ul.mgmt_pn, ++ nla_data(ul_tb[NL80211_SMD_CTX_UL_ATTR_MGMT_PN]), ++ pn_len); ++ ctx->mgmt_tid_valid = 1; ++ ctx->pn_valid = 1; ++ }  + }  +  + return 0; @@ net/wireless/nl80211.c: static int nl80211_set_sar_specs(struct sk_buff *skb, st ((__sel) == (value)) ? NL80211_IFL_SEL_##name : int __missing_selector(void);  @@ net/wireless/nl80211.c: static const struct genl_small_ops nl80211_small_ops[] = { - .flags = GENL_ADMIN_PERM, - .internal_flags = IFLAGS(NL80211_FLAG_NEED_WDEV_UP), + .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP | + NL80211_FLAG_NO_WIPHY_MTX), },  + {  + .cmd = NL80211_CMD_SET_SMD_CTX, @@ net/wireless/trace.h: TRACE_EVENT(cfg80211_nan_channel_evac,  + WDEV_ENTRY  + MAC_ENTRY(sta_addr)  + __field(u8, st_type) -+ __field(unsigned int, num_tids) -+ __dynamic_array(unsigned long, tx_tid_bitmap, -+ BITS_TO_LONGS(IEEE80211_SMD_CTX_NUM_TIDS)) -+ __dynamic_array(unsigned long, rx_tid_bitmap, -+ BITS_TO_LONGS(IEEE80211_SMD_CTX_NUM_TIDS)) ++ __field(u8, dl_tid_bitmap) ++ __field(u8, ul_tid_bitmap)  + ),  +  + TP_fast_assign(  + struct ieee80211_smd_ctx *ctx = st_info->ctx; -+ unsigned long sz;  +  + WIPHY_ASSIGN;  + WDEV_ASSIGN;  + MAC_ASSIGN(sta_addr, addr);  + __entry->st_type = st_info->type; -+ -+ __entry->num_tids = IEEE80211_SMD_CTX_NUM_TIDS; -+ sz = BITS_TO_LONGS(__entry->num_tids) * sizeof(unsigned long); -+ -+ memcpy(__get_dynamic_array(tx_tid_bitmap), -+ ctx->dl.valid_tid_bmap, sz); -+ memcpy(__get_dynamic_array(rx_tid_bitmap), -+ ctx->ul.valid_tid_bmap, sz); ++ __entry->dl_tid_bitmap = ctx->dl.valid_tid_bmap; ++ __entry->ul_tid_bitmap = ctx->ul.valid_tid_bmap;  + ),  +  + TP_printk(WIPHY_PR_FMT ", " WDEV_PR_FMT -+ ", sta=%pM, ST type=%u DL TIDs: %*pb, UL TIDs: %*pb", ++ ", sta=%pM, ST type=%u DL TIDs: 0x%x, UL TIDs: 0x%x",  + WIPHY_PR_ARG, WDEV_PR_ARG, __entry->sta_addr,  + __entry->st_type, -+ __entry->num_tids, -+ (unsigned long *)__get_dynamic_array(tx_tid_bitmap), -+ __entry->num_tids, -+ (unsigned long *)__get_dynamic_array(rx_tid_bitmap)) ++ __entry->dl_tid_bitmap, __entry->ul_tid_bitmap ++ )  +);  + #endif /* !__RDEV_OPS_TRACE || TRACE_HEADER_MULTI_READ */ 15: c48102db71f3 ! 10: 6d619b777c2d wifi: nl80211/cfg80211: Add support for querying SMD context for target AP MLD @@ Metadata Author: Pooventhiran G   ## Commit message ## - wifi: nl80211/cfg80211: Add support for querying SMD context for target AP MLD + wifi: nl80211/cfg80211: add support for querying SMD context for target AP MLD  During SMD BSS Transition, the non-AP MLD that has been prepared with a target AP MLD may send the ST Execution frame directly to the target AP @@ include/net/cfg80211.h: struct cfg80211_smd_transition_info {  + * struct cfg80211_smd_get_ctx_pending - pending async GET_SMD_CTX request  + * @list: linkage on wireless_dev::smd_get_ctx_pending_list  + * @sta_addr: non-AP MLD address for which context was requested ++ * @dev_tracker: tracker object for the netdev referenced  + */  +struct cfg80211_smd_get_ctx_pending {  + struct list_head list;  + u8 sta_addr[ETH_ALEN]; ++ netdevice_tracker dev_tracker;  +};  + /** @@ include/net/cfg80211.h: struct wireless_dev { u32 radio_mask; };  -@@ include/net/cfg80211.h: void cfg80211_incumbent_signal_notify(struct wiphy *wiphy, - u32 signal_interference_bitmap, - gfp_t gfp); +@@ include/net/cfg80211.h: struct ieee80211_smd_ctx { + } ul; + };   +/**  + * cfg80211_free_pending_smd_ctx_req - Free remaining entries in the @@ net/wireless/core.c: void wiphy_rfkill_set_hw_state_reason(struct wiphy *wiphy,  + list) {  + if (!addr || ether_addr_equal(pending->sta_addr, addr)) {  + list_del(&pending->list); ++ netdev_put(wdev->netdev, &pending->dev_tracker);  + kfree(pending); -+ dev_put(wdev->netdev);  + }  + }  + spin_unlock_bh(&wdev->smd_get_ctx_lock); @@ net/wireless/mlme.c: void cfg80211_mlo_reconf_add_done(struct net_device *dev,  +  + nl80211_notify_get_smd_ctx_done(wdev, sta_addr, st_info);  + -+ dev_put(wdev->netdev); ++ netdev_put(wdev->netdev, &pending->dev_tracker);  + kfree(pending);  +}  +EXPORT_SYMBOL(cfg80211_get_smd_ctx_done);  ## net/wireless/nl80211.c ## -@@ net/wireless/nl80211.c: static size_t _nl80211_smd_ctx_nl_ba_param_size(u8 n_tids) +@@ net/wireless/nl80211.c: static size_t _nl80211_smd_ctx_nl_ba_param_size(u8 n_tids, bool dl) return n; }  @@ net/wireless/nl80211.c: static int nl80211_set_smd_ctx(struct sk_buff *skb, stru  + * race. Upon success, the reference is released while reporting back  + * the context or in the list removal and drain path.  + */ -+ dev_hold(wdev->netdev); ++ netdev_hold(wdev->netdev, &pending->dev_tracker, GFP_KERNEL);  +  + spin_lock_bh(&wdev->smd_get_ctx_lock);  + list_add_tail(&pending->list, &wdev->smd_get_ctx_pending_list); @@ net/wireless/nl80211.c: static int nl80211_set_smd_ctx(struct sk_buff *skb, stru  + pending = __cfg80211_get_smd_sta_pending_ctx(wdev, addr);  + if (pending) {  + list_del(&pending->list); ++ netdev_put(wdev->netdev, &pending->dev_tracker);  + kfree(pending); -+ dev_put(wdev->netdev);  + }  + spin_unlock_bh(&wdev->smd_get_ctx_lock);  + return err; @@ net/wireless/rdev-ops.h: static inline int rdev_set_smd_ctx(struct cfg80211_regi  ## net/wireless/trace.h ##  @@ net/wireless/trace.h: TRACE_EVENT(rdev_set_smd_ctx, - (unsigned long *)__get_dynamic_array(rx_tid_bitmap)) + ) );   +TRACE_EVENT(rdev_get_smd_ctx,  -: ------------ > 11: 6036cdd97b4f wifi: nl80211/cfg80211: define API to notify SMD Drain completion  -: ------------ > 12: ae5cfe315173 wifi: mac80211: set SMD parameters for AP setup and STA update  6: b6a345fb53db ! 13: b839d6390f4a wifi: nl80211/mac80211: Add SMD BSS Transition sub-state STA flags @@ Metadata Author: Pooventhiran G   ## Commit message ## - wifi: nl80211/mac80211: Add SMD BSS Transition sub-state STA flags + wifi: mac80211: apply SMD BSS Transition STA sub-states  - 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 and internal STA flags to track - the SMD BSS Transition sub-states (4a, 4b, or 4c) in mac80211: - - NL80211_STA_FLAG_SMD_PREP_TARGET (WLAN_STA_SMD_PREP_TARGET): - State 4a: non-AP MLD is prepared with target AP MLD - - NL80211_STA_FLAG_SMD_EXEC_CURRENT (WLAN_STA_SMD_EXEC_CURRENT): - State 4b: ST execution active on current AP MLD - - NL80211_STA_FLAG_SMD_DL_DRAIN (WLAN_STA_SMD_DL_DRAIN): - State 4c: DL draining period + During SMD BSS Transition, the non-AP STA MLD roaming from the current + AP MLD to the target AP MLD switches its new SMD sub-states. Capture + these state transitions to track the station roaming.  These are transient sub-states of WLAN_STA_AUTHORIZED with the SMD-ME. The station remains authenticated, associated, and RSNA-established @@ Commit message  Signed-off-by: Pooventhiran G   - ## include/uapi/linux/nl80211.h ## -@@ include/uapi/linux/nl80211.h: enum nl80211_iftype { - * previously added station into associated state - * @NL80211_STA_FLAG_SPP_AMSDU: station supports SPP A-MSDUs - * @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 - */ -@@ include/uapi/linux/nl80211.h: enum nl80211_sta_flags { - NL80211_STA_FLAG_ASSOCIATED, - NL80211_STA_FLAG_SPP_AMSDU, - 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, - ## net/mac80211/cfg.c ##  @@ net/mac80211/cfg.c: static int sta_link_apply_parameters(struct ieee80211_local *local, return 0; @@ net/mac80211/sta_info.h: enum ieee80211_sta_info_flags {  NUM_WLAN_STA_FLAGS, }; - - ## net/wireless/nl80211.c ## -@@ net/wireless/nl80211.c: 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, -@@ net/wireless/nl80211.c: 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) | -@@ net/wireless/nl80211.c: 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 != 9); -+ BUILD_BUG_ON(NL80211_STA_FLAG_MAX != 12); -  - switch (statype) { - case CFG80211_STA_MESH_PEER_KERNEL: -@@ net/wireless/nl80211.c: int cfg80211_check_station_change(struct wiphy *wiphy, - BIT(NL80211_STA_FLAG_WME) | - BIT(NL80211_STA_FLAG_MFP) | - BIT(NL80211_STA_FLAG_SPP_AMSDU) | -- 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 */ -@@ net/wireless/nl80211.c: 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 != 9); -+ BUILD_BUG_ON(NL80211_STA_FLAG_MAX != 12); -  - switch (wdev->iftype) { - case NL80211_IFTYPE_AP:  7: b75b9bf05bca ! 14: 493acdcde955 wifi: mac80211: Add driver_op for SMD substate changes @@ Metadata Author: Pooventhiran G   ## Commit message ## - wifi: mac80211: Add driver_op for SMD substate changes + wifi: mac80211: add driver_op for SMD substate changes  During SMD BSS Transition, a non-AP MLD moves through sub-states 4a, 4b, and 4c that impose different frame-filtering rules on the AP MLD. @@ net/mac80211/cfg.c: static int ieee80211_add_station(struct wiphy *wiphy, struct   ## net/mac80211/driver-ops.c ## -@@ net/mac80211/driver-ops.c: int drv_change_sta_links(struct ieee80211_local *local, +@@ net/mac80211/driver-ops.c: int drv_link_sta_statistics(struct ieee80211_local *local,  return 0; }  -: ------------ > 15: 1351cb3f0c6d wifi: mac80211: filter uplink and downlink frames based on SMD states  8: f66d1129e71d ! 16: 67a7933f8897 wifi: mac80211: Send BlockAck policy in AMPDU action @@ Metadata Author: Pooventhiran G   ## Commit message ## - wifi: mac80211: Send BlockAck policy in AMPDU action + wifi: mac80211: send BlockAck policy in AMPDU action  While sending AMPDU action hints to drivers, mac80211 does not include the BlockAck policy extracted from ADDBA frames. Drivers may require the 11: 07dd3bd1e229 ! 17: 4b4b81a4f9e4 wifi: cfg80211/mac80211: Handle UHR Link Reconfiguration frame @@ Metadata Author: Pooventhiran G   ## Commit message ## - wifi: cfg80211/mac80211: Handle UHR Link Reconfiguration frame + wifi: mac80211: pass rx_data directly from ieee80211_rx_list()  - A UHR Link Reconfiguration Request frame (ST Preparation or Execution) - triggers SMD BSS Transition on the current AP MLD. Userspace needs - reporting of the STA's dynamic context along with such frames so that - the same can be transported to the target AP MLD for setting up the STA - TX and RX queues. - - Reserve a field in ieee80211_rx_status that enables drivers to attach - the STA's dynamic context to the corresponding frame. Since the maximum - possible context can grow too big, attach the pointer to the context to - the frame. Add handling for UHR ST Preparation and Execution Request - frames so that the associated context is propagated through cfg80211 and - nl80211 for userspace reporting. + rx_data is currently defined inside __ieee80211_rx_handle_packet() and + is passed to the rx handlers called inside. Define it early in + ieee80211_rx_list() so that new transient data that live just between + the driver and mac80211 interface layer can be passed to the rx handlers + as well.  Signed-off-by: Pooventhiran G   - ## include/linux/ieee80211-uhr.h ## -@@ include/linux/ieee80211-uhr.h: ieee80211_uhr_mode_change_tuple_size(const struct ieee80211_uhr_mode_change_tupl - IEEE80211_UHR_MODE_CHANGE_CONTROL_MODE_LENGTH); - } -  -+/** -+ * ieee80211_is_uhr_link_reconf_req - check if frame is UHR Link Reconf Request -+ * @skb: the SKB to check -+ * Return: whether or not the frame is a UHR Link Reconf Request frame -+ */ -+static inline bool ieee80211_is_uhr_link_reconf_req(struct sk_buff *skb) -+{ -+ struct ieee80211_mgmt *mgmt = (void *)skb->data; -+ u8 category, action; -+ -+ if (!ieee80211_is_action(mgmt->frame_control)) -+ return false; -+ -+ if (skb->len < IEEE80211_MIN_ACTION_SIZE(uhr_link_reconf_req)) -+ return false; -+ -+ category = mgmt->u.action.category; -+ action = mgmt->u.action.action_code; -+ -+ return category == WLAN_CATEGORY_PROTECTED_UHR && -+ action == IEEE80211_PROTECTED_UHR_ACTION_LINK_RECONFIG_REQUEST; -+} -+ -+/** -+ * ieee80211_is_st_prep_req - check if frame is ST Preparation Request -+ * @skb: the SKB to check -+ * Return: whether or not the frame is an ST Prep request frame -+ */ -+static inline bool ieee80211_is_st_prep_req(struct sk_buff *skb) -+{ -+ struct ieee80211_mgmt *mgmt = (void *)skb->data; -+ u8 type; -+ -+ if (!ieee80211_is_uhr_link_reconf_req(skb)) -+ return false; -+ -+ type = mgmt->u.action.uhr_link_reconf_req.type; -+ -+ return type == IEEE80211_UHR_LINK_RECONFIG_REQUEST_ST_PREP; -+} -+ -+/** -+ * ieee80211_is_st_exec_req - check if frame is ST Execution Request -+ * @skb: the SKB to check -+ * Return: whether or not the frame is an ST Exec request frame -+ */ -+static inline bool ieee80211_is_st_exec_req(struct sk_buff *skb) -+{ -+ struct ieee80211_mgmt *mgmt = (void *)skb->data; -+ u8 type; -+ -+ if (!ieee80211_is_uhr_link_reconf_req(skb)) -+ return false; -+ -+ type = mgmt->u.action.uhr_link_reconf_req.type; -+ -+ return type == IEEE80211_UHR_LINK_RECONFIG_REQUEST_ST_EXEC; -+} -+ - #define for_each_uhr_mode_change_tuple(data, len, tuple) \ - for (tuple = (const void *)(data); \ - (len) - ((const u8 *)tuple - (data)) >= sizeof(*tuple) && \ - - ## include/net/cfg80211.h ## -@@ include/net/cfg80211.h: struct mgmt_frame_regs { - u32 global_mcast_stypes, interface_mcast_stypes; - }; -  -+/** -+ * struct cfg80211_smd_transition_info - SMD BSS Transition info -+ * -+ * @ctx: Dynamic context to be transferred as part of ST -+ * @type: Type of ST indication -+ */ -+struct cfg80211_smd_transition_info { -+ struct ieee80211_smd_ctx *ctx; -+ enum nl80211_smd_ctx_type type; -+}; -+ - /** - * struct cfg80211_ops - backend description for wireless configuration - * -@@ include/net/cfg80211.h: void cfg80211_conn_failed(struct net_device *dev, const u8 *mac_addr, - * @rx_tstamp: Hardware timestamp of frame RX in nanoseconds - * @ack_tstamp: Hardware timestamp of ack TX in nanoseconds - * @no_sta: set if no station is known for the frame (relevant for MLD) -+ * @st_info: SMD BSS Transition data - */ - struct cfg80211_rx_info { - int freq; -@@ include/net/cfg80211.h: struct cfg80211_rx_info { - u64 rx_tstamp; - u64 ack_tstamp; - bool no_sta; -+ struct cfg80211_smd_transition_info st_info; - }; -  - /** - - ## include/net/mac80211.h ## -@@ include/net/mac80211.h: enum mac80211_rx_encoding { - * @ack_tx_hwtstamp: Hardware timestamp for the ack TX in nanoseconds. Only - * needed for Timing measurement and Fine timing measurement action frames. - * Only reported by devices that have timestamping enabled. -+ * @smd_ctx: Pointer to IEEE P802.11bn SMD BSS Transition context information. -+ * Only needed for ST Preparation Request and ST Execution Request action -+ * frames. The pointer will be consumed by mac80211; must be kmalloc-ed. -+ * Indicated by @smd_ctx_valid. - * @device_timestamp: arbitrary timestamp for the device, mac80211 doesn't use - * it but can store it and pass it back to the driver for synchronisation - * @band: the active band when this frame was received -@@ include/net/mac80211.h: enum mac80211_rx_encoding { - * @link_id: id of the link used to receive the packet. Set and used by - * mac80211 internally, it uses @freq set by the driver to identify the - * correct link per vif. -+ * @smd_ctx_valid: if @smd_ctx has a valid pointer to the ST context. This flag -+ * is used only for ST Preparation or ST Execution Request frames. - */ - struct ieee80211_rx_status { - u64 mactime; - union { - u64 boottime_ns; - ktime_t ack_tx_hwtstamp; -+ struct ieee80211_smd_ctx *smd_ctx; - }; - u32 device_timestamp; - u32 ampdu_reference; -@@ include/net/mac80211.h: struct ieee80211_rx_status { - u8 chains; - s8 chain_signal[IEEE80211_MAX_CHAINS]; - u8 zero_length_psdu_type; -- u8 link_id:4; -+ u8 link_id:4, -+ smd_ctx_valid:1; - }; -  - static_assert(sizeof(struct ieee80211_rx_status) <= sizeof_field(struct sk_buff, cb)); - - ## net/mac80211/ieee80211_i.h ## -@@ net/mac80211/ieee80211_i.h: struct ieee80211_rx_data { - }; -  - u8 link_addrs[3 * ETH_ALEN]; -+ -+ struct ieee80211_smd_ctx *smd_ctx; - }; -  - struct ieee80211_csa_settings { - ## net/mac80211/rx.c ## -@@ net/mac80211/rx.c: ieee80211_rx_h_action(struct ieee80211_rx_data *rx) - return RX_QUEUED; - } -  -+static void -+ieee80211_rx_h_userspace_mgmt_st_req_frame(struct cfg80211_rx_info *info, -+ struct ieee80211_rx_data *rx) -+{ -+ struct ieee80211_mgmt *mgmt = (void *)info->buf; -+ u8 type; -+ -+ if (!rx->smd_ctx) -+ return; -+ -+ type = mgmt->u.action.uhr_link_reconf_req.type; -+ -+ info->st_info.type = type; -+ info->st_info.ctx = rx->smd_ctx; -+} -+ - static ieee80211_rx_result debug_noinline - ieee80211_rx_h_userspace_mgmt(struct ieee80211_rx_data *rx) - { -@@ net/mac80211/rx.c: ieee80211_rx_h_userspace_mgmt(struct ieee80211_rx_data *rx) - .link_id = rx->link_id, - .have_link_id = rx->link_id >= 0, - .no_sta = !rx->sta, -+ .st_info.ctx = NULL, - }; -  - /* skip known-bad action frames and return them in the next handler */ -@@ net/mac80211/rx.c: ieee80211_rx_h_userspace_mgmt(struct ieee80211_rx_data *rx) - ieee80211_is_ftm(rx->skb)) { - info.rx_tstamp = ktime_to_ns(skb_hwtstamps(rx->skb)->hwtstamp); - info.ack_tstamp = ktime_to_ns(status->ack_tx_hwtstamp); -+ } else if (ieee80211_is_st_prep_req(rx->skb) || -+ ieee80211_is_st_exec_req(rx->skb)) { -+ ieee80211_rx_h_userspace_mgmt_st_req_frame(&info, rx); - } -  - if (cfg80211_rx_mgmt_ext(&rx->sdata->wdev, &info)) { -@@ net/mac80211/rx.c: static bool ieee80211_rx_valid_freq(int freq, struct ieee80211_link_data *link) +@@ net/mac80211/rx.c: static bool ieee80211_rx_valid_freq(struct ieee80211_rx_status *status, static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw, struct ieee80211_link_sta *link_pubsta, struct sk_buff *skb, @@ net/mac80211/rx.c: static void __ieee80211_rx_handle_packet(struct ieee80211_hw goto out;  @@ net/mac80211/rx.c: static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw, if (link_sta && link && - ieee80211_rx_valid_freq(status->freq, link)) { + ieee80211_rx_valid_freq(status, link)) { if (rx_data_pending) {  - ieee80211_prepare_and_rx_handle(&rx, skb, false);  + ieee80211_prepare_and_rx_handle(rx, skb, false); @@ net/mac80211/rx.c: static void __ieee80211_rx_handle_packet(struct ieee80211_hw  - rx.sdata = sdata;  - rx.local = sdata->local;  - rx.link = link; -- rx.link_id = link->link_id; +- rx.link_id = link != &sdata->deflink ? link->link_id : -1;  - rx.sta = NULL;  - rx.link_sta = NULL;  + rx->sdata = sdata;  + rx->local = sdata->local;  + rx->link = link; -+ rx->link_id = link->link_id; ++ rx->link_id = link != &sdata->deflink ? link->link_id : -1;  + rx->sta = NULL;  + rx->link_sta = NULL;  @@ net/mac80211/rx.c: void ieee80211_rx_list(struct ieee80211_hw *hw, struct ieee80211_supported_band *sband; struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; -+ struct ieee80211_smd_ctx *smd_ctx = NULL;  + struct ieee80211_rx_data rx = {}; -+ -+ /* cache the pointer to free it later */ -+ if (status->smd_ctx_valid) { -+ smd_ctx = status->smd_ctx; -+ status->smd_ctx = NULL; -+ status->smd_ctx_valid = false; -+ }  WARN_ON_ONCE(softirq_count() == 0);  -@@ net/mac80211/rx.c: void ieee80211_rx_list(struct ieee80211_hw *hw, -  - kcov_remote_start_common(skb_get_kcov_handle(skb)); -  -+ rx.smd_ctx = smd_ctx; -+ -+ if (WARN_ONCE((status->flag & RX_FLAG_8023) && rx.smd_ctx, -+ "802.3 packet but with IEEE P802.11bn SMD context")) -+ goto drop; -+ - /* - * Frames with failed FCS/PLCP checksum are not returned, - * all other frames are returned without radiotap header  @@ net/mac80211/rx.c: void ieee80211_rx_list(struct ieee80211_hw *hw, __ieee80211_rx_handle_8023(hw, link_pubsta, skb, list); else @@ net/mac80211/rx.c: void ieee80211_rx_list(struct ieee80211_hw *hw,  - list);  + list, &rx); } -+ kfree(smd_ctx);  kcov_remote_stop(); - return; - drop: -+ kfree(smd_ctx); - kfree_skb(skb); - } - EXPORT_SYMBOL(ieee80211_rx_list);  -: ------------ > 18: 1f1ec66eaf77 wifi: mac80211: handle UHR Link Reconfiguration frame 14: 1cba72a0fdfc ! 19: 96682e263f53 wifi: mac80211: Add mac80211 support to handle NL80211_CMD_SET_SMD_CTX @@ Metadata Author: Pooventhiran G   ## Commit message ## - wifi: mac80211: Add mac80211 support to handle NL80211_CMD_SET_SMD_CTX + wifi: mac80211: add mac80211 support to handle NL80211_CMD_SET_SMD_CTX  cfg80211 supports NL80211_CMD_SET_SMD_CTX on the target AP MLD for userspace to program the station's dynamic context on the TX and RX @@ include/net/mac80211.h: struct ieee80211_ops { /**  ## net/mac80211/cfg.c ## -@@ net/mac80211/cfg.c: ieee80211_set_peer_nan_sched(struct wiphy *wiphy, - return ieee80211_nan_set_peer_sched(sdata, sched); +@@ net/mac80211/cfg.c: ieee80211_set_nan_non_evac_channels(struct wiphy *wiphy, + return ieee80211_nan_set_non_evac_channels(sdata, channels); }   +static int ieee80211_set_smd_ctx(struct wiphy *wiphy, struct wireless_dev *wdev, @@ net/mac80211/cfg.c: ieee80211_set_peer_nan_sched(struct wiphy *wiphy,  + sta = sta_info_get_bss(sdata, addr);  + if (!sta)  + return -ENOENT; -+ if (!sta->sta.smd_params.smd_sta) ++ if (!sta->sta.smd_params.smd_sta || ++ sta->smd_state != IEEE80211_STA_SMD_PREP) ++ return -EINVAL; ++ ++ if (!wiphy_dereference(wiphy, sta->ptk[sta->ptk_idx]))  + return -EINVAL;  +  + return drv_set_smd_ctx(local, sdata, &sta->sta, st_info); @@ net/mac80211/cfg.c: ieee80211_set_peer_nan_sched(struct wiphy *wiphy, .add_virtual_intf = ieee80211_add_iface, .del_virtual_intf = ieee80211_del_iface,  @@ net/mac80211/cfg.c: const struct cfg80211_ops mac80211_config_ops = { - .set_epcs = ieee80211_set_epcs, .nan_set_local_sched = ieee80211_set_local_nan_sched, .nan_set_peer_sched = ieee80211_set_peer_nan_sched, + .nan_set_non_evac_channels = ieee80211_set_nan_non_evac_channels,  + .set_smd_ctx = ieee80211_set_smd_ctx, };  @@ net/mac80211/trace.h: TRACE_EVENT(drv_sta_smd_state,  + VIF_ENTRY  + STA_ENTRY  + __field(u8, st_type) -+ __field(unsigned int, num_tids) -+ __dynamic_array(unsigned long, dl_tid_bitmap, -+ BITS_TO_LONGS(IEEE80211_SMD_CTX_NUM_TIDS)) -+ __dynamic_array(unsigned long, ul_tid_bitmap, -+ BITS_TO_LONGS(IEEE80211_SMD_CTX_NUM_TIDS)) ++ __field(u8, dl_tid_bitmap) ++ __field(u8, ul_tid_bitmap)  + ),  +  + TP_fast_assign(  + struct ieee80211_smd_ctx *ctx = st_info->ctx; -+ unsigned long sz;  +  + LOCAL_ASSIGN;  + VIF_ASSIGN;  + STA_ASSIGN;  + __entry->st_type = st_info->type; -+ -+ __entry->num_tids = IEEE80211_SMD_CTX_NUM_TIDS; -+ sz = BITS_TO_LONGS(__entry->num_tids) * sizeof(unsigned long); -+ -+ memcpy(__get_dynamic_array(dl_tid_bitmap), -+ ctx->dl.valid_tid_bmap, sz); -+ memcpy(__get_dynamic_array(ul_tid_bitmap), -+ ctx->ul.valid_tid_bmap, sz); ++ __entry->dl_tid_bitmap = ctx->dl.valid_tid_bmap; ++ __entry->ul_tid_bitmap = ctx->ul.valid_tid_bmap;  + ),  +  + TP_printk(LOCAL_PR_FMT VIF_PR_FMT STA_PR_FMT -+ ", ST type=%u DL TIDs: %*pb, UL TIDs: %*pb", ++ ", ST type=%u DL TIDs: 0x%x, UL TIDs: 0x%x",  + LOCAL_PR_ARG, VIF_PR_ARG, STA_PR_ARG,  + __entry->st_type, -+ __entry->num_tids, -+ (unsigned long *)__get_dynamic_array(dl_tid_bitmap), -+ __entry->num_tids, -+ (unsigned long *)__get_dynamic_array(ul_tid_bitmap) ++ __entry->dl_tid_bitmap, __entry->ul_tid_bitmap  + )  +);  + 16: 74167bb33aa7 ! 20: 802ba7c3980f wifi: mac80211: Add mac80211 support to handle NL80211_CMD_GET_SMD_CTX @@ Metadata Author: Pooventhiran G   ## Commit message ## - wifi: mac80211: Add mac80211 support to handle NL80211_CMD_GET_SMD_CTX + wifi: mac80211: add mac80211 support to handle NL80211_CMD_GET_SMD_CTX  cfg80211 supports NL80211_CMD_GET_SMD_CTX from userspace on the current AP MLD to collect the station's context on the target AP MLD's behalf; @@ net/mac80211/cfg.c: static int ieee80211_set_smd_ctx(struct wiphy *wiphy, struct .add_virtual_intf = ieee80211_add_iface, .del_virtual_intf = ieee80211_del_iface,  @@ net/mac80211/cfg.c: const struct cfg80211_ops mac80211_config_ops = { - .nan_set_local_sched = ieee80211_set_local_nan_sched, .nan_set_peer_sched = ieee80211_set_peer_nan_sched, + .nan_set_non_evac_channels = ieee80211_set_nan_non_evac_channels, .set_smd_ctx = ieee80211_set_smd_ctx,  + .get_smd_ctx = ieee80211_get_smd_ctx, };  -: ------------ > 21: 8244862521a9 wifi: mac80211: define API to invoke SMD Drain Completion event