[PATCH RESEND wireless-next 16/18] wifi: mac80211: Add mac80211 support to handle NL80211_CMD_SET_SMD_CTX
From: Pooventhiran G
Date: Tue Sep 08 2026 - 14:06:43 EST
cfg80211 accepts NL80211_CMD_SET_SMD_CTX on the target AP MLD for
userspace to be able to program the station's dynamic context on the TX
and RX queues.
Add mac80211 support to handle NL80211_CMD_SET_SMD_CTX in the driver and
lower layers so that the context is installed and the target AP MLD is
ready to continue data transfer with the non-AP MLD from where it may
have left off at the current AP MLD. This enables the seamless roaming
facilitation of the SMD BSS Transition procedure.
Signed-off-by: Pooventhiran G <pooventhiran.g@xxxxxxxxxxxxxxxx>
---
include/net/mac80211.h | 5 +++++
net/mac80211/cfg.c | 24 +++++++++++++++++++++++
net/mac80211/driver-ops.h | 24 +++++++++++++++++++++++
net/mac80211/trace.h | 49 +++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 102 insertions(+)
diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index b4b1d3db9b19..e0e13f0ed31f 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -4780,6 +4780,8 @@ enum ieee80211_sta_smd_state {
* @sta_smd_state: Notifies low level driver about IEEE 802.11bn SMD substate
* transition of a station. It must not fail while transitioning down.
* The callback can sleep.
+ * @set_smd_ctx: Set the UHR SMD context for the non-AP MLD. This is used on
+ * the target AP MLD side to program dynamic context.
*/
struct ieee80211_ops {
void (*tx)(struct ieee80211_hw *hw,
@@ -5186,6 +5188,9 @@ struct ieee80211_ops {
struct ieee80211_sta *sta,
enum ieee80211_sta_smd_state old_state,
enum ieee80211_sta_smd_state new_state);
+ int (*set_smd_ctx)(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ struct cfg80211_smd_transition_info *st_info);
};
/**
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index 0ea8b610a771..268d5f42293a 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -6081,6 +6081,29 @@ ieee80211_set_peer_nan_sched(struct wiphy *wiphy,
return ieee80211_nan_set_peer_sched(sdata, sched);
}
+static int ieee80211_set_smd_ctx(struct wiphy *wiphy, struct wireless_dev *wdev,
+ const u8 *addr,
+ struct cfg80211_smd_transition_info *st_info)
+{
+ struct ieee80211_local *local = wiphy_priv(wiphy);
+ struct ieee80211_sub_if_data *sdata;
+ struct sta_info *sta;
+
+ lockdep_assert_wiphy(wiphy);
+
+ sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
+
+ if (sdata->vif.type != NL80211_IFTYPE_AP &&
+ sdata->vif.type != NL80211_IFTYPE_AP_VLAN)
+ return -EOPNOTSUPP;
+
+ sta = sta_info_get_bss(sdata, addr);
+ if (!sta || !sta->sta.smd_params.smd_sta)
+ return -EINVAL;
+
+ return drv_set_smd_ctx(local, sdata, &sta->sta, st_info);
+}
+
const struct cfg80211_ops mac80211_config_ops = {
.add_virtual_intf = ieee80211_add_iface,
.del_virtual_intf = ieee80211_del_iface,
@@ -6199,4 +6222,5 @@ 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,
+ .set_smd_ctx = ieee80211_set_smd_ctx,
};
diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h
index ce11a723ec36..e764dd394de2 100644
--- a/net/mac80211/driver-ops.h
+++ b/net/mac80211/driver-ops.h
@@ -1820,4 +1820,28 @@ int drv_sta_smd_state(struct ieee80211_local *local,
enum ieee80211_sta_smd_state old_state,
enum ieee80211_sta_smd_state new_state);
+static inline int drv_set_smd_ctx(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_sta *sta,
+ struct cfg80211_smd_transition_info *st_info)
+{
+ int ret;
+
+ if (!local->ops->set_smd_ctx)
+ return -EOPNOTSUPP;
+
+ sdata = get_bss_sdata(sdata);
+
+ lockdep_assert_wiphy(local->hw.wiphy);
+
+ if (!check_sdata_in_driver(sdata))
+ return -EIO;
+
+ trace_drv_set_smd_ctx(local, sdata, sta, st_info);
+ ret = local->ops->set_smd_ctx(&local->hw, &sdata->vif, sta, st_info);
+ trace_drv_return_int(local, ret);
+
+ return ret;
+}
+
#endif /* __MAC80211_DRIVER_OPS */
diff --git a/net/mac80211/trace.h b/net/mac80211/trace.h
index 63838a6d5d07..7aba79e441cf 100644
--- a/net/mac80211/trace.h
+++ b/net/mac80211/trace.h
@@ -3438,6 +3438,55 @@ TRACE_EVENT(drv_sta_smd_state,
)
);
+TRACE_EVENT(drv_set_smd_ctx,
+ TP_PROTO(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_sta *sta,
+ struct cfg80211_smd_transition_info *st_info),
+
+ TP_ARGS(local, sdata, sta, st_info),
+
+ TP_STRUCT__entry(
+ LOCAL_ENTRY
+ 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))
+ ),
+
+ 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);
+ ),
+
+ TP_printk(LOCAL_PR_FMT VIF_PR_FMT STA_PR_FMT
+ ", ST type=%u DL TIDs: %*pb, UL TIDs: %*pb",
+ 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)
+ )
+);
+
#endif /* !__MAC80211_DRIVER_TRACE || TRACE_HEADER_MULTI_READ */
#undef TRACE_INCLUDE_PATH
--
2.34.1